@snagset/server 0.0.1
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/README.md +83 -0
- package/dist/app.js +453 -0
- package/dist/app.js.map +1 -0
- package/dist/bin.js +951 -0
- package/dist/bin.js.map +1 -0
- package/dist/boot.js +321 -0
- package/dist/boot.js.map +1 -0
- package/dist/config.generated.js +116 -0
- package/dist/config.generated.js.map +1 -0
- package/dist/config.js +180 -0
- package/dist/config.js.map +1 -0
- package/dist/crypto.js +26 -0
- package/dist/crypto.js.map +1 -0
- package/dist/db/connect.js +49 -0
- package/dist/db/connect.js.map +1 -0
- package/dist/db/migrate.js +210 -0
- package/dist/db/migrate.js.map +1 -0
- package/dist/db/migrations/0001_init.sql +1060 -0
- package/dist/db/migrations/0002_maintenance_claim.sql +109 -0
- package/dist/db/migrations/0003_email_provider.sql +35 -0
- package/dist/db/migrations/0004_unsubscribe.sql +24 -0
- package/dist/db/rls.js +58 -0
- package/dist/db/rls.js.map +1 -0
- package/dist/jobs/index.js +171 -0
- package/dist/jobs/index.js.map +1 -0
- package/dist/jobs/reaper.js +182 -0
- package/dist/jobs/reaper.js.map +1 -0
- package/dist/keys.js +77 -0
- package/dist/keys.js.map +1 -0
- package/dist/mail/index.js +75 -0
- package/dist/mail/index.js.map +1 -0
- package/dist/mail/mailer.js +27 -0
- package/dist/mail/mailer.js.map +1 -0
- package/dist/mail/notify.js +405 -0
- package/dist/mail/notify.js.map +1 -0
- package/dist/mail/resend.js +95 -0
- package/dist/mail/resend.js.map +1 -0
- package/dist/mail/smtp.js +315 -0
- package/dist/mail/smtp.js.map +1 -0
- package/dist/mail/unsubscribe.js +134 -0
- package/dist/mail/unsubscribe.js.map +1 -0
- package/dist/projectCache.js +50 -0
- package/dist/projectCache.js.map +1 -0
- package/dist/projects.js +101 -0
- package/dist/projects.js.map +1 -0
- package/dist/routes/attachments.js +159 -0
- package/dist/routes/attachments.js.map +1 -0
- package/dist/routes/auth.js +55 -0
- package/dist/routes/auth.js.map +1 -0
- package/dist/routes/commentEdit.js +201 -0
- package/dist/routes/commentEdit.js.map +1 -0
- package/dist/routes/comments.js +313 -0
- package/dist/routes/comments.js.map +1 -0
- package/dist/routes/cors.js +174 -0
- package/dist/routes/cors.js.map +1 -0
- package/dist/routes/jobs.js +56 -0
- package/dist/routes/jobs.js.map +1 -0
- package/dist/routes/mcpAuth.js +187 -0
- package/dist/routes/mcpAuth.js.map +1 -0
- package/dist/routes/mcpToken.js +94 -0
- package/dist/routes/mcpToken.js.map +1 -0
- package/dist/routes/owner.js +288 -0
- package/dist/routes/owner.js.map +1 -0
- package/dist/routes/reviewLinks.js +169 -0
- package/dist/routes/reviewLinks.js.map +1 -0
- package/dist/routes/rounds.js +137 -0
- package/dist/routes/rounds.js.map +1 -0
- package/dist/routes/sessions.js +349 -0
- package/dist/routes/sessions.js.map +1 -0
- package/dist/routes/sites.js +192 -0
- package/dist/routes/sites.js.map +1 -0
- package/dist/routes/threads.js +1080 -0
- package/dist/routes/threads.js.map +1 -0
- package/dist/routes/widget.js +132 -0
- package/dist/routes/widget.js.map +1 -0
- package/dist/serverless.js +97 -0
- package/dist/serverless.js.map +1 -0
- package/dist/session.js +152 -0
- package/dist/session.js.map +1 -0
- package/dist/storage/bucket.js +116 -0
- package/dist/storage/bucket.js.map +1 -0
- package/dist/storage/settings.js +132 -0
- package/dist/storage/settings.js.map +1 -0
- package/dist/vendor/adapters/index.js +1 -0
- package/dist/vendor/adapters/outbound/fence.js +44 -0
- package/dist/vendor/adapters/outbound/sanitise.js +233 -0
- package/dist/vendor/types/index.js +4 -0
- package/dist/vendor/types/names.js +254 -0
- package/dist/vendor/widget/object.js +38 -0
- package/dist/vendor/widget/predicates.js +377 -0
- package/package.json +53 -0
- package/static/dashboard/assets/index-CaPfKhFP.css +1 -0
- package/static/dashboard/assets/index-DfTNjHCK.js +1 -0
- package/static/dashboard/index.html +14 -0
- package/static/widget/snag.anchor-ext.js +1 -0
- package/static/widget/snag.anchor-fuzzy.js +1 -0
- package/static/widget/snag.capture.js +4 -0
- package/static/widget/snag.crumbs.js +4 -0
- package/static/widget/snag.dock.js +5 -0
- package/static/widget/snag.js +5 -0
- package/static/widget/snag.panels.js +2 -0
- package/static/widget/snag.runtime.js +2 -0
- package/static/widget/snag.screenshot.js +1 -0
- package/static/widget/snag.text.js +2 -0
- package/vercel.json +6 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"threads.js","sourceRoot":"","sources":["../../src/routes/threads.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
4
|
+
import { dirname, join, resolve } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
/** `1` from `1.4.2`; `0` while the package is pre-1.0. */
|
|
7
|
+
const majorOf = (version) => version.split(".")[0] ?? "0";
|
|
8
|
+
/**
|
|
9
|
+
* Immutable for a version path, five minutes for the major alias.
|
|
10
|
+
*
|
|
11
|
+
* 08 §3.8 sets both, and the asymmetry is the whole point of having two paths:
|
|
12
|
+
* a client who pins gets bytes that can be cached forever and hashed for SRI,
|
|
13
|
+
* and a client who never touches the tag again gets a fix within five minutes.
|
|
14
|
+
*/
|
|
15
|
+
const IMMUTABLE = "public, max-age=31536000, immutable";
|
|
16
|
+
const ALIAS = "public, max-age=300";
|
|
17
|
+
export function widgetRoutes(deps) {
|
|
18
|
+
const app = new Hono();
|
|
19
|
+
const root = deps.dir ?? join(dirname(fileURLToPath(import.meta.url)), "..", "..", "static", "widget");
|
|
20
|
+
const major = majorOf(deps.version);
|
|
21
|
+
/** One artefact, or null when the name escapes the directory or is absent. */
|
|
22
|
+
const read = (name) => {
|
|
23
|
+
// `..` cannot escape: the join is resolved and checked against the root.
|
|
24
|
+
const full = resolve(root, name);
|
|
25
|
+
if (!full.startsWith(resolve(root)))
|
|
26
|
+
return null;
|
|
27
|
+
if (!existsSync(full) || !statSync(full).isFile())
|
|
28
|
+
return null;
|
|
29
|
+
return readFileSync(full);
|
|
30
|
+
};
|
|
31
|
+
const serve = (c, name, cache) => {
|
|
32
|
+
const body = read(name);
|
|
33
|
+
if (!body) {
|
|
34
|
+
// 404 and a sentence, never the SPA shell. An operator who mistypes a
|
|
35
|
+
// path should learn that here rather than from a browser console saying
|
|
36
|
+
// the module has an unexpected MIME type.
|
|
37
|
+
return c.json({
|
|
38
|
+
error: "artefact_not_found",
|
|
39
|
+
message: `No widget artefact called ${JSON.stringify(name)} in this build. ` +
|
|
40
|
+
`The set is listed at /widget/manifest.json.`,
|
|
41
|
+
}, 404);
|
|
42
|
+
}
|
|
43
|
+
c.header("Content-Type", name.endsWith(".map") ? "application/json" : "text/javascript; charset=utf-8");
|
|
44
|
+
c.header("Cache-Control", cache);
|
|
45
|
+
// The widget is loaded BY OTHER ORIGINS — that is the entire product — so
|
|
46
|
+
// these bytes are explicitly cross-origin readable. Without it a client's
|
|
47
|
+
// site cannot fetch the module at all.
|
|
48
|
+
c.header("Access-Control-Allow-Origin", "*");
|
|
49
|
+
// 06 §7.2 / L8.5: a client hashing this file for PCI 6.4.3 needs the digest
|
|
50
|
+
// to be the one they can compute themselves. Published here too so they do
|
|
51
|
+
// not have to.
|
|
52
|
+
c.header("X-Content-Type-Options", "nosniff");
|
|
53
|
+
// Hono's body() takes an ArrayBuffer view, not Node's pooled Buffer.
|
|
54
|
+
return c.body(new Uint8Array(body));
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* `/widget/manifest.json` — version plus a `sha384` for every artefact.
|
|
58
|
+
*
|
|
59
|
+
* This is the answer to LOCK L8.5's open question, and it is cheap to give
|
|
60
|
+
* even while that question is open: a client who must inventory and
|
|
61
|
+
* integrity-check every script on a payment page (PCI DSS 6.4.3, mandatory
|
|
62
|
+
* since 2025-03-31) can read the digest of the exact bytes this instance is
|
|
63
|
+
* serving. Computed on demand rather than at build, because the file on disk
|
|
64
|
+
* is what will actually be served and a build-time digest can only ever be a
|
|
65
|
+
* claim about it.
|
|
66
|
+
*/
|
|
67
|
+
app.get("/widget/manifest.json", (c) => {
|
|
68
|
+
let files = [];
|
|
69
|
+
try {
|
|
70
|
+
files = readdirSync(root).filter((f) => f.endsWith(".js"));
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
/* no widget directory in this build — reported as an empty set below */
|
|
74
|
+
}
|
|
75
|
+
const assets = files.map((name) => {
|
|
76
|
+
const body = readFileSync(join(root, name));
|
|
77
|
+
return {
|
|
78
|
+
name,
|
|
79
|
+
bytes: body.byteLength,
|
|
80
|
+
integrity: `sha384-${createHash("sha384").update(body).digest("base64")}`,
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
c.header("Cache-Control", ALIAS);
|
|
84
|
+
c.header("Access-Control-Allow-Origin", "*");
|
|
85
|
+
return c.json({ version: deps.version, major: `v${major}`, assets });
|
|
86
|
+
});
|
|
87
|
+
// `/widget/v1/snag.js` — the major-pinned alias, and the path the dashboard
|
|
88
|
+
// prints. Registered before the version route so `v1` is not read as one.
|
|
89
|
+
// The whole segment is the param, `v1` included. Written first as
|
|
90
|
+
// `/widget/v:major{v?[0-9]+}/…` — a literal `v` followed by a param whose
|
|
91
|
+
// own pattern also allowed one — which matched nothing and fell through to
|
|
92
|
+
// the dashboard's catch-all, i.e. straight back into the bug this router
|
|
93
|
+
// exists to fix. Caught because the check asserted the content type.
|
|
94
|
+
app.get("/widget/:major{v[0-9]+}/:name", (c) => serve(c, c.req.param("name"), ALIAS));
|
|
95
|
+
// `/widget/1.4.2/snag.js` — immutable, and what an SRI hash is taken against.
|
|
96
|
+
app.get("/widget/:version{[0-9]+\\.[0-9]+\\.[0-9]+}/:name", (c) => serve(c, c.req.param("name"), IMMUTABLE));
|
|
97
|
+
// `/widget/snag.js` — unversioned, the shortest thing to paste. Same cache
|
|
98
|
+
// policy as the alias: it can change under a client who never revisits.
|
|
99
|
+
app.get("/widget/:name", (c) => serve(c, c.req.param("name"), ALIAS));
|
|
100
|
+
/**
|
|
101
|
+
* `/snag.js` AND ITS SIBLINGS — 08 §3.8's convenience path. **Serves bytes,
|
|
102
|
+
* never a 302**: a redirect costs a round trip on the critical path of every
|
|
103
|
+
* page load, and a client's CSP would need the redirect target listed too.
|
|
104
|
+
*
|
|
105
|
+
* ── WHY THE PATTERN, AND NOT JUST `/snag.js` ──
|
|
106
|
+
*
|
|
107
|
+
* It WAS just `/snag.js`, and that made the documented install broken past
|
|
108
|
+
* its first file. The stub resolves its lazy chunks relative to its own
|
|
109
|
+
* `<script src>` (02 §5.3's `resolveAssetBase`), so a tag pointing at
|
|
110
|
+
* `https://host/snag.js` asks for `https://host/snag.runtime.js` — which
|
|
111
|
+
* nothing served, so the dashboard's SPA fallback answered **200 with
|
|
112
|
+
* `text/html`**, and the dynamic `import()` failed with "Failed to fetch
|
|
113
|
+
* dynamically imported module". The stub loaded, installed its global, and
|
|
114
|
+
* the widget could never activate.
|
|
115
|
+
*
|
|
116
|
+
* Exactly the failure this router was written to fix, at the one path this
|
|
117
|
+
* router did not cover — and invisible for the same reason: a 200 with the
|
|
118
|
+
* wrong content type. The CLI, the docs and the dashboard's Sites screen all
|
|
119
|
+
* print this path. Found by activating the widget in a browser rather than by
|
|
120
|
+
* requesting `/snag.js` and seeing 200.
|
|
121
|
+
*
|
|
122
|
+
* The pattern is anchored to `snag.<something>.js` and to `snag.js`, so it
|
|
123
|
+
* cannot become a second catch-all: `serve()` still confines every name to
|
|
124
|
+
* the widget directory, and anything not on disk 404s with a sentence.
|
|
125
|
+
*/
|
|
126
|
+
app.get("/:name{snag\\.js}", (c) => serve(c, c.req.param("name"), ALIAS));
|
|
127
|
+
app.get("/:name{snag\\.[a-z0-9-]+\\.js}", (c) => serve(c, c.req.param("name"), ALIAS));
|
|
128
|
+
// The source maps the chunks reference, on the same terms.
|
|
129
|
+
app.get("/:name{snag\\.[a-z0-9.-]+\\.map}", (c) => serve(c, c.req.param("name"), ALIAS));
|
|
130
|
+
return app;
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=widget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"widget.js","sourceRoot":"","sources":["../../src/routes/widget.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import { connect } from "./db/connect.js";
|
|
3
|
+
import { loadConfig } from "./config.js";
|
|
4
|
+
import { createApp } from "./app.js";
|
|
5
|
+
import { resolveSecretKey } from "./boot.js";
|
|
6
|
+
/**
|
|
7
|
+
* The serverless entry point — 08 §3.2, LOCK L5, L2.3.
|
|
8
|
+
*
|
|
9
|
+
* The SECOND entry on the same `createApp(deps)`. The bin owns a process and
|
|
10
|
+
* can boot, tick and hold a pool; a function instance owns none of that, and
|
|
11
|
+
* §3.2 names the four differences. All four are implemented here and nowhere
|
|
12
|
+
* else, which is the point of there being one app factory.
|
|
13
|
+
*
|
|
14
|
+
* 1. **Jobs are a cron, not an interval.** `/api/jobs/run` invokes
|
|
15
|
+
* `runDueJobs`, driven by `vercel.json`'s schedule. Nothing may depend on
|
|
16
|
+
* a long-lived process — no LISTEN/NOTIFY, no resident state.
|
|
17
|
+
* 2. **Migrations run at deploy**, as a release step. A function instance has
|
|
18
|
+
* no boot to hook, and migrating on cold start would take the advisory
|
|
19
|
+
* lock on a request's critical path — every instance queueing behind one
|
|
20
|
+
* that is applying DDL, on somebody's first page load.
|
|
21
|
+
* 3. **`DATABASE_URL` must be POOLED.** The marketplace resources hand one
|
|
22
|
+
* out. Interactive transactions work in transaction-pooling mode, so L6's
|
|
23
|
+
* same-transaction outbox guarantee holds.
|
|
24
|
+
* 4. **`SNAGSET_SECRET_KEY` is required**, because §4.2's generate-a-key-to-
|
|
25
|
+
* disk is bin-only: the filesystem here is ephemeral, so a generated key
|
|
26
|
+
* would differ per instance and every session and encrypted credential
|
|
27
|
+
* would decrypt on one and fail on the next. Checked below, with the
|
|
28
|
+
* reason, rather than left to fail as corrupted ciphertext.
|
|
29
|
+
*/
|
|
30
|
+
/**
|
|
31
|
+
* The app, built ONCE per instance and reused across invocations.
|
|
32
|
+
*
|
|
33
|
+
* Module scope is the only warm thing a function has. Rebuilding per request
|
|
34
|
+
* would re-read config, re-derive the keyring and open a new pool on every
|
|
35
|
+
* call — which on a pooled Postgres is how an instance exhausts the very
|
|
36
|
+
* connection limit the pooler exists to protect.
|
|
37
|
+
*/
|
|
38
|
+
let cached = null;
|
|
39
|
+
function boot() {
|
|
40
|
+
if (cached)
|
|
41
|
+
return cached;
|
|
42
|
+
const cfg = loadConfig(process.env);
|
|
43
|
+
if (!cfg.get("SNAGSET_SECRET_KEY") && !cfg.get("SNAGSET_SECRET_KEY_FILE")) {
|
|
44
|
+
throw new Error("SNAGSET_SECRET_KEY is required on serverless. The filesystem here is ephemeral, so " +
|
|
45
|
+
"the bin's generate-a-key-on-first-boot path cannot work: each instance would invent " +
|
|
46
|
+
"a different key, and a session or an encrypted credential written by one would fail " +
|
|
47
|
+
"to decrypt on the next. Generate 32 random bytes, base64url them, and set it in the " +
|
|
48
|
+
"deploy's environment (08 §4.2).");
|
|
49
|
+
}
|
|
50
|
+
const sql = connect(cfg.required("DATABASE_URL"), { max: cfg.int("SNAGSET_DATABASE_POOL_MAX") });
|
|
51
|
+
const app = createApp({
|
|
52
|
+
sql,
|
|
53
|
+
cfg,
|
|
54
|
+
secretKey: resolveSecretKey(cfg, null, () => { }),
|
|
55
|
+
// A function instance cannot run the boot state machine — that reads and
|
|
56
|
+
// writes the claim token and would race across instances. The state is
|
|
57
|
+
// derived per request from the database instead, which is what `createApp`
|
|
58
|
+
// does when it is told nothing.
|
|
59
|
+
state: "READY",
|
|
60
|
+
version: process.env["SNAGSET_VERSION"] ?? "0.0.0",
|
|
61
|
+
commit: process.env["VERCEL_GIT_COMMIT_SHA"] ?? "unknown",
|
|
62
|
+
sourceUrl: "https://github.com/snagset/snagset",
|
|
63
|
+
modified: false,
|
|
64
|
+
});
|
|
65
|
+
cached = { app, sql };
|
|
66
|
+
return cached;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The Web-standard handler every serverless target accepts.
|
|
70
|
+
*
|
|
71
|
+
* A `fetch(Request) => Response` rather than a Vercel-shaped adapter, because
|
|
72
|
+
* that is the same signature Cloudflare Workers, Deno and Bun take — D24 makes
|
|
73
|
+
* Workers the next hosting milestone on this same app factory, and an entry
|
|
74
|
+
* point written to one vendor's helper is one that has to be rewritten for it.
|
|
75
|
+
*/
|
|
76
|
+
export default {
|
|
77
|
+
fetch(request) {
|
|
78
|
+
return boot().app.fetch(request);
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
/** Named, for platforms whose convention is a bare exported function. */
|
|
82
|
+
export const handler = (request) => boot().app.fetch(request);
|
|
83
|
+
/**
|
|
84
|
+
* Release the instance's pool. **Tests only** — a function instance is torn
|
|
85
|
+
* down by the platform and never calls this.
|
|
86
|
+
*
|
|
87
|
+
* It exists because the cache above is module scope, which is exactly what
|
|
88
|
+
* makes a warm instance cheap and what makes a test process hang: without it
|
|
89
|
+
* node's runner waits forever on a pool nothing will close.
|
|
90
|
+
*/
|
|
91
|
+
export async function closeForTests() {
|
|
92
|
+
const held = cached;
|
|
93
|
+
cached = null;
|
|
94
|
+
if (held)
|
|
95
|
+
await held.sql.end({ timeout: 5 });
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=serverless.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serverless.js","sourceRoot":"","sources":["../src/serverless.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,SAAS,EAAY,MAAM,UAAU,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE7C;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH;;;;;;;GAOG;AACH,IAAI,MAAM,GAA+D,IAAI,CAAC;AAE9E,SAAS,IAAI;IACX,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAEpC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,oBAAoB,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,yBAAyB,CAAC,EAAE,CAAC;QAC1E,MAAM,IAAI,KAAK,CACb,qFAAqF;YACnF,sFAAsF;YACtF,sFAAsF;YACtF,sFAAsF;YACtF,iCAAiC,CACpC,CAAC;IACJ,CAAC;IAED,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,2BAA2B,CAAC,EAAE,CAAC,CAAC;IAEjG,MAAM,GAAG,GAAG,SAAS,CAAC;QACpB,GAAG;QACH,GAAG;QACH,SAAS,EAAE,gBAAgB,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC;QAChD,yEAAyE;QACzE,uEAAuE;QACvE,2EAA2E;QAC3E,gCAAgC;QAChC,KAAK,EAAE,OAAO;QACd,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,OAAO;QAClD,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,IAAI,SAAS;QACzD,SAAS,EAAE,oCAAoC;QAC/C,QAAQ,EAAE,KAAK;KAChB,CAAC,CAAC;IAEH,MAAM,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;IACtB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;GAOG;AACH,eAAe;IACb,KAAK,CAAC,OAAgB;QACpB,OAAO,IAAI,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACnC,CAAC;CACF,CAAC;AAEF,yEAAyE;AACzE,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,OAAgB,EAAgC,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AAErG;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,MAAM,IAAI,GAAG,MAAM,CAAC;IACpB,MAAM,GAAG,IAAI,CAAC;IACd,IAAI,IAAI;QAAE,MAAM,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;AAC/C,CAAC"}
|
package/dist/session.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { SignJWT, jwtVerify, errors as joseErrors } from "jose";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { PRODUCT, ID_PREFIX } from "./vendor/types/names.js";
|
|
4
|
+
export const PERMISSIONS = [
|
|
5
|
+
"thread:create",
|
|
6
|
+
"comment:create",
|
|
7
|
+
"thread:resolve",
|
|
8
|
+
"thread:delete:own",
|
|
9
|
+
"thread:private",
|
|
10
|
+
"thread:assign",
|
|
11
|
+
];
|
|
12
|
+
/**
|
|
13
|
+
* `refreshableUntil` — §4.4's table, verbatim.
|
|
14
|
+
*
|
|
15
|
+
* Every row is a `min()` against `iat + ABSOLUTE_TTL`, so no credential can
|
|
16
|
+
* outlive the absolute cap however generous its own expiry is. The two extra
|
|
17
|
+
* ceilings are the ones that would otherwise be silently discarded: a link's
|
|
18
|
+
* `expires_at`, and `iex`, the identity envelope's expiry — §4.4 records that
|
|
19
|
+
* draft 1 checked `iex` at exchange and threw it away, so a host app signing a
|
|
20
|
+
* sensible 5-minute envelope still handed out an 8-hour credential that
|
|
21
|
+
* refreshed forever.
|
|
22
|
+
*/
|
|
23
|
+
function refreshableUntil(kind, iat, ttls, linkExpiresAt, iex) {
|
|
24
|
+
const cap = iat + ttls.absoluteS;
|
|
25
|
+
if (kind === "link")
|
|
26
|
+
return Math.min(linkExpiresAt ?? cap, cap);
|
|
27
|
+
if (kind === "verified")
|
|
28
|
+
return Math.min(cap, iex ?? cap);
|
|
29
|
+
return cap;
|
|
30
|
+
}
|
|
31
|
+
export function mintSession(keys, input, ttls, now = () => new Date()) {
|
|
32
|
+
const nowS = Math.floor(now().getTime() / 1000);
|
|
33
|
+
// Carried forward on refresh, minted on first exchange. See the header:
|
|
34
|
+
// re-stamping `iat` slides the absolute cap forever.
|
|
35
|
+
const iat = input.iat ?? nowS;
|
|
36
|
+
const sid = input.sid ?? randomUUID();
|
|
37
|
+
const exp = nowS + ttls.slidingS;
|
|
38
|
+
const until = refreshableUntil(input.kind, iat, ttls, input.linkExpiresAt, input.iex);
|
|
39
|
+
const claims = {
|
|
40
|
+
iss: PRODUCT.toLowerCase(),
|
|
41
|
+
aud: input.projectUid,
|
|
42
|
+
sub: input.reviewerId,
|
|
43
|
+
sid,
|
|
44
|
+
jti: randomUUID(),
|
|
45
|
+
iat,
|
|
46
|
+
nbf: nowS,
|
|
47
|
+
// A token must never outlive the window it can be refreshed within —
|
|
48
|
+
// otherwise the last token issued before the cap stays valid past it.
|
|
49
|
+
exp: Math.min(exp, until),
|
|
50
|
+
env: input.environmentKey,
|
|
51
|
+
kind: input.kind,
|
|
52
|
+
lnk: input.reviewLinkId,
|
|
53
|
+
perm: input.permissions,
|
|
54
|
+
epo: input.sessionEpoch,
|
|
55
|
+
...(input.iex ? { iex: input.iex } : {}),
|
|
56
|
+
};
|
|
57
|
+
return new SignJWT(claims)
|
|
58
|
+
.setProtectedHeader({ alg: "HS256", kid: keys.kid })
|
|
59
|
+
.sign(keys.derive("reviewer-session"))
|
|
60
|
+
.then((token) => ({
|
|
61
|
+
token,
|
|
62
|
+
claims,
|
|
63
|
+
expiresAt: new Date(claims.exp * 1000),
|
|
64
|
+
refreshableUntil: new Date(until * 1000),
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Verify a presented bearer.
|
|
69
|
+
*
|
|
70
|
+
* `expectedEpoch` is the project's current `session_epoch`, read from the 10 s
|
|
71
|
+
* cache — which is what makes revocation cost nothing per request (§4.4). A
|
|
72
|
+
* mismatch is `session_revoked`, not `expired`: the reviewer is not late, they
|
|
73
|
+
* have been signed out, and the two need different copy (F-19 vs F-18b).
|
|
74
|
+
*
|
|
75
|
+
* `allowExpired` is for the REFRESH path only. §4.4 allows refreshing "an
|
|
76
|
+
* expiring or recently expired token", and the alternative — refusing a token
|
|
77
|
+
* that lapsed while a laptop was shut — is the four-re-auths-an-afternoon
|
|
78
|
+
* behaviour foundations §14.2b calls a bug rather than a policy.
|
|
79
|
+
*/
|
|
80
|
+
export async function verifySession(keys, token, opts) {
|
|
81
|
+
const now = opts.now ?? (() => new Date());
|
|
82
|
+
const ttlToleranceS = opts.absoluteTtlS ?? 43_200;
|
|
83
|
+
// The header is read BEFORE verification to find the key, which is the whole
|
|
84
|
+
// point of `kid`. An unknown kid is refused rather than falling back to
|
|
85
|
+
// trying every key — see keys.ts.
|
|
86
|
+
let kid;
|
|
87
|
+
try {
|
|
88
|
+
const [rawHeader] = token.split(".");
|
|
89
|
+
if (!rawHeader)
|
|
90
|
+
return { ok: false, reason: "malformed" };
|
|
91
|
+
kid = JSON.parse(Buffer.from(rawHeader, "base64url").toString("utf8")).kid;
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return { ok: false, reason: "malformed" };
|
|
95
|
+
}
|
|
96
|
+
if (!kid)
|
|
97
|
+
return { ok: false, reason: "malformed" };
|
|
98
|
+
const key = keys.forKid(kid, "reviewer-session");
|
|
99
|
+
if (!key)
|
|
100
|
+
return { ok: false, reason: "unknown_key" };
|
|
101
|
+
let claims;
|
|
102
|
+
try {
|
|
103
|
+
const { payload } = await jwtVerify(token, key, {
|
|
104
|
+
issuer: PRODUCT.toLowerCase(),
|
|
105
|
+
audience: opts.projectUid,
|
|
106
|
+
currentDate: now(),
|
|
107
|
+
/**
|
|
108
|
+
* On the REFRESH path, tolerate a token that has already lapsed.
|
|
109
|
+
*
|
|
110
|
+
* §4.4 allows refreshing "an expiring or recently expired token", and
|
|
111
|
+
* jose has no "ignore exp" switch — so the tolerance is widened to the
|
|
112
|
+
* absolute cap, and `withinRefreshWindow()` applies the real bound
|
|
113
|
+
* afterwards against the ORIGINAL `iat`. Nothing is loosened: a token
|
|
114
|
+
* past the 12-hour cap still fails there, and this path is reachable
|
|
115
|
+
* only when the caller asks for it.
|
|
116
|
+
*
|
|
117
|
+
* NOT `currentDate: new Date(0)`, which was the first attempt: moving
|
|
118
|
+
* the clock to 1970 makes every token fail its `nbf` instead, and the
|
|
119
|
+
* refresh silently stops working for a reason the error does not name.
|
|
120
|
+
*/
|
|
121
|
+
clockTolerance: opts.allowExpired ? ttlToleranceS : 5,
|
|
122
|
+
});
|
|
123
|
+
claims = payload;
|
|
124
|
+
}
|
|
125
|
+
catch (e) {
|
|
126
|
+
// jose's TYPED errors, not its message text: the strings are not API and
|
|
127
|
+
// matching them made a wrong-project token report as a bad signature,
|
|
128
|
+
// which sends a reviewer to "something is broken" instead of "this link is
|
|
129
|
+
// for a different site".
|
|
130
|
+
if (e instanceof joseErrors.JWTExpired)
|
|
131
|
+
return { ok: false, reason: "expired" };
|
|
132
|
+
if (e instanceof joseErrors.JWTClaimValidationFailed) {
|
|
133
|
+
return { ok: false, reason: e.claim === "aud" ? "wrong_project" : "malformed" };
|
|
134
|
+
}
|
|
135
|
+
return { ok: false, reason: "bad_signature" };
|
|
136
|
+
}
|
|
137
|
+
// A session minted before the project's epoch was bumped is revoked — the
|
|
138
|
+
// zero-query half of §4.4's revocation story.
|
|
139
|
+
if (claims.epo !== opts.expectedEpoch)
|
|
140
|
+
return { ok: false, reason: "session_revoked" };
|
|
141
|
+
if (typeof claims.sub !== "string" || !claims.sub)
|
|
142
|
+
return { ok: false, reason: "malformed" };
|
|
143
|
+
return { ok: true, claims };
|
|
144
|
+
}
|
|
145
|
+
/** Is this session still inside its refresh window? §4.4's absolute cap. */
|
|
146
|
+
export function withinRefreshWindow(claims, ttls, linkExpiresAt, now = () => new Date()) {
|
|
147
|
+
const until = refreshableUntil(claims.kind, claims.iat, ttls, linkExpiresAt, claims.iex);
|
|
148
|
+
return Math.floor(now().getTime() / 1000) < until;
|
|
149
|
+
}
|
|
150
|
+
/** `rev_…` — the wire identity a session's `sub` becomes on the way out. */
|
|
151
|
+
export const reviewerWireId = (uuid) => ID_PREFIX.reviewer + uuid;
|
|
152
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,UAAU,EAAmB,MAAM,MAAM,CAAC;AACjF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AA2C1D,MAAM,CAAC,MAAM,WAAW,GAA0B;IAChD,eAAe;IACf,gBAAgB;IAChB,gBAAgB;IAChB,mBAAmB;IACnB,gBAAgB;IAChB,eAAe;CAChB,CAAC;AAmDF;;;;;;;;;;GAUG;AACH,SAAS,gBAAgB,CAAC,IAAiB,EAAE,GAAW,EAAE,IAAU,EAAE,aAAsB,EAAE,GAAY;IACxG,MAAM,GAAG,GAAG,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC;IACjC,IAAI,IAAI,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC,GAAG,CAAC,aAAa,IAAI,GAAG,EAAE,GAAG,CAAC,CAAC;IAChE,IAAI,IAAI,KAAK,UAAU;QAAE,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,CAAC,CAAC;IAC1D,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,WAAW,CACzB,IAAa,EACb,KAAgB,EAChB,IAAU,EACV,MAAkB,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE;IAElC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;IAChD,wEAAwE;IACxE,qDAAqD;IACrD,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,IAAI,IAAI,CAAC;IAC9B,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,IAAI,UAAU,EAAE,CAAC;IACtC,MAAM,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC;IACjC,MAAM,KAAK,GAAG,gBAAgB,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,aAAa,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAEtF,MAAM,MAAM,GAAkB;QAC5B,GAAG,EAAE,OAAO,CAAC,WAAW,EAAE;QAC1B,GAAG,EAAE,KAAK,CAAC,UAAU;QACrB,GAAG,EAAE,KAAK,CAAC,UAAU;QACrB,GAAG;QACH,GAAG,EAAE,UAAU,EAAE;QACjB,GAAG;QACH,GAAG,EAAE,IAAI;QACT,qEAAqE;QACrE,sEAAsE;QACtE,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC;QACzB,GAAG,EAAE,KAAK,CAAC,cAAc;QACzB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,GAAG,EAAE,KAAK,CAAC,YAAY;QACvB,IAAI,EAAE,KAAK,CAAC,WAAW;QACvB,GAAG,EAAE,KAAK,CAAC,YAAY;QACvB,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACzC,CAAC;IAEF,OAAO,IAAI,OAAO,CAAC,MAA+B,CAAC;SAChD,kBAAkB,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC;SACnD,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC;SACrC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAChB,KAAK;QACL,MAAM;QACN,SAAS,EAAE,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,GAAG,IAAI,CAAC;QACtC,gBAAgB,EAAE,IAAI,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;KACzC,CAAC,CAAC,CAAC;AACR,CAAC;AAcD;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,IAAa,EACb,KAAa,EACb,IAQC;IAED,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IAC3C,MAAM,aAAa,GAAG,IAAI,CAAC,YAAY,IAAI,MAAM,CAAC;IAElD,6EAA6E;IAC7E,wEAAwE;IACxE,kCAAkC;IAClC,IAAI,GAAuB,CAAC;IAC5B,IAAI,CAAC;QACH,MAAM,CAAC,SAAS,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACrC,IAAI,CAAC,SAAS;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;QAC1D,GAAG,GAAI,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAsB,CAAC,GAAG,CAAC;IACnG,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;IAC5C,CAAC;IACD,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;IAEpD,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAC;IACjD,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;IAEtD,IAAI,MAAqB,CAAC;IAC1B,IAAI,CAAC;QACH,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE;YAC9C,MAAM,EAAE,OAAO,CAAC,WAAW,EAAE;YAC7B,QAAQ,EAAE,IAAI,CAAC,UAAU;YACzB,WAAW,EAAE,GAAG,EAAE;YAClB;;;;;;;;;;;;;eAaG;YACH,cAAc,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;SACtD,CAAC,CAAC;QACH,MAAM,GAAG,OAAmC,CAAC;IAC/C,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,yEAAyE;QACzE,sEAAsE;QACtE,2EAA2E;QAC3E,yBAAyB;QACzB,IAAI,CAAC,YAAY,UAAU,CAAC,UAAU;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;QAChF,IAAI,CAAC,YAAY,UAAU,CAAC,wBAAwB,EAAE,CAAC;YACrD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QAClF,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC;IAChD,CAAC;IAED,0EAA0E;IAC1E,8CAA8C;IAC9C,IAAI,MAAM,CAAC,GAAG,KAAK,IAAI,CAAC,aAAa;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAEvF,IAAI,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,GAAG;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;IAE7F,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAC9B,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,mBAAmB,CACjC,MAAqB,EACrB,IAAU,EACV,aAAsB,EACtB,MAAkB,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE;IAElC,MAAM,KAAK,GAAG,gBAAgB,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;IACzF,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,KAAK,CAAC;AACpD,CAAC;AAED,4EAA4E;AAC5E,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,SAAS,CAAC,QAAQ,GAAG,IAAI,CAAC"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { AwsClient } from "aws4fetch";
|
|
2
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
3
|
+
/**
|
|
4
|
+
* Turn an S3 error into a sentence that names the fix.
|
|
5
|
+
*
|
|
6
|
+
* Every S3-compatible service answers with the same XML `<Code>`, and those
|
|
7
|
+
* codes map cleanly onto the four things an operator actually got wrong. The
|
|
8
|
+
* raw XML is 200 characters of RequestId and HostId around one useful word —
|
|
9
|
+
* and the first real attempt at connecting a bucket failed with
|
|
10
|
+
* `InvalidAccessKeyId`, where the useful next step ("that is the secret, not
|
|
11
|
+
* the access key id") is not in the response at all.
|
|
12
|
+
*/
|
|
13
|
+
async function describeS3Error(method, res) {
|
|
14
|
+
const body = await res.text().catch(() => "");
|
|
15
|
+
const code = /<Code>([^<]+)<\/Code>/.exec(body)?.[1] ?? "";
|
|
16
|
+
const hint = {
|
|
17
|
+
InvalidAccessKeyId: "the ACCESS KEY ID is not recognised. On DigitalOcean Spaces it is the short value " +
|
|
18
|
+
"(20 chars, usually starting DO00…), not the 43-character secret beside it — and a DO " +
|
|
19
|
+
"API token is a different credential that cannot sign S3 requests.",
|
|
20
|
+
SignatureDoesNotMatch: "the SECRET does not match that access key. Check for a truncated paste, and that the " +
|
|
21
|
+
"region matches the one in the endpoint (ams3 with ams3, fra1 with fra1).",
|
|
22
|
+
NoSuchBucket: "there is no bucket/Space with that name in this region.",
|
|
23
|
+
AccessDenied: "the credentials are valid but not permitted to do this. The key needs write, read AND " +
|
|
24
|
+
"delete — delete is what an erasure request needs.",
|
|
25
|
+
RequestTimeTooSkewed: "this machine's clock is too far from the server's for the signature to validate.",
|
|
26
|
+
};
|
|
27
|
+
const known = hint[code];
|
|
28
|
+
if (known)
|
|
29
|
+
return `${code} — ${known}`;
|
|
30
|
+
return `${method} ${res.status}${code ? ` ${code}` : ""}: ${body.replace(/\s+/g, " ").slice(0, 160)}`;
|
|
31
|
+
}
|
|
32
|
+
export function createBucket(config, creds) {
|
|
33
|
+
const aws = new AwsClient({
|
|
34
|
+
accessKeyId: creds.accessKeyId,
|
|
35
|
+
secretAccessKey: creds.secretAccessKey,
|
|
36
|
+
service: "s3",
|
|
37
|
+
region: config.region,
|
|
38
|
+
});
|
|
39
|
+
const base = `${config.endpoint.replace(/\/+$/, "")}/${config.bucket}`;
|
|
40
|
+
const url = (key) => `${base}/${encodeURI(key)}`;
|
|
41
|
+
return {
|
|
42
|
+
async put(key, body, contentType) {
|
|
43
|
+
const res = await aws.fetch(url(key), {
|
|
44
|
+
method: "PUT",
|
|
45
|
+
body,
|
|
46
|
+
headers: {
|
|
47
|
+
"content-type": contentType,
|
|
48
|
+
// 05 §6.6 / M2-23(c): the disposition is signed into the object
|
|
49
|
+
// itself, not decided at read time. This is the XSS-on-bucket-domain
|
|
50
|
+
// prevention — a stored `text/html` served inline from the bucket's
|
|
51
|
+
// own origin is a script running beside whatever else is in there.
|
|
52
|
+
"content-disposition": "attachment",
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
if (!res.ok)
|
|
56
|
+
throw new Error(await describeS3Error("PUT", res));
|
|
57
|
+
},
|
|
58
|
+
async get(key) {
|
|
59
|
+
const res = await aws.fetch(url(key));
|
|
60
|
+
if (res.status === 404)
|
|
61
|
+
return null;
|
|
62
|
+
if (!res.ok)
|
|
63
|
+
throw new Error(await describeS3Error("GET", res));
|
|
64
|
+
return new Uint8Array(await res.arrayBuffer());
|
|
65
|
+
},
|
|
66
|
+
async del(key) {
|
|
67
|
+
const res = await aws.fetch(url(key), { method: "DELETE" });
|
|
68
|
+
// 204 and 404 are both "it is not there", which is what was asked for.
|
|
69
|
+
if (!res.ok && res.status !== 404)
|
|
70
|
+
throw new Error(await describeS3Error("DELETE", res));
|
|
71
|
+
},
|
|
72
|
+
async probe() {
|
|
73
|
+
const key = `.snagset-probe/${randomUUID()}`;
|
|
74
|
+
const payload = new TextEncoder().encode("snagset");
|
|
75
|
+
try {
|
|
76
|
+
await this.put(key, payload, "application/octet-stream");
|
|
77
|
+
}
|
|
78
|
+
catch (e) {
|
|
79
|
+
return { ok: false, step: "write", detail: e.message };
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
const back = await this.get(key);
|
|
83
|
+
if (!back || Buffer.compare(Buffer.from(back), Buffer.from(payload)) !== 0) {
|
|
84
|
+
return { ok: false, step: "read", detail: "the object read back did not match what was written" };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch (e) {
|
|
88
|
+
return { ok: false, step: "read", detail: e.message };
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
await this.del(key);
|
|
92
|
+
}
|
|
93
|
+
catch (e) {
|
|
94
|
+
// Write and read work, delete does not: usable for uploads and unable
|
|
95
|
+
// to honour erasure (05 §13). Worth failing the connect over, because
|
|
96
|
+
// discovering it during a deletion request is far worse.
|
|
97
|
+
return { ok: false, step: "delete", detail: e.message };
|
|
98
|
+
}
|
|
99
|
+
return { ok: true };
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* The object key for an attachment.
|
|
105
|
+
*
|
|
106
|
+
* Hash-based, never the reviewer's filename: 05 §3.6's comment says "storage
|
|
107
|
+
* keys are hashes (LOCK L3)", and the reason is that a key derived from user
|
|
108
|
+
* input is a path traversal, a collision and an information leak in one
|
|
109
|
+
* field. The project id prefixes it so a bucket shared between projects can
|
|
110
|
+
* still be reasoned about, and so erasure can list by prefix.
|
|
111
|
+
*/
|
|
112
|
+
export function storageKeyFor(projectId, bytes, ext) {
|
|
113
|
+
const digest = createHash("sha256").update(bytes).digest("hex");
|
|
114
|
+
return `${projectId}/${digest.slice(0, 2)}/${digest}.${ext}`;
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=bucket.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bucket.js","sourceRoot":"","sources":["../../src/storage/bucket.ts"],"names":[],"mappings":"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"}
|