{"record":{"id":"115f105ae983394b","repo":"can1357/oh-my-pi","slug":"delta-table-string-table-is-not-in-the-allowli","errorCode":null,"errorMessage":"Delta table ${String(table)} is not in the allowlist","messagePattern":"Delta table (.+?) is not in the allowlist","errorType":"validation","errorClass":"RangeError","httpStatus":null,"severity":"error","filePath":"packages/mnemopi/src/core/streaming.ts","lineNumber":288,"sourceCode":"\tstatic fromJSON(text: string): SyncCheckpoint {\n\t\treturn new SyncCheckpoint(JSON.parse(text) as SyncCheckpointInit);\n\t}\n}\n\ntype MemoryHost = {\n\treadonly conn?: Database;\n\treadonly db?: Database;\n\treadonly dbPath?: string;\n\treadonly db_path?: string;\n};\nfunction databaseOf(host: MemoryHost): Database {\n\tconst db = host.conn ?? host.db;\n\tif (db === undefined) throw new TypeError(\"DeltaSync requires a memory object with conn or db\");\n\treturn db;\n}\nfunction assertDeltaTable(table: unknown): asserts table is DeltaTable {\n\tif (typeof table !== \"string\" || !ALLOWED_DELTA_TABLES.has(table as DeltaTable))\n\t\tthrow new RangeError(`Delta table ${String(table)} is not in the allowlist`);\n}\nfunction checkpointRoot(host: MemoryHost): string {\n\tconst path = host.dbPath ?? host.db_path;\n\treturn path === undefined || path === \":memory:\"\n\t\t? join(process.cwd(), \".mnemopi-sync\")\n\t\t: join(path, \"..\", \"sync_checkpoints\");\n}\n\nexport class DeltaSync {\n\treadonly checkpointDir: string;\n\tprivate readonly db: Database;\n\tconstructor(\n\t\treadonly mnemopi: MemoryHost,\n\t\tcheckpointDir?: string,\n\t) {\n\t\tthis.db = databaseOf(mnemopi);\n\t\tthis.checkpointDir = checkpointDir ?? checkpointRoot(mnemopi);\n\t\tmkdirSync(this.checkpointDir, { recursive: true });","sourceCodeStart":270,"sourceCodeEnd":306,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/mnemopi/src/core/streaming.ts#L270-L306","documentation":"Delta sync table names are restricted to an explicit allowlist (ALLOWED_DELTA_TABLES). assertDeltaTable validates any user-supplied table name and throws a RangeError for anything not in that set, preventing SQL injection and checkpoint/delta corruption from arbitrary table names.","triggerScenarios":"Calling getCheckpoint, saveCheckpoint, setCheckpoint, computeDelta, or applyDelta with a table argument that is not a string or is not one of the allowlisted DeltaTable names; typos in the table name (e.g. \"memmories\"); dynamically built table names with suffixes or prefixes.","commonSituations":"Building the table name from user input or env config; schema renames after a migration; concatenating prefixes like \"tmp_\" or schema qualifiers like \"main.memories\"; passing a table name with different casing than the allowlist.","solutions":["Use one of the exact allowlisted table-name string literals (import the DeltaTable type if available)","Log/inspect ALLOWED_DELTA_TABLES to see the valid names and correct the call site","If a new table is genuinely needed, add it to the allowlist in streaming.ts rather than bypassing the check"],"exampleFix":"// before\nsync.computeDelta(\"main.memories\" as string);\n// after\nsync.computeDelta(\"memories\"); // exact allowlisted name","handlingStrategy":"validation","validationCode":"const ALLOWED = [\"memories\", /* ... other allowlisted names */];\nfunction assertTable(table) {\n\tif (typeof table !== \"string\" || !ALLOWED.includes(table)) throw new RangeError(`table ${table} not allowlisted`);\n}","typeGuard":"function isDeltaTable(table) {\n\treturn typeof table === \"string\" && ALLOWED_DELTA_TABLES.has(table);\n}","tryCatchPattern":"try {\n\tsync.computeDelta(table);\n} catch (err) {\n\tif (err instanceof RangeError && err.message.includes(\"allowlist\")) {\n\t\tlogger.warn(\"rejecting non-allowlisted delta table\", { table });\n\t\treturn null;\n\t}\n\tthrow err;\n}","preventionTips":["Use only string literals from the DeltaTable union type","Never build table names from user input or string concatenation","Keep table names lowercase and exact; avoid schema-qualified names","Update the allowlist (not call sites) when a new table is truly needed"],"tags":["validation","whitelist","sql"],"backgroundTag":"invalid-identifier-allowlist","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}