diff --git a/bun.lock b/bun.lock index 00d8776c..bbbb024a 100644 --- a/bun.lock +++ b/bun.lock @@ -257,7 +257,7 @@ }, "packages/authz": { "name": "@wrnexus/authz", - "version": "0.8.14", + "version": "0.8.15", "dependencies": { "@wrnexus/core": "workspace:*", "@wrnexus/db": "workspace:*", diff --git a/packages/authz/package.json b/packages/authz/package.json index fbeefd12..d3d0d119 100644 --- a/packages/authz/package.json +++ b/packages/authz/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/authz", - "version": "0.8.14", + "version": "0.8.15", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/authz/src/registry.ts b/packages/authz/src/registry.ts index ef86c987..430eee48 100644 --- a/packages/authz/src/registry.ts +++ b/packages/authz/src/registry.ts @@ -16,6 +16,11 @@ export function defineAuthz( const bindings = module.bindings ?? {}; for (const id of Object.keys(permissions)) { + if (id.startsWith("role:")) { + throw new Error( + `WRN-AUTHZ-DECL: permission id '${id}' uses the reserved 'role:' prefix; role grants use 'role:' for inheritance.`, + ); + } if (id.includes("*")) { throw new Error( `WRN-AUTHZ-DECL: permission id '${id}' must not contain a wildcard; wildcards belong in roles.`, diff --git a/packages/authz/test/registry.test.ts b/packages/authz/test/registry.test.ts index 8fef1d4f..d7c7d165 100644 --- a/packages/authz/test/registry.test.ts +++ b/packages/authz/test/registry.test.ts @@ -26,6 +26,12 @@ describe("defineAuthz", () => { expect(() => defineAuthz({ permissions: { "post:*": {} } })).toThrow(/wildcard/i); }); + test("rejects permission ids that collide with role inheritance", () => { + expect(() => defineAuthz({ permissions: { "role:assign": {} } })).toThrow( + /reserved 'role:' prefix/i, + ); + }); + test("rejects a role granting an unknown-shaped entry", () => { expect(() => defineAuthz({ roles: { editor: [""] } })).toThrow(/role 'editor'/i); });