mirror of
https://github.com/PR0M3TH3AN/bitlogin.git
synced 2026-11-03 03:51:22 +00:00
Authored by ChatGPT/Codex in a separate review pass. Reviewed and verified here before committing rather than taken on trust; findings below. The substantive change is that the vault sudo key now lives ONLY in the phrase-gated recovery capsule. A newly written password credential capsule carries just connection_vault_root, so an ordinary login restores connectable-tier records without putting personal-tier material behind a password capsule that is downloadable and therefore offline-guessable forever. Pre-hardening capsules stay readable, password login does not return their sudo key, and the next rotation scrubs it. That closes the gate this project had explicitly set on personal-tier UI. Legacy vault-enable migration now derives roots deterministically from phrase-protected material and account_id, so two concurrent attempts cannot mint divergent roots -- the failure mode the docs previously mitigated with "enable the vault once, from one device". Manual (user-chosen) passwords are REMOVED, not merely discouraged, and a new securityPolicy.test.ts pins that along with several things tests do not usually reach: the deployment CSP (default-src 'none', frame-ancestors 'none') plus six baseline headers, demo pages free of inline scripts and remote fonts, and README documenting same-origin self-hosting only rather than a remote demo embed. llms.txt was updated to match, which I checked because that test only reads README. What I verified, since a passing suite proves less than it appears to: - All 12 fixes from the 2026-07-30 audit survived the rewrite of recover.ts, changePassword.ts, validation.ts, relay.ts, session.ts and element.ts. Checked semantically where grep cannot help: changePassword still checks publish success BEFORE building the tombstone, and vaultSetBinding still unbinds on origin === null rather than silently rebinding to the worker global. - No dependency changes; the package.json diff is scripts only. - Argon2id and KDF parameters unchanged. The NIP-44 diff ADDS a base64 length cap before decoding, a memory-DoS guard. - Validation increased rather than decreased: 44 throws added, 33 removed. - No disabled or skipped tests, no secrets in the diff. typecheck clean, 165/165 tests, build green, and the account manager exercised in a browser: creation correctly refused when relay quorum could not be met, and on retry failed at the recovery capsule with "No login credential was published" -- the §18.1 fail-closed ordering doing its job against a flaky public relay set. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01CMr9Yt8SYmZcfgrg3CV2G1
42 lines
1.8 KiB
JavaScript
42 lines
1.8 KiB
JavaScript
// Copies the built @bitlogin/widget bundle into the static demo and stamps the
|
|
// service worker with a hash and manifest covering the complete release graph.
|
|
import { cpSync, mkdirSync, rmSync, existsSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
import { fileURLToPath } from "node:url";
|
|
import { dirname, join } from "node:path";
|
|
import { execSync } from "node:child_process";
|
|
import { renderServiceWorker } from "./swBuild.mjs";
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const widgetDir = join(__dirname, "..", "widget");
|
|
const widgetDist = join(widgetDir, "dist");
|
|
const publicDir = join(__dirname, "public");
|
|
const outDir = join(__dirname, "dist");
|
|
|
|
if (!existsSync(widgetDist)) {
|
|
console.log("Building @bitlogin/widget first...");
|
|
execSync("npm run build", { cwd: widgetDir, stdio: "inherit" });
|
|
}
|
|
|
|
rmSync(outDir, { recursive: true, force: true });
|
|
mkdirSync(outDir, { recursive: true });
|
|
|
|
cpSync(publicDir, outDir, { recursive: true });
|
|
mkdirSync(join(outDir, "vendor", "bitlogin"), { recursive: true });
|
|
cpSync(widgetDist, join(outDir, "vendor", "bitlogin"), { recursive: true });
|
|
|
|
function filesBelow(directory, prefix = "") {
|
|
return readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
const relative = join(prefix, entry.name);
|
|
return entry.isDirectory() ? filesBelow(join(directory, entry.name), relative) : [relative];
|
|
});
|
|
}
|
|
|
|
const swPath = join(outDir, "sw.js");
|
|
const swTemplate = readFileSync(swPath, "utf8");
|
|
const artifacts = filesBelow(outDir)
|
|
.filter((path) => path !== "sw.js")
|
|
.map((path) => ({ path, content: readFileSync(join(outDir, path)) }));
|
|
writeFileSync(swPath, renderServiceWorker(swTemplate, artifacts));
|
|
|
|
console.log(`Demo site assembled with a content-versioned service worker at ${outDir}`);
|