Security [HIGH]: Resource.dynamicFields JSONB merge accepts attacker-controlled keys + unbounded metadata #48
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
batchUpdateCustomFieldsin resource-mutations.ts uses$executeRawto merge${JSON.stringify(input.fields)}::jsonbinto Resource.dynamicFields. Keys are user-provided viaz.record(z.string(), ...)and are NOT validated against the blueprint'sfieldDefs[].keywhitelist. BypassesassertBlueprintDynamicFieldsused in regular create/update. Separately, allocation metadata usesz.record(z.string(), z.unknown())with no depth/size cap.Evidence
packages/api/src/router/resource-mutations.ts:335-339 — tx.$executeRaw with unvalidated keyspackages/api/src/router/resource-mutations.ts:323-326 — fields schema allows any string keypackages/api/src/router/allocation/support.ts:21,36,112 — metadata: z.record(z.string(), z.unknown())Impact
(1) Manager-role user overwrites arbitrary JSONB keys including fields never defined in blueprint → namespace pollution, possible privilege-escalation via admin-tool interpretation. (2) Metadata can carry unbounded attacker-controlled payload that flows back via read paths unparsed.
Proposed Fix
(1) Validate each key against
Blueprint.fieldDefs[].keywhitelist before merging. UsePrisma.sqltagged template with whitelist-constructed object. (2) Replacez.record(z.string(), z.unknown())withz.object({...}).strict()schemas per metadata-owning table.Acceptance Criteria
Parent Epic: #1
Source: Full-Codebase Security Audit 2026-04-16 (B-3, B-11, B-12)
Resolved in commit
c0c5f76(security: bound JSONB inputs + whitelist batchUpdateCustomFields keys). Resource.dynamicFields merge now goes through a whitelist of known keys; attacker-controlled keys are rejected. Inputs are capped on total byte size.