Repository: owncloud/ocis
Pull request #8041 (2023-12-21 06:07:35)

build(deps): bump github.com/open-policy-agent/opa from 0.59.0 to 0.60.0
Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 0.59.0 to 0.60.0.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md">github.com/open-policy-agent/opa's changelog</a>.</em></p>
<blockquote>
<h2>0.60.0</h2>
<h3>Runtime, Tooling, SDK</h3>
<ul>
<li>OPA can be run in 1.0 compatibility mode by using the new <code>--v1-compatible</code> flag. When this mode is enabled, the current release of OPA will behave as OPA <code>v1.0</code> will eventually behave by default. This flag is currently supported on the <code>build</code>, <code>check</code>, <code>fmt</code>, <code>eval</code> and <code>test</code> commands (<a href="https://redirect.github.com/open-policy-agent/opa/pull/6478">#6478</a>) authored by <a href="https://github.com/johanfylling"><code>@​johanfylling</code></a></li>
<li>Extend the telemetry report to include the minimum compatible version of policies loaded into OPA (<a href="https://redirect.github.com/open-policy-agent/opa/issues/6361">#6361</a>) co-authored by <a href="https://github.com/srenatus"><code>@​srenatus</code></a> and <a href="https://github.com/ashutosh-narkar"><code>@​ashutosh-narkar</code></a></li>
<li>server: Support fsnotify based reloading of certificate, key and CA cert pool when they change on disk (<a href="https://redirect.github.com/open-policy-agent/opa/issues/5788">#5788</a>) authored by <a href="https://github.com/charlieegan3"><code>@​charlieegan3</code></a></li>
<li>Add option on the unit test runner to surface builtin errors. This should help with debugging errors generated while running unit tests (<a href="https://redirect.github.com/open-policy-agent/opa/issues/6489">#6489</a>) authored by <a href="https://github.com/jalseth"><code>@​jalseth</code></a></li>
<li>Fix issue in <code>opa fmt</code> where the assignment operator and term in the rule head of chain rules are removed from the re-written rule head  (<a href="https://redirect.github.com/open-policy-agent/opa/issues/6467">#6467</a>) authored by <a href="https://github.com/anderseknert"><code>@​anderseknert</code></a></li>
<li>cmd/fmt: Replace dependency on <code>diff</code> tool with an external golang library function (<a href="https://redirect.github.com/open-policy-agent/opa/issues/6284">#6284</a>) authored by <a href="https://github.com/colinjlacy"><code>@​colinjlacy</code></a></li>
</ul>
<h3>Topdown and Rego</h3>
<ul>
<li>topdown/providers: Preserve user provided http headers in the <code>providers.aws.sign_req</code> builtin command (<a href="https://redirect.github.com/open-policy-agent/opa/pull/6456">#6456</a>) authored by <a href="https://github.com/c2zwdjnlcg"><code>@​c2zwdjnlcg</code></a></li>
<li>rego: Allow custom builtin function registration to provide a description for the builtin (<a href="https://redirect.github.com/open-policy-agent/opa/issues/6449">#6449</a>) authored by <a href="https://github.com/lcarva"><code>@​lcarva</code></a></li>
<li>ast+cmd: Allow bundle to contain calls to unknown functions when inspected (<a href="https://redirect.github.com/open-policy-agent/opa/issues/6457">#6457</a>) authored by <a href="https://github.com/johanfylling"><code>@​johanfylling</code></a></li>
</ul>
<h3>Docs</h3>
<ul>
<li>Add section on the changes proposed for a future OPA v1.0 and update Rego examples to be OPA v1.0 compliant(<a href="https://redirect.github.com/open-policy-agent/opa/issues/6453">#6453</a>) authored by <a href="https://github.com/johanfylling"><code>@​johanfylling</code></a></li>
<li>Clarify behavior of the <code>sprintf</code> builtin command when used with the <code>%T</code> marker (<a href="https://redirect.github.com/open-policy-agent/opa/issues/6487">#6487</a>) authored by <a href="https://github.com/lcarva"><code>@​lcarva</code></a></li>
</ul>
<h3>Website + Ecosystem</h3>
<ul>
<li>Ecosystem: Digger (<a href="https://redirect.github.com/open-policy-agent/opa/pull/6464">#6464</a>) authored by <a href="https://github.com/anderseknert"><code>@​anderseknert</code></a></li>
</ul>
<h3>Miscellaneous</h3>
<ul>
<li>Update <code>Makefile</code> to allow custom <code>GOFLAGS</code> to be provided to the golang executable (<a href="https://redirect.github.com/open-policy-agent/opa/issues/6458">#6458</a>) authored by <a href="https://github.com/cova-fe"><code>@​cova-fe</code></a></li>
<li>Dependency updates; notably:
<ul>
<li>bump golang 1.21.4 -&gt; 1.21.5 (<a href="https://redirect.github.com/open-policy-agent/opa/pull/6460">#6460</a>) authored by <a href="https://github.com/srenatus"><code>@​srenatus</code></a></li>
<li>bump aquasecurity/trivy-action from 0.14.0 to 0.16.0</li>
<li>bump github.com/containerd/containerd from 1.7.9 to 1.7.11</li>
<li>bump google.golang.org/grpc from 1.59.0 to 1.60.1</li>
<li>bump github.com/google/uuid from 1.4.0 to 1.5.0</li>
</ul>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/open-policy-agent/opa/commit/a1a2ae3cbb5fdddf306b1ef67ca5a856fd94fa15"><code>a1a2ae3</code></a> Prepare v0.60.0 release</li>
<li><a href="https://github.com/open-policy-agent/opa/commit/9f63fdb0417f39776f3536f7b93122a55a56f092"><code>9f63fdb</code></a> ast+cmd: Allowing bundle to contain calls to unknown functions when inspected...</li>
<li><a href="https://github.com/open-policy-agent/opa/commit/f04636adc273929c6eba1b92bc5c784ad89a865d"><code>f04636a</code></a> docs: Update generated CLI docs</li>
<li><a href="https://github.com/open-policy-agent/opa/commit/38c2f0c5e0d908c6454b96516816098665c6885c"><code>38c2f0c</code></a> Adding <code>--v1-compatible</code> flag to <code>build</code>, <code>opa eval</code> (<a href="https://redirect.github.com/open-policy-agent/opa/issues/6478">#6478</a>)</li>
<li><a href="https://github.com/open-policy-agent/opa/commit/84751f083631290f1cf45d65cab113cb330ae161"><code>84751f0</code></a> build(deps): bump google.golang.org/grpc from 1.60.0 to 1.60.1</li>
<li><a href="https://github.com/open-policy-agent/opa/commit/979e6265950fe33c5ef4b3988a247e845d0c5da2"><code>979e626</code></a> internal/presentation: cleanup location handling (<a href="https://redirect.github.com/open-policy-agent/opa/issues/6498">#6498</a>)</li>
<li><a href="https://github.com/open-policy-agent/opa/commit/b85eb642a5afb771e9933dbba403ee3f64ed041e"><code>b85eb64</code></a> .github/workflows: Update name of the checkout action token</li>
<li><a href="https://github.com/open-policy-agent/opa/commit/24f09cd0785c485c2d1cf785bf976de5f0dbc6f0"><code>24f09cd</code></a> .github/workflows: Update checkout action token</li>
<li><a href="https://github.com/open-policy-agent/opa/commit/ba4611227ead5405bd3682826f5f7df35a351cc3"><code>ba46112</code></a> Docs: Explain %T behavior of sprintf</li>
<li><a href="https://github.com/open-policy-agent/opa/commit/6d26f98a336f24fea1c0892522c9d71b7bd79941"><code>6d26f98</code></a> feat: Support raising builtin errors in the test runner</li>
<li>Additional commits viewable in <a href="https://github.com/open-policy-agent/opa/compare/v0.59.0...v0.60.0">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/open-policy-agent/opa&package-manager=go_modules&previous-version=0.59.0&new-version=0.60.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

update-docs[bot]: Thanks for opening this pull request! The maintainers of this repository would appreciate it if you would create a [changelog](https://github.com/owncloud/ocis/blob/master/changelog/README.md) item based on your changes.

sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=8041) **Quality Gate passed**  
Kudos, no new issues were introduced!

[0 New issues](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=8041&resolved=false&inNewCodePeriod=true)  
[0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=8041&resolved=false&inNewCodePeriod=true)  
No data about Coverage  
No data about Duplication  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=8041)



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index de60364dd2a..99c986416a7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -42,16 +42,18 @@ The following sections list the changes for unreleased.
 
 ## Summary
 
-* Bugfix - Fix search by exact email: [#8035](https://github.com/owncloud/ocis/pull/8035)
+* Bugfix - Fix search by containing special characters: [#8050](https://github.com/owncloud/ocis/pull/8050)
 
 ## Details
 
-* Bugfix - Fix search by exact email: [#8035](https://github.com/owncloud/ocis/pull/8035)
+* Bugfix - Fix search by containing special characters: [#8050](https://github.com/owncloud/ocis/pull/8050)
 
-   Users can be searched by exact email by using double quotes on the search
-   parameter. Note that double quotes are required because the "@" char is being
-   interpreted by the parser.
+   As the OData query parser interprets characters like '@' or '-' in a special
+   way. Search request for users or groups needs to be quoted. We fixed the
+   libregraph users and groups endpoints to handle quoted search terms correctly.
 
+   https://github.com/owncloud/ocis/issues/7990
+   https://github.com/owncloud/ocis/pull/8050
    https://github.com/owncloud/ocis/pull/8035
 
 # Changelog for [5.0.0-beta.2] (2023-12-20)
diff --git a/changelog/unreleased/fix-search-by-exact-mail.md b/changelog/unreleased/fix-search-by-exact-mail.md
index 7e85130ba33..988f7faa4ee 100644
--- a/changelog/unreleased/fix-search-by-exact-mail.md
+++ b/changelog/unreleased/fix-search-by-exact-mail.md
@@ -1,7 +1,9 @@
-Bugfix: Fix search by exact email
+Bugfix: Fix search by containing special characters
 
-Users can be searched by exact email by using double quotes on the search
-parameter. Note that double quotes are required because the "@" char is
-being interpreted by the parser.
+As the OData query parser interprets characters like '@' or '-' in a special
+way. Search request for users or groups needs to be quoted. We fixed the libregraph
+users and groups endpoints to handle quoted search terms correctly.
 
+https://github.com/owncloud/ocis/pull/8050
 https://github.com/owncloud/ocis/pull/8035
+https://github.com/owncloud/ocis/issues/7990
diff --git a/go.mod b/go.mod
index 61f4f832bd1..c2f6693d0ea 100644
--- a/go.mod
+++ b/go.mod
@@ -67,7 +67,7 @@ require (
 	github.com/onsi/ginkgo v1.16.5
 	github.com/onsi/ginkgo/v2 v2.13.2
 	github.com/onsi/gomega v1.30.0
-	github.com/open-policy-agent/opa v0.59.0
+	github.com/open-policy-agent/opa v0.60.0
 	github.com/orcaman/concurrent-map v1.0.0
 	github.com/owncloud/libre-graph-api-go v1.0.5-0.20231201125350-a08244876423
 	github.com/pkg/errors v0.9.1
@@ -297,7 +297,7 @@ require (
 	github.com/russellhaering/goxmldsig v1.4.0 // indirect
 	github.com/russross/blackfriday/v2 v2.1.0 // indirect
 	github.com/segmentio/ksuid v1.0.4 // indirect
-	github.com/sergi/go-diff v1.2.0 // indirect
+	github.com/sergi/go-diff v1.3.1 // indirect
 	github.com/sethvargo/go-password v0.2.0 // indirect
 	github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect
 	github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 // indirect
diff --git a/go.sum b/go.sum
index 420e7a984b2..13220e206ce 100644
--- a/go.sum
+++ b/go.sum
@@ -1784,8 +1784,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
 github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
 github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
-github.com/open-policy-agent/opa v0.59.0 h1:1WFU/KUhJAr3qatm0Lf8Ea5jp10ZmlE2M07oaLiHypg=
-github.com/open-policy-agent/opa v0.59.0/go.mod h1:rdJSkEc4oQ+0074/3Fsgno5bkPsYxTjU5aLNmMujIvI=
+github.com/open-policy-agent/opa v0.60.0 h1:ZPoPt4yeNs5UXCpd/P/btpSyR8CR0wfhVoh9BOwgJNs=
+github.com/open-policy-agent/opa v0.60.0/go.mod h1:aD5IK6AiLNYBjNXn7E02++yC8l4Z+bRDvgM6Ss0bBzA=
 github.com/opencontainers/runtime-spec v1.1.0-rc.1 h1:wHa9jroFfKGQqFHj0I1fMRKLl0pfj+ynAqBxo3v6u9w=
 github.com/opencontainers/runtime-spec v1.1.0-rc.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
 github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
@@ -1933,8 +1933,8 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg
 github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c=
 github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE=
 github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
-github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
-github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
+github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
+github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
 github.com/sethgrid/pester v1.2.0/go.mod h1:hEUINb4RqvDxtoCaU0BNT/HV4ig5kfgOasrf1xcvr0A=
 github.com/sethvargo/go-password v0.2.0 h1:BTDl4CC/gjf/axHMaDQtw507ogrXLci6XRiLc7i/UHI=
 github.com/sethvargo/go-password v0.2.0/go.mod h1:Ym4Mr9JXLBycr02MFuVQ/0JHidNetSgbzutTr3zsYXE=
diff --git a/services/graph/pkg/identity/backend.go b/services/graph/pkg/identity/backend.go
index cc94141d3ab..08afc0d4f7e 100644
--- a/services/graph/pkg/identity/backend.go
+++ b/services/graph/pkg/identity/backend.go
@@ -37,7 +37,7 @@ type Backend interface {
 	// UpdateGroupName updates the group name
 	UpdateGroupName(ctx context.Context, groupID string, groupName string) error
 	GetGroup(ctx context.Context, nameOrID string, queryParam url.Values) (*libregraph.Group, error)
-	GetGroups(ctx context.Context, queryParam url.Values) ([]*libregraph.Group, error)
+	GetGroups(ctx context.Context, oreq *godata.GoDataRequest) ([]*libregraph.Group, error)
 	// GetGroupMembers list all members of a group
 	GetGroupMembers(ctx context.Context, id string, oreq *godata.GoDataRequest) ([]*libregraph.User, error)
 	// AddMembersToGroup adds new members (reference by a slice of IDs) to supplied group in the identity backend.
diff --git a/services/graph/pkg/identity/cs3.go b/services/graph/pkg/identity/cs3.go
index 7a305c22fb6..b704ae312b0 100644
--- a/services/graph/pkg/identity/cs3.go
+++ b/services/graph/pkg/identity/cs3.go
@@ -112,7 +112,7 @@ func (i *CS3) GetUsers(ctx context.Context, oreq *godata.GoDataRequest) ([]*libr
 }
 
 // GetGroups implements the Backend Interface.
-func (i *CS3) GetGroups(ctx context.Context, queryParam url.Values) ([]*libregraph.Group, error) {
+func (i *CS3) GetGroups(ctx context.Context, oreq *godata.GoDataRequest) ([]*libregraph.Group, error) {
 	logger := i.Logger.SubloggerWithRequestID(ctx)
 	logger.Debug().Str("backend", "cs3").Msg("GetGroups")
 	gatewayClient, err := i.GatewaySelector.Next()
@@ -121,9 +121,9 @@ func (i *CS3) GetGroups(ctx context.Context, queryParam url.Values) ([]*libregra
 		return nil, errorcode.New(errorcode.ServiceNotAvailable, err.Error())
 	}
 
-	search := queryParam.Get("search")
-	if search == "" {
-		search = queryParam.Get("$search")
+	search, err := GetSearchValues(oreq.Query)
+	if err != nil {
+		return nil, err
 	}
 
 	res, err := gatewayClient.FindGroups(ctx, &cs3group.FindGroupsRequest{
diff --git a/services/graph/pkg/identity/ldap_group.go b/services/graph/pkg/identity/ldap_group.go
index c7c66f94c4a..ed55ea00a51 100644
--- a/services/graph/pkg/identity/ldap_group.go
+++ b/services/graph/pkg/identity/ldap_group.go
@@ -55,19 +55,26 @@ func (i *LDAP) GetGroup(ctx context.Context, nameOrID string, queryParam url.Val
 }
 
 // GetGroups implements the Backend Interface for the LDAP Backend
-func (i *LDAP) GetGroups(ctx context.Context, queryParam url.Values) ([]*libregraph.Group, error) {
+func (i *LDAP) GetGroups(ctx context.Context, oreq *godata.GoDataRequest) ([]*libregraph.Group, error) {
 	logger := i.logger.SubloggerWithRequestID(ctx)
 	logger.Debug().Str("backend", "ldap").Msg("GetGroups")
 
-	search := queryParam.Get("search")
-	if search == "" {
-		search = queryParam.Get("$search")
+	search, err := GetSearchValues(oreq.Query)
+	if err != nil {
+		return nil, err
 	}
 
 	var expandMembers bool
-	sel := strings.Split(queryParam.Get("$select"), ",")
-	exp := strings.Split(queryParam.Get("$expand"), ",")
-	if slices.Contains(sel, "members") || slices.Contains(exp, "members") {
+	exp, err := GetExpandValues(oreq.Query)
+	if err != nil {
+		return nil, err
+	}
+	sel, err := GetSelectValues(oreq.Query)
+	if err != nil {
+		return nil, err
+	}
+
+	if slices.Contains(exp, "members") || slices.Contains(sel, "members") {
 		expandMembers = true
 	}
 
diff --git a/services/graph/pkg/identity/mocks/backend.go b/services/graph/pkg/identity/mocks/backend.go
index 69e876002a8..8bb281f42e1 100644
--- a/services/graph/pkg/identity/mocks/backend.go
+++ b/services/graph/pkg/identity/mocks/backend.go
@@ -165,25 +165,25 @@ func (_m *Backend) GetGroupMembers(ctx context.Context, id string, oreq *godata.
 	return r0, r1
 }
 
-// GetGroups provides a mock function with given fields: ctx, queryParam
-func (_m *Backend) GetGroups(ctx context.Context, queryParam url.Values) ([]*libregraph.Group, error) {
-	ret := _m.Called(ctx, queryParam)
+// GetGroups provides a mock function with given fields: ctx, oreq
+func (_m *Backend) GetGroups(ctx context.Context, oreq *godata.GoDataRequest) ([]*libregraph.Group, error) {
+	ret := _m.Called(ctx, oreq)
 
 	var r0 []*libregraph.Group
 	var r1 error
-	if rf, ok := ret.Get(0).(func(context.Context, url.Values) ([]*libregraph.Group, error)); ok {
-		return rf(ctx, queryParam)
+	if rf, ok := ret.Get(0).(func(context.Context, *godata.GoDataRequest) ([]*libregraph.Group, error)); ok {
+		return rf(ctx, oreq)
 	}
-	if rf, ok := ret.Get(0).(func(context.Context, url.Values) []*libregraph.Group); ok {
-		r0 = rf(ctx, queryParam)
+	if rf, ok := ret.Get(0).(func(context.Context, *godata.GoDataRequest) []*libregraph.Group); ok {
+		r0 = rf(ctx, oreq)
 	} else {
 		if ret.Get(0) != nil {
 			r0 = ret.Get(0).([]*libregraph.Group)
 		}
 	}
 
-	if rf, ok := ret.Get(1).(func(context.Context, url.Values) error); ok {
-		r1 = rf(ctx, queryParam)
+	if rf, ok := ret.Get(1).(func(context.Context, *godata.GoDataRequest) error); ok {
+		r1 = rf(ctx, oreq)
 	} else {
 		r1 = ret.Error(1)
 	}
diff --git a/services/graph/pkg/identity/odata.go b/services/graph/pkg/identity/odata.go
index ef102cd7eb4..698b84e69e8 100644
--- a/services/graph/pkg/identity/odata.go
+++ b/services/graph/pkg/identity/odata.go
@@ -29,6 +29,23 @@ func GetExpandValues(req *godata.GoDataQuery) ([]string, error) {
 	return expand, nil
 }
 
+// GetSelectValues extracts the values of the $select query parameter and
+// returns them in a []string, rejects any $select value that consists of more
+// than just a single path segment
+func GetSelectValues(req *godata.GoDataQuery) ([]string, error) {
+	if req == nil || req.Select == nil {
+		return []string{}, nil
+	}
+	sel := make([]string, 0, len(req.Select.SelectItems))
+	for _, item := range req.Select.SelectItems {
+		if len(item.Segments) > 1 {
+			return []string{}, godata.NotImplementedError("multiple segments in $select not supported")
+		}
+		sel = append(sel, item.Segments[0].Value)
+	}
+	return sel, nil
+}
+
 // GetSearchValues extracts the value of the $search query parameter and returns
 // it as a string. Rejects any search query that is more than just a simple string
 func GetSearchValues(req *godata.GoDataQuery) (string, error) {
diff --git a/services/graph/pkg/service/v0/groups.go b/services/graph/pkg/service/v0/groups.go
index f80924025ac..8f6f707a55e 100644
--- a/services/graph/pkg/service/v0/groups.go
+++ b/services/graph/pkg/service/v0/groups.go
@@ -32,7 +32,17 @@ func (g Graph) GetGroups(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 	ctxHasFullPerms := g.contextUserHasFullAccountPerms(r.Context())
-	if !ctxHasFullPerms && (odataReq.Query == nil || odataReq.Query.Search == nil || len(odataReq.Query.Search.RawValue) < g.config.API.IdentitySearchMinLength) {
+	searchHasAcceptableLength := false
+	if odataReq.Query != nil && odataReq.Query.Search != nil {
+		minSearchLength := g.config.API.IdentitySearchMinLength
+		if strings.HasPrefix(odataReq.Query.Search.RawValue, "\"") {
+			// if search starts with double quotes then it must finish with double quotes
+			// add +2 to the minimum search length in this case
+			minSearchLength += 2
+		}
+		searchHasAcceptableLength = len(odataReq.Query.Search.RawValue) >= minSearchLength
+	}
+	if !ctxHasFullPerms && !searchHasAcceptableLength {
 		// for regular user the search term must have a minimum length
 		logger.Debug().Interface("query", r.URL.Query()).Msgf("search with less than %d chars for a regular user", g.config.API.IdentitySearchMinLength)
 		errorcode.AccessDenied.Render(w, r, http.StatusForbidden, "search term too short")
@@ -46,7 +56,7 @@ func (g Graph) GetGroups(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	groups, err := g.identityBackend.GetGroups(r.Context(), r.URL.Query())
+	groups, err := g.identityBackend.GetGroups(r.Context(), odataReq)
 	if err != nil {
 		logger.Debug().Err(err).Msg("could not get groups: backend error")
 		errorcode.RenderError(w, r, err)
diff --git a/services/graph/pkg/service/v0/users.go b/services/graph/pkg/service/v0/users.go
index eec46b13d75..6902af8dda1 100644
--- a/services/graph/pkg/service/v0/users.go
+++ b/services/graph/pkg/service/v0/users.go
@@ -223,9 +223,9 @@ func (g Graph) GetUsers(w http.ResponseWriter, r *http.Request) {
 	}
 
 	ctxHasFullPerms := g.contextUserHasFullAccountPerms(r.Context())
-	minSearchLength := g.config.API.IdentitySearchMinLength
 	searchHasAcceptableLength := false
 	if odataReq.Query != nil && odataReq.Query.Search != nil {
+		minSearchLength := g.config.API.IdentitySearchMinLength
 		if strings.HasPrefix(odataReq.Query.Search.RawValue, "\"") {
 			// if search starts with double quotes then it must finish with double quotes
 			// add +2 to the minimum search length in this case
diff --git a/vendor/github.com/open-policy-agent/opa/ast/check.go b/vendor/github.com/open-policy-agent/opa/ast/check.go
index 90ed2747a0e..3723b33cc0a 100644
--- a/vendor/github.com/open-policy-agent/opa/ast/check.go
+++ b/vendor/github.com/open-policy-agent/opa/ast/check.go
@@ -24,14 +24,15 @@ type exprChecker func(*TypeEnv, *Expr) *Error
 // 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
+	builtins            map[string]*Builtin
+	required            *Capabilities
+	errs                Errors
+	exprCheckers        map[string]exprChecker
+	varRewriter         varRewriter
+	ss                  *SchemaSet
+	allowNet            []string
+	input               types.Type
+	allowUndefinedFuncs bool
 }
 
 // newTypeChecker returns a new typeChecker object that has no errors.
@@ -92,6 +93,11 @@ func (tc *typeChecker) WithInputType(tpe types.Type) *typeChecker {
 	return tc
 }
 
+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.
@@ -347,6 +353,9 @@ func (tc *typeChecker) checkExprBuiltin(env *TypeEnv, expr *Expr) *Error {
 	tpe := env.Get(name)
 
 	if tpe == nil {
+		if tc.allowUndefinedFuncs {
+			return nil
+		}
 		return NewError(TypeErr, expr.Location, "undefined function %v", name)
 	}
 
diff --git a/vendor/github.com/open-policy-agent/opa/ast/compile.go b/vendor/github.com/open-policy-agent/opa/ast/compile.go
index 67c8ef38469..d558d4d3f03 100644
--- a/vendor/github.com/open-policy-agent/opa/ast/compile.go
+++ b/vendor/github.com/open-policy-agent/opa/ast/compile.go
@@ -146,6 +146,7 @@ type Compiler struct {
 	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
 }
 
@@ -457,6 +458,11 @@ func (c *Compiler) WithUseTypeCheckAnnotations(enabled bool) *Compiler {
 	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
@@ -1513,7 +1519,8 @@ func (c *Compiler) checkTypes() {
 		WithInputType(c.inputType).
 		WithBuiltins(c.builtins).
 		WithRequiredCapabilities(c.Required).
-		WithVarRewriter(rewriteVarsInRef(c.RewrittenVars))
+		WithVarRewriter(rewriteVarsInRef(c.RewrittenVars)).
+		WithAllowUndefinedFunctionCalls(c.allowUndefinedFuncCalls)
 	var as *AnnotationSet
 	if c.useTypeCheckAnnotations {
 		as = c.annotationSet
@@ -1537,7 +1544,7 @@ func (c *Compiler) checkUnsafeBuiltins() {
 func (c *Compiler) checkDeprecatedBuiltins() {
 	for _, name := range c.sorted {
 		mod := c.Modules[name]
-		if c.strict || mod.regoV1Compatible {
+		if c.strict || mod.regoV1Compatible() {
 			errs := checkDeprecatedBuiltins(c.deprecatedBuiltinsMap, mod)
 			for _, err := range errs {
 				c.err(err)
@@ -1577,6 +1584,11 @@ func (c *Compiler) compile() {
 				continue // skip these stages
 			}
 		}
+
+		if c.allowUndefinedFuncCalls && s.name == "CheckUndefinedFuncs" {
+			continue
+		}
+
 		c.runStage(s.metricName, s.f)
 		if c.Failed() {
 			return
@@ -1683,7 +1695,7 @@ func (c *Compiler) checkDuplicateImports() {
 
 	for _, name := range c.sorted {
 		mod := c.Modules[name]
-		if c.strict || mod.regoV1Compatible {
+		if c.strict || mod.regoV1Compatible() {
 			modules = append(modules, mod)
 		}
 	}
@@ -1697,7 +1709,7 @@ func (c *Compiler) checkDuplicateImports() {
 func (c *Compiler) checkKeywordOverrides() {
 	for _, name := range c.sorted {
 		mod := c.Modules[name]
-		if c.strict || mod.regoV1Compatible {
+		if c.strict || mod.regoV1Compatible() {
 			errs := checkRootDocumentOverrides(mod)
 			for _, err := range errs {
 				c.err(err)
diff --git a/vendor/github.com/open-policy-agent/opa/ast/parser.go b/vendor/github.com/open-policy-agent/opa/ast/parser.go
index a2a30889521..25b1a4f1a86 100644
--- a/vendor/github.com/open-policy-agent/opa/ast/parser.go
+++ b/vendor/github.com/open-policy-agent/opa/ast/parser.go
@@ -27,6 +27,22 @@ import (
 
 var RegoV1CompatibleRef = Ref{VarTerm("rego"), StringTerm("v1")}
 
+// RegoVersion defines the Rego syntax requirements for a module.
+type RegoVersion int
+
+const (
+	// RegoV0 is the default, original Rego syntax.
+	RegoV0 RegoVersion = iota
+	// 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
+)
+
 // 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().
@@ -99,14 +115,27 @@ func (e *parsedTermCacheItem) String() string {
 
 // ParserOptions defines the options for parsing Rego statements.
 type ParserOptions struct {
-	Capabilities       *Capabilities
-	ProcessAnnotation  bool
-	AllFutureKeywords  bool
-	FutureKeywords     []string
-	SkipRules          bool
-	JSONOptions        *astJSON.Options
-	unreleasedKeywords bool // TODO(sr): cleanup
+	Capabilities      *Capabilities
+	ProcessAnnotation bool
+	AllFutureKeywords bool
+	FutureKeywords    []string
+	SkipRules         bool
+	JSONOptions       *astJSON.Options
+	// RegoVersion is the version of Rego to parse for.
+	// RegoV1Compatible additionally affects the Rego version. Use EffectiveRegoVersion to get the effective Rego version.
+	RegoVersion RegoVersion
+	// RegoV1Compatible is equivalent to setting RegoVersion to RegoV0CompatV1.
+	// RegoV1Compatible takes precedence, and if set to true, RegoVersion is ignored.
+	// Deprecated: use RegoVersion instead. Will be removed in a future version of OPA.
 	RegoV1Compatible   bool
+	unreleasedKeywords bool // TODO(sr): cleanup
+}
+
+func (po *ParserOptions) EffectiveRegoVersion() RegoVersion {
+	if po.RegoV1Compatible {
+		return RegoV0CompatV1
+	}
+	return po.RegoVersion
 }
 
 // NewParser creates and initializes a Parser.
@@ -189,6 +218,11 @@ func (p *Parser) WithJSONOptions(jsonOptions *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
@@ -257,16 +291,23 @@ func (p *Parser) Parse() ([]Statement, []*Comment, Errors) {
 
 	allowedFutureKeywords := map[string]tokens.Token{}
 
-	for _, kw := range p.po.Capabilities.FutureKeywords {
-		var ok bool
-		allowedFutureKeywords[kw], ok = futureKeywords[kw]
-		if !ok {
-			return nil, nil, Errors{
-				&Error{
-					Code:     ParseErr,
-					Message:  fmt.Sprintf("illegal capabilities: unknown keyword: %v", kw),
-					Location: nil,
-				},
+	if p.po.EffectiveRegoVersion() == RegoV1 {
+		// RegoV1 includes all future keywords in the default language definition
+		for k, v := range futureKeywords {
+			allowedFutureKeywords[k] = v
+		}
+	} else {
+		for _, kw := range p.po.Capabilities.FutureKeywords {
+			var ok bool
+			allowedFutureKeywords[kw], ok = futureKeywords[kw]
+			if !ok {
+				return nil, nil, Errors{
+					&Error{
+						Code:     ParseErr,
+						Message:  fmt.Sprintf("illegal capabilities: unknown keyword: %v", kw),
+						Location: nil,
+					},
+				}
 			}
 		}
 	}
@@ -284,7 +325,7 @@ func (p *Parser) Parse() ([]Statement, []*Comment, Errors) {
 	}
 
 	selected := map[string]tokens.Token{}
-	if p.po.AllFutureKeywords {
+	if p.po.AllFutureKeywords || p.po.EffectiveRegoVersion() == RegoV1 {
 		for kw, tok := range allowedFutureKeywords {
 			selected[kw] = tok
 		}
@@ -305,6 +346,12 @@ func (p *Parser) Parse() ([]Statement, []*Comment, Errors) {
 	}
 	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()
 
@@ -2567,6 +2614,11 @@ func (p *Parser) regoV1Import(imp *Import) {
 		return
 	}
 
+	if p.po.EffectiveRegoVersion() == RegoV1 {
+		// We're parsing for Rego v1, where the 'rego.v1' import is a no-op.
+		return
+	}
+
 	path := imp.Path.Value.(Ref)
 
 	if len(path) == 1 || !path[1].Equal(RegoV1CompatibleRef[1]) || len(path) > 2 {
diff --git a/vendor/github.com/open-policy-agent/opa/ast/parser_ext.go b/vendor/github.com/open-policy-agent/opa/ast/parser_ext.go
index c2559152342..413416da29e 100644
--- a/vendor/github.com/open-policy-agent/opa/ast/parser_ext.go
+++ b/vendor/github.com/open-policy-agent/opa/ast/parser_ext.go
@@ -477,7 +477,7 @@ func ParseModuleWithOpts(filename, input string, popts ParserOptions) (*Module,
 	if err != nil {
 		return nil, err
 	}
-	return parseModule(filename, stmts, comments, popts.RegoV1Compatible)
+	return parseModule(filename, stmts, comments, popts.EffectiveRegoVersion())
 }
 
 // ParseBody returns exactly one body.
@@ -626,6 +626,7 @@ func ParseStatementsWithOpts(filename, input string, popts ParserOptions) ([]Sta
 		WithCapabilities(popts.Capabilities).
 		WithSkipRules(popts.SkipRules).
 		WithJSONOptions(popts.JSONOptions).
+		WithRegoVersion(popts.EffectiveRegoVersion()).
 		withUnreleasedKeywords(popts.unreleasedKeywords)
 
 	stmts, comments, errs := parser.Parse()
@@ -637,7 +638,7 @@ func ParseStatementsWithOpts(filename, input string, popts ParserOptions) ([]Sta
 	return stmts, comments, nil
 }
 
-func parseModule(filename string, stmts []Statement, comments []*Comment, regoV1Compatible bool) (*Module, error) {
+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")
@@ -658,14 +659,14 @@ func parseModule(filename string, stmts []Statement, comments []*Comment, regoV1
 
 	// The comments slice only holds comments that were not their own statements.
 	mod.Comments = append(mod.Comments, comments...)
-	mod.regoV1Compatible = regoV1Compatible
+	mod.regoVersion = regoCompatibilityMode
 
 	for i, stmt := range stmts[1:] {
 		switch stmt := stmt.(type) {
 		case *Import:
 			mod.Imports = append(mod.Imports, stmt)
-			if Compare(stmt.Path.Value, RegoV1CompatibleRef) == 0 {
-				mod.regoV1Compatible = true
+			if mod.regoVersion == RegoV0 && Compare(stmt.Path.Value, RegoV1CompatibleRef) == 0 {
+				mod.regoVersion = RegoV0CompatV1
 			}
 		case *Rule:
 			setRuleModule(stmt, mod)
@@ -694,7 +695,7 @@ func parseModule(filename string, stmts []Statement, comments []*Comment, regoV1
 		}
 	}
 
-	if mod.regoV1Compatible {
+	if mod.regoVersion == RegoV0CompatV1 || mod.regoVersion == RegoV1 {
 		for _, rule := range mod.Rules {
 			for r := rule; r != nil; r = r.Else {
 				var t string
diff --git a/vendor/github.com/open-policy-agent/opa/ast/policy.go b/vendor/github.com/open-policy-agent/opa/ast/policy.go
index 2679a5f10c2..187bb8d7650 100644
--- a/vendor/github.com/open-policy-agent/opa/ast/policy.go
+++ b/vendor/github.com/open-policy-agent/opa/ast/policy.go
@@ -145,13 +145,13 @@ type (
 	// 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
-		regoV1Compatible bool
+		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.
@@ -399,6 +399,14 @@ func (mod *Module) UnmarshalJSON(bs []byte) error {
 	return nil
 }
 
+func (mod *Module) regoV1Compatible() bool {
+	return mod.regoVersion == RegoV1 || mod.regoVersion == RegoV0CompatV1
+}
+
+func (mod *Module) RegoVersion() RegoVersion {
+	return mod.regoVersion
+}
+
 // NewComment returns a new Comment object.
 func NewComment(text []byte) *Comment {
 	return &Comment{
diff --git a/vendor/github.com/open-policy-agent/opa/bundle/bundle.go b/vendor/github.com/open-policy-agent/opa/bundle/bundle.go
index 66c8ce37c63..72927123a88 100644
--- a/vendor/github.com/open-policy-agent/opa/bundle/bundle.go
+++ b/vendor/github.com/open-policy-agent/opa/bundle/bundle.go
@@ -400,6 +400,7 @@ type Reader struct {
 	lazyLoadingMode       bool
 	name                  string
 	persist               bool
+	regoVersion           ast.RegoVersion
 }
 
 // NewReader is deprecated. Use NewCustomReader instead.
@@ -503,11 +504,17 @@ func (r *Reader) WithBundlePersistence(persist bool) *Reader {
 	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,
 		JSONOptions:       r.jsonOptions,
+		RegoVersion:       r.regoVersion,
 	}
 }
 
@@ -1005,12 +1012,18 @@ func hashBundleFiles(hash SignatureHasher, b *Bundle) ([]FileInfo, error) {
 }
 
 // FormatModules formats Rego modules
+// Modules will be formatted to comply with rego-v1, 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.RegoV0, 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 {
 	var err error
 
 	for i, module := range b.Modules {
 		if module.Raw == nil {
-			module.Raw, err = format.Ast(module.Parsed)
+			module.Raw, err = format.AstWithOpts(module.Parsed, format.Opts{RegoVersion: version})
 			if err != nil {
 				return err
 			}
@@ -1020,7 +1033,14 @@ func (b *Bundle) FormatModules(useModulePath bool) error {
 				path = module.Path
 			}
 
-			module.Raw, err = format.Source(path, module.Raw)
+			opts := format.Opts{}
+			if preserveModuleRegoVersion {
+				opts.RegoVersion = module.Parsed.RegoVersion()
+			} else {
+				opts.RegoVersion = version
+			}
+
+			module.Raw, err = format.SourceWithOpts(path, module.Raw, opts)
 			if err != nil {
 				return err
 			}
diff --git a/vendor/github.com/open-policy-agent/opa/bundle/store.go b/vendor/github.com/open-policy-agent/opa/bundle/store.go
index d93d147c054..45bcf6e559e 100644
--- a/vendor/github.com/open-policy-agent/opa/bundle/store.go
+++ b/vendor/github.com/open-policy-agent/opa/bundle/store.go
@@ -301,6 +301,7 @@ type ActivateOpts struct {
 	Bundles                  map[string]*Bundle     // Optional
 	ExtraModules             map[string]*ast.Module // Optional
 	AuthorizationDecisionRef ast.Ref
+	ParserOptions            ast.ParserOptions
 
 	legacy bool
 }
@@ -314,10 +315,11 @@ func Activate(opts *ActivateOpts) error {
 
 // DeactivateOpts defines options for the Deactivate API call
 type DeactivateOpts struct {
-	Ctx         context.Context
-	Store       storage.Store
-	Txn         storage.Transaction
-	BundleNames map[string]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.
@@ -332,7 +334,7 @@ func Deactivate(opts *DeactivateOpts) error {
 			erase[root] = struct{}{}
 		}
 	}
-	_, err := eraseBundles(opts.Ctx, opts.Store, opts.Txn, opts.BundleNames, erase)
+	_, err := eraseBundles(opts.Ctx, opts.Store, opts.Txn, opts.ParserOptions, opts.BundleNames, erase)
 	return err
 }
 
@@ -382,7 +384,7 @@ func activateBundles(opts *ActivateOpts) error {
 
 	// 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, names, erase)
+	remaining, err := eraseBundles(opts.Ctx, opts.Store, opts.Txn, opts.ParserOptions, names, erase)
 	if err != nil {
 		return err
 	}
@@ -585,13 +587,13 @@ func activateDeltaBundles(opts *ActivateOpts, bundles map[string]*Bundle) error
 
 // 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, names map[string]struct{}, roots map[string]struct{}) (map[string]*ast.Module, error) {
+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, err := erasePolicies(ctx, store, txn, roots)
+	remaining, err := erasePolicies(ctx, store, txn, parserOpts, roots)
 	if err != nil {
 		return nil, err
 	}
@@ -633,7 +635,7 @@ func eraseData(ctx context.Context, store storage.Store, txn storage.Transaction
 	return nil
 }
 
-func erasePolicies(ctx context.Context, store storage.Store, txn storage.Transaction, roots map[string]struct{}) (map[string]*ast.Module, error) {
+func erasePolicies(ctx context.Context, store storage.Store, txn storage.Transaction, parserOpts ast.ParserOptions, roots map[string]struct{}) (map[string]*ast.Module, error) {
 
 	ids, err := store.ListPolicies(ctx, txn)
 	if err != nil {
@@ -647,7 +649,7 @@ func erasePolicies(ctx context.Context, store storage.Store, txn storage.Transac
 		if err != nil {
 			return nil, err
 		}
-		module, err := ast.ParseModule(id, string(bs))
+		module, err := ast.ParseModuleWithOpts(id, string(bs), parserOpts)
 		if err != nil {
 			return nil, err
 		}
diff --git a/vendor/github.com/open-policy-agent/opa/capabilities/v0.60.0.json b/vendor/github.com/open-policy-agent/opa/capabilities/v0.60.0.json
new file mode 100644
index 00000000000..e38edc9482c
--- /dev/null
+++ b/vendor/github.com/open-policy-agent/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/vendor/github.com/open-policy-agent/opa/format/format.go b/vendor/github.com/open-policy-agent/opa/format/format.go
index 1736cd3d8c6..0550f18251c 100644
--- a/vendor/github.com/open-policy-agent/opa/format/format.go
+++ b/vendor/github.com/open-policy-agent/opa/format/format.go
@@ -26,7 +26,18 @@ type Opts struct {
 	// carry along their original source locations.
 	IgnoreLocations bool
 
-	RegoV1 bool
+	// RegoV1 is equivalent to setting RegoVersion to ast.RegoV0Compat1.
+	// RegoV1 takes precedence over RegoVersion.
+	// Deprecated: use RegoVersion instead.
+	RegoV1      bool
+	RegoVersion ast.RegoVersion
+}
+
+func (o *Opts) effectiveRegoVersion() ast.RegoVersion {
+	if o.RegoV1 {
+		return ast.RegoV0CompatV1
+	}
+	return o.RegoVersion
 }
 
 // defaultLocationFile is the file name used in `Ast()` for terms
@@ -42,12 +53,19 @@ func Source(filename string, src []byte) ([]byte, error) {
 }
 
 func SourceWithOpts(filename string, src []byte, opts Opts) ([]byte, error) {
-	module, err := ast.ParseModule(filename, string(src))
+	parserOpts := ast.ParserOptions{}
+	if opts.effectiveRegoVersion() == ast.RegoV1 {
+		// If the rego version is V1, wee need to parse it as such, to allow for future keywords not being imported.
+		// Otherwise, we'll default to RegoV0
+		parserOpts.RegoVersion = ast.RegoV1
+	}
+
+	module, err := ast.ParseModuleWithOpts(filename, string(src), parserOpts)
 	if err != nil {
 		return nil, err
 	}
 
-	if opts.RegoV1 {
+	if opts.effectiveRegoVersion() == ast.RegoV0CompatV1 || opts.effectiveRegoVersion() == ast.RegoV1 {
 		errors := ast.CheckRegoV1(module)
 		if len(errors) > 0 {
 			return nil, errors
@@ -115,7 +133,7 @@ func AstWithOpts(x interface{}, opts Opts) ([]byte, error) {
 
 	o := fmtOpts{}
 
-	if opts.RegoV1 {
+	if opts.effectiveRegoVersion() == ast.RegoV0CompatV1 || opts.effectiveRegoVersion() == ast.RegoV1 {
 		o.regoV1 = true
 		o.ifs = true
 		o.contains = true
@@ -176,10 +194,13 @@ func AstWithOpts(x interface{}, opts Opts) ([]byte, error) {
 
 	switch x := x.(type) {
 	case *ast.Module:
-		if o.regoV1 {
+		if opts.effectiveRegoVersion() == ast.RegoV1 {
+			x.Imports = filterRegoV1Import(x.Imports)
+		} else if opts.effectiveRegoVersion() == ast.RegoV0CompatV1 {
 			x.Imports = ensureRegoV1Import(x.Imports)
 		}
-		if o.regoV1 || moduleIsRegoV1Compatible(x) {
+
+		if opts.effectiveRegoVersion() == ast.RegoV0CompatV1 || opts.effectiveRegoVersion() == ast.RegoV1 || moduleIsRegoV1Compatible(x) {
 			x.Imports = future.FilterFutureImports(x.Imports)
 		} else {
 			for kw := range extraFutureKeywordImports {
@@ -535,7 +556,11 @@ func (w *writer) writeHead(head *ast.Head, isDefault, isExpandedConst bool, o fm
 		// * a.b.c.d -> a.b.c.d := true
 		isRegoV1RefConst := o.regoV1 && isExpandedConst && head.Key == nil && len(head.Args) == 0
 
-		if head.Location == head.Value.Location && head.Name != "else" && !isRegoV1RefConst {
+		if len(head.Args) > 0 &&
+			head.Location == head.Value.Location &&
+			head.Name != "else" &&
+			ast.Compare(head.Value, ast.BooleanTerm(true)) == 0 &&
+			!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.
@@ -1444,6 +1469,17 @@ 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)
diff --git a/vendor/github.com/open-policy-agent/opa/internal/report/report.go b/vendor/github.com/open-policy-agent/opa/internal/report/report.go
new file mode 100644
index 00000000000..145d0a94651
--- /dev/null
+++ b/vendor/github.com/open-policy-agent/opa/internal/report/report.go
@@ -0,0 +1,184 @@
+// 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"
+	"fmt"
+	"net/http"
+	"os"
+	"runtime"
+	"strconv"
+	"strings"
+	"sync"
+	"time"
+
+	"github.com/open-policy-agent/opa/keys"
+	"github.com/open-policy-agent/opa/logging"
+
+	"github.com/open-policy-agent/opa/plugins/rest"
+	"github.com/open-policy-agent/opa/util"
+	"github.com/open-policy-agent/opa/version"
+)
+
+// 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=<url>"
+//
+// This will be overridden if the OPA_TELEMETRY_SERVICE_URL environment variable
+// is provided.
+var ExternalServiceURL = "https://telemetry.openpolicyagent.org"
+
+// Reporter reports information such as the version, heap usage about the running OPA instance to an external service
+type Reporter struct {
+	body   map[string]any
+	client rest.Client
+
+	gatherers    map[string]Gatherer
+	gatherersMtx sync.Mutex
+}
+
+// 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
+}
+
+// New returns an instance of the Reporter
+func New(id string, opts Options) (*Reporter, error) {
+	r := Reporter{
+		gatherers: map[string]Gatherer{},
+	}
+	r.body = map[string]any{
+		"id":      id,
+		"version": version.Version,
+	}
+
+	url := os.Getenv("OPA_TELEMETRY_SERVICE_URL")
+	if url == "" {
+		url = ExternalServiceURL
+	}
+
+	restConfig := []byte(fmt.Sprintf(`{
+		"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 *Reporter) SendReport(ctx context.Context) (*DataResponse, error) {
+	rCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
+	defer cancel()
+
+	r.gatherersMtx.Lock()
+	defer r.gatherersMtx.Unlock()
+	for key, g := range r.gatherers {
+		var err error
+		r.body[key], err = g(rCtx)
+		if err != nil {
+			return nil, fmt.Errorf("gather telemetry error for key %s: %w", key, err)
+		}
+	}
+
+	resp, err := r.client.WithJSON(r.body).Do(rCtx, "POST", "/v1/version")
+	if err != nil {
+		return nil, err
+	}
+
+	defer util.Close(resp)
+
+	switch resp.StatusCode {
+	case http.StatusOK:
+		if resp.Body != nil {
+			var result DataResponse
+			err := json.NewDecoder(resp.Body).Decode(&result)
+			if err != nil {
+				return nil, err
+			}
+			return &result, nil
+		}
+		return nil, nil
+	default:
+		return nil, fmt.Errorf("server replied with HTTP %v", resp.StatusCode)
+	}
+}
+
+func (r *Reporter) RegisterGatherer(key string, f Gatherer) {
+	r.gatherersMtx.Lock()
+	r.gatherers[key] = f
+	r.gatherersMtx.Unlock()
+}
+
+// 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/vendor/github.com/open-policy-agent/opa/internal/runtime/init/init.go b/vendor/github.com/open-policy-agent/opa/internal/runtime/init/init.go
index bd72e1bd0c8..ba316efcb85 100644
--- a/vendor/github.com/open-policy-agent/opa/internal/runtime/init/init.go
+++ b/vendor/github.com/open-policy-agent/opa/internal/runtime/init/init.go
@@ -122,6 +122,18 @@ func LoadPaths(paths []string,
 	processAnnotations bool,
 	caps *ast.Capabilities,
 	fsys fs.FS) (*LoadPathsResult, error) {
+	return LoadPathsForRegoVersion(ast.RegoV0, paths, filter, asBundle, bvc, skipVerify, processAnnotations, caps, fsys)
+}
+
+func LoadPathsForRegoVersion(regoVersion ast.RegoVersion,
+	paths []string,
+	filter loader.Filter,
+	asBundle bool,
+	bvc *bundle.VerificationConfig,
+	skipVerify bool,
+	processAnnotations bool,
+	caps *ast.Capabilities,
+	fsys fs.FS) (*LoadPathsResult, error) {
 
 	if caps == nil {
 		caps = ast.CapabilitiesForThisVersion()
@@ -146,6 +158,7 @@ func LoadPaths(paths []string,
 				WithFilter(filter).
 				WithProcessAnnotation(processAnnotations).
 				WithCapabilities(caps).
+				WithRegoVersion(regoVersion).
 				AsBundle(path)
 			if err != nil {
 				return nil, err
@@ -161,6 +174,7 @@ func LoadPaths(paths []string,
 		WithFS(fsys).
 		WithProcessAnnotation(processAnnotations).
 		WithCapabilities(caps).
+		WithRegoVersion(regoVersion).
 		Filtered(nonBundlePaths, filter)
 
 	if err != nil {
diff --git a/vendor/github.com/open-policy-agent/opa/loader/loader.go b/vendor/github.com/open-policy-agent/opa/loader/loader.go
index 7b3eac40a57..2960cababb3 100644
--- a/vendor/github.com/open-policy-agent/opa/loader/loader.go
+++ b/vendor/github.com/open-policy-agent/opa/loader/loader.go
@@ -103,7 +103,11 @@ type FileLoader interface {
 	WithCapabilities(*ast.Capabilities) FileLoader
 	WithJSONOptions(*astJSON.Options) FileLoader
 
+	// WithRegoV1Compatible
+	// Deprecated: use WithRegoVersion instead
 	WithRegoV1Compatible(bool) FileLoader
+
+	WithRegoVersion(ast.RegoVersion) FileLoader
 }
 
 // NewFileLoader returns a new FileLoader instance.
@@ -183,11 +187,20 @@ func (fl *fileLoader) WithJSONOptions(opts *astJSON.Options) FileLoader {
 	return fl
 }
 
+// WithRegoV1Compatible enforces Rego v0 with Rego v1 compatibility.
+// See ParserOptions.RegoV1Compatible for more details.
+// Deprecated: use WithRegoVersion instead
 func (fl *fileLoader) WithRegoV1Compatible(compatible bool) FileLoader {
 	fl.opts.RegoV1Compatible = compatible
 	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
+}
+
 // All returns a Result object loaded (recursively) from the specified paths.
 func (fl fileLoader) All(paths []string) (*Result, error) {
 	return fl.Filtered(paths, nil)
@@ -256,7 +269,8 @@ func (fl fileLoader) AsBundle(path string) (*bundle.Bundle, error) {
 		WithSkipBundleVerification(fl.skipVerify).
 		WithProcessAnnotations(fl.opts.ProcessAnnotation).
 		WithCapabilities(fl.opts.Capabilities).
-		WithJSONOptions(fl.opts.JSONOptions)
+		WithJSONOptions(fl.opts.JSONOptions).
+		WithRegoVersion(fl.opts.EffectiveRegoVersion())
 
 	// For bundle directories add the full path in front of module file names
 	// to simplify debugging.
@@ -706,7 +720,7 @@ func loadKnownTypes(path string, bs []byte, m metrics.Metrics, opts ast.ParserOp
 		return loadYAML(path, bs, m)
 	default:
 		if strings.HasSuffix(path, ".tar.gz") {
-			r, err := loadBundleFile(path, bs, m)
+			r, err := loadBundleFile(path, bs, m, opts)
 			if err != nil {
 				err = fmt.Errorf("bundle %s: %w", path, err)
 			}
@@ -732,9 +746,15 @@ func loadFileForAnyType(path string, bs []byte, m metrics.Metrics, opts ast.Pars
 	return nil, unrecognizedFile(path)
 }
 
-func loadBundleFile(path string, bs []byte, m metrics.Metrics) (bundle.Bundle, error) {
+func loadBundleFile(path string, bs []byte, m metrics.Metrics, opts ast.ParserOptions) (bundle.Bundle, error) {
 	tl := bundle.NewTarballLoaderWithBaseURL(bytes.NewBuffer(bs), path)
-	br := bundle.NewCustomReader(tl).WithMetrics(m).WithSkipBundleVerification(true).IncludeManifestInData(true)
+	br := bundle.NewCustomReader(tl).
+		WithRegoVersion(opts.RegoVersion).
+		WithJSONOptions(opts.JSONOptions).
+		WithProcessAnnotations(opts.ProcessAnnotation).
+		WithMetrics(m).
+		WithSkipBundleVerification(true).
+		IncludeManifestInData(true)
 	return br.Read()
 }
 
diff --git a/vendor/github.com/open-policy-agent/opa/plugins/plugins.go b/vendor/github.com/open-policy-agent/opa/plugins/plugins.go
index 830da78bdc1..7bd47075e9c 100644
--- a/vendor/github.com/open-policy-agent/opa/plugins/plugins.go
+++ b/vendor/github.com/open-policy-agent/opa/plugins/plugins.go
@@ -8,9 +8,11 @@ package plugins
 import (
 	"context"
 	"fmt"
+	mr "math/rand"
 	"sync"
 	"time"
 
+	"github.com/open-policy-agent/opa/internal/report"
 	"github.com/prometheus/client_golang/prometheus"
 	"go.opentelemetry.io/otel/sdk/trace"
 
@@ -148,6 +150,9 @@ const (
 	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"`
@@ -198,8 +203,13 @@ type Manager struct {
 	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{}
 }
 
 type managerContextKey string
@@ -385,6 +395,21 @@ func WithHooks(hs hooks.Hooks) func(*Manager) {
 	}
 }
 
+// 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
+	}
+}
+
 // New creates a new Manager using config.
 func New(raw []byte, id string, store storage.Store, opts ...func(*Manager)) (*Manager, error) {
 
@@ -453,6 +478,27 @@ func New(raw []byte, id string, store storage.Store, opts ...func(*Manager)) (*M
 		return nil, err
 	}
 
+	if m.enableTelemetry {
+		reporter, err := report.New(id, 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 m.compiler != nil && m.compiler.Required != nil {
+				minimumCompatibleVersion, _ = m.compiler.Required.MinimumCompatibleVersion()
+			}
+			return minimumCompatibleVersion, nil
+		})
+
+		// register any additional gatherers
+		for k, g := range m.registeredTelemetryGatherers {
+			m.reporter.RegisterGatherer(k, g)
+		}
+	}
+
 	return m, nil
 }
 
@@ -469,6 +515,12 @@ func (m *Manager) Init(ctx context.Context) error {
 		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{
@@ -497,6 +549,12 @@ func (m *Manager) Init(ctx context.Context) error {
 	})
 
 	if err != nil {
+		if m.stop != nil {
+			done := make(chan struct{})
+			m.stop <- done
+			<-done
+		}
+
 		return err
 	}
 
@@ -673,6 +731,12 @@ func (m *Manager) Stop(ctx context.Context) {
 			m.logger.Error("Error closing store: %v", err)
 		}
 	}
+
+	if m.stop != nil {
+		done := make(chan struct{})
+		m.stop <- done
+		<-done
+	}
 }
 
 // Reconfigure updates the configuration on the manager.
@@ -817,6 +881,11 @@ func (m *Manager) onCommit(ctx context.Context, txn storage.Transaction, event s
 
 	if compiler != nil {
 		m.setCompiler(compiler)
+
+		if m.enableTelemetry && event.PolicyChanged() {
+			m.opaReportNotifyCh <- struct{}{}
+		}
+
 		for _, f := range m.registeredTriggers {
 			f(txn)
 		}
@@ -982,3 +1051,37 @@ func (m *Manager) RegisterNDCacheTrigger(trigger func(bool)) {
 	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]interface{}{"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
+		}
+	}
+}
diff --git a/vendor/github.com/open-policy-agent/opa/rego/rego.go b/vendor/github.com/open-policy-agent/opa/rego/rego.go
index 321dd7e4449..17873e380a6 100644
--- a/vendor/github.com/open-policy-agent/opa/rego/rego.go
+++ b/vendor/github.com/open-policy-agent/opa/rego/rego.go
@@ -594,11 +594,13 @@ type Rego struct {
 	pluginMgr              *plugins.Manager
 	plugins                []TargetPlugin
 	targetPrepState        TargetPluginEval
+	regoVersion            ast.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
@@ -629,6 +631,7 @@ type (
 func RegisterBuiltin1(decl *Function, impl Builtin1) {
 	ast.RegisterBuiltin(&ast.Builtin{
 		Name:             decl.Name,
+		Description:      decl.Description,
 		Decl:             decl.Decl,
 		Nondeterministic: decl.Nondeterministic,
 	})
@@ -642,6 +645,7 @@ func RegisterBuiltin1(decl *Function, impl Builtin1) {
 func RegisterBuiltin2(decl *Function, impl Builtin2) {
 	ast.RegisterBuiltin(&ast.Builtin{
 		Name:             decl.Name,
+		Description:      decl.Description,
 		Decl:             decl.Decl,
 		Nondeterministic: decl.Nondeterministic,
 	})
@@ -655,6 +659,7 @@ func RegisterBuiltin2(decl *Function, impl Builtin2) {
 func RegisterBuiltin3(decl *Function, impl Builtin3) {
 	ast.RegisterBuiltin(&ast.Builtin{
 		Name:             decl.Name,
+		Description:      decl.Description,
 		Decl:             decl.Decl,
 		Nondeterministic: decl.Nondeterministic,
 	})
@@ -668,6 +673,7 @@ func RegisterBuiltin3(decl *Function, impl Builtin3) {
 func RegisterBuiltin4(decl *Function, impl Builtin4) {
 	ast.RegisterBuiltin(&ast.Builtin{
 		Name:             decl.Name,
+		Description:      decl.Description,
 		Decl:             decl.Decl,
 		Nondeterministic: decl.Nondeterministic,
 	})
@@ -681,6 +687,7 @@ func RegisterBuiltin4(decl *Function, impl Builtin4) {
 func RegisterBuiltinDyn(decl *Function, impl BuiltinDyn) {
 	ast.RegisterBuiltin(&ast.Builtin{
 		Name:             decl.Name,
+		Description:      decl.Description,
 		Decl:             decl.Decl,
 		Nondeterministic: decl.Nondeterministic,
 	})
@@ -1187,6 +1194,12 @@ func Strict(yes bool) func(r *Rego) {
 	}
 }
 
+func SetRegoVersion(version ast.RegoVersion) func(r *Rego) {
+	return func(r *Rego) {
+		r.regoVersion = version
+	}
+}
+
 // New returns a new Rego object.
 func New(options ...func(r *Rego)) *Rego {
 
@@ -1797,7 +1810,7 @@ func (r *Rego) parseModules(ctx context.Context, txn storage.Transaction, m metr
 			return err
 		}
 
-		parsed, err := ast.ParseModule(id, string(bs))
+		parsed, err := ast.ParseModuleWithOpts(id, string(bs), ast.ParserOptions{RegoVersion: r.regoVersion})
 		if err != nil {
 			errs = append(errs, err)
 		}
@@ -1807,7 +1820,7 @@ func (r *Rego) parseModules(ctx context.Context, txn storage.Transaction, m metr
 
 	// Parse any passed in as arguments to the Rego object
 	for _, module := range r.modules {
-		p, err := module.Parse()
+		p, err := module.ParseWithOpts(ast.ParserOptions{RegoVersion: r.regoVersion})
 		if err != nil {
 			switch errorWithType := err.(type) {
 			case ast.Errors:
@@ -1839,6 +1852,7 @@ func (r *Rego) loadFiles(ctx context.Context, txn storage.Transaction, m metrics
 	result, err := loader.NewFileLoader().
 		WithMetrics(m).
 		WithProcessAnnotation(true).
+		WithRegoVersion(r.regoVersion).
 		Filtered(r.loadPaths.paths, r.loadPaths.filter)
 	if err != nil {
 		return err
@@ -1869,6 +1883,7 @@ func (r *Rego) loadBundles(ctx context.Context, txn storage.Transaction, m metri
 			WithMetrics(m).
 			WithProcessAnnotation(true).
 			WithSkipBundleVerification(r.skipBundleVerification).
+			WithRegoVersion(r.regoVersion).
 			AsBundle(path)
 		if err != nil {
 			return fmt.Errorf("loading error: %s", err)
@@ -1934,13 +1949,14 @@ func (r *Rego) compileModules(ctx context.Context, txn storage.Transaction, m me
 		// Use this as the single-point of compiling everything only a
 		// single time.
 		opts := &bundle.ActivateOpts{
-			Ctx:          ctx,
-			Store:        r.store,
-			Txn:          txn,
-			Compiler:     r.compilerForTxn(ctx, r.store, txn),
-			Metrics:      m,
-			Bundles:      r.bundles,
-			ExtraModules: r.parsedModules,
+			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 {
@@ -2608,6 +2624,10 @@ 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
diff --git a/vendor/github.com/open-policy-agent/opa/topdown/providers.go b/vendor/github.com/open-policy-agent/opa/topdown/providers.go
index 0b9e012268f..113281b0f9d 100644
--- a/vendor/github.com/open-policy-agent/opa/topdown/providers.go
+++ b/vendor/github.com/open-policy-agent/opa/topdown/providers.go
@@ -172,10 +172,21 @@ func builtinAWSSigV4SignReq(ctx BuiltinContext, operands []*ast.Term, iter func(
 	}
 
 	// Sign the request object's headers, and reconstruct the headers map.
-	authHeader, signedHeadersMap := aws.SignV4(objectToMap(headers), method, theURL, body, service, awsCreds, signingTimestamp)
+	headersMap := objectToMap(headers)
+	authHeader, awsHeadersMap := aws.SignV4(headersMap, method, theURL, body, service, awsCreds, signingTimestamp)
 	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))
-	for k, v := range signedHeadersMap {
+
+	// set aws signature headers
+	for k, v := range awsHeadersMap {
 		signedHeadersObj.Insert(ast.StringTerm(k), ast.StringTerm(v))
 	}
 
diff --git a/vendor/github.com/open-policy-agent/opa/version/version.go b/vendor/github.com/open-policy-agent/opa/version/version.go
index 6fc4766fdc9..0fcbba17eb0 100644
--- a/vendor/github.com/open-policy-agent/opa/version/version.go
+++ b/vendor/github.com/open-policy-agent/opa/version/version.go
@@ -11,7 +11,7 @@ import (
 )
 
 // Version is the canonical version of OPA.
-var Version = "0.59.0"
+var Version = "0.60.0"
 
 // GoVersion is the version of Go this was built with
 var GoVersion = runtime.Version()
diff --git a/vendor/github.com/sergi/go-diff/diffmatchpatch/diff.go b/vendor/github.com/sergi/go-diff/diffmatchpatch/diff.go
index 2a9f2dc3b94..4f7b42488aa 100644
--- a/vendor/github.com/sergi/go-diff/diffmatchpatch/diff.go
+++ b/vendor/github.com/sergi/go-diff/diffmatchpatch/diff.go
@@ -1313,17 +1313,17 @@ func (dmp *DiffMatchPatch) diffLinesToStrings(text1, text2 string) (string, stri
 	// '\x00' is a valid character, but various debuggers don't like it. So we'll insert a junk entry to avoid generating a null character.
 	lineArray := []string{""} // e.g. lineArray[4] == 'Hello\n'
 
+	lineHash := make(map[string]int)
 	//Each string has the index of lineArray which it points to
-	strIndexArray1 := dmp.diffLinesToStringsMunge(text1, &lineArray)
-	strIndexArray2 := dmp.diffLinesToStringsMunge(text2, &lineArray)
+	strIndexArray1 := dmp.diffLinesToStringsMunge(text1, &lineArray, lineHash)
+	strIndexArray2 := dmp.diffLinesToStringsMunge(text2, &lineArray, lineHash)
 
 	return intArrayToString(strIndexArray1), intArrayToString(strIndexArray2), lineArray
 }
 
 // diffLinesToStringsMunge splits a text into an array of strings, and reduces the texts to a []string.
-func (dmp *DiffMatchPatch) diffLinesToStringsMunge(text string, lineArray *[]string) []uint32 {
+func (dmp *DiffMatchPatch) diffLinesToStringsMunge(text string, lineArray *[]string, lineHash map[string]int) []uint32 {
 	// Walk the text, pulling out a substring for each line. text.split('\n') would would temporarily double our memory footprint. Modifying text would create many large strings to garbage collect.
-	lineHash := map[string]int{} // e.g. lineHash['Hello\n'] == 4
 	lineStart := 0
 	lineEnd := -1
 	strs := []uint32{}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index ba1b458723f..ee46d08962e 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -1482,7 +1482,7 @@ github.com/onsi/gomega/matchers/support/goraph/edge
 github.com/onsi/gomega/matchers/support/goraph/node
 github.com/onsi/gomega/matchers/support/goraph/util
 github.com/onsi/gomega/types
-# github.com/open-policy-agent/opa v0.59.0
+# github.com/open-policy-agent/opa v0.60.0
 ## explicit; go 1.19
 github.com/open-policy-agent/opa/ast
 github.com/open-policy-agent/opa/ast/internal/scanner
@@ -1531,6 +1531,7 @@ github.com/open-policy-agent/opa/internal/providers/aws/crypto
 github.com/open-policy-agent/opa/internal/providers/aws/v4
 github.com/open-policy-agent/opa/internal/ref
 github.com/open-policy-agent/opa/internal/rego/opa
+github.com/open-policy-agent/opa/internal/report
 github.com/open-policy-agent/opa/internal/runtime/init
 github.com/open-policy-agent/opa/internal/semver
 github.com/open-policy-agent/opa/internal/strings
@@ -1675,7 +1676,7 @@ github.com/russross/blackfriday/v2
 # github.com/segmentio/ksuid v1.0.4
 ## explicit; go 1.12
 github.com/segmentio/ksuid
-# github.com/sergi/go-diff v1.2.0
+# github.com/sergi/go-diff v1.3.1
 ## explicit; go 1.12
 github.com/sergi/go-diff/diffmatchpatch
 # github.com/sethvargo/go-password v0.2.0


Test patch:
diff --git a/services/graph/pkg/identity/ldap_group_test.go b/services/graph/pkg/identity/ldap_group_test.go
index 79431d4760f..bb7adfa9f35 100644
--- a/services/graph/pkg/identity/ldap_group_test.go
+++ b/services/graph/pkg/identity/ldap_group_test.go
@@ -6,6 +6,7 @@ import (
 	"net/url"
 	"testing"
 
+	"github.com/CiscoM31/godata"
 	"github.com/go-ldap/ldap/v3"
 	"github.com/owncloud/ocis/v2/services/graph/mocks"
 	"github.com/stretchr/testify/assert"
@@ -219,15 +220,19 @@ func TestGetGroupReadOnlySubtree(t *testing.T) {
 
 func TestGetGroups(t *testing.T) {
 	lm := &mocks.Client{}
+	oDataReq, err := godata.ParseRequest(context.Background(), "", url.Values{})
+	if err != nil {
+		t.Errorf("Expected success, got '%s'", err.Error())
+	}
 	lm.On("Search", mock.Anything).Return(nil, ldap.NewError(ldap.LDAPResultOperationsError, errors.New("mock")))
 	b, _ := getMockedBackend(lm, lconfig, &logger)
-	_, err := b.GetGroups(context.Background(), url.Values{})
+	_, err = b.GetGroups(context.Background(), oDataReq)
 	assert.ErrorContains(t, err, "itemNotFound:")
 
 	lm = &mocks.Client{}
 	lm.On("Search", mock.Anything).Return(&ldap.SearchResult{}, nil)
 	b, _ = getMockedBackend(lm, lconfig, &logger)
-	g, err := b.GetGroups(context.Background(), url.Values{})
+	g, err := b.GetGroups(context.Background(), oDataReq)
 	if err != nil {
 		t.Errorf("Expected success, got '%s'", err.Error())
 	} else if g == nil || len(g) != 0 {
@@ -239,7 +244,7 @@ func TestGetGroups(t *testing.T) {
 		Entries: []*ldap.Entry{groupEntry},
 	}, nil)
 	b, _ = getMockedBackend(lm, lconfig, &logger)
-	g, err = b.GetGroups(context.Background(), url.Values{})
+	g, err = b.GetGroups(context.Background(), oDataReq)
 	if err != nil {
 		t.Errorf("Expected GetGroup to succeed. Got %s", err.Error())
 	} else if *g[0].Id != groupEntry.GetEqualFoldAttributeValue(b.groupAttributeMap.id) {
@@ -264,11 +269,15 @@ func TestGetGroups(t *testing.T) {
 	}
 
 	for _, param := range []url.Values{queryParamSelect, queryParamExpand} {
+		oDataReq, err := godata.ParseRequest(context.Background(), "", param)
+		if err != nil {
+			t.Errorf("Expected success, got '%s'", err.Error())
+		}
 		lm.On("Search", groupListSeachRequest).Return(&ldap.SearchResult{Entries: []*ldap.Entry{groupEntry}}, nil)
 		lm.On("Search", sr2).Return(&ldap.SearchResult{Entries: []*ldap.Entry{userEntry}}, nil)
 		lm.On("Search", sr3).Return(&ldap.SearchResult{Entries: []*ldap.Entry{invalidUserEntry}}, nil)
 		b, _ = getMockedBackend(lm, lconfig, &logger)
-		g, err = b.GetGroups(context.Background(), param)
+		g, err = b.GetGroups(context.Background(), oDataReq)
 		switch {
 		case err != nil:
 			t.Errorf("Expected GetGroup to succeed. Got %s", err.Error())
@@ -281,6 +290,32 @@ func TestGetGroups(t *testing.T) {
 		}
 	}
 }
+
+func TestGetGroupsSearch(t *testing.T) {
+	lm := &mocks.Client{}
+	odataReqDefault, err := godata.ParseRequest(context.Background(), "",
+		url.Values{
+			"$search": []string{"\"term\""},
+		},
+	)
+	if err != nil {
+		t.Errorf("Expected success got '%s'", err.Error())
+	}
+
+	// only match if the filter contains the search term unquoted
+	lm.On("Search", mock.MatchedBy(
+		func(req *ldap.SearchRequest) bool {
+			return req.Filter == "(&(objectClass=groupOfNames)(|(cn=*term*)(entryUUID=*term*)))"
+		})).
+		Return(&ldap.SearchResult{}, nil)
+	b, _ := getMockedBackend(lm, lconfig, &logger)
+	g, err := b.GetGroups(context.Background(), odataReqDefault)
+	if err != nil {
+		t.Errorf("Expected success, got '%s'", err.Error())
+	} else if g == nil || len(g) != 0 {
+		t.Errorf("Expected zero length user slice")
+	}
+}
 func TestUpdateGroupName(t *testing.T) {
 	groupDn := "cn=TheGroup,ou=groups,dc=owncloud,dc=com"
 
diff --git a/services/graph/pkg/identity/ldap_test.go b/services/graph/pkg/identity/ldap_test.go
index 908862f8197..3d95795426c 100644
--- a/services/graph/pkg/identity/ldap_test.go
+++ b/services/graph/pkg/identity/ldap_test.go
@@ -286,6 +286,32 @@ func TestGetUsers(t *testing.T) {
 	}
 }
 
+func TestGetUsersSearch(t *testing.T) {
+	lm := &mocks.Client{}
+	odataReqDefault, err := godata.ParseRequest(context.Background(), "",
+		url.Values{
+			"$search": []string{"\"term\""},
+		},
+	)
+	if err != nil {
+		t.Errorf("Expected success got '%s'", err.Error())
+	}
+
+	// only match if the filter contains the search term unquoted
+	lm.On("Search", mock.MatchedBy(
+		func(req *ldap.SearchRequest) bool {
+			return req.Filter == "(&(objectClass=inetOrgPerson)(|(uid=*term*)(mail=*term*)(displayname=*term*)))"
+		})).
+		Return(&ldap.SearchResult{}, nil)
+	b, _ := getMockedBackend(lm, lconfig, &logger)
+	g, err := b.GetUsers(context.Background(), odataReqDefault)
+	if err != nil {
+		t.Errorf("Expected success, got '%s'", err.Error())
+	} else if g == nil || len(g) != 0 {
+		t.Errorf("Expected zero length user slice")
+	}
+}
+
 func TestUpdateUser(t *testing.T) {
 	falseBool := false
 	trueBool := true
diff --git a/services/graph/pkg/service/v0/groups_test.go b/services/graph/pkg/service/v0/groups_test.go
index d8aab638466..1ce23af4446 100644
--- a/services/graph/pkg/service/v0/groups_test.go
+++ b/services/graph/pkg/service/v0/groups_test.go
@@ -225,6 +225,13 @@ var _ = Describe("Groups", func() {
 
 			Expect(rr.Code).To(Equal(http.StatusForbidden))
 		})
+		It("denies using to short quoted search terms for unprivileged users", func() {
+			permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settings.GetPermissionByIDResponse{}, nil)
+			r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/groups?$search=%22ab%22", nil)
+			svc.GetGroups(rr, r)
+
+			Expect(rr.Code).To(Equal(http.StatusForbidden))
+		})
 		It("only returns a restricted set of attributes for unprivileged users", func() {
 			permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settings.GetPermissionByIDResponse{}, nil)
 			group := &libregraph.Group{}
diff --git a/services/graph/pkg/service/v0/users_test.go b/services/graph/pkg/service/v0/users_test.go
index 4cc9561e268..20bc10aa5d8 100644
--- a/services/graph/pkg/service/v0/users_test.go
+++ b/services/graph/pkg/service/v0/users_test.go
@@ -278,6 +278,13 @@ var _ = Describe("Users", func() {
 
 			Expect(rr.Code).To(Equal(http.StatusForbidden))
 		})
+		It("denies using to short quoted search terms for unprivileged users", func() {
+			permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settings.GetPermissionByIDResponse{}, nil)
+			r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users?$search=%22ab%22", nil)
+			svc.GetUsers(rr, r)
+
+			Expect(rr.Code).To(Equal(http.StatusForbidden))
+		})
 		It("only returns a restricted set of attributes for unprivileged users", func() {
 			permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settings.GetPermissionByIDResponse{}, nil)
 			user := &libregraph.User{}
diff --git a/tests/acceptance/expected-failures-localAPI-on-OCIS-storage.md b/tests/acceptance/expected-failures-localAPI-on-OCIS-storage.md
index ae74e323247..c16582c00a4 100644
--- a/tests/acceptance/expected-failures-localAPI-on-OCIS-storage.md
+++ b/tests/acceptance/expected-failures-localAPI-on-OCIS-storage.md
@@ -258,6 +258,8 @@ The expected failures in this file are from features in the owncloud/ocis repo.
 ### [blocksDownload link type is not implemented yet (sharing-ng)](https://github.com/owncloud/ocis/issues/7879)
 - [apiSharingNg/createLink.feature:78](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/createLink.feature#L78)
 - [apiSharingNg/createLink.feature:147](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/createLink.feature#L147)
+- [apiSharingNg/createLink.feature:225](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/createLink.feature#L225)
+- [apiSharingNg/createLink.feature:301](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/createLink.feature#L301)
 
 - Note: always have an empty line at the end of this file.
 The bash script that processes this file requires that the last line has a newline on the end.
diff --git a/tests/acceptance/features/apiSharingNg/createLink.feature b/tests/acceptance/features/apiSharingNg/createLink.feature
index 5eeadb4bdd3..cd20282f673 100644
--- a/tests/acceptance/features/apiSharingNg/createLink.feature
+++ b/tests/acceptance/features/apiSharingNg/createLink.feature
@@ -6,7 +6,7 @@ Feature: Create a share link for a resource
       | username |
       | Alice    |
 
-
+  @issue-7879
   Scenario Outline: create a link share of a folder
     Given user "Alice" has created folder "folder"
     When user "Alice" creates the following link share using the Graph API:
@@ -77,7 +77,7 @@ Feature: Create a share link for a resource
       | createOnly     |
       | blocksDownload |
 
-
+  @issue-7879
   Scenario Outline: create a link share of a file
     Given user "Alice" has uploaded file with content "other data" to "textfile1.txt"
     When user "Alice" creates the following link share using the Graph API:
@@ -145,3 +145,157 @@ Feature: Create a share link for a resource
       | view           |
       | edit           |
       | blocksDownload |
+
+  @issue-7879
+  Scenario Outline: create a link share of a folder with display name and expiry date
+    Given user "Alice" has created folder "folder"
+    When user "Alice" creates the following link share using the Graph API:
+      | resourceType       | folder                   |
+      | resource           | folder                   |
+      | space              | Personal                 |
+      | role               | <role>                   |
+      | password           | %public%                 |
+      | displayName        | Homework                 |
+      | expirationDateTime | 2200-07-15T14:00:00.000Z |
+    Then the HTTP status code should be "200"
+    And the JSON data of the response should match
+      """
+      {
+        "type": "object",
+        "required": [
+          "hasPassword",
+          "id",
+          "link",
+          "expirationDateTime"
+        ],
+        "properties": {
+          "hasPassword": {
+            "type": "boolean",
+            "enum": [true]
+          },
+          "id": {
+            "type": "string",
+            "pattern": "^[a-zA-Z]{15}$"
+          },
+          "expirationDateTime": {
+            "type": "string",
+            "enum": ["2200-07-15T23:59:59Z"]
+          },
+          "link": {
+            "type": "object",
+            "required": [
+              "@libre.graph.displayName",
+              "@libre.graph.quickLink",
+              "preventsDownload",
+              "type",
+              "webUrl"
+            ],
+            "properties": {
+              "@libre.graph.displayName": {
+                "type": "string",
+                "enum": ["Homework"]
+              },
+              "@libre.graph.quickLink": {
+                "type": "boolean",
+                "enum": [false]
+              },
+              "preventsDownload": {
+                "type": "boolean",
+                "enum": [false]
+              },
+              "type": {
+                "type": "string",
+                "enum": ["<role>"]
+              },
+              "webUrl": {
+                "type": "string",
+                "pattern": "^%base_url%\/s\/[a-zA-Z]{15}$"
+              }
+            }
+          }
+        }
+      }
+      """
+    Examples:
+      | role           |
+      | view           |
+      | edit           |
+      | upload         |
+      | createOnly     |
+      | blocksDownload |
+
+  @issue-7879
+  Scenario Outline: create a link share of a file with display name and expiry date
+    Given user "Alice" has uploaded file with content "other data" to "textfile1.txt"
+    When user "Alice" creates the following link share using the Graph API:
+      | resourceType       | file                     |
+      | resource           | textfile1.txt            |
+      | space              | Personal                 |
+      | role               | <role>                   |
+      | password           | %public%                 |
+      | displayName        | Homework                 |
+      | expirationDateTime | 2200-07-15T14:00:00.000Z |
+    Then the HTTP status code should be "200"
+    And the JSON data of the response should match
+      """
+      {
+        "type": "object",
+        "required": [
+          "hasPassword",
+          "id",
+          "link",
+          "expirationDateTime"
+        ],
+        "properties": {
+          "hasPassword": {
+            "type": "boolean",
+            "enum": [true]
+          },
+          "id": {
+            "type": "string",
+            "pattern": "^[a-zA-Z]{15}$"
+          },
+          "expirationDateTime": {
+            "type": "string",
+            "enum": ["2200-07-15T23:59:59Z"]
+          },
+          "link": {
+            "type": "object",
+            "required": [
+              "@libre.graph.displayName",
+              "@libre.graph.quickLink",
+              "preventsDownload",
+              "type",
+              "webUrl"
+            ],
+            "properties": {
+              "@libre.graph.displayName": {
+                "type": "string",
+                "enum": ["Homework"]
+              },
+              "@libre.graph.quickLink": {
+                "type": "boolean",
+                "enum": [false]
+              },
+              "preventsDownload": {
+                "type": "boolean",
+                "enum": [false]
+              },
+              "type": {
+                "type": "string",
+                "enum": ["<role>"]
+              },
+              "webUrl": {
+                "type": "string",
+                "pattern": "^%base_url%\/s\/[a-zA-Z]{15}$"
+              }
+            }
+          }
+        }
+      }
+      """
+    Examples:
+      | role           |
+      | view           |
+      | edit           |
+      | blocksDownload |