@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.
- package/dist/attachment-extractor.d.ts +9 -1
- package/dist/attachment-extractor.d.ts.map +1 -1
- package/dist/attachment-extractor.js +21 -1
- package/dist/attachment-extractor.js.map +1 -1
- package/dist/attachment-text.d.ts +64 -0
- package/dist/attachment-text.d.ts.map +1 -1
- package/dist/attachment-text.js +87 -16
- package/dist/attachment-text.js.map +1 -1
- package/dist/attribution.d.ts +36 -3
- package/dist/attribution.d.ts.map +1 -1
- package/dist/attribution.js +21 -8
- package/dist/attribution.js.map +1 -1
- package/dist/entity-state-reads.d.ts +43 -0
- package/dist/entity-state-reads.d.ts.map +1 -0
- package/dist/entity-state-reads.js +135 -0
- package/dist/entity-state-reads.js.map +1 -0
- package/dist/entity-state.d.ts +177 -0
- package/dist/entity-state.d.ts.map +1 -0
- package/dist/entity-state.js +345 -0
- package/dist/entity-state.js.map +1 -0
- package/dist/findings.d.ts +143 -0
- package/dist/findings.d.ts.map +1 -0
- package/dist/findings.js +463 -0
- package/dist/findings.js.map +1 -0
- package/dist/index.d.ts +13 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -6
- package/dist/index.js.map +1 -1
- package/dist/list-index.d.ts +25 -3
- package/dist/list-index.d.ts.map +1 -1
- package/dist/list-index.js +70 -26
- package/dist/list-index.js.map +1 -1
- package/dist/membership-executor.d.ts +17 -5
- package/dist/membership-executor.d.ts.map +1 -1
- package/dist/membership-executor.js +26 -11
- package/dist/membership-executor.js.map +1 -1
- package/dist/module-migrations.d.ts +6 -3
- package/dist/module-migrations.d.ts.map +1 -1
- package/dist/module-migrations.js +12 -5
- package/dist/module-migrations.js.map +1 -1
- package/dist/permission-eval.d.ts +41 -0
- package/dist/permission-eval.d.ts.map +1 -1
- package/dist/permission-eval.js +53 -0
- package/dist/permission-eval.js.map +1 -1
- package/dist/platform-sweep.d.ts +7 -1
- package/dist/platform-sweep.d.ts.map +1 -1
- package/dist/platform-sweep.js +12 -0
- package/dist/platform-sweep.js.map +1 -1
- package/dist/scope-host.d.ts +187 -11
- package/dist/scope-host.d.ts.map +1 -1
- package/dist/scope-host.js +10 -2
- package/dist/scope-host.js.map +1 -1
- package/dist/scope-role-admin.d.ts +68 -0
- package/dist/scope-role-admin.d.ts.map +1 -0
- package/dist/scope-role-admin.js +80 -0
- package/dist/scope-role-admin.js.map +1 -0
- package/dist/search-index.d.ts +13 -1
- package/dist/search-index.d.ts.map +1 -1
- package/dist/search-index.js +9 -16
- package/dist/search-index.js.map +1 -1
- package/dist/spine-guard.d.ts +64 -2
- package/dist/spine-guard.d.ts.map +1 -1
- package/dist/spine-guard.js +345 -10
- package/dist/spine-guard.js.map +1 -1
- package/dist/sql-identifier.d.ts +12 -0
- package/dist/sql-identifier.d.ts.map +1 -0
- package/dist/sql-identifier.js +17 -0
- package/dist/sql-identifier.js.map +1 -0
- package/package.json +2 -2
package/dist/spine-guard.d.ts
CHANGED
|
@@ -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
|
|
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,
|
|
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"}
|
package/dist/spine-guard.js
CHANGED
|
@@ -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
|
-
|
|
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
|
package/dist/spine-guard.js.map
CHANGED
|
@@ -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.
|
|
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.
|
|
25
|
+
"@substrat-run/contracts": "^0.139.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/node": "^22.20.5",
|