@substrat-run/kernel 0.138.0 → 0.139.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/attachment-extractor.d.ts +9 -1
  2. package/dist/attachment-extractor.d.ts.map +1 -1
  3. package/dist/attachment-extractor.js +21 -1
  4. package/dist/attachment-extractor.js.map +1 -1
  5. package/dist/attachment-text.d.ts +64 -0
  6. package/dist/attachment-text.d.ts.map +1 -1
  7. package/dist/attachment-text.js +87 -16
  8. package/dist/attachment-text.js.map +1 -1
  9. package/dist/attribution.d.ts +36 -3
  10. package/dist/attribution.d.ts.map +1 -1
  11. package/dist/attribution.js +21 -8
  12. package/dist/attribution.js.map +1 -1
  13. package/dist/entity-state-reads.d.ts +43 -0
  14. package/dist/entity-state-reads.d.ts.map +1 -0
  15. package/dist/entity-state-reads.js +135 -0
  16. package/dist/entity-state-reads.js.map +1 -0
  17. package/dist/entity-state.d.ts +177 -0
  18. package/dist/entity-state.d.ts.map +1 -0
  19. package/dist/entity-state.js +345 -0
  20. package/dist/entity-state.js.map +1 -0
  21. package/dist/findings.d.ts +143 -0
  22. package/dist/findings.d.ts.map +1 -0
  23. package/dist/findings.js +463 -0
  24. package/dist/findings.js.map +1 -0
  25. package/dist/index.d.ts +13 -7
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +10 -6
  28. package/dist/index.js.map +1 -1
  29. package/dist/list-index.d.ts +25 -3
  30. package/dist/list-index.d.ts.map +1 -1
  31. package/dist/list-index.js +70 -26
  32. package/dist/list-index.js.map +1 -1
  33. package/dist/membership-executor.d.ts +17 -5
  34. package/dist/membership-executor.d.ts.map +1 -1
  35. package/dist/membership-executor.js +26 -11
  36. package/dist/membership-executor.js.map +1 -1
  37. package/dist/module-migrations.d.ts +6 -3
  38. package/dist/module-migrations.d.ts.map +1 -1
  39. package/dist/module-migrations.js +12 -5
  40. package/dist/module-migrations.js.map +1 -1
  41. package/dist/permission-eval.d.ts +41 -0
  42. package/dist/permission-eval.d.ts.map +1 -1
  43. package/dist/permission-eval.js +53 -0
  44. package/dist/permission-eval.js.map +1 -1
  45. package/dist/platform-sweep.d.ts +7 -1
  46. package/dist/platform-sweep.d.ts.map +1 -1
  47. package/dist/platform-sweep.js +12 -0
  48. package/dist/platform-sweep.js.map +1 -1
  49. package/dist/scope-host.d.ts +187 -11
  50. package/dist/scope-host.d.ts.map +1 -1
  51. package/dist/scope-host.js +10 -2
  52. package/dist/scope-host.js.map +1 -1
  53. package/dist/scope-role-admin.d.ts +68 -0
  54. package/dist/scope-role-admin.d.ts.map +1 -0
  55. package/dist/scope-role-admin.js +80 -0
  56. package/dist/scope-role-admin.js.map +1 -0
  57. package/dist/search-index.d.ts +13 -1
  58. package/dist/search-index.d.ts.map +1 -1
  59. package/dist/search-index.js +9 -16
  60. package/dist/search-index.js.map +1 -1
  61. package/dist/spine-guard.d.ts +64 -2
  62. package/dist/spine-guard.d.ts.map +1 -1
  63. package/dist/spine-guard.js +345 -10
  64. package/dist/spine-guard.js.map +1 -1
  65. package/dist/sql-identifier.d.ts +12 -0
  66. package/dist/sql-identifier.d.ts.map +1 -0
  67. package/dist/sql-identifier.js +17 -0
  68. package/dist/sql-identifier.js.map +1 -0
  69. package/package.json +2 -2
@@ -6,7 +6,61 @@ import type { ScopedSql } from './scope-host.js';
6
6
  * is a fault in the module, not in the caller's permissions, and the message names
7
7
  * the table so the author sees which line to delete.
8
8
  */
9
- export declare function assertNoSpineWrite(sql: string): void;
9
+ export declare function assertNoSpineWrite(sql: string, statefulTables?: ReadonlySet<string>): void;
10
+ /**
11
+ * Refuse runtime DDL that would take a stateful table's guarantees away (#119, Codex r3).
12
+ *
13
+ * #1811 made a module's own DDL through `ctx.sql` a supported path, and it stays one. But a table
14
+ * that carries archive/trash state is guarded by derived triggers ON THAT TABLE, and DDL can move
15
+ * the rows out from under them: `ALTER TABLE docs RENAME TO x; CREATE TABLE docs AS SELECT …,
16
+ * NULL AS _substrat_trashed_at FROM x` un-trashes everything with no move and no event. So, for a
17
+ * stateful table, in any spelling — quoted, any case, `main.`/`temp.`-qualified:
18
+ *
19
+ * - `ALTER TABLE <it>` that renames the table or a column, or drops a column; and `RENAME TO <it>`
20
+ * from another table;
21
+ * - `DROP TABLE <it>`;
22
+ * - `CREATE [TEMP] TABLE|VIEW <it>` — a temp object of the same name SHADOWS the real table in
23
+ * every unqualified reference, the kernel's own included;
24
+ * - `CREATE [TEMP] TRIGGER … ON <it>`.
25
+ *
26
+ * Plus, for any table, a trigger, index or view carrying the reserved prefix (the kernel's derived
27
+ * objects), and `ATTACH`/`DETACH` outright: nothing in module code needs a second database, and an
28
+ * attached one is a second place a stateful name can resolve. Ordinary runtime DDL — a module's own
29
+ * unrelated tables, an index on a stateful table, an added column — is untouched.
30
+ */
31
+ export declare function assertNoStatefulDdl(sql: string, statefulTables: ReadonlySet<string>): void;
32
+ /** Does this SQL change the schema — any statement in it a CREATE, ALTER or DROP? */
33
+ export declare function changesSchema(sql: string): boolean;
34
+ /**
35
+ * Refuse a statement that WRITES a `_substrat_*` column of a module's own table (#119).
36
+ *
37
+ * The kernel keeps state on module rows — `_substrat_archived_at`, `_substrat_trashed_at` —
38
+ * and a row's way into or out of the trash must be `ctx.trash`/`ctx.restore`, which check the
39
+ * declared key and emit the event. A module that could `UPDATE todo_lists SET
40
+ * _substrat_trashed_at = NULL` would restore without the key and without a record, which is
41
+ * the forgery the table rule above exists to stop, one level down.
42
+ *
43
+ * Judged by POSITION, so reading the columns stays allowed — in a `SELECT`, and in the `WHERE`
44
+ * of a write (`UPDATE … SET done = 1 WHERE _substrat_trashed_at IS NULL`). Refused:
45
+ *
46
+ * - an assignment target after `SET` — `UPDATE … SET`, an upsert's `DO UPDATE SET`, and a
47
+ * trigger body's `UPDATE`, row-value targets `SET (a, b) = …` included;
48
+ * - a name in an `INSERT`/`REPLACE` column list;
49
+ * - any reserved name in an `ALTER TABLE` (add, rename or drop the column itself);
50
+ * - into a table that CARRIES the columns (`statefulTables`, lowercased names): an `INSERT`
51
+ * with no column list — `VALUES (…)` and `SELECT …` fill the columns by position, the
52
+ * reserved ones included, without ever naming them — and any `REPLACE` / `INSERT OR
53
+ * REPLACE`, which deletes the existing row and writes a new one with the columns NULL, so
54
+ * it un-archives and un-trashes without the key. An `INSERT … (columns) SELECT` and an
55
+ * upsert's `DO UPDATE SET` are judged by the rules above.
56
+ *
57
+ * Below this sits a trigger on each such table that refuses a row born archived or trashed
58
+ * (`entityStateMigrations`), so a form this scan misses still cannot set the state.
59
+ *
60
+ * The prefix is reserved whole, not the two names: the next kernel-owned column is covered
61
+ * without anyone remembering to list it here.
62
+ */
63
+ export declare function assertNoReservedColumnWrite(sql: string, statefulTables?: ReadonlySet<string>): void;
10
64
  /**
11
65
  * Refuse a statement whose `REFERENCES` clause names a `_substrat_*` table (#1898), for the
12
66
  * same reason `CREATE TRIGGER … ON` is refused above: the spine's rows become the parent of a
@@ -24,5 +78,13 @@ export declare function assertNoSpineReference(sql: string, what: string): void;
24
78
  * first. `query` is guarded too: SQLite runs `INSERT … RETURNING` perfectly well
25
79
  * through a `.all()`, so guarding only `exec` would leave the door open.
26
80
  */
27
- export declare function guardSpine(inner: ScopedSql): ScopedSql;
81
+ export declare function guardSpine(inner: ScopedSql,
82
+ /** The module tables that carry archive/trash columns (#119), lowercased — see `assertNoReservedColumnWrite`. */
83
+ statefulTables?: ReadonlySet<string>,
84
+ /**
85
+ * Run after any statement that changed the schema passed (#119, Codex r3): the kernel's own
86
+ * check that every stateful table still carries what it derived. It throws to fail closed —
87
+ * the operation, and the DDL with it, roll back.
88
+ */
89
+ afterDdl?: () => void): ScopedSql;
28
90
  //# sourceMappingURL=spine-guard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"spine-guard.d.ts","sourceRoot":"","sources":["../src/spine-guard.ts"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EAAE,SAAS,EAAY,MAAM,iBAAiB,CAAC;AAsD3D;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAsBpD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAEtE;AAcD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,CAWtD"}
1
+ {"version":3,"file":"spine-guard.d.ts","sourceRoot":"","sources":["../src/spine-guard.ts"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EAAE,SAAS,EAAY,MAAM,iBAAiB,CAAC;AAsD3D;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI,CAwB1F;AAYD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,cAAc,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI,CA+E1F;AAED,qFAAqF;AACrF,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAelD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI,CAgJnG;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAEtE;AAcD;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,SAAS;AAChB,iHAAiH;AACjH,cAAc,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC;AACpC;;;;GAIG;AACH,QAAQ,CAAC,EAAE,MAAM,IAAI,GACpB,SAAS,CAYX"}
@@ -94,7 +94,7 @@ function spineTargetFrom(tokens, from, verb) {
94
94
  * is a fault in the module, not in the caller's permissions, and the message names
95
95
  * the table so the author sees which line to delete.
96
96
  */
97
- export function assertNoSpineWrite(sql) {
97
+ export function assertNoSpineWrite(sql, statefulTables) {
98
98
  const tokens = tokenizeSql(sql);
99
99
  for (let i = 0; i < tokens.length; i += 1) {
100
100
  const token = tokens[i];
@@ -115,6 +115,332 @@ export function assertNoSpineWrite(sql) {
115
115
  'Reads are fine; writes go through ctx.emit / ctx.link / ctx.grant.', { reason: 'spine_write' });
116
116
  }
117
117
  refuseSpineReference(referencedTablesIn(tokens), 'ctx.sql');
118
+ assertNoReservedColumnWrite(sql, statefulTables);
119
+ if (statefulTables?.size)
120
+ assertNoStatefulDdl(sql, statefulTables);
121
+ }
122
+ /** Statement verbs whose execution changes the schema — what `guardSpine`'s `afterDdl` follows. */
123
+ const DDL_VERBS = new Set(['create', 'alter', 'drop']);
124
+ /**
125
+ * A statement can be DDL only if its text spells one of these words, and almost none does — so the
126
+ * two scans below are skipped for nearly every statement a module runs. Text, not tokens: a word
127
+ * inside a string makes the scan run and find nothing, which costs a scan and nothing else.
128
+ */
129
+ const MAYBE_DDL = /\b(create|alter|drop|attach|detach)\b/i;
130
+ /**
131
+ * Refuse runtime DDL that would take a stateful table's guarantees away (#119, Codex r3).
132
+ *
133
+ * #1811 made a module's own DDL through `ctx.sql` a supported path, and it stays one. But a table
134
+ * that carries archive/trash state is guarded by derived triggers ON THAT TABLE, and DDL can move
135
+ * the rows out from under them: `ALTER TABLE docs RENAME TO x; CREATE TABLE docs AS SELECT …,
136
+ * NULL AS _substrat_trashed_at FROM x` un-trashes everything with no move and no event. So, for a
137
+ * stateful table, in any spelling — quoted, any case, `main.`/`temp.`-qualified:
138
+ *
139
+ * - `ALTER TABLE <it>` that renames the table or a column, or drops a column; and `RENAME TO <it>`
140
+ * from another table;
141
+ * - `DROP TABLE <it>`;
142
+ * - `CREATE [TEMP] TABLE|VIEW <it>` — a temp object of the same name SHADOWS the real table in
143
+ * every unqualified reference, the kernel's own included;
144
+ * - `CREATE [TEMP] TRIGGER … ON <it>`.
145
+ *
146
+ * Plus, for any table, a trigger, index or view carrying the reserved prefix (the kernel's derived
147
+ * objects), and `ATTACH`/`DETACH` outright: nothing in module code needs a second database, and an
148
+ * attached one is a second place a stateful name can resolve. Ordinary runtime DDL — a module's own
149
+ * unrelated tables, an index on a stateful table, an added column — is untouched.
150
+ */
151
+ export function assertNoStatefulDdl(sql, statefulTables) {
152
+ if (!MAYBE_DDL.test(sql))
153
+ return;
154
+ const tokens = tokenizeSql(sql, { punctuation: true });
155
+ const refuse = (what) => {
156
+ throw substratError('forbidden', `ctx.sql cannot ${what}: its archive/trash state is guarded by the kernel's own triggers on it. ` +
157
+ 'Change its schema in a declared migration.', { reason: 'spine_write' });
158
+ };
159
+ const bare = (t) => (t && !t.punct ? (t.text.split('.').pop() ?? '').toLowerCase() : '');
160
+ const stateful = (t) => statefulTables.has(bare(t));
161
+ const reserved = (t) => bare(t).startsWith('_substrat');
162
+ // One statement at a time: a `;` at the top ends it (trigger bodies hold their own `;`s, but
163
+ // a trigger is judged whole from its CREATE, and its body's statements are DML).
164
+ let start = 0;
165
+ while (start < tokens.length) {
166
+ let end = start;
167
+ let depth = 0;
168
+ let inBody = false;
169
+ // Only a CREATE … TRIGGER has a body. `begin` is a legal identifier elsewhere (`SELECT 1 AS
170
+ // begin`), and reading it as a body would swallow every statement after it into this one.
171
+ const head = tokens.slice(start, start + 4).map((t) => (t.quoted || t.punct ? '' : t.text.toLowerCase()));
172
+ const opensTrigger = head[0] === 'create' && head.slice(1).includes('trigger');
173
+ for (; end < tokens.length; end += 1) {
174
+ const t = tokens[end];
175
+ const kw = t.quoted || t.punct ? undefined : t.text.toLowerCase();
176
+ if (kw === 'begin' && opensTrigger)
177
+ inBody = true;
178
+ else if (kw === 'end' && inBody && depth === 0)
179
+ inBody = false;
180
+ else if (t.punct && t.text === '(')
181
+ depth += 1;
182
+ else if (t.punct && t.text === ')')
183
+ depth -= 1;
184
+ else if (t.punct && t.text === ';' && depth === 0 && !inBody)
185
+ break;
186
+ }
187
+ const st = tokens.slice(start, end);
188
+ start = end + 1;
189
+ const words = st.map((t) => (t.quoted || t.punct ? '' : t.text.toLowerCase()));
190
+ const verb = words[0];
191
+ if (verb === 'attach' || verb === 'detach')
192
+ refuse(`${verb.toUpperCase()} a database`);
193
+ if (verb === 'alter' && words[1] === 'table') {
194
+ const target = st[2];
195
+ const rest = words.slice(3);
196
+ if (stateful(target) && (rest.includes('rename') || rest[0] === 'drop')) {
197
+ refuse(`rename '${target.text}' or drop or rename its columns`);
198
+ }
199
+ const to = rest.indexOf('to');
200
+ if (rest[0] === 'rename' && to !== -1 && stateful(st[3 + to + 1])) {
201
+ refuse(`rename a table to '${st[3 + to + 1].text}'`);
202
+ }
203
+ continue;
204
+ }
205
+ if (verb === 'drop') {
206
+ let k = 1;
207
+ const kind = words[k];
208
+ k += 1;
209
+ if (words[k] === 'if' && words[k + 1] === 'exists')
210
+ k += 2;
211
+ if (kind === 'table' && stateful(st[k]))
212
+ refuse(`drop '${st[k].text}'`);
213
+ if ((kind === 'trigger' || kind === 'index' || kind === 'view') && reserved(st[k])) {
214
+ refuse(`drop the kernel's ${kind} '${st[k].text}'`);
215
+ }
216
+ continue;
217
+ }
218
+ if (verb === 'create') {
219
+ let k = 1;
220
+ while (['temp', 'temporary', 'unique', 'virtual'].includes(words[k] ?? ''))
221
+ k += 1;
222
+ const kind = words[k];
223
+ k += 1;
224
+ if (words[k] === 'if' && words[k + 1] === 'not' && words[k + 2] === 'exists')
225
+ k += 3;
226
+ const name = st[k];
227
+ if ((kind === 'table' || kind === 'view') && stateful(name))
228
+ refuse(`create a ${kind} named '${name.text}'`);
229
+ if ((kind === 'trigger' || kind === 'index' || kind === 'view') && reserved(name)) {
230
+ refuse(`create a ${kind} with the kernel's prefix ('${name.text}')`);
231
+ }
232
+ if (kind === 'trigger') {
233
+ const on = words.indexOf('on', k + 1);
234
+ if (on !== -1 && stateful(st[on + 1]))
235
+ refuse(`create a trigger on '${st[on + 1].text}'`);
236
+ }
237
+ }
238
+ }
239
+ }
240
+ /** Does this SQL change the schema — any statement in it a CREATE, ALTER or DROP? */
241
+ export function changesSchema(sql) {
242
+ if (!MAYBE_DDL.test(sql))
243
+ return false;
244
+ const tokens = tokenizeSql(sql, { punctuation: true });
245
+ let first = true;
246
+ for (const t of tokens) {
247
+ if (t.punct && t.text === ';') {
248
+ first = true;
249
+ continue;
250
+ }
251
+ if (first && !t.punct) {
252
+ if (!t.quoted && DDL_VERBS.has(t.text.toLowerCase()))
253
+ return true;
254
+ first = false;
255
+ }
256
+ }
257
+ return false;
258
+ }
259
+ /**
260
+ * Refuse a statement that WRITES a `_substrat_*` column of a module's own table (#119).
261
+ *
262
+ * The kernel keeps state on module rows — `_substrat_archived_at`, `_substrat_trashed_at` —
263
+ * and a row's way into or out of the trash must be `ctx.trash`/`ctx.restore`, which check the
264
+ * declared key and emit the event. A module that could `UPDATE todo_lists SET
265
+ * _substrat_trashed_at = NULL` would restore without the key and without a record, which is
266
+ * the forgery the table rule above exists to stop, one level down.
267
+ *
268
+ * Judged by POSITION, so reading the columns stays allowed — in a `SELECT`, and in the `WHERE`
269
+ * of a write (`UPDATE … SET done = 1 WHERE _substrat_trashed_at IS NULL`). Refused:
270
+ *
271
+ * - an assignment target after `SET` — `UPDATE … SET`, an upsert's `DO UPDATE SET`, and a
272
+ * trigger body's `UPDATE`, row-value targets `SET (a, b) = …` included;
273
+ * - a name in an `INSERT`/`REPLACE` column list;
274
+ * - any reserved name in an `ALTER TABLE` (add, rename or drop the column itself);
275
+ * - into a table that CARRIES the columns (`statefulTables`, lowercased names): an `INSERT`
276
+ * with no column list — `VALUES (…)` and `SELECT …` fill the columns by position, the
277
+ * reserved ones included, without ever naming them — and any `REPLACE` / `INSERT OR
278
+ * REPLACE`, which deletes the existing row and writes a new one with the columns NULL, so
279
+ * it un-archives and un-trashes without the key. An `INSERT … (columns) SELECT` and an
280
+ * upsert's `DO UPDATE SET` are judged by the rules above.
281
+ *
282
+ * Below this sits a trigger on each such table that refuses a row born archived or trashed
283
+ * (`entityStateMigrations`), so a form this scan misses still cannot set the state.
284
+ *
285
+ * The prefix is reserved whole, not the two names: the next kernel-owned column is covered
286
+ * without anyone remembering to list it here.
287
+ */
288
+ export function assertNoReservedColumnWrite(sql, statefulTables) {
289
+ // Every token is a substring of the text, so a statement that never spells the prefix names
290
+ // no reserved column — and that is nearly every statement, which then skips the second scan.
291
+ // A positional write names no column at all, so it is looked for whenever a stateful table
292
+ // exists and the statement could be one.
293
+ const positional = statefulTables !== undefined && statefulTables.size > 0 && /\b(insert|replace)\b/i.test(sql);
294
+ if (!positional && !/_substrat/i.test(sql))
295
+ return;
296
+ const tokens = tokenizeSql(sql, { punctuation: true });
297
+ const refuse = (column, how) => {
298
+ throw substratError('forbidden', `ctx.sql cannot write the platform's column '${column}' (${how}). ` +
299
+ 'Reads are fine; archive and trash go through ctx.archive / ctx.trash / ctx.restore.', { reason: 'spine_write' });
300
+ };
301
+ const refuseRow = (table, why) => {
302
+ throw substratError('forbidden', `ctx.sql cannot write '${table}' this way: ${why}. Name the columns you write; ` +
303
+ 'archive and trash go through ctx.archive / ctx.trash / ctx.restore.', { reason: 'spine_write' });
304
+ };
305
+ const word = (k) => {
306
+ const t = tokens[k];
307
+ return t && !t.quoted && !t.punct ? t.text.toLowerCase() : undefined;
308
+ };
309
+ const isPunct = (k, c) => tokens[k]?.punct === true && tokens[k].text === c;
310
+ // Names inside one parenthesised group starting at `open`; returns the index after `)`.
311
+ const namesInParens = (open, onName) => {
312
+ let depth = 0;
313
+ let k = open;
314
+ for (; k < tokens.length; k += 1) {
315
+ const t = tokens[k];
316
+ if (t.punct && t.text === '(')
317
+ depth += 1;
318
+ else if (t.punct && t.text === ')') {
319
+ depth -= 1;
320
+ if (depth === 0)
321
+ return k + 1;
322
+ }
323
+ else if (!t.punct && depth === 1)
324
+ onName(t.text);
325
+ }
326
+ return k;
327
+ };
328
+ /**
329
+ * Keywords that end an assignment list when they stand at its own depth. All reserved: none can
330
+ * be an unquoted identifier, so none can be mistaken for one. `END` is deliberately NOT here — it
331
+ * is a legal identifier (`SET end = 1, …`), and a trigger body's statements end at their own `;`.
332
+ */
333
+ const SET_ENDS = new Set(['where', 'from', 'returning', 'order', 'limit']);
334
+ /**
335
+ * The assignment targets of the `SET` list starting after `start`, judged by STRUCTURE.
336
+ *
337
+ * Everything that can hold a comma, a keyword or another `SET` is nested: a parenthesised
338
+ * expression or subquery, and a `CASE … END`, each pushed on one stack, so an `END` closes a
339
+ * `CASE` that is open and ends the list only when none is (the trigger body's `END`). Strings,
340
+ * comments and quoted identifiers never reach here as keywords — `tokenizeSql` keeps a quoted
341
+ * token apart. A target is the first token of the list and the first after each comma at the
342
+ * list's own depth; a `(a, b)` there is a row-value target, every name in it a target.
343
+ */
344
+ const setTargets = (start, onTarget) => {
345
+ const nesting = [];
346
+ let expectTarget = true;
347
+ for (let k = start; k < tokens.length; k += 1) {
348
+ const t = tokens[k];
349
+ const kw = t.quoted || t.punct ? undefined : t.text.toLowerCase();
350
+ if (nesting.length === 0) {
351
+ if (t.punct && (t.text === ';' || t.text === ')'))
352
+ return;
353
+ if (kw !== undefined && SET_ENDS.has(kw))
354
+ return;
355
+ if (t.punct && t.text === ',') {
356
+ expectTarget = true;
357
+ continue;
358
+ }
359
+ if (expectTarget) {
360
+ expectTarget = false;
361
+ if (t.punct && t.text === '(') {
362
+ k = namesInParens(k, onTarget) - 1;
363
+ continue;
364
+ }
365
+ if (!t.punct) {
366
+ onTarget(t.text);
367
+ continue;
368
+ }
369
+ }
370
+ }
371
+ if (t.punct && t.text === '(')
372
+ nesting.push('paren');
373
+ else if (kw === 'case')
374
+ nesting.push('case');
375
+ else if (t.punct && t.text === ')') {
376
+ while (nesting.length && nesting.pop() !== 'paren')
377
+ ;
378
+ }
379
+ else if (kw === 'end' && nesting[nesting.length - 1] === 'case')
380
+ nesting.pop();
381
+ }
382
+ };
383
+ for (let i = 0; i < tokens.length; i += 1) {
384
+ const verb = word(i);
385
+ if (verb === 'set') {
386
+ setTargets(i + 1, (name) => {
387
+ if (namesSpineTable(name))
388
+ refuse(name, 'SET target');
389
+ });
390
+ continue;
391
+ }
392
+ if (verb === 'insert' || verb === 'replace') {
393
+ // `replace(` is SQLite's string function, not the statement — which always reads
394
+ // `REPLACE INTO`. Read as a statement, its argument list became a "column list".
395
+ if (verb === 'replace' && word(i + 1) !== 'into')
396
+ continue;
397
+ // By the grammar, not by a word list: `INSERT [OR <resolution>] INTO <table>` and
398
+ // `REPLACE INTO <table>`. A table may be NAMED `replace` or `ignore` — both are legal
399
+ // identifiers — and a loop skipping modifier words would skip it too.
400
+ let k = i + 1;
401
+ let replaces = verb === 'replace';
402
+ if (verb === 'insert' && word(k) === 'or') {
403
+ if (word(k + 1) === 'replace')
404
+ replaces = true;
405
+ k += 2;
406
+ }
407
+ if (word(k) !== 'into')
408
+ continue;
409
+ k += 1;
410
+ const target = tokens[k];
411
+ if (!target || target.punct)
412
+ continue;
413
+ k += 1; // past the target table
414
+ if (word(k) === 'as')
415
+ k += 2; // an alias
416
+ const table = target && !target.punct ? (target.text.split('.').pop() ?? '').toLowerCase() : '';
417
+ if (statefulTables?.has(table)) {
418
+ if (replaces)
419
+ refuseRow(target.text, 'REPLACE deletes the row and writes it back with no archive or trash state');
420
+ if (!isPunct(k, '(') && word(k) !== 'default') {
421
+ refuseRow(target.text, 'an INSERT with no column list fills the archive/trash columns by position');
422
+ }
423
+ }
424
+ if (isPunct(k, '(')) {
425
+ namesInParens(k, (name) => {
426
+ if (namesSpineTable(name))
427
+ refuse(name, 'INSERT column');
428
+ });
429
+ }
430
+ continue;
431
+ }
432
+ if (verb === 'alter') {
433
+ // ALTER TABLE <t> …: the table is judged above; here, every other name in the statement.
434
+ let k = i + 1;
435
+ while (word(k) !== undefined && MODIFIERS.alter.has(word(k)))
436
+ k += 1;
437
+ for (k += 1; k < tokens.length && !isPunct(k, ';'); k += 1) {
438
+ const t = tokens[k];
439
+ if (!t.punct && namesSpineTable(t.text))
440
+ refuse(t.text, 'ALTER TABLE');
441
+ }
442
+ }
443
+ }
118
444
  }
119
445
  /**
120
446
  * Refuse a statement whose `REFERENCES` clause names a `_substrat_*` table (#1898), for the
@@ -143,16 +469,25 @@ function refuseSpineReference(referenced, what) {
143
469
  * first. `query` is guarded too: SQLite runs `INSERT … RETURNING` perfectly well
144
470
  * through a `.all()`, so guarding only `exec` would leave the door open.
145
471
  */
146
- export function guardSpine(inner) {
472
+ export function guardSpine(inner,
473
+ /** The module tables that carry archive/trash columns (#119), lowercased — see `assertNoReservedColumnWrite`. */
474
+ statefulTables,
475
+ /**
476
+ * Run after any statement that changed the schema passed (#119, Codex r3): the kernel's own
477
+ * check that every stateful table still carries what it derived. It throws to fail closed —
478
+ * the operation, and the DDL with it, roll back.
479
+ */
480
+ afterDdl) {
481
+ const follow = (sql, run) => {
482
+ assertNoSpineWrite(sql, statefulTables);
483
+ const result = run();
484
+ if (afterDdl && changesSchema(sql))
485
+ afterDdl();
486
+ return result;
487
+ };
147
488
  return {
148
- query: (sql, params) => {
149
- assertNoSpineWrite(sql);
150
- return inner.query(sql, params);
151
- },
152
- exec: (sql, params) => {
153
- assertNoSpineWrite(sql);
154
- return inner.exec(sql, params);
155
- },
489
+ query: (sql, params) => follow(sql, () => inner.query(sql, params)),
490
+ exec: (sql, params) => follow(sql, () => inner.exec(sql, params)),
156
491
  };
157
492
  }
158
493
  //# sourceMappingURL=spine-guard.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"spine-guard.js","sourceRoot":"","sources":["../src/spine-guard.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"spine-guard.js","sourceRoot":"","sources":["../src/spine-guard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The check every kernel-derived DDL runs on a name before interpolating it (#827, #811, #119).
3
+ *
4
+ * SQL identifiers reach the DDL by interpolation — there is no parameter form for a table or
5
+ * column name — so every one is checked first. The inputs are declarations rather than user
6
+ * input, but a declaration is still a string somebody typed, and "it came from the manifest"
7
+ * is exactly the reasoning that makes an injection a surprise.
8
+ */
9
+ export declare const SQL_IDENTIFIER: RegExp;
10
+ /** `value`, or a throw naming the declaration (`where`) and the role it plays (`kind`). */
11
+ export declare function assertSqlIdentifier(area: string, kind: string, value: string, where: string): string;
12
+ //# sourceMappingURL=sql-identifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql-identifier.d.ts","sourceRoot":"","sources":["../src/sql-identifier.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,QAA6B,CAAC;AAEzD,2FAA2F;AAC3F,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAKpG"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * The check every kernel-derived DDL runs on a name before interpolating it (#827, #811, #119).
3
+ *
4
+ * SQL identifiers reach the DDL by interpolation — there is no parameter form for a table or
5
+ * column name — so every one is checked first. The inputs are declarations rather than user
6
+ * input, but a declaration is still a string somebody typed, and "it came from the manifest"
7
+ * is exactly the reasoning that makes an injection a surprise.
8
+ */
9
+ export const SQL_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
10
+ /** `value`, or a throw naming the declaration (`where`) and the role it plays (`kind`). */
11
+ export function assertSqlIdentifier(area, kind, value, where) {
12
+ if (!SQL_IDENTIFIER.test(value)) {
13
+ throw new Error(`${area}: ${where} names ${kind} '${value}', which is not a plain SQL identifier`);
14
+ }
15
+ return value;
16
+ }
17
+ //# sourceMappingURL=sql-identifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql-identifier.js","sourceRoot":"","sources":["../src/sql-identifier.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,0BAA0B,CAAC;AAEzD,2FAA2F;AAC3F,MAAM,UAAU,mBAAmB,CAAC,IAAY,EAAE,IAAY,EAAE,KAAa,EAAE,KAAa;IAC1F,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,KAAK,KAAK,UAAU,IAAI,KAAK,KAAK,wCAAwC,CAAC,CAAC;IACrG,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@substrat-run/kernel",
3
- "version": "0.138.0",
3
+ "version": "0.139.0",
4
4
  "description": "Substrat kernel contracts and services — pure TypeScript, no platform imports (D-14)",
5
5
  "license": "AGPL-3.0-only",
6
6
  "repository": {
@@ -22,7 +22,7 @@
22
22
  "dist"
23
23
  ],
24
24
  "dependencies": {
25
- "@substrat-run/contracts": "^0.138.0"
25
+ "@substrat-run/contracts": "^0.139.0"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/node": "^22.20.5",