Skip to content

Commit e84519e

Browse files
committed
fix(permission-groups): read the successor map by its own keys only
1 parent 3e30fe3 commit e84519e

2 files changed

Lines changed: 43 additions & 5 deletions

File tree

apps/sim/lib/permission-groups/integration-allowlist.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,18 @@ describe('resolveAccessControlBlockType', () => {
4040
it('accepts the dashed spelling the registry also normalizes', () => {
4141
expect(resolveAccessControlBlockType('google-sheets')).toBe('google_sheets_v2')
4242
})
43+
44+
/**
45+
* `allowedIntegrations` is admin-supplied jsonb and `ALLOWED_INTEGRATIONS` is
46+
* hand-written, so an arbitrary string reaches the successor map. An
47+
* inherited key must stay an ordinary unresolved id rather than answering
48+
* with `Object.prototype`'s function.
49+
*/
50+
it('leaves an object-prototype key alone', () => {
51+
expect(resolveAccessControlBlockType('constructor')).toBe('constructor')
52+
expect(resolveAccessControlBlockType('toString')).toBe('toString')
53+
expect(resolveAccessControlBlockType('__proto__')).toBe('__proto__')
54+
})
4355
})
4456

4557
describe('toAccessControlAllowlist', () => {
@@ -61,6 +73,18 @@ describe('toAccessControlAllowlist', () => {
6173
it('denies everything for an empty allowlist', () => {
6274
expect(toAccessControlAllowlist([])?.size).toBe(0)
6375
})
76+
77+
/**
78+
* A prototype key used to resolve to an inherited function and throw on
79+
* `.toLowerCase()`, turning one configured string into a 500 on every
80+
* enforcement path that read the group.
81+
*/
82+
it('indexes an object-prototype entry as an ordinary block type', () => {
83+
const allowlist = toAccessControlAllowlist(['constructor', 'slack'])
84+
85+
expect(allowlist?.has('constructor')).toBe(true)
86+
expect(allowlist?.has('slack_v2')).toBe(true)
87+
})
6488
})
6589

6690
describe('intersectAccessControlAllowlists', () => {

apps/sim/lib/permission-groups/integration-allowlist.ts

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,25 @@ import { BLOCK_ACCESS_SUCCESSORS } from '@/lib/permission-groups/block-successor
1717
* `check:block-successors` fails the build when the projection drifts.
1818
*/
1919
export function resolveAccessControlBlockType(blockType: string): string {
20-
return (
21-
BLOCK_ACCESS_SUCCESSORS[blockType] ??
22-
BLOCK_ACCESS_SUCCESSORS[blockType.replace(/-/g, '_')] ??
23-
blockType
24-
)
20+
return ownSuccessor(blockType) ?? ownSuccessor(blockType.replace(/-/g, '_')) ?? blockType
21+
}
22+
23+
/**
24+
* Reads the successor map by its own keys only.
25+
*
26+
* The generated map is an object literal with an intact prototype, so a bare
27+
* bracket lookup answers `constructor`, `toString`, `valueOf` and friends with
28+
* an inherited function. The ids reaching here come from admin-supplied jsonb
29+
* (`allowedIntegrations`) and from `ALLOWED_INTEGRATIONS`, so a group naming
30+
* `constructor` made {@link toAccessControlAllowlist} call `.toLowerCase()` on
31+
* a function and throw — an unclassified 500 on every enforcement path that
32+
* read that group. `getBlock` guards the registry the same way for the same
33+
* reason.
34+
*/
35+
function ownSuccessor(blockType: string): string | undefined {
36+
return Object.hasOwn(BLOCK_ACCESS_SUCCESSORS, blockType)
37+
? BLOCK_ACCESS_SUCCESSORS[blockType]
38+
: undefined
2539
}
2640

2741
/**

0 commit comments

Comments
 (0)