@somewhere-tech/cli 0.12.7 → 0.12.9
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/commands/deploy.js +29 -0
- package/dist/commands/deploy.js.map +1 -1
- package/package.json +1 -1
- package/runtime/platform-context.mjs +668 -23
- package/runtime/sw-init.mjs +1 -1
package/dist/commands/deploy.js
CHANGED
|
@@ -11,6 +11,15 @@ import { dim, error, green, info, red, success, teal, warn, yellow } from '../li
|
|
|
11
11
|
export function resolveTargetDir(dir, cwd = process.cwd()) {
|
|
12
12
|
return dir ? resolve(cwd, dir) : cwd;
|
|
13
13
|
}
|
|
14
|
+
// `--prebuilt` (alias `--allow-bundled`) opts the deploy out of the
|
|
15
|
+
// raw-source contract: the worker's bundled-deploy guard keeps pre-built /
|
|
16
|
+
// bundled output as-is instead of rejecting it. We surface it as
|
|
17
|
+
// `allow_bundled: true` in the deploy body (the field the worker already
|
|
18
|
+
// reads). Returns true only when a flag was passed, so a normal deploy never
|
|
19
|
+
// sends the field — raw source stays the default.
|
|
20
|
+
export function prebuiltOptIn(opts) {
|
|
21
|
+
return Boolean(opts.prebuilt || opts.allowBundled);
|
|
22
|
+
}
|
|
14
23
|
export function registerDeploy(program) {
|
|
15
24
|
program
|
|
16
25
|
.command('deploy [dir]')
|
|
@@ -19,6 +28,12 @@ export function registerDeploy(program) {
|
|
|
19
28
|
.option('--scope <scope>', "Partial deploy: 'functions' (backend only, leaves the site untouched) or 'static' (site only, leaves functions untouched). Default deploys both.")
|
|
20
29
|
.option('--dry-run', 'Show what would change (added / modified / removed files) without deploying')
|
|
21
30
|
.option('--replace-functions', 'Delete deployed functions that are not in this directory (repo-as-truth). Default keeps them.')
|
|
31
|
+
.option('--prebuilt', 'Deploy pre-built / bundled output (e.g. a dist/ folder) instead of raw source. ' +
|
|
32
|
+
'Raw source is the default and recommended path: the platform compiles your TSX/JSX/TS ' +
|
|
33
|
+
'on deploy, which keeps the app editable end-to-end. Choosing --prebuilt trades that away — ' +
|
|
34
|
+
'pull/export round-trips, find/replace patches, and visual (source-map) editing are disabled ' +
|
|
35
|
+
'for the bundled files. Use it only when you need full control of your own build.')
|
|
36
|
+
.option('--allow-bundled', 'Alias for --prebuilt.')
|
|
22
37
|
.action(async (dir, opts) => {
|
|
23
38
|
const token = getToken();
|
|
24
39
|
const client = new ApiClient(token);
|
|
@@ -74,6 +89,13 @@ export function registerDeploy(program) {
|
|
|
74
89
|
body.replace_functions = true;
|
|
75
90
|
if (opts.dryRun)
|
|
76
91
|
body.dry_run = true;
|
|
92
|
+
// --prebuilt (alias --allow-bundled) opts out of the raw-source
|
|
93
|
+
// contract: the deploy keeps pre-built / bundled output as-is instead
|
|
94
|
+
// of being rejected by the bundled-deploy guard. Omitted entirely when
|
|
95
|
+
// not set so a normal deploy stays the raw-source default.
|
|
96
|
+
const prebuilt = prebuiltOptIn(opts);
|
|
97
|
+
if (prebuilt)
|
|
98
|
+
body.allow_bundled = true;
|
|
77
99
|
if (opts.dryRun) {
|
|
78
100
|
const plan = await client.call('POST', '/deploy', body, undefined, {
|
|
79
101
|
timeoutMs: LONG_CALL_TIMEOUT_MS,
|
|
@@ -129,6 +151,13 @@ export function registerDeploy(program) {
|
|
|
129
151
|
else {
|
|
130
152
|
success(`Live at ${teal(result.url)}`);
|
|
131
153
|
}
|
|
154
|
+
// Remind the dev of the round-trip trade-off they opted into. Raw
|
|
155
|
+
// source stays editable end-to-end; bundled files do not — pull/export
|
|
156
|
+
// round-trips, find/replace patches, and visual editing are disabled
|
|
157
|
+
// for them.
|
|
158
|
+
if (prebuilt) {
|
|
159
|
+
warn('Prebuilt deploy — source round-trip (pull / export / patch / visual edit) is disabled for the bundled files.');
|
|
160
|
+
}
|
|
132
161
|
// Exit non-zero if any function failed to deploy — a CI step that
|
|
133
162
|
// shells out to `somewhere deploy` should fail, not pass green.
|
|
134
163
|
if (result.function_errors && result.function_errors.length > 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deploy.js","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"deploy.js","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// VENDORED from worker/src/
|
|
1
|
+
// VENDORED from worker/src/runtime/context.ts (PLATFORM_CONTEXT_JS) @ e8da46e
|
|
2
2
|
// — the exact runtime deployed functions run against. Do not edit by hand;
|
|
3
3
|
// re-sync with: node scripts/extract-runtime.mjs <monorepo>
|
|
4
4
|
// Process-wide cache for sw.auth.me — keyed by the JWT signature
|
|
@@ -104,6 +104,16 @@ function buildPlatformContext(env, request) {
|
|
|
104
104
|
opts = opts || {};
|
|
105
105
|
const headers = {
|
|
106
106
|
'Authorization': 'Bearer ' + apiKey,
|
|
107
|
+
// tsk_95ecae23 — tell the platform which execution slot this bundle is
|
|
108
|
+
// running in (prod vs dev), so the REST-fallback data path (/v1/db/query,
|
|
109
|
+
// /v1/db/batch) and the direct `dev --local` client select the DEV data
|
|
110
|
+
// slot instead of silently defaulting to prod. The slot is baked into the
|
|
111
|
+
// bundle at deploy/run time as PROJECT_ENV (see function-bundle.ts /
|
|
112
|
+
// code-bundle.ts); 'dev' for run_code + preview + draft, 'prod' for the
|
|
113
|
+
// promoted production bundle. The server only DIVERTS off prod when the
|
|
114
|
+
// project is enrolled in DEV_SLOT_ENFORCE_PROJECTS, so an unenrolled
|
|
115
|
+
// project sees this header but its binding is unchanged.
|
|
116
|
+
'X-Sw-Env-Slot': projectEnv,
|
|
107
117
|
...(opts.headers || {}),
|
|
108
118
|
};
|
|
109
119
|
if (opts.body && !headers['Content-Type'] && !headers['content-type']) {
|
|
@@ -173,6 +183,94 @@ function buildPlatformContext(env, request) {
|
|
|
173
183
|
__sw_pendingCookies.push(__sw_authCookie('sw_refresh_token', '', 0));
|
|
174
184
|
}
|
|
175
185
|
|
|
186
|
+
// Expected-failure marking for the cookie helpers (tsk_a5f3e7d3). The
|
|
187
|
+
// routing shim's catch-all turns every uncaught throw into an opaque 500
|
|
188
|
+
// FUNCTION_ERROR — right for real bugs, wrong for "wrong password": the
|
|
189
|
+
// doc'd cookie snippet has no try/catch, so every expected auth failure
|
|
190
|
+
// surfaced as a 500 (and fed the 5xx-based uptime alerting). An error
|
|
191
|
+
// flagged here carries a customer-safe message from the platform auth
|
|
192
|
+
// API; the shim returns it as a structured 4xx with the real code +
|
|
193
|
+
// message instead. Platform 5xx and anything unflagged stay opaque.
|
|
194
|
+
function __sw_markExpected(err) {
|
|
195
|
+
if (err && typeof err.status === 'number' && err.status >= 400 && err.status < 500) {
|
|
196
|
+
err.__sw_expose = true;
|
|
197
|
+
}
|
|
198
|
+
return err;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Arg-shape forgiveness for the cookie helpers (tsk_3bc373ea): every other
|
|
202
|
+
// sw.auth method takes one options object, so agents naturally pass one
|
|
203
|
+
// here too — and req is never read by these helpers. Accepted shapes:
|
|
204
|
+
// (req, email, password) — documented positional form
|
|
205
|
+
// (req, { email, password })
|
|
206
|
+
// ({ email, password }) — req omitted
|
|
207
|
+
// Anything else throws a 400 VALIDATION_ERROR naming the supported shapes,
|
|
208
|
+
// surfaced as a structured 4xx via __sw_expose (never INTERNAL_ERROR/500).
|
|
209
|
+
function __sw_cookieCreds(method, req, email, password) {
|
|
210
|
+
if (email !== null && typeof email === 'object') {
|
|
211
|
+
password = email.password;
|
|
212
|
+
email = email.email;
|
|
213
|
+
} else if (req !== null && typeof req === 'object' &&
|
|
214
|
+
!(req.headers && typeof req.headers.get === 'function') &&
|
|
215
|
+
email === undefined && password === undefined) {
|
|
216
|
+
password = req.password;
|
|
217
|
+
email = req.email;
|
|
218
|
+
}
|
|
219
|
+
if (typeof email !== 'string' || !email || typeof password !== 'string' || !password) {
|
|
220
|
+
const err = new Error('sw.auth.' + method + ': email and password are required — call ' +
|
|
221
|
+
method + '(req, email, password) or ' + method + '(req, { email, password }).');
|
|
222
|
+
err.code = 'VALIDATION_ERROR';
|
|
223
|
+
err.status = 400;
|
|
224
|
+
err.__sw_expose = true;
|
|
225
|
+
throw err;
|
|
226
|
+
}
|
|
227
|
+
return { email: email, password: password };
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Cross-origin guard for cookie sessions (tsk_1dd4e1b4). SameSite=Lax
|
|
231
|
+
// stops cross-SITE cookie sends, but every *.<platformDomain> project is
|
|
232
|
+
// the SAME site in browser terms, so a sibling project's page can make
|
|
233
|
+
// the browser attach THIS app's cookies — and the platform's permissive
|
|
234
|
+
// /api/* CORS (reflected origin + credentials) would even let it read the
|
|
235
|
+
// response. A cookie-authed request whose cross-origin source names a
|
|
236
|
+
// different host is therefore treated as unauthenticated, unless both
|
|
237
|
+
// hosts share a customer-owned registrable domain (www.shop.com →
|
|
238
|
+
// shop.com) — never across the shared platform domain (sibling projects
|
|
239
|
+
// are different security principals).
|
|
240
|
+
// - Writes (POST/PUT/PATCH/DELETE): checked against Origin, falling
|
|
241
|
+
// back to Referer (CSRF).
|
|
242
|
+
// - Reads (GET/HEAD): checked only when an explicit CORS Origin header
|
|
243
|
+
// is present (credentialed cross-origin fetch). Top-level navigations
|
|
244
|
+
// carry no Origin and stay allowed — that's exactly what Lax intends —
|
|
245
|
+
// and their Referer legitimately names the previous, often foreign,
|
|
246
|
+
// page, so Referer is ignored for reads.
|
|
247
|
+
// - No Origin/Referer at all (curl, server-to-server), same-origin
|
|
248
|
+
// requests, and Bearer-header auth are all unaffected.
|
|
249
|
+
function __sw_cookieCsrfBlocked(req) {
|
|
250
|
+
const method = (req.method || 'GET').toUpperCase();
|
|
251
|
+
const isRead = method === 'GET' || method === 'HEAD' || method === 'OPTIONS';
|
|
252
|
+
let src = req.headers.get('Origin') || req.headers.get('origin');
|
|
253
|
+
if (!src && !isRead) src = req.headers.get('Referer') || req.headers.get('referer');
|
|
254
|
+
if (!src) return false;
|
|
255
|
+
let srcHost, reqHost;
|
|
256
|
+
try {
|
|
257
|
+
// 'null' (opaque origin: sandboxed iframe, data: page) fails to parse
|
|
258
|
+
// and is rejected — a same-site sandboxed page still sends the cookie
|
|
259
|
+
// under Lax, so it must never authenticate.
|
|
260
|
+
srcHost = new URL(src).hostname;
|
|
261
|
+
reqHost = new URL(req.url).hostname;
|
|
262
|
+
} catch (_) {
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
265
|
+
if (srcHost === reqHost) return false;
|
|
266
|
+
const tail2 = function (h) { return h.split('.').slice(-2).join('.'); };
|
|
267
|
+
const t = tail2(srcHost);
|
|
268
|
+
if (t !== tail2(reqHost)) return true;
|
|
269
|
+
if (t === platformDomain) return true;
|
|
270
|
+
if (reqHost === platformDomain || reqHost.slice(-(platformDomain.length + 1)) === '.' + platformDomain) return true;
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
|
|
176
274
|
const sw = {
|
|
177
275
|
project_id: projectId,
|
|
178
276
|
subdomain: env.SUBDOMAIN || '',
|
|
@@ -363,6 +461,105 @@ function buildPlatformContext(env, request) {
|
|
|
363
461
|
catch (_) { return {}; }
|
|
364
462
|
})();
|
|
365
463
|
|
|
464
|
+
// Rule-9 grace for newly-baked scopes (tsk_fa2f41f6). Map of
|
|
465
|
+
// lowercased table name -> epoch-ms after which a scope violation
|
|
466
|
+
// THROWS. Before the deadline a violation logs loudly and the
|
|
467
|
+
// query PROCEEDS, so declaring a scope can never 500 already-
|
|
468
|
+
// deployed, previously-working code on the next deploy (the
|
|
469
|
+
// RailTime outage class). Absent/empty -> no grace, enforcement
|
|
470
|
+
// exactly as before. Deadlines come from loadProjectScopesForBake
|
|
471
|
+
// at deploy time; the check is re-evaluated per query, so a
|
|
472
|
+
// window closes on time without needing a redeploy.
|
|
473
|
+
const SCOPE_GRACE = (function () {
|
|
474
|
+
try { return env.PROJECT_SCOPES_GRACE ? JSON.parse(env.PROJECT_SCOPES_GRACE) : {}; }
|
|
475
|
+
catch (_) { return {}; }
|
|
476
|
+
})();
|
|
477
|
+
|
|
478
|
+
// Owner-shaped-but-UNSCOPED tables baked at deploy (scope-hardening,
|
|
479
|
+
// pfb_02e0d66c1275). Map of lowercased table name -> owner-style column.
|
|
480
|
+
// These are GRANDFATHERED tables: they have a user_id/owner_id-style
|
|
481
|
+
// column but NO declared scope, and predate the deploy hard-block, so the
|
|
482
|
+
// deploy still ships them. The runtime uses this set to close the
|
|
483
|
+
// fails-open hole where passing { user } against such a table was a
|
|
484
|
+
// SILENT NO-OP (the table is not in SCOPES, so __sw_checkScope returns
|
|
485
|
+
// null and the raw, unscoped query ran). New deploys cannot introduce
|
|
486
|
+
// such a table (the deploy is blocked), so for new code this set is
|
|
487
|
+
// empty and the no-op cannot arise. Absent/empty -> unchanged behavior.
|
|
488
|
+
const UNSCOPED_OWNER = (function () {
|
|
489
|
+
try { return env.PROJECT_UNSCOPED_OWNER ? JSON.parse(env.PROJECT_UNSCOPED_OWNER) : {}; }
|
|
490
|
+
catch (_) { return {}; }
|
|
491
|
+
})();
|
|
492
|
+
|
|
493
|
+
// Per-table grace deadline (epoch-ms) for the UNSCOPED_OWNER set. Passing
|
|
494
|
+
// { user } against an unscoped-owner table WARNS loudly before the
|
|
495
|
+
// deadline and THROWS SCOPE_NOT_DECLARED after it — same warn-then-arm
|
|
496
|
+
// shape as SCOPE_GRACE. A table in UNSCOPED_OWNER with no deadline here
|
|
497
|
+
// is treated as already armed (throws).
|
|
498
|
+
const UNSCOPED_OWNER_GRACE = (function () {
|
|
499
|
+
try { return env.PROJECT_UNSCOPED_OWNER_GRACE ? JSON.parse(env.PROJECT_UNSCOPED_OWNER_GRACE) : {}; }
|
|
500
|
+
catch (_) { return {}; }
|
|
501
|
+
})();
|
|
502
|
+
|
|
503
|
+
function __sw_scopeInGrace(table) {
|
|
504
|
+
const until = SCOPE_GRACE[table];
|
|
505
|
+
return typeof until === 'number' && Date.now() < until;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function __sw_scopeGraceWarn(v, api) {
|
|
509
|
+
try {
|
|
510
|
+
console.error('[scope-grace] ' + api + ' ran UNSCOPED on user-scoped table "' + v.table + '": ' + v.reason +
|
|
511
|
+
' Enforcement arms at ' + new Date(SCOPE_GRACE[v.table]).toISOString() +
|
|
512
|
+
' - after that this exact query THROWS SCOPE_VIOLATION. Fix now: pass { user } (the platform auto-scopes simple queries) or { unscoped: true } if cross-user access is intentional.');
|
|
513
|
+
} catch (_) { /* logging must never break the query */ }
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// scope-hardening (pfb_02e0d66c1275): the { user } no-op closer. Returns
|
|
517
|
+
// { table, ownerCol } when the query touches a table that is owner-shaped
|
|
518
|
+
// but has no declared scope (so { user } would do nothing), else null.
|
|
519
|
+
function __sw_checkUnscopedOwner(sql) {
|
|
520
|
+
const names = Object.keys(UNSCOPED_OWNER);
|
|
521
|
+
if (names.length === 0) return null;
|
|
522
|
+
const touched = __sw_extractTables(sql);
|
|
523
|
+
for (const table of touched) {
|
|
524
|
+
const ownerCol = UNSCOPED_OWNER[table];
|
|
525
|
+
if (ownerCol) return { table: table, ownerCol: ownerCol };
|
|
526
|
+
}
|
|
527
|
+
return null;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
function __sw_unscopedOwnerInGrace(table) {
|
|
531
|
+
const until = UNSCOPED_OWNER_GRACE[table];
|
|
532
|
+
return typeof until === 'number' && Date.now() < until;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
function __sw_unscopedOwnerWarn(u, api) {
|
|
536
|
+
try {
|
|
537
|
+
const until = UNSCOPED_OWNER_GRACE[u.table];
|
|
538
|
+
const when = (typeof until === 'number') ? new Date(until).toISOString() : 'the next deploy';
|
|
539
|
+
console.error('[scope-grace] ' + api + ' was passed { user } on table "' + u.table +
|
|
540
|
+
'" which has an owner-style column "' + u.ownerCol + '" but NO declared per-user scope, ' +
|
|
541
|
+
'so { user } did NOTHING and the query ran UNSCOPED (every user can see every row). ' +
|
|
542
|
+
'This throws SCOPE_NOT_DECLARED after ' + when + '. Fix now: declare a scope so the ' +
|
|
543
|
+
'platform filters by the current user (sw.db.scope("' + u.table + '", { owner_column: "' +
|
|
544
|
+
u.ownerCol + '" }) then redeploy), or mark it sw.db.scope("' + u.table +
|
|
545
|
+
'", { intent: "shared" }) if cross-user access is intended and pass { unscoped: true } here.');
|
|
546
|
+
} catch (_) { /* logging must never break the query */ }
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
function __sw_throwScopeNotDeclared(u, api) {
|
|
550
|
+
const err = new Error(api + ' was passed { user } on table "' + u.table +
|
|
551
|
+
'", which has an owner-style column "' + u.ownerCol + '" but no declared per-user scope. ' +
|
|
552
|
+
'Passing { user } here does NOT scope the query — without a declared scope every user would ' +
|
|
553
|
+
'see every row. Declare the scope so the platform filters by the current user: ' +
|
|
554
|
+
'sw.db.scope("' + u.table + '", { owner_column: "' + u.ownerCol + '" }) then redeploy. ' +
|
|
555
|
+
'If "' + u.table + '" is intentionally shared across users, mark it sw.db.scope("' + u.table +
|
|
556
|
+
'", { intent: "shared" }) and pass { unscoped: true } here.');
|
|
557
|
+
err.code = 'SCOPE_NOT_DECLARED';
|
|
558
|
+
err.table = u.table;
|
|
559
|
+
err.owner_column = u.ownerCol;
|
|
560
|
+
throw err;
|
|
561
|
+
}
|
|
562
|
+
|
|
366
563
|
function ensureBinding() {
|
|
367
564
|
// Always satisfied now — DB is either the native binding or
|
|
368
565
|
// the REST facade. Kept as a no-op so the call sites below
|
|
@@ -370,6 +567,16 @@ function buildPlatformContext(env, request) {
|
|
|
370
567
|
}
|
|
371
568
|
|
|
372
569
|
function __sw_stripStringsAndComments(sql) {
|
|
570
|
+
// BLANKS every string-literal and comment character with a SPACE,
|
|
571
|
+
// one-for-one, so the returned string is the SAME LENGTH as the input.
|
|
572
|
+
// __sw_autoScopeRewrite computes a tail-clause anchor offset against
|
|
573
|
+
// the STRIPPED sql and then slices the ORIGINAL sql at that offset to
|
|
574
|
+
// inject the owner predicate — so the two must stay byte-aligned. The
|
|
575
|
+
// earlier version collapsed each literal to a single space, which
|
|
576
|
+
// shifted every offset after the first literal; a query like
|
|
577
|
+
// "... WHERE user_id = ? AND status = 'confirmed' ORDER BY ..." had the
|
|
578
|
+
// predicate spliced INTO the literal, producing SQL that silently
|
|
579
|
+
// matched no rows (pfb_20be20f5ec83 / pfb_60b8d245f609).
|
|
373
580
|
let out = '', i = 0;
|
|
374
581
|
const n = sql.length;
|
|
375
582
|
while (i < n) {
|
|
@@ -377,20 +584,20 @@ function buildPlatformContext(env, request) {
|
|
|
377
584
|
if (ch === "'") {
|
|
378
585
|
out += ' '; i++;
|
|
379
586
|
while (i < n) {
|
|
380
|
-
if (sql[i] === "'" && sql[i + 1] === "'") { i += 2; continue; }
|
|
381
|
-
if (sql[i] === "'") { i++; break; }
|
|
382
|
-
i++;
|
|
587
|
+
if (sql[i] === "'" && sql[i + 1] === "'") { out += ' '; i += 2; continue; }
|
|
588
|
+
if (sql[i] === "'") { out += ' '; i++; break; }
|
|
589
|
+
out += ' '; i++;
|
|
383
590
|
}
|
|
384
591
|
continue;
|
|
385
592
|
}
|
|
386
593
|
if (ch === '-' && sql[i + 1] === '-') {
|
|
387
|
-
while (i < n && sql[i] !== '\n') i++;
|
|
594
|
+
while (i < n && sql[i] !== '\n') { out += ' '; i++; }
|
|
388
595
|
continue;
|
|
389
596
|
}
|
|
390
597
|
if (ch === '/' && sql[i + 1] === '*') {
|
|
391
|
-
i += 2;
|
|
392
|
-
while (i < n && !(sql[i] === '*' && sql[i + 1] === '/')) i++;
|
|
393
|
-
if (i < n) i += 2;
|
|
598
|
+
out += ' '; i += 2;
|
|
599
|
+
while (i < n && !(sql[i] === '*' && sql[i + 1] === '/')) { out += ' '; i++; }
|
|
600
|
+
if (i < n) { out += ' '; i += 2; }
|
|
394
601
|
continue;
|
|
395
602
|
}
|
|
396
603
|
out += ch; i++;
|
|
@@ -429,6 +636,227 @@ function buildPlatformContext(env, request) {
|
|
|
429
636
|
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
430
637
|
}
|
|
431
638
|
|
|
639
|
+
// ─── Positional owner proof (tsk_6475aa86) ──────────────────────────
|
|
640
|
+
// The original single-table check (the bindRx below) was NON-POSITIONAL:
|
|
641
|
+
// it accepted "<owner> = $current_user" appearing ANYWHERE in the SQL,
|
|
642
|
+
// so an app-user could park the sentinel in the SELECT projection, an
|
|
643
|
+
// ORDER BY, an UPDATE SET, or inside an OR and read/overwrite every
|
|
644
|
+
// user's rows. There is no SQL parser inside the deployed function
|
|
645
|
+
// bundle (this is the byte-escaped runtime mirror of the REST checker in
|
|
646
|
+
// worker/src/utils/scope-enforcement.ts), so the proof is a small
|
|
647
|
+
// structural scan of the (length-preserving-stripped) WHERE clause:
|
|
648
|
+
// '<owner> = $current_user' must be an AND-reachable, top-level conjunct
|
|
649
|
+
// of the WHERE — descent stops at any OR exactly as the REST AST does.
|
|
650
|
+
|
|
651
|
+
// True iff the uppercase keyword 'word' sits at offset i in 'up' with a
|
|
652
|
+
// word boundary on both sides.
|
|
653
|
+
function __sw_kwAt(up, i, word) {
|
|
654
|
+
if (up.substr(i, word.length) !== word) return false;
|
|
655
|
+
const before = up[i - 1];
|
|
656
|
+
const after = up[i + word.length];
|
|
657
|
+
const isWord = function (ch) { return ch != null && /[A-Z0-9_]/.test(ch); };
|
|
658
|
+
return !isWord(before) && !isWord(after);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// Index of the ')' matching the '(' at 'open', or -1 if unbalanced.
|
|
662
|
+
function __sw_matchingParen(s, open) {
|
|
663
|
+
let depth = 0;
|
|
664
|
+
for (let i = open; i < s.length; i++) {
|
|
665
|
+
if (s[i] === '(') depth++;
|
|
666
|
+
else if (s[i] === ')') { depth--; if (depth === 0) return i; }
|
|
667
|
+
}
|
|
668
|
+
return -1;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
// Remove one (or more) fully-enclosing paren pair(s): "((x))" -> "x".
|
|
672
|
+
function __sw_stripWrapParens(s) {
|
|
673
|
+
s = s.trim();
|
|
674
|
+
while (s.length >= 2 && s[0] === '(' && __sw_matchingParen(s, 0) === s.length - 1) {
|
|
675
|
+
s = s.slice(1, -1).trim();
|
|
676
|
+
}
|
|
677
|
+
return s;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// Split 'expr' on the depth-0, word-bounded keyword 'kw' ('AND' / 'OR').
|
|
681
|
+
function __sw_splitTopLevel(expr, kw) {
|
|
682
|
+
const up = expr.toUpperCase();
|
|
683
|
+
const parts = [];
|
|
684
|
+
let depth = 0, last = 0, i = 0;
|
|
685
|
+
const n = expr.length;
|
|
686
|
+
while (i < n) {
|
|
687
|
+
const ch = expr[i];
|
|
688
|
+
if (ch === '(') { depth++; i++; continue; }
|
|
689
|
+
if (ch === ')') { if (depth > 0) depth--; i++; continue; }
|
|
690
|
+
if (depth === 0 && __sw_kwAt(up, i, kw)) {
|
|
691
|
+
parts.push(expr.slice(last, i));
|
|
692
|
+
i += kw.length;
|
|
693
|
+
last = i;
|
|
694
|
+
continue;
|
|
695
|
+
}
|
|
696
|
+
i++;
|
|
697
|
+
}
|
|
698
|
+
parts.push(expr.slice(last));
|
|
699
|
+
return parts;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// True iff the atomic term is exactly "<owner> = $current_user" (either
|
|
703
|
+
// side). The owner column may be bare, table-qualified, or double-quoted.
|
|
704
|
+
function __sw_isOwnerEqTerm(term, ownerCol) {
|
|
705
|
+
const t = term.trim();
|
|
706
|
+
const oc = __sw_escapeRegex(ownerCol);
|
|
707
|
+
const col = '(?:[A-Za-z_][A-Za-z0-9_]*\\.)?"?' + oc + '"?';
|
|
708
|
+
const fwd = new RegExp('^' + col + '\\s*=\\s*\\$current_user$', 'i');
|
|
709
|
+
const rev = new RegExp('^\\$current_user\\s*=\\s*' + col + '$', 'i');
|
|
710
|
+
return fwd.test(t) || rev.test(t);
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
// True iff "<owner> = $current_user" is AND-reachable from the root of
|
|
714
|
+
// the boolean expression 'expr' (a top-level conjunct). A top-level OR
|
|
715
|
+
// anywhere on the path makes the constraint non-guaranteeing -> false.
|
|
716
|
+
function __sw_andReachesOwner(expr, ownerCol) {
|
|
717
|
+
expr = __sw_stripWrapParens(expr);
|
|
718
|
+
if (__sw_splitTopLevel(expr, 'OR').length > 1) return false;
|
|
719
|
+
const ands = __sw_splitTopLevel(expr, 'AND');
|
|
720
|
+
if (ands.length > 1) {
|
|
721
|
+
for (let i = 0; i < ands.length; i++) {
|
|
722
|
+
if (__sw_andReachesOwner(ands[i], ownerCol)) return true;
|
|
723
|
+
}
|
|
724
|
+
return false;
|
|
725
|
+
}
|
|
726
|
+
return __sw_isOwnerEqTerm(expr, ownerCol);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
// Extract the depth-0 WHERE clause text (between WHERE and the next
|
|
730
|
+
// top-level tail keyword / ';' / end), or null when there is no
|
|
731
|
+
// top-level WHERE. Anything in a subquery WHERE is at depth>0 and is
|
|
732
|
+
// intentionally ignored.
|
|
733
|
+
function __sw_whereClauseSpan(stripped) {
|
|
734
|
+
const up = stripped.toUpperCase();
|
|
735
|
+
const n = stripped.length;
|
|
736
|
+
let depth = 0, start = -1, i = 0;
|
|
737
|
+
while (i < n) {
|
|
738
|
+
const ch = stripped[i];
|
|
739
|
+
if (ch === '(') { depth++; i++; continue; }
|
|
740
|
+
if (ch === ')') { if (depth > 0) depth--; i++; continue; }
|
|
741
|
+
if (depth === 0) {
|
|
742
|
+
if (start === -1) {
|
|
743
|
+
if (__sw_kwAt(up, i, 'WHERE')) { start = i + 5; i = start; continue; }
|
|
744
|
+
} else {
|
|
745
|
+
if (ch === ';') return stripped.slice(start, i);
|
|
746
|
+
if (__sw_kwAt(up, i, 'GROUP') || __sw_kwAt(up, i, 'ORDER') ||
|
|
747
|
+
__sw_kwAt(up, i, 'LIMIT') || __sw_kwAt(up, i, 'HAVING') ||
|
|
748
|
+
__sw_kwAt(up, i, 'WINDOW') || __sw_kwAt(up, i, 'RETURNING') ||
|
|
749
|
+
__sw_kwAt(up, i, 'OFFSET')) {
|
|
750
|
+
return stripped.slice(start, i);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
i++;
|
|
755
|
+
}
|
|
756
|
+
if (start === -1) return null;
|
|
757
|
+
return stripped.slice(start);
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
// Prove the owner predicate constrains the WHERE clause. 'stripped' is the
|
|
761
|
+
// length-preserving-blanked SQL (string literals/comments already removed)
|
|
762
|
+
// so keyword/paren scanning is never fooled by literal contents.
|
|
763
|
+
function __sw_whereProvesOwner(stripped, ownerCol) {
|
|
764
|
+
const span = __sw_whereClauseSpan(stripped);
|
|
765
|
+
if (span == null || span.trim() === '') return false;
|
|
766
|
+
return __sw_andReachesOwner(span, ownerCol);
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
// Split 's' on depth-0 commas (parens-aware). Used to walk an INSERT
|
|
770
|
+
// column list and each VALUES tuple positionally.
|
|
771
|
+
function __sw_splitTopLevelComma(s) {
|
|
772
|
+
const parts = [];
|
|
773
|
+
let depth = 0, last = 0;
|
|
774
|
+
for (let i = 0; i < s.length; i++) {
|
|
775
|
+
const ch = s[i];
|
|
776
|
+
if (ch === '(') depth++;
|
|
777
|
+
else if (ch === ')') { if (depth > 0) depth--; }
|
|
778
|
+
else if (ch === ',' && depth === 0) { parts.push(s.slice(last, i)); last = i + 1; }
|
|
779
|
+
}
|
|
780
|
+
parts.push(s.slice(last));
|
|
781
|
+
return parts;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// ─── Positional INSERT owner proof (tsk_3006e8bc) ───────────────────
|
|
785
|
+
// The original INSERT check only asked whether the owner column APPEARED
|
|
786
|
+
// in the column list and the $current_user sentinel APPEARED somewhere —
|
|
787
|
+
// never whether the owner column's VALUE is the sentinel. An app-user
|
|
788
|
+
// could park the sentinel in a non-owner column and plant a row owned by
|
|
789
|
+
// someone else: INSERT INTO notes (user_id, body) VALUES ('VICTIM', $current_user).
|
|
790
|
+
// 'stripped' is the length-preserving-blanked SQL (string literals
|
|
791
|
+
// removed), so the sentinel survives while any literal in the owner slot
|
|
792
|
+
// is blanked away. Proven only when the owner column's VALUE is
|
|
793
|
+
// $current_user at the owner column's own index in EVERY VALUES tuple, or
|
|
794
|
+
// (INSERT...SELECT) in the matching SELECT projection. Fail-closed.
|
|
795
|
+
function __sw_insertProvesOwner(stripped, ownerCol) {
|
|
796
|
+
const up = stripped.toUpperCase();
|
|
797
|
+
function isSentinelTerm(seg) {
|
|
798
|
+
let s = __sw_stripWrapParens(String(seg).trim());
|
|
799
|
+
s = s.replace(/\s+AS\s+[A-Za-z_][A-Za-z0-9_]*$/i, '').trim();
|
|
800
|
+
return s.toLowerCase() === '$current_user';
|
|
801
|
+
}
|
|
802
|
+
// Locate the explicit column list: first '(' after the INTO keyword.
|
|
803
|
+
let intoEnd = -1;
|
|
804
|
+
for (let i = 0; i < stripped.length; i++) {
|
|
805
|
+
if (__sw_kwAt(up, i, 'INTO')) { intoEnd = i + 4; break; }
|
|
806
|
+
}
|
|
807
|
+
if (intoEnd === -1) return false;
|
|
808
|
+
const open = stripped.indexOf('(', intoEnd);
|
|
809
|
+
if (open === -1) return false;
|
|
810
|
+
const close = __sw_matchingParen(stripped, open);
|
|
811
|
+
if (close === -1) return false;
|
|
812
|
+
const cols = __sw_splitTopLevelComma(stripped.slice(open + 1, close)).map(function (c) {
|
|
813
|
+
return c.trim().replace(/^"|"$/g, '').toLowerCase();
|
|
814
|
+
});
|
|
815
|
+
const ownerIdx = cols.indexOf(ownerCol.toLowerCase());
|
|
816
|
+
if (ownerIdx === -1) return false;
|
|
817
|
+
// After the column list: VALUES (...)[, (...)]... or SELECT ...
|
|
818
|
+
const rest = stripped.slice(close + 1);
|
|
819
|
+
const restUp = rest.toUpperCase();
|
|
820
|
+
let depth = 0, kind = null, kidx = -1;
|
|
821
|
+
for (let i = 0; i < rest.length; i++) {
|
|
822
|
+
const ch = rest[i];
|
|
823
|
+
if (ch === '(') { depth++; continue; }
|
|
824
|
+
if (ch === ')') { if (depth > 0) depth--; continue; }
|
|
825
|
+
if (depth === 0) {
|
|
826
|
+
if (__sw_kwAt(restUp, i, 'VALUES')) { kind = 'values'; kidx = i + 6; break; }
|
|
827
|
+
if (__sw_kwAt(restUp, i, 'SELECT')) { kind = 'select'; kidx = i + 6; break; }
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
if (kind === 'values') {
|
|
831
|
+
let j = kidx, tuples = 0;
|
|
832
|
+
while (j < rest.length) {
|
|
833
|
+
while (j < rest.length && /[\s,]/.test(rest[j])) j++;
|
|
834
|
+
if (j >= rest.length || rest[j] !== '(') break; // RETURNING / end
|
|
835
|
+
const tClose = __sw_matchingParen(rest, j);
|
|
836
|
+
if (tClose === -1) return false;
|
|
837
|
+
const parts = __sw_splitTopLevelComma(rest.slice(j + 1, tClose));
|
|
838
|
+
if (ownerIdx >= parts.length) return false;
|
|
839
|
+
if (!isSentinelTerm(parts[ownerIdx])) return false;
|
|
840
|
+
tuples++;
|
|
841
|
+
j = tClose + 1;
|
|
842
|
+
}
|
|
843
|
+
return tuples > 0;
|
|
844
|
+
}
|
|
845
|
+
if (kind === 'select') {
|
|
846
|
+
let d2 = 0, projEnd = rest.length;
|
|
847
|
+
for (let i = kidx; i < rest.length; i++) {
|
|
848
|
+
const ch = rest[i];
|
|
849
|
+
if (ch === '(') { d2++; continue; }
|
|
850
|
+
if (ch === ')') { if (d2 > 0) d2--; continue; }
|
|
851
|
+
if (d2 === 0 && __sw_kwAt(restUp, i, 'FROM')) { projEnd = i; break; }
|
|
852
|
+
}
|
|
853
|
+
const parts = __sw_splitTopLevelComma(rest.slice(kidx, projEnd));
|
|
854
|
+
if (ownerIdx >= parts.length) return false;
|
|
855
|
+
return isSentinelTerm(parts[ownerIdx]);
|
|
856
|
+
}
|
|
857
|
+
return false;
|
|
858
|
+
}
|
|
859
|
+
|
|
432
860
|
function __sw_checkScope(sql) {
|
|
433
861
|
const tableNames = Object.keys(SCOPES);
|
|
434
862
|
if (tableNames.length === 0) return null;
|
|
@@ -443,13 +871,19 @@ function buildPlatformContext(env, request) {
|
|
|
443
871
|
if (!m) return { table: table, ownerCol: ownerCol, reason: 'INSERT into scoped table `' + table + '` must use the explicit-column form: INSERT INTO ' + table + ' (' + ownerCol + ', …) VALUES ($current_user, …).' };
|
|
444
872
|
const cols = m[1].split(',').map(function (s) { return s.trim().replace(/^"|"$/g, '').toLowerCase(); });
|
|
445
873
|
if (cols.indexOf(ownerCol.toLowerCase()) === -1) return { table: table, ownerCol: ownerCol, reason: 'INSERT into scoped table `' + table + '` must include column `' + ownerCol + '` set to $current_user.' };
|
|
446
|
-
|
|
874
|
+
// POSITIONAL proof (tsk_3006e8bc): the owner column's VALUE must be
|
|
875
|
+
// $current_user at the owner column's own index — in every VALUES
|
|
876
|
+
// row, or the matching SELECT projection for INSERT...SELECT. A
|
|
877
|
+
// sentinel parked in a different column does not scope the row.
|
|
878
|
+
if (!__sw_insertProvesOwner(stripped, ownerCol)) return { table: table, ownerCol: ownerCol, reason: 'INSERT into scoped table `' + table + '` must bind `' + ownerCol + '` to $current_user at its own position in every VALUES row (or the matching SELECT column for INSERT…SELECT). A $current_user value in a different column does not scope the row to the current user.' };
|
|
447
879
|
continue;
|
|
448
880
|
}
|
|
449
881
|
if (kind === 'select' || kind === 'update' || kind === 'delete') {
|
|
450
882
|
if (!/\bWHERE\b/i.test(stripped)) return { table: table, ownerCol: ownerCol, reason: kind.toUpperCase() + ' on scoped table `' + table + '` must have a WHERE clause that constrains `' + ownerCol + '` to $current_user.' };
|
|
451
|
-
|
|
452
|
-
|
|
883
|
+
// POSITIONAL proof (tsk_6475aa86): the owner predicate must be a
|
|
884
|
+
// top-level AND condition of the WHERE clause, not merely present
|
|
885
|
+
// somewhere in the SQL (projection / ORDER BY / SET / OR).
|
|
886
|
+
if (!__sw_whereProvesOwner(stripped, ownerCol)) return { table: table, ownerCol: ownerCol, reason: kind.toUpperCase() + ' on scoped table `' + table + '` must constrain `' + ownerCol + '` to the current user with `' + ownerCol + ' = $current_user` as a top-level condition in the WHERE clause - AND-ed (not inside an OR), and not only in the SELECT list, ORDER BY, or an UPDATE SET.' };
|
|
453
887
|
continue;
|
|
454
888
|
}
|
|
455
889
|
return { table: table, ownerCol: ownerCol, reason: 'Statement type not recognized for scope enforcement on `' + table + '`. Use SELECT, INSERT, UPDATE, or DELETE.' };
|
|
@@ -583,6 +1017,14 @@ function buildPlatformContext(env, request) {
|
|
|
583
1017
|
err.code = 'SCOPE_VIOLATION';
|
|
584
1018
|
err.table = v.table;
|
|
585
1019
|
err.owner_column = v.ownerCol;
|
|
1020
|
+
// A scope violation is a developer-fixable 4xx, not a platform bug
|
|
1021
|
+
// (tsk_81271d84). Mark it exposed so the function-bundle catch-all
|
|
1022
|
+
// surfaces a clean 403 SCOPE_VIOLATION with the owner-binding message,
|
|
1023
|
+
// instead of swallowing it into an opaque 500 FUNCTION_ERROR. The
|
|
1024
|
+
// message names the table + owner column the developer must bind —
|
|
1025
|
+
// product/SDK terms only, no infra leak.
|
|
1026
|
+
err.status = 403;
|
|
1027
|
+
err.__sw_expose = true;
|
|
586
1028
|
throw err;
|
|
587
1029
|
}
|
|
588
1030
|
|
|
@@ -763,18 +1205,50 @@ function buildPlatformContext(env, request) {
|
|
|
763
1205
|
// the actual fix is db_migrate. Logs and dashboard Logs tab
|
|
764
1206
|
// surface both tags ([SW_SLOW_QUERY] / [SW_SCHEMA_DRIFT]).
|
|
765
1207
|
const SW_SLOW_QUERY_MS = 100;
|
|
1208
|
+
// Strip engine/infra labels from a raw database error so the developer
|
|
1209
|
+
// sees the actionable cause ("UNIQUE constraint failed: users.email")
|
|
1210
|
+
// without the provider noise ("D1_ERROR", "SQLITE_CONSTRAINT_UNIQUE").
|
|
1211
|
+
// No D1/SQLite/Cloudflare term survives (rule 8 — product language only).
|
|
1212
|
+
function __sw_productizeDbMessage(msg) {
|
|
1213
|
+
return String(msg)
|
|
1214
|
+
.replace(/D1_ERROR:?/gi, '')
|
|
1215
|
+
.replace(/SQLITE_[A-Z_]+/gi, '')
|
|
1216
|
+
.replace(/\bSQLite\b/gi, 'database')
|
|
1217
|
+
.replace(/\bD1\b/gi, 'database')
|
|
1218
|
+
.replace(/\bCloudflare\b/gi, '')
|
|
1219
|
+
.replace(/\s{2,}/g, ' ')
|
|
1220
|
+
.replace(/^[\s:;.,]+|[\s:;.,]+$/g, '')
|
|
1221
|
+
.trim();
|
|
1222
|
+
}
|
|
766
1223
|
function __sw_decorateError(label, sqlPreview, err) {
|
|
767
1224
|
const msg = (err && err.message) || String(err);
|
|
1225
|
+
const snippet = String(sqlPreview).replace(/\s+/g, ' ').slice(0, 200);
|
|
768
1226
|
const m = msg.match(/no such (?:column|table)[^A-Za-z0-9_]*([A-Za-z0-9_.]*)/i);
|
|
769
1227
|
if (m) {
|
|
770
1228
|
const ident = m[1] || 'unknown';
|
|
771
|
-
const snippet = String(sqlPreview).replace(/\s+/g, ' ').slice(0, 200);
|
|
772
1229
|
console.warn('[SW_SCHEMA_DRIFT] ' + label + ' — ' + ident + ' missing — ' + snippet);
|
|
773
1230
|
const better = new Error('Schema drift: ' + msg + '. Your code references "' + ident + '" but the database schema does not. Did you forget to run db_migrate before deploying?');
|
|
774
1231
|
better.code = 'SCHEMA_DRIFT';
|
|
775
1232
|
better.original = err;
|
|
776
1233
|
return better;
|
|
777
1234
|
}
|
|
1235
|
+
// Any OTHER database error: the native binding / provider passthrough
|
|
1236
|
+
// surfaces raw strings like "D1_ERROR: ... : SQLITE_CONSTRAINT" that
|
|
1237
|
+
// leak the engine and read as noise. Rewrite to product language, but
|
|
1238
|
+
// FAIL LOUD: log the real cause server-side (Logs tab) and keep
|
|
1239
|
+
// .original — never swallow it.
|
|
1240
|
+
if (/D1_ERROR|SQLITE/i.test(msg)) {
|
|
1241
|
+
console.error('[SW_DB_ERROR] ' + label + ' — ' + msg + ' — ' + snippet);
|
|
1242
|
+
const detail = __sw_productizeDbMessage(msg);
|
|
1243
|
+
const wrapped = new Error(
|
|
1244
|
+
(detail ? 'Your database query could not be completed: ' + detail + '.'
|
|
1245
|
+
: 'Your database query could not be completed.') +
|
|
1246
|
+
' Check the query and your table and column names against your schema; the full database error is in your project Logs.'
|
|
1247
|
+
);
|
|
1248
|
+
wrapped.code = 'DB_QUERY_ERROR';
|
|
1249
|
+
wrapped.original = err;
|
|
1250
|
+
return wrapped;
|
|
1251
|
+
}
|
|
778
1252
|
return err;
|
|
779
1253
|
}
|
|
780
1254
|
// Retry-on-backpressure (founder 2026-06-08): a write that gets
|
|
@@ -1150,6 +1624,57 @@ function buildPlatformContext(env, request) {
|
|
|
1150
1624
|
}
|
|
1151
1625
|
}
|
|
1152
1626
|
|
|
1627
|
+
// sw.db.scope(table, opts) — declare a table as user-scoped IN-BAND,
|
|
1628
|
+
// so a deployed function / run_code setup script can arm per-user row
|
|
1629
|
+
// scoping without minting a developer key and curl-ing /v1/db/scopes
|
|
1630
|
+
// out of band (the gooddog gap). Routes through the SAME POST
|
|
1631
|
+
// /v1/db/scopes the dashboard + MCP use, with the same owner authority:
|
|
1632
|
+
// the runtime key is the project owner's, pinned to THIS project by the
|
|
1633
|
+
// auth middleware, so a scope can only ever be declared on this project.
|
|
1634
|
+
//
|
|
1635
|
+
// Declaring a scope TIGHTENS access (it arms owner-column enforcement on
|
|
1636
|
+
// app-user queries); it never widens a query result set. Scopes are
|
|
1637
|
+
// baked into the bundle at DEPLOY time (PROJECT_SCOPES), so a scope
|
|
1638
|
+
// declared at runtime takes effect for app-user queries on the NEXT
|
|
1639
|
+
// deploy — this records the declaration; it does NOT hot-swap the
|
|
1640
|
+
// running bundle's SCOPES map. Unlike sw.db.query, failures throw.
|
|
1641
|
+
//
|
|
1642
|
+
// await sw.db.scope('bookings', { owner_column: 'user_id' })
|
|
1643
|
+
// await sw.db.scope('audit', { intent: 'shared' }) // intentionally cross-user
|
|
1644
|
+
// await sw.db.scope.list() // all declared scopes
|
|
1645
|
+
// await sw.db.scope.get('bookings') // one scope, or null
|
|
1646
|
+
async function __sw_scopeDeclare(table, opts) {
|
|
1647
|
+
if (typeof table !== 'string' || !table) {
|
|
1648
|
+
const err = new Error('sw.db.scope(table, opts): table must be a non-empty string.');
|
|
1649
|
+
err.code = 'VALIDATION_ERROR';
|
|
1650
|
+
throw err;
|
|
1651
|
+
}
|
|
1652
|
+
opts = opts || {};
|
|
1653
|
+
const body = { project_id: projectId, table: table };
|
|
1654
|
+
if (opts.owner_column != null) body.owner_column = opts.owner_column;
|
|
1655
|
+
if (opts.sensitive_columns != null) body.sensitive_columns = opts.sensitive_columns;
|
|
1656
|
+
if (opts.intent != null) body.intent = opts.intent;
|
|
1657
|
+
return platformJSON('/v1/db/scopes', { method: 'POST', body: JSON.stringify(body) });
|
|
1658
|
+
}
|
|
1659
|
+
__sw_scopeDeclare.list = async function () {
|
|
1660
|
+
const d = await platformJSON('/v1/db/scopes?project_id=' + encodeURIComponent(projectId));
|
|
1661
|
+
return (d && d.scopes) || [];
|
|
1662
|
+
};
|
|
1663
|
+
__sw_scopeDeclare.get = async function (table) {
|
|
1664
|
+
if (typeof table !== 'string' || !table) {
|
|
1665
|
+
const err = new Error('sw.db.scope.get(table): table must be a non-empty string.');
|
|
1666
|
+
err.code = 'VALIDATION_ERROR';
|
|
1667
|
+
throw err;
|
|
1668
|
+
}
|
|
1669
|
+
const lc = table.toLowerCase();
|
|
1670
|
+
const d = await platformJSON('/v1/db/scopes?project_id=' + encodeURIComponent(projectId));
|
|
1671
|
+
const scopes = (d && d.scopes) || [];
|
|
1672
|
+
for (let i = 0; i < scopes.length; i++) {
|
|
1673
|
+
if (scopes[i] && String(scopes[i].table).toLowerCase() === lc) return scopes[i];
|
|
1674
|
+
}
|
|
1675
|
+
return null;
|
|
1676
|
+
};
|
|
1677
|
+
|
|
1153
1678
|
return {
|
|
1154
1679
|
async query(sql, params, options) {
|
|
1155
1680
|
ensureBinding();
|
|
@@ -1190,11 +1715,28 @@ function buildPlatformContext(env, request) {
|
|
|
1190
1715
|
changes: (rr.meta && rr.meta.changes) || 0,
|
|
1191
1716
|
};
|
|
1192
1717
|
}
|
|
1718
|
+
if (!__sw_scopeInGrace(v.table)) {
|
|
1719
|
+
__sw_throwViolation(v,
|
|
1720
|
+
'Pass { user } and we will scope your query automatically, but only for single-table SELECT/UPDATE/DELETE/INSERT. This query has a JOIN, UNION, or subquery — use sw.db.scoped(user.id) to write the scoped form explicitly, or { unscoped: true } if cross-user is intentional.');
|
|
1721
|
+
}
|
|
1722
|
+
__sw_scopeGraceWarn(v, 'sw.db.query');
|
|
1723
|
+
} else if (__sw_scopeInGrace(v.table)) {
|
|
1724
|
+
// Grace window: warn loudly, run the query as written.
|
|
1725
|
+
__sw_scopeGraceWarn(v, 'sw.db.query');
|
|
1726
|
+
} else {
|
|
1193
1727
|
__sw_throwViolation(v,
|
|
1194
|
-
'Pass { user }
|
|
1728
|
+
'Pass { user } as the third arg — sw.db.query(sql, params, { user: sw.auth.fromRequest(req).user }) — or { unscoped: true } if cross-user is intentional. The verbose form sw.db.scoped(user.id).query(...) still works.');
|
|
1729
|
+
}
|
|
1730
|
+
} else if (userId) {
|
|
1731
|
+
// No scoped-table violation, but { user } was passed. If the
|
|
1732
|
+
// query touches an owner-shaped table with NO declared scope,
|
|
1733
|
+
// { user } would silently no-op (pfb_02e0d66c1275) — warn during
|
|
1734
|
+
// grace, throw SCOPE_NOT_DECLARED after. Never a silent leak.
|
|
1735
|
+
const u = __sw_checkUnscopedOwner(sql);
|
|
1736
|
+
if (u) {
|
|
1737
|
+
if (__sw_unscopedOwnerInGrace(u.table)) __sw_unscopedOwnerWarn(u, 'sw.db.query');
|
|
1738
|
+
else __sw_throwScopeNotDeclared(u, 'sw.db.query');
|
|
1195
1739
|
}
|
|
1196
|
-
__sw_throwViolation(v,
|
|
1197
|
-
'Pass { user } as the third arg — sw.db.query(sql, params, { user: sw.auth.fromRequest(req).user }) — or { unscoped: true } if cross-user is intentional. The verbose form sw.db.scoped(user.id).query(...) still works.');
|
|
1198
1740
|
}
|
|
1199
1741
|
}
|
|
1200
1742
|
const r = await __sw_timedExec('sw.db.query', sql, function () { return prep(sql, params).all(); });
|
|
@@ -1229,8 +1771,13 @@ function buildPlatformContext(env, request) {
|
|
|
1229
1771
|
for (const s of statements) {
|
|
1230
1772
|
const v = __sw_checkScope(s.sql);
|
|
1231
1773
|
if (v && !userId) {
|
|
1232
|
-
|
|
1233
|
-
|
|
1774
|
+
if (__sw_scopeInGrace(v.table)) {
|
|
1775
|
+
// Grace window: warn loudly, let the batch run as written.
|
|
1776
|
+
__sw_scopeGraceWarn(v, 'sw.db.batch');
|
|
1777
|
+
} else {
|
|
1778
|
+
__sw_throwViolation(v,
|
|
1779
|
+
'Pass { user } as the second arg to sw.db.batch, or { unscoped: true } if cross-user is intentional. The verbose form sw.db.scoped(user.id).batch(...) still works.');
|
|
1780
|
+
}
|
|
1234
1781
|
}
|
|
1235
1782
|
}
|
|
1236
1783
|
}
|
|
@@ -1243,8 +1790,20 @@ function buildPlatformContext(env, request) {
|
|
|
1243
1790
|
const subst = __sw_substCurrentUser(rewritten, s.params, userId);
|
|
1244
1791
|
return prep(subst.sql, subst.params);
|
|
1245
1792
|
}
|
|
1246
|
-
|
|
1247
|
-
|
|
1793
|
+
if (!__sw_scopeInGrace(v.table)) {
|
|
1794
|
+
__sw_throwViolation(v,
|
|
1795
|
+
'Auto-scope only handles single-table SELECT/UPDATE/DELETE/INSERT. Use sw.db.scoped(user.id).batch(...) for JOIN/UNION/subquery shapes.');
|
|
1796
|
+
}
|
|
1797
|
+
__sw_scopeGraceWarn(v, 'sw.db.batch');
|
|
1798
|
+
} else {
|
|
1799
|
+
// { user } against an owner-shaped table with no declared scope
|
|
1800
|
+
// would silently no-op (pfb_02e0d66c1275) — warn during grace,
|
|
1801
|
+
// throw SCOPE_NOT_DECLARED after.
|
|
1802
|
+
const u = __sw_checkUnscopedOwner(s.sql);
|
|
1803
|
+
if (u) {
|
|
1804
|
+
if (__sw_unscopedOwnerInGrace(u.table)) __sw_unscopedOwnerWarn(u, 'sw.db.batch');
|
|
1805
|
+
else __sw_throwScopeNotDeclared(u, 'sw.db.batch');
|
|
1806
|
+
}
|
|
1248
1807
|
}
|
|
1249
1808
|
}
|
|
1250
1809
|
return prep(s.sql, s.params);
|
|
@@ -1291,6 +1850,7 @@ function buildPlatformContext(env, request) {
|
|
|
1291
1850
|
return (r.results || []).map((row) => row.name);
|
|
1292
1851
|
},
|
|
1293
1852
|
scoped: scopedClient,
|
|
1853
|
+
scope: __sw_scopeDeclare,
|
|
1294
1854
|
async dump() {
|
|
1295
1855
|
// sw.db.dump is developer-only and is NOT callable from a
|
|
1296
1856
|
// deployed function. A full-database export must run with
|
|
@@ -1373,11 +1933,26 @@ function buildPlatformContext(env, request) {
|
|
|
1373
1933
|
// fs_write path worked, the in-function shim silently dropped it).
|
|
1374
1934
|
const headers = { 'Content-Type': ct };
|
|
1375
1935
|
if (opts.visibility === 'public' || opts.public === true) headers['X-Visibility'] = 'public';
|
|
1936
|
+
// Opt-in conditional write: { if_match: <version> } pins the version
|
|
1937
|
+
// this writer last read (write/stat/versions all return it). If the
|
|
1938
|
+
// file changed since, the platform refuses with FS_VERSION_CONFLICT
|
|
1939
|
+
// instead of silently last-write-wins. Omit for today's behavior.
|
|
1940
|
+
const ifMatch = opts.ifMatch ?? opts.if_match;
|
|
1941
|
+
if (ifMatch !== undefined && ifMatch !== null) headers['If-Match'] = String(ifMatch);
|
|
1376
1942
|
const r = await platformFetch('/v1/fs/' + projectId + path, {
|
|
1377
1943
|
method: 'PUT',
|
|
1378
1944
|
headers,
|
|
1379
1945
|
body,
|
|
1380
1946
|
});
|
|
1947
|
+
if (r.status === 412) {
|
|
1948
|
+
const conflict = await r.json().catch(() => null);
|
|
1949
|
+
const err = new Error('FS_VERSION_CONFLICT: ' + (conflict && conflict.message
|
|
1950
|
+
? conflict.message
|
|
1951
|
+
: 'the file changed since it was read — re-read it and retry with the current version'));
|
|
1952
|
+
err.code = 'FS_VERSION_CONFLICT';
|
|
1953
|
+
if (conflict && conflict.data) err.current_version = conflict.data.current_version;
|
|
1954
|
+
throw err;
|
|
1955
|
+
}
|
|
1381
1956
|
if (!r.ok) throw new Error('fs.write failed: ' + r.status);
|
|
1382
1957
|
return r.json();
|
|
1383
1958
|
},
|
|
@@ -2481,14 +3056,20 @@ function buildPlatformContext(env, request) {
|
|
|
2481
3056
|
// The cookies ride out on the Response via the shim, so the handler
|
|
2482
3057
|
// returns a plain user/Response and does zero header work.
|
|
2483
3058
|
async loginWithCookie(req, email, password) {
|
|
2484
|
-
const
|
|
3059
|
+
const creds = __sw_cookieCreds('loginWithCookie', req, email, password);
|
|
3060
|
+
let d;
|
|
3061
|
+
try { d = await this.login(creds); }
|
|
3062
|
+
catch (err) { throw __sw_markExpected(err); }
|
|
2485
3063
|
const access = d && (d.token || d.access_token);
|
|
2486
3064
|
const refresh = d && d.refresh_token;
|
|
2487
3065
|
if (access && refresh) __sw_setAuthCookies(access, refresh);
|
|
2488
3066
|
return (d && d.user) || null;
|
|
2489
3067
|
},
|
|
2490
3068
|
async signupWithCookie(req, email, password) {
|
|
2491
|
-
const
|
|
3069
|
+
const creds = __sw_cookieCreds('signupWithCookie', req, email, password);
|
|
3070
|
+
let d;
|
|
3071
|
+
try { d = await this.signup(creds); }
|
|
3072
|
+
catch (err) { throw __sw_markExpected(err); }
|
|
2492
3073
|
const access = d && (d.token || d.access_token);
|
|
2493
3074
|
const refresh = d && d.refresh_token;
|
|
2494
3075
|
if (access && refresh) __sw_setAuthCookies(access, refresh);
|
|
@@ -2499,13 +3080,34 @@ function buildPlatformContext(env, request) {
|
|
|
2499
3080
|
async googleCallbackWithCookie(req, redirectTo) {
|
|
2500
3081
|
const u = new URL(req.url);
|
|
2501
3082
|
const code = u.searchParams.get('code');
|
|
2502
|
-
if (!code) { const e = new Error('Missing ?code on the OAuth callback.'); e.code = 'VALIDATION_ERROR'; throw e; }
|
|
2503
|
-
|
|
3083
|
+
if (!code) { const e = new Error('Missing ?code on the OAuth callback.'); e.code = 'VALIDATION_ERROR'; e.status = 400; e.__sw_expose = true; throw e; }
|
|
3084
|
+
let d;
|
|
3085
|
+
try { d = await this.googleExchange({ code: code, redirect_uri: u.origin + u.pathname }); }
|
|
3086
|
+
catch (err) { throw __sw_markExpected(err); }
|
|
2504
3087
|
const access = d && (d.token || d.access_token);
|
|
2505
3088
|
const refresh = d && d.refresh_token;
|
|
2506
3089
|
if (access && refresh) __sw_setAuthCookies(access, refresh);
|
|
2507
3090
|
return new Response(null, { status: 302, headers: { Location: redirectTo || '/' } });
|
|
2508
3091
|
},
|
|
3092
|
+
// Mint the httpOnly session cookies from an existing token pair —
|
|
3093
|
+
// the primitive the *WithCookie helpers wrap (tsk_1dd4e1b4). For
|
|
3094
|
+
// backends (e.g. @somewhere-tech/auth/server) that already called
|
|
3095
|
+
// login / googleExchange / verifyOtp and want THAT session as
|
|
3096
|
+
// cookies without a second platform call.
|
|
3097
|
+
setSessionCookies(access, refresh) {
|
|
3098
|
+
if (typeof access !== 'string' || !access || typeof refresh !== 'string' || !refresh) {
|
|
3099
|
+
const err = new Error('sw.auth.setSessionCookies: access and refresh must be non-empty strings');
|
|
3100
|
+
err.code = 'VALIDATION_ERROR';
|
|
3101
|
+
throw err;
|
|
3102
|
+
}
|
|
3103
|
+
__sw_setAuthCookies(access, refresh);
|
|
3104
|
+
},
|
|
3105
|
+
// Park the Set-Cookie expirations that end a cookie session (the
|
|
3106
|
+
// clear half of setSessionCookies). logoutWithCookie does this AND
|
|
3107
|
+
// revokes server-side — prefer it when you have the Request.
|
|
3108
|
+
clearSessionCookies() {
|
|
3109
|
+
__sw_clearAuthCookies();
|
|
3110
|
+
},
|
|
2509
3111
|
// Revokes the session server-side and clears the cookies.
|
|
2510
3112
|
async logoutWithCookie(req) {
|
|
2511
3113
|
const ch = (req && req.headers && (req.headers.get('cookie') || req.headers.get('Cookie'))) || '';
|
|
@@ -2729,6 +3331,16 @@ function buildPlatformContext(env, request) {
|
|
|
2729
3331
|
}
|
|
2730
3332
|
if (!token) return null;
|
|
2731
3333
|
|
|
3334
|
+
// Cross-origin guard (tsk_1dd4e1b4): a cookie-authed request from a
|
|
3335
|
+
// foreign origin is treated as unauthenticated (CSRF on writes,
|
|
3336
|
+
// credentialed cross-origin reads on GET). Loud log so blocked
|
|
3337
|
+
// attempts show in the function logs, never silently.
|
|
3338
|
+
if (fromCookie && __sw_cookieCsrfBlocked(req)) {
|
|
3339
|
+
console.error('[CSRF] blocked cookie-authed ' + req.method + ' ' + (req.url || '') +
|
|
3340
|
+
' — cross-origin source does not match this app. Browser calls must be same-origin (or use token auth).');
|
|
3341
|
+
return null;
|
|
3342
|
+
}
|
|
3343
|
+
|
|
2732
3344
|
let refreshToken = null;
|
|
2733
3345
|
const optOut = req.headers.get('X-No-Auto-Refresh') || req.headers.get('x-no-auto-refresh');
|
|
2734
3346
|
if (optOut !== '1') {
|
|
@@ -2942,6 +3554,39 @@ function buildPlatformContext(env, request) {
|
|
|
2942
3554
|
info(msg, data) { return this._send('info', msg, data); },
|
|
2943
3555
|
warn(msg, data) { return this._send('warn', msg, data); },
|
|
2944
3556
|
error(msg, data) { return this._send('error', msg, data); },
|
|
3557
|
+
// sw.logs.tail({ limit?, level?, since?, source?, search? }) — read this
|
|
3558
|
+
// project's OWN recent logs back, in one call, so a script can run an
|
|
3559
|
+
// action then read the logs it produced without a second round-trip
|
|
3560
|
+
// through the dashboard. Reads the same owner-scoped app_logs the
|
|
3561
|
+
// GET /v1/logs read path returns; the runtime key is pinned to this
|
|
3562
|
+
// project, so it can only ever see this project's logs. Read-only —
|
|
3563
|
+
// unlike _send (fire-and-forget), failures throw so the caller sees them.
|
|
3564
|
+
//
|
|
3565
|
+
// Read consistency: log writes (sw.logs.* and platform internalLog) are
|
|
3566
|
+
// ingested through a durable queue that batch-inserts, so a line you just
|
|
3567
|
+
// wrote can lag a short window before tail() returns it (pfb
|
|
3568
|
+
// F-LOG-WRITE-READ-GAP). tail() reports only committed rows — it never
|
|
3569
|
+
// invents or waits on an un-ingested write. Poll briefly if you need
|
|
3570
|
+
// read-your-write.
|
|
3571
|
+
//
|
|
3572
|
+
// since: epoch-ms number OR ISO-8601 string — returns logs at/after it.
|
|
3573
|
+
// level: 'debug' | 'info' | 'warn' | 'error'. source: filter by source.
|
|
3574
|
+
// limit: default 50, capped at 500 by the read path.
|
|
3575
|
+
// Returns an array of { id, level, message, data, source, created_at }
|
|
3576
|
+
// (newest first).
|
|
3577
|
+
async tail(opts) {
|
|
3578
|
+
opts = opts || {};
|
|
3579
|
+
const qs = new URLSearchParams({ project_id: projectId });
|
|
3580
|
+
if (opts.limit != null) qs.set('limit', String(opts.limit));
|
|
3581
|
+
if (opts.level) qs.set('level', String(opts.level));
|
|
3582
|
+
if (opts.source) qs.set('source', String(opts.source));
|
|
3583
|
+
if (opts.search) qs.set('search', String(opts.search));
|
|
3584
|
+
if (opts.since != null) {
|
|
3585
|
+
qs.set('after', typeof opts.since === 'number' ? String(opts.since) : String(opts.since));
|
|
3586
|
+
}
|
|
3587
|
+
const d = await platformJSON('/v1/logs?' + qs.toString());
|
|
3588
|
+
return (d && d.logs) || [];
|
|
3589
|
+
},
|
|
2945
3590
|
},
|
|
2946
3591
|
|
|
2947
3592
|
realtime: {
|
package/runtime/sw-init.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// VENDORED from worker/src/utils/function-bundle.ts (SW_INIT_JS) @
|
|
1
|
+
// VENDORED from worker/src/utils/function-bundle.ts (SW_INIT_JS) @ e8da46e
|
|
2
2
|
// — the exact runtime deployed functions run against. Do not edit by hand;
|
|
3
3
|
// re-sync with: node scripts/extract-runtime.mjs <monorepo>
|
|
4
4
|
// Generated by somewhere.tech deploy pipeline. Sets up the global
|