diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9d2efcb4..7fb61e8f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,6 +62,10 @@ jobs: --chain.chainId 1717658228 \ --wallet.seed "spoon ostrich survey tumble tube used person also wasp rack cabbage liberty" & working-directory: ./pkg/ethereum + - name: 🤖 Start cerbos + run: | + pnpm exec cerbos server --config=./test/.cerbos.yaml & + working-directory: ./pkg/cerbos - name: 🧪 Run the tests run: pnpm coverage - name: 🧪 Test grammar package diff --git a/.gitignore b/.gitignore index 93db1c6a..7f6464b0 100644 --- a/.gitignore +++ b/.gitignore @@ -97,5 +97,10 @@ pkg/oauth/test/didroom_microservices/ !/pkg/oauth/test/credential_issuer.keys.json !/pkg/oauth/test/relying_party.keys.json +# cerbos test stuff +!/pkg/cerbos/test/.cerbos.yaml +!/pkg/cerbos/test/policies/document.yaml +!/pkg/cerbos/test/policies/_schemas/document.json + # mise stuff !/mise.toml diff --git a/docs/statements/index.mjs b/docs/statements/index.mjs index 42237b89..b9d2caf1 100644 --- a/docs/statements/index.mjs +++ b/docs/statements/index.mjs @@ -9,6 +9,7 @@ import {markdownTable} from "markdown-table"; // slangroom import {Slangroom} from "@slangroom/core"; // packages +import {cerbos} from "@slangroom/cerbos"; import {db} from "@slangroom/db"; import {dcql} from "@slangroom/dcql"; import {did} from "@slangroom/did"; @@ -58,6 +59,7 @@ const generateTable = (plugin, name) => { } [ + [cerbos, 'cerbos'], [db, 'db'], [dcql, 'dcql'], [did, 'did'], diff --git a/docs/statements/package.json b/docs/statements/package.json index 737d0332..472fb0b7 100644 --- a/docs/statements/package.json +++ b/docs/statements/package.json @@ -10,6 +10,7 @@ "author": "", "license": "ISC", "dependencies": { + "@slangroom/cerbos": "workspace:^", "@slangroom/core": "workspace:^", "@slangroom/db": "workspace:^", "@slangroom/dcql": "workspace:^", diff --git a/examples/cerbos/evaluate_access.data.json b/examples/cerbos/evaluate_access.data.json new file mode 100644 index 00000000..254d8dd6 --- /dev/null +++ b/examples/cerbos/evaluate_access.data.json @@ -0,0 +1,19 @@ +{ + "principal": { + "id": "user@example.com", + "roles": [ + "user" + ], + "attr": { + "tier": "PREMIUM" + } + }, + "resource": { + "kind": "document", + "id": "1", + "attr": { + "owner": "user@example.com" + } + }, + "action": "view" +} diff --git a/examples/cerbos/evaluate_access.keys.json b/examples/cerbos/evaluate_access.keys.json new file mode 100644 index 00000000..df9e4de9 --- /dev/null +++ b/examples/cerbos/evaluate_access.keys.json @@ -0,0 +1,3 @@ +{ + "cerbos": "http://localhost:3592" +} diff --git a/examples/cerbos/evaluate_access.meta.json b/examples/cerbos/evaluate_access.meta.json new file mode 100644 index 00000000..c4bc1477 --- /dev/null +++ b/examples/cerbos/evaluate_access.meta.json @@ -0,0 +1,4 @@ +{ + "title": "evaluate access for principal to perform action on resource", + "highlight": "1" +} diff --git a/examples/cerbos/evaluate_access.slang b/examples/cerbos/evaluate_access.slang new file mode 100644 index 00000000..765df851 --- /dev/null +++ b/examples/cerbos/evaluate_access.slang @@ -0,0 +1,4 @@ +Prepare 'res': connect to 'cerbos' and evaluate access with principal 'principal', resource 'resource', action 'action' + +Given I have a 'boolean' named 'res' +Then print data diff --git a/pkg/cerbos/package.json b/pkg/cerbos/package.json new file mode 100644 index 00000000..95b6aff7 --- /dev/null +++ b/pkg/cerbos/package.json @@ -0,0 +1,40 @@ +{ + "name": "@slangroom/cerbos", + "version": "1.51.0", + "dependencies": { + "@cerbos/http": "0.23.4", + "@slangroom/core": "workspace:*", + "@slangroom/shared": "workspace:*", + "zod": "4.1.13" + }, + "repository": "https://github.com/dyne/slangroom", + "license": "AGPL-3.0-only", + "type": "module", + "main": "./build/esm/src/index.js", + "types": "./build/esm/src/index.d.ts", + "exports": { + ".": { + "import": { + "types": "./build/esm/src/index.d.ts", + "default": "./build/esm/src/index.js" + } + }, + "./*": { + "import": { + "types": "./build/esm/src/*.d.ts", + "default": "./build/esm/src/*.js" + } + }, + "./package.json": "./package.json" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": "^18.20.0 || ^20.10.0 || >=22" + }, + "devDependencies": { + "cerbos": "^0.47.0", + "cerbosctl": "^0.47.0" + } +} diff --git a/pkg/cerbos/package.json.license b/pkg/cerbos/package.json.license new file mode 100644 index 00000000..e7fc1c66 --- /dev/null +++ b/pkg/cerbos/package.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2024 Dyne.org foundation + +SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/pkg/cerbos/src/index.ts b/pkg/cerbos/src/index.ts new file mode 100644 index 00000000..682f2330 --- /dev/null +++ b/pkg/cerbos/src/index.ts @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: 2024 Dyne.org foundation +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +export * from '@slangroom/cerbos/plugin'; diff --git a/pkg/cerbos/src/plugin.ts b/pkg/cerbos/src/plugin.ts new file mode 100644 index 00000000..9b343d58 --- /dev/null +++ b/pkg/cerbos/src/plugin.ts @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: 2024 Dyne.org foundation +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +import { Plugin } from '@slangroom/core'; +import { HTTP } from "@cerbos/http"; +// read the version from the package.json +import packageJson from '@slangroom/cerbos/package.json' with { type: 'json' }; +import { actionSchema, principalSchema, resourceSchema } from './types.js'; + +export const version = packageJson.version; + +export class CerbosError extends Error { + constructor(message: string) { + super(message); + this.name = 'Slangroom @slangroom/cerbos@' + packageJson.version + ' Error'; + } +}; + +const p = new Plugin(); + +/** + * @internal + */ +export const allowed = p.new('connect', + ['principal', 'resource', 'action'], + 'evaluate access', + async (ctx) => { + const cerbosUrl = ctx.fetchConnect()[0]; + const { success: principalIsValid, data: principal } = principalSchema.safeParse(ctx.fetch("principal")); + if (!principalIsValid) + return ctx.fail(new CerbosError("Principal is not valid")); + const { success: resourceIsValid, data: resource } = resourceSchema.safeParse(ctx.fetch("resource")); + if (!resourceIsValid) + return ctx.fail(new CerbosError("Resource is not valid")); + const { success: actionIsValid, data: action } = actionSchema.safeParse(ctx.fetch("action")); + if (!actionIsValid) + return ctx.fail(new CerbosError("Action is not valid")); + try { + const cerbos = new HTTP(cerbosUrl); + const result = await cerbos.isAllowed({ + principal, + resource, + action, + }); + return ctx.pass(result); + } catch (e) { + return ctx.fail(new CerbosError(e.message)); + } + } +) + +export const cerbos = p; diff --git a/pkg/cerbos/src/types.ts b/pkg/cerbos/src/types.ts new file mode 100644 index 00000000..18cf5b84 --- /dev/null +++ b/pkg/cerbos/src/types.ts @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: 2025 Dyne.org foundation +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +import { z } from 'zod'; + +type Value = + | string + | number + | boolean + | null + | { [key: string]: Value } + | Value[]; + +const valueSchema: z.ZodType = z.lazy(() => + z.union([ + z.string(), + z.number(), + z.boolean(), + z.null(), + z.array(valueSchema), + z.record(z.string(), valueSchema), + ]) +); + +export const principalSchema = z.object({ + id: z.string(), + roles: z.array(z.string()), + attr: z.record(z.string(), valueSchema).optional(), + attributes: z.record(z.string(), valueSchema).optional(), // deprecated, prefer attr + policyVersion: z.string().optional(), + scope: z.string().optional(), +}); + +export const resourceSchema = z.object({ + kind: z.string(), + id: z.string(), + attr: z.record(z.string(), valueSchema).optional(), + attributes: z.record(z.string(), valueSchema).optional(), // deprecated, prefer attr + policyVersion: z.string().optional(), + scope: z.string().optional(), +}); + +export const actionSchema = z.string(); diff --git a/pkg/cerbos/test/.cerbos.yaml b/pkg/cerbos/test/.cerbos.yaml new file mode 100644 index 00000000..4c1bed01 --- /dev/null +++ b/pkg/cerbos/test/.cerbos.yaml @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: 2024 Dyne.org foundation +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +engine: + defaultPolicyVersion: "default" +server: + httpListenAddr: ":3592" +storage: + driver: "disk" + disk: + directory: test/policies +schema: + enforcement: reject diff --git a/pkg/cerbos/test/e2e.ts b/pkg/cerbos/test/e2e.ts new file mode 100644 index 00000000..0135e877 --- /dev/null +++ b/pkg/cerbos/test/e2e.ts @@ -0,0 +1,138 @@ +// SPDX-FileCopyrightText: 2024 Dyne.org foundation +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +import test from 'ava'; +import { Slangroom } from '@slangroom/core'; +import { cerbos } from '@slangroom/cerbos'; +import packageJson from '@slangroom/cerbos/package.json' with { type: 'json' }; + +const stripAnsiCodes = (str: string) => str.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, '').replace(/[ \t]+(?=\r?\n|$)/g, ''); + +const script = ` +Prepare 'res': connect to 'cerbos' and evaluate access with principal 'principal', resource 'resource', action 'action' + +Given I have a 'boolean' named 'res' +Then print data +`; + +test('Valid access', async (t) => { + const slangroom = new Slangroom(cerbos); + const res = await slangroom.execute(script, { + data: { + cerbos: "http://localhost:3592", + principal: { + id: "user@example.com", + roles: ["user"], + attr: { tier: "PREMIUM" }, + }, + resource: { + kind: "document", + id: "1", + attr: { owner: "user@example.com" }, + }, + action: "view", + }, + }); + t.deepEqual(res.result, { res: true }, JSON.stringify(res, null, 2)); +}); + +test('Invalid access [tier not met]', async (t) => { + const slangroom = new Slangroom(cerbos); + const res = await slangroom.execute(script, { + data: { + cerbos: "http://localhost:3592", + principal: { + id: "user@example.com", + roles: ["user"], + attr: { tier: "GOLD" }, + }, + resource: { + kind: "document", + id: "1", + attr: { owner: "user@example.com" }, + }, + action: "view", + }, + }); + t.deepEqual(res.result, { res: false }, JSON.stringify(res, null, 2)); +}); + +test('Invalid access [action not allowed]', async (t) => { + const slangroom = new Slangroom(cerbos); + const res = await slangroom.execute(script, { + data: { + cerbos: "http://localhost:3592", + principal: { + id: "user@example.com", + roles: ["user"], + attr: { tier: "PREMIUM" }, + }, + resource: { + kind: "document", + id: "1", + attr: { owner: "user@example.com" }, + }, + action: "delete", + }, + }); + t.deepEqual(res.result, { res: false }, JSON.stringify(res, null, 2)); +}); + +test('Invalid url', async (t) => { + const slangroom = new Slangroom(cerbos); + const fn = slangroom.execute(script, { + data: { + cerbos: "not_a_url", + principal: { + id: "user@example.com", + roles: ["user"], + attr: { tier: "PREMIUM" }, + }, + resource: { + kind: "document", + id: "1", + attr: { owner: "user@example.com" }, + }, + action: "view", + }, + }); + const error = await t.throwsAsync(fn); + t.is(stripAnsiCodes((error as Error).message), +`0 | +1 | Prepare 'res': connect to 'cerbos' and evaluate access with principal 'principal', resource 'resource', action 'action' + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +2 | +3 | Given I have a 'boolean' named 'res' + +Error colors: + - error + - suggested words + - missing words + - extra words + +Slangroom @slangroom/cerbos@${packageJson.version} Error: gRPC error 2 (UNKNOWN): Request failed: Failed to parse URL from not_a_url/api/check/resources + +Heap: +{ + "cerbos": "not_a_url", + "principal": { + "id": "user@example.com", + "roles": [ + "user" + ], + "attr": { + "tier": "PREMIUM" + } + }, + "resource": { + "kind": "document", + "id": "1", + "attr": { + "owner": "user@example.com" + } + }, + "action": "view" +} +`); +}); diff --git a/pkg/cerbos/test/policies/_schemas/document.json b/pkg/cerbos/test/policies/_schemas/document.json new file mode 100644 index 00000000..9732b1a0 --- /dev/null +++ b/pkg/cerbos/test/policies/_schemas/document.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "owner": { + "type": "string" + } + }, + "required": [ + "owner" + ] +} diff --git a/pkg/cerbos/test/policies/_schemas/document.json.license b/pkg/cerbos/test/policies/_schemas/document.json.license new file mode 100644 index 00000000..f63d8687 --- /dev/null +++ b/pkg/cerbos/test/policies/_schemas/document.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2025 Dyne.org foundation + +SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/pkg/cerbos/test/policies/document.yaml b/pkg/cerbos/test/policies/document.yaml new file mode 100644 index 00000000..121682c6 --- /dev/null +++ b/pkg/cerbos/test/policies/document.yaml @@ -0,0 +1,30 @@ +# SPDX-FileCopyrightText: 2024 Dyne.org foundation +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +apiVersion: api.cerbos.dev/v1 +resourcePolicy: + version: "default" + resource: "document" + rules: + - actions: + - create + - read + effect: EFFECT_ALLOW + roles: + - user + + - actions: + - view + effect: EFFECT_ALLOW + roles: + - user + condition: + match: + expr: P.attr.tier == "PREMIUM" && R.attr.owner == P.id + + - actions: + - "*" + effect: EFFECT_ALLOW + roles: + - admin diff --git a/pkg/cerbos/tsconfig.json b/pkg/cerbos/tsconfig.json new file mode 120000 index 00000000..fd0e4743 --- /dev/null +++ b/pkg/cerbos/tsconfig.json @@ -0,0 +1 @@ +../../tsconfig.json \ No newline at end of file diff --git a/pkg/cerbos/tsconfig.json.license b/pkg/cerbos/tsconfig.json.license new file mode 100644 index 00000000..e7fc1c66 --- /dev/null +++ b/pkg/cerbos/tsconfig.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2024 Dyne.org foundation + +SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ba661178..85505a4a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -53,6 +53,9 @@ importers: docs/statements: dependencies: + '@slangroom/cerbos': + specifier: workspace:^ + version: link:../../pkg/cerbos '@slangroom/core': specifier: workspace:^ version: link:../../pkg/core @@ -305,6 +308,28 @@ importers: specifier: ^1.0.1 version: 1.0.1 + pkg/cerbos: + dependencies: + '@cerbos/http': + specifier: 0.23.4 + version: 0.23.4(@bufbuild/protobuf@2.10.1)(@cerbos/api@0.3.0) + '@slangroom/core': + specifier: workspace:* + version: link:../core + '@slangroom/shared': + specifier: workspace:* + version: link:../shared + zod: + specifier: 4.1.13 + version: 4.1.13 + devDependencies: + cerbos: + specifier: ^0.47.0 + version: 0.47.0 + cerbosctl: + specifier: ^0.47.0 + version: 0.47.0 + pkg/core: dependencies: '@slangroom/deps': @@ -366,7 +391,7 @@ importers: version: 10.5.0 zenroom: specifier: ^5.0.0 - version: 5.25.1 + version: 5.28.5 pkg/did: dependencies: @@ -718,6 +743,9 @@ packages: '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + '@bufbuild/protobuf@2.10.1': + resolution: {integrity: sha512-ckS3+vyJb5qGpEYv/s1OebUHDi/xSNtfgw1wqKZo7MR9F2z+qXr0q5XagafAG/9O0QPVIUfST0smluYSTpYFkg==} + '@capacitor/core@6.0.0': resolution: {integrity: sha512-NvxIQsJcMiIV+Le1DilR2GGyQQbDInfXK1UywGROQ5mycdFlW5XoAPZ+MKnFGB123RoEgE3uhDGgwTXUmSlX9A==} @@ -726,6 +754,63 @@ packages: peerDependencies: '@capacitor/core': ^6.0.0 + '@cerbos/api@0.3.0': + resolution: {integrity: sha512-5FDET4b/483FxT32OoUspzJd80v07WBE8TjoD96mmpAtlGah0w3jRqu7ST7imXS8mrSENvmVSoEYvQc1fMuJPw==} + engines: {node: '>= 20'} + + '@cerbos/cerbos-darwin-arm64@0.47.0': + resolution: {integrity: sha512-iosce8nO5gCviXltdP0HQpxiB0x3WWAMpwef1dZcQeNEuyPnxI83WIKfGJo2kmpjaqkhlY1FhndzR0FI4IDtCw==} + cpu: [arm64] + os: [darwin] + + '@cerbos/cerbos-darwin-x64@0.47.0': + resolution: {integrity: sha512-72OPabYn2tqrSVqmpabd2y3V5C3oCBqQct5gvbxhn4+Sd1XhP0jJOfMYF5YQOiKKV+OPMf6xv3VQ8PXqsPViCQ==} + cpu: [x64] + os: [darwin] + + '@cerbos/cerbos-linux-arm64@0.47.0': + resolution: {integrity: sha512-Od3u0jpNts4bodeugrG8ZopGFyq/Qp2nditND11ZKs60KnHSvuLdIGraR0Ey23bazPZzcrUmVoSIpEeCxJlbNw==} + cpu: [arm64] + os: [linux] + + '@cerbos/cerbos-linux-x64@0.47.0': + resolution: {integrity: sha512-E3S0YeXRZrrnp7ZEHNilinjR9WBcJWIngvAYUISQwpPs8IimQhg8zFMrib4/hIBz6zVeXzt2Aa4+1d3A0XEErw==} + cpu: [x64] + os: [linux] + + '@cerbos/cerbosctl-darwin-arm64@0.47.0': + resolution: {integrity: sha512-mbrNr6HuBR3zg6NqkgG/DbRROB3c4x9qt6m9HX3TMucl7SC3lOJqyB6Vm3sGiaK/hdzjaYRmP1P4Z/EUlrwGmA==} + cpu: [arm64] + os: [darwin] + + '@cerbos/cerbosctl-darwin-x64@0.47.0': + resolution: {integrity: sha512-u98iEKG0PU/bZbCEHXdGcyG1fVimS6Jw6XUvhTuD08LHx3VOoOIKKSmEAPkgqWqbDJ3enFd7ATBA0txVGhWOHg==} + cpu: [x64] + os: [darwin] + + '@cerbos/cerbosctl-linux-arm64@0.47.0': + resolution: {integrity: sha512-HnG1F1CqS3DKkoK7ZCZlv5Q2bMmfJJaElBNx9JnOizEnMeuY08/byozIy+XEytvnRkf/vGwvAkN9DZAG9dIc+g==} + cpu: [arm64] + os: [linux] + + '@cerbos/cerbosctl-linux-x64@0.47.0': + resolution: {integrity: sha512-q6n+SlULIwK1EEagmd2ucdaIWkND5d70yqqiLz0U7metw1BLOqSmAK8lLF0SbDdZAHin5AvxuzgwUq0tXop1vg==} + cpu: [x64] + os: [linux] + + '@cerbos/core@0.25.2': + resolution: {integrity: sha512-iPVTTxuxRR+y6Xe4O61LoJQ7afZGobMZrVqKlI+sEg8YrPoJel+NOtZn2D4xAFO2hBEUROr/uoYp400FZ7Pzrg==} + engines: {node: '>= 20'} + peerDependencies: + '@bufbuild/protobuf': ^2.10.1 + + '@cerbos/http@0.23.4': + resolution: {integrity: sha512-JtY/rrE43kJqF4qCoyPo+2ogcm3bJ6UMIFLxfg2JlLF2Sz4e7aIMRfvKDneZGWVkbWgYDNJnzEQHm5/zkkVu0w==} + engines: {node: '>= 20'} + peerDependencies: + '@bufbuild/protobuf': ^2.10.1 + '@cerbos/api': ^0.3.0 + '@chevrotain/cst-dts-gen@10.5.0': resolution: {integrity: sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw==} @@ -2255,9 +2340,17 @@ packages: resolution: {integrity: sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==} engines: {node: ^16.14.0 || >=18.0.0} + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + call-bind@1.0.5: resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==} + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -2289,6 +2382,14 @@ packages: ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + cerbos@0.47.0: + resolution: {integrity: sha512-5YvzrpqMQ5cH2Qh8Nui9ge6qJaIeDfC7Wa/EZUg49IBTMx1D3/smW8EwjJhr2VH96uSH+Cx3X1UZoUrlIBUR0Q==} + hasBin: true + + cerbosctl@0.47.0: + resolution: {integrity: sha512-O1A14DYtR3wWP0IfLAIHm2vdZuhYXtJQRCEA1xXwCTzcPliu+EwfU5u71K6epESttRbxMGA/1XJe/5Dc4pQZGw==} + hasBin: true + chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -2692,6 +2793,10 @@ packages: dottie@2.0.6: resolution: {integrity: sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA==} + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + duplexer2@0.1.4: resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==} @@ -2749,6 +2854,18 @@ packages: error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + esbuild@0.21.5: resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} engines: {node: '>=12'} @@ -3068,6 +3185,14 @@ packages: get-intrinsic@1.2.2: resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==} + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} @@ -3137,6 +3262,10 @@ packages: gopd@1.0.1: resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + graceful-fs@4.2.10: resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} @@ -3174,6 +3303,10 @@ packages: resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} engines: {node: '>= 0.4'} + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + has-tostringtag@1.0.0: resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} engines: {node: '>= 0.4'} @@ -3748,6 +3881,10 @@ packages: resolution: {integrity: sha512-s2EMBOWtXFc8dgqvoAzKJXxNHibcdJMV0gwqKUaw9E2JBJuGUK7DrNKrA6g/i+v72TT16+6sVm5mS3thaMLQUw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + md5-hex@3.0.1: resolution: {integrity: sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==} engines: {node: '>=8'} @@ -4173,6 +4310,10 @@ packages: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -4538,6 +4679,10 @@ packages: engines: {node: '>=10.13.0'} hasBin: true + qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -4782,6 +4927,22 @@ packages: shiki@1.22.0: resolution: {integrity: sha512-/t5LlhNs+UOKQCYBtl5ZsH/Vclz73GIqT2yQsCBygr8L/ppTdmpL4w3kPLoZJbMKVWtoG77Ue1feOjZfDxvMkw==} + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} @@ -5277,6 +5438,10 @@ packages: resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} hasBin: true + uuid@13.0.0: + resolution: {integrity: sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==} + hasBin: true + uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true @@ -5621,9 +5786,8 @@ packages: resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==} engines: {node: '>=18'} - zenroom@5.25.1: - resolution: {integrity: sha512-lQJUApp2IIIFQTbTp5TcE3g80oRpUHyed7hq/26hxST48Y/DugK8tovdRJlOYTNE0Q9dmMFHuCiNCWuvl8d+tg==} - engines: {node: '>=22'} + zenroom@5.28.5: + resolution: {integrity: sha512-llT5iXrd5kaE7YpGMGK3CFhszyH8cL49fwmbuIYkJFd7YuB0mJ+gkBDWYelwlK0nN0leMsfoRqwSvBq82MDkaQ==} zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -5631,6 +5795,9 @@ packages: zod@4.1.12: resolution: {integrity: sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==} + zod@4.1.13: + resolution: {integrity: sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==} + zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} @@ -5784,6 +5951,8 @@ snapshots: '@bcoe/v8-coverage@0.2.3': {} + '@bufbuild/protobuf@2.10.1': {} + '@capacitor/core@6.0.0': dependencies: tslib: 2.8.1 @@ -5792,6 +5961,47 @@ snapshots: dependencies: '@capacitor/core': 6.0.0 + '@cerbos/api@0.3.0': + dependencies: + '@bufbuild/protobuf': 2.10.1 + + '@cerbos/cerbos-darwin-arm64@0.47.0': + optional: true + + '@cerbos/cerbos-darwin-x64@0.47.0': + optional: true + + '@cerbos/cerbos-linux-arm64@0.47.0': + optional: true + + '@cerbos/cerbos-linux-x64@0.47.0': + optional: true + + '@cerbos/cerbosctl-darwin-arm64@0.47.0': + optional: true + + '@cerbos/cerbosctl-darwin-x64@0.47.0': + optional: true + + '@cerbos/cerbosctl-linux-arm64@0.47.0': + optional: true + + '@cerbos/cerbosctl-linux-x64@0.47.0': + optional: true + + '@cerbos/core@0.25.2(@bufbuild/protobuf@2.10.1)': + dependencies: + '@bufbuild/protobuf': 2.10.1 + '@cerbos/api': 0.3.0 + uuid: 13.0.0 + + '@cerbos/http@0.23.4(@bufbuild/protobuf@2.10.1)(@cerbos/api@0.3.0)': + dependencies: + '@bufbuild/protobuf': 2.10.1 + '@cerbos/api': 0.3.0 + '@cerbos/core': 0.25.2(@bufbuild/protobuf@2.10.1) + qs: 6.14.0 + '@chevrotain/cst-dts-gen@10.5.0': dependencies: '@chevrotain/gast': 10.5.0 @@ -7600,12 +7810,22 @@ snapshots: tar: 6.2.1 unique-filename: 3.0.0 + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + call-bind@1.0.5: dependencies: function-bind: 1.1.2 get-intrinsic: 1.2.2 set-function-length: 1.1.1 + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + callsites@3.1.0: {} callsites@4.2.0: {} @@ -7624,6 +7844,20 @@ snapshots: ccount@2.0.1: {} + cerbos@0.47.0: + optionalDependencies: + '@cerbos/cerbos-darwin-arm64': 0.47.0 + '@cerbos/cerbos-darwin-x64': 0.47.0 + '@cerbos/cerbos-linux-arm64': 0.47.0 + '@cerbos/cerbos-linux-x64': 0.47.0 + + cerbosctl@0.47.0: + optionalDependencies: + '@cerbos/cerbosctl-darwin-arm64': 0.47.0 + '@cerbos/cerbosctl-darwin-x64': 0.47.0 + '@cerbos/cerbosctl-linux-arm64': 0.47.0 + '@cerbos/cerbosctl-linux-x64': 0.47.0 + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 @@ -8008,6 +8242,12 @@ snapshots: dottie@2.0.6: {} + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + duplexer2@0.1.4: dependencies: readable-stream: 2.3.8 @@ -8054,6 +8294,14 @@ snapshots: dependencies: is-arrayish: 0.2.1 + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + esbuild@0.21.5: optionalDependencies: '@esbuild/aix-ppc64': 0.21.5 @@ -8449,6 +8697,24 @@ snapshots: has-symbols: 1.0.3 hasown: 2.0.2 + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + get-stream@6.0.1: {} get-stream@7.0.1: {} @@ -8541,6 +8807,8 @@ snapshots: dependencies: get-intrinsic: 1.2.2 + gopd@1.2.0: {} + graceful-fs@4.2.10: {} graceful-fs@4.2.11: {} @@ -8570,6 +8838,8 @@ snapshots: has-symbols@1.0.3: {} + has-symbols@1.1.0: {} + has-tostringtag@1.0.0: dependencies: has-symbols: 1.0.3 @@ -9135,6 +9405,8 @@ snapshots: dependencies: escape-string-regexp: 5.0.0 + math-intrinsics@1.1.0: {} + md5-hex@3.0.1: dependencies: blueimp-md5: 2.19.0 @@ -9527,6 +9799,8 @@ snapshots: object-assign@4.1.1: {} + object-inspect@1.13.4: {} + once@1.4.0: dependencies: wrappy: 1.0.2 @@ -9859,6 +10133,10 @@ snapshots: pngjs: 5.0.0 yargs: 15.4.1 + qs@6.14.0: + dependencies: + side-channel: 1.1.0 + queue-microtask@1.2.3: {} randombytes@2.1.0: @@ -10145,6 +10423,34 @@ snapshots: '@shikijs/vscode-textmate': 9.3.0 '@types/hast': 3.0.4 + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + signal-exit@3.0.7: {} signal-exit@4.1.0: {} @@ -10652,6 +10958,8 @@ snapshots: uuid@10.0.0: {} + uuid@13.0.0: {} + uuid@8.3.2: {} v8-compile-cache-lib@3.0.1: {} @@ -11137,10 +11445,12 @@ snapshots: yoctocolors@2.1.1: {} - zenroom@5.25.1: {} + zenroom@5.28.5: {} zod@3.25.76: {} zod@4.1.12: {} + zod@4.1.13: {} + zwitch@2.0.4: {}