mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
[auth] Vendor BitLogin 30b4f34 and allow WASM in the app CSP
Picks up BitLogin's login-surface expansion (NIP-07 extension, NIP-46
remote signer with nostrconnect QR, passkey sign-in), three rounds of
adversarial-audit fixes, and the mandatory recovery-phrase ceremony for
passkey-created accounts. Vendored via scripts/build-bitlogin-widget.mjs,
which pins the source commit in vendor/bitlogin.integrity.json; the
shared-chunk hashes changed, so three old chunks are removed and three
new ones added.
CSP fix, required for sign-in to work at all: BitLogin's crypto worker
compiles an Argon2id WASM module, and index.html's script-src had no
'wasm-unsafe-eval', so WebAssembly.compile would be blocked and every
BitLogin password flow would fail -- silently, because the page still
loads. This exact omission broke sign-in on bitlogin.network for four
days. The keyword permits WASM compilation only; JS eval stays blocked,
and the CSP comment's stale "no bundle uses wasm" claim is corrected.
tests/csp-policy.test.mjs now pins the keyword for index.html so it
cannot be dropped again. Baseline verified unchanged: npm run test:unit
green, npm run lint clean, and the raw tests/*.test.mjs glob fails the
same 10 pre-existing files before and after this change.
test_integrity_note:
change_type: ["new_tests"]
scenarios:
- id: SCN-csp-allows-wasm-for-bitlogin
given: "index.html mounts <bitlogin-auth>, whose worker compiles an Argon2id WASM module"
when: "the page's Content-Security-Policy script-src is evaluated"
then: "'wasm-unsafe-eval' is present, so WebAssembly.compile is permitted while 'unsafe-eval' remains absent"
observable_outcomes:
- "script-src directive parsed from the index.html CSP meta tag contains 'wasm-unsafe-eval'"
- "existing assertions still forbid 'unsafe-inline' and 'unsafe-eval'"
determinism_controls:
- "pure static parse of a committed HTML file; no clock, network, or browser"
anti_cheat_rationale:
prevents:
- "silently relaxing script-src to make sign-in work"
- "dropping the keyword and shipping a login page that cannot run Argon2id"
relaxation:
did_relax_any_assertion: false
if_true_explain_spec_basis: ""
This commit is contained in:
1 parent
fbac54dd48
commit
1ddfa95a84
8 files changed
+3589
-1250
No files matched your search
+7
-3
@@ -6,8 +6,12 @@
|
||||
Content-Security-Policy: defense-in-depth against XSS. The critical
|
||||
directive is script-src, which is strict — only same-origin scripts, the
|
||||
hash of the inline theme-bootstrap below, and the nostr-tools CDN
|
||||
fallbacks (js/nostrToolsBootstrap.js). No 'unsafe-inline'/'unsafe-eval'
|
||||
(verified: no bundle uses eval/Function/wasm). This blocks injected
|
||||
fallbacks (js/nostrToolsBootstrap.js). No 'unsafe-inline'/'unsafe-eval'.
|
||||
'wasm-unsafe-eval' IS required: BitLogin's crypto worker compiles an
|
||||
Argon2id WASM module, and a script-src without it blocks
|
||||
WebAssembly.compile and breaks every BitLogin password flow. That
|
||||
keyword permits WASM compilation only — JS eval stays blocked. This
|
||||
blocks injected
|
||||
script and object elements, inline handlers (none exist), and base-tag
|
||||
hijack, so an injected payload cannot execute and read the in-memory
|
||||
storage master key / signer / DM plaintext. connect/img/media stay broad
|
||||
@@ -19,7 +23,7 @@
|
||||
-->
|
||||
<meta
|
||||
http-equiv="Content-Security-Policy"
|
||||
content="default-src 'self'; script-src 'self' 'sha256-dnv/l3q8gLqdw0etekr5/atoIRbM/wSVSgQjZEsC7G0=' https://cdn.jsdelivr.net https://esm.sh; style-src 'self' 'unsafe-inline'; img-src 'self' https: data: blob:; media-src 'self' https: blob: mediastream:; connect-src 'self' https: wss: ws://localhost:* ws://127.0.0.1:* blob: data:; worker-src 'self' blob:; font-src 'self' data:; frame-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; manifest-src 'self'"
|
||||
content="default-src 'self'; script-src 'self' 'wasm-unsafe-eval' 'sha256-dnv/l3q8gLqdw0etekr5/atoIRbM/wSVSgQjZEsC7G0=' https://cdn.jsdelivr.net https://esm.sh; style-src 'self' 'unsafe-inline'; img-src 'self' https: data: blob:; media-src 'self' https: blob: mediastream:; connect-src 'self' https: wss: ws://localhost:* ws://127.0.0.1:* blob: data:; worker-src 'self' blob:; font-src 'self' data:; frame-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; manifest-src 'self'"
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>bitvid | Decentralized Video Sharing</title>
|
||||
|
||||
@@ -87,6 +87,18 @@ for (const file of ["index.html", "embed.html"]) {
|
||||
["'self'"],
|
||||
"base-uri must be 'self'",
|
||||
);
|
||||
|
||||
// index.html mounts <bitlogin-auth> (components/login-modal.html), whose
|
||||
// crypto worker compiles an Argon2id WASM module. Without
|
||||
// 'wasm-unsafe-eval' the browser blocks WebAssembly.compile and EVERY
|
||||
// BitLogin password flow fails -- silently, since the page still loads.
|
||||
// This exact omission broke sign-in on bitlogin.network for four days.
|
||||
if (file === "index.html") {
|
||||
assert.ok(
|
||||
scriptSrc.includes("'wasm-unsafe-eval'"),
|
||||
"script-src must include 'wasm-unsafe-eval' or BitLogin sign-in breaks",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test(`${file}: every inline <script> is hash-allowlisted in script-src`, () => {
|
||||
|
||||
+14
-14
@@ -3,8 +3,8 @@
|
||||
"version": "0.1.0",
|
||||
"source": {
|
||||
"repository": "https://github.com/PR0M3TH3AN/bitlogin.git",
|
||||
"commit": "205b0678cc67ecbde18278b8f46e7f8270fb519c",
|
||||
"commitDate": "2026-07-31T22:04:01-04:00",
|
||||
"commit": "30b4f34300f0cb6195f4bd54652ec75e814889dd",
|
||||
"commitDate": "2026-08-04T15:41:24-04:00",
|
||||
"dirty": false,
|
||||
"provenance": "attributed",
|
||||
"toolchain": {
|
||||
@@ -13,25 +13,25 @@
|
||||
}
|
||||
},
|
||||
"files": {
|
||||
"bitlogin-shared--Zs4uvXt.js": {
|
||||
"sha256": "550434b2ed3df975fc1d8896dca02f8622f2819d1fd9fa288049f2c22baa6bc4",
|
||||
"bytes": 483718
|
||||
"bitlogin-shared-CEfP5Eg9.js": {
|
||||
"sha256": "6e19179b46deab241ffa9baded2ab0245122cf88e5b4191fcd4ca905b81a67be",
|
||||
"bytes": 289142
|
||||
},
|
||||
"bitlogin-shared-BxVSgyOf.js": {
|
||||
"sha256": "d7f157c2de3d4b81de0055df2582306df9d862706d1dad35ff4096d06e1c1c93",
|
||||
"bitlogin-shared-Co4_WTb5.js": {
|
||||
"sha256": "ffecf43e4856105d485402c7d46ca6950b621965a85c5962daf0177f0e538237",
|
||||
"bytes": 545712
|
||||
},
|
||||
"bitlogin-shared-DTz11m56.js": {
|
||||
"sha256": "c52f485c6390658e8da75d7c31f01167eb4f8c22d836f9d92e22c2b0885bf79e",
|
||||
"bytes": 289118
|
||||
"bitlogin-shared-DeDxZ3Jc.js": {
|
||||
"sha256": "290c293f288a16f5c753d19b0eecee795ea07b5fe31219812f260aae407de29b",
|
||||
"bytes": 483718
|
||||
},
|
||||
"bitlogin.js": {
|
||||
"sha256": "56eeb7584b0117e2e360c80d37a5bfddb2e1f7a8ee6d94995c24043c0e87b906",
|
||||
"bytes": 77303
|
||||
"sha256": "823e4ec24a8e34d96428d0c933cf16be1755a80ff105dcf52dd96391e52da513",
|
||||
"bytes": 146039
|
||||
},
|
||||
"cryptoWorker.js": {
|
||||
"sha256": "4d569dac3b808d118bf432d628f084c8c1d8ba76d94cf101030ace1077c63b36",
|
||||
"bytes": 110827
|
||||
"sha256": "adea9e4934e7b6df024cc97385ce227ad4870dad436ee8266fbe644cd5cec5b5",
|
||||
"bytes": 126329
|
||||
}
|
||||
}
|
||||
}
|
||||
+61
-60
@@ -10406,7 +10406,7 @@ function J1(e, t) {
|
||||
function Y1(e, t) {
|
||||
return e.tags.find((n) => n[0] === t)?.[1];
|
||||
}
|
||||
const M1 = 0, q1 = 5, j1 = 10002, R1 = 10050, _1 = 22242, P1 = 30078, L1 = "bitlogin:password:v1", T1 = "bitlogin:recovery:v1", X1 = "bitlogin.credential.v1", Z1 = "bitlogin.recovery.v1", kn = "bitlogin.recovery-export.v3";
|
||||
const M1 = 0, q1 = 5, j1 = 10002, R1 = 10050, _1 = 22242, P1 = 24133, L1 = 30078, T1 = "bitlogin:password:v1", X1 = "bitlogin:recovery:v1", Z1 = "bitlogin.credential.v1", $1 = "bitlogin.recovery.v1", kn = "bitlogin.recovery-export.v3";
|
||||
var Ut, Ur;
|
||||
function g1() {
|
||||
return Ur || (Ur = 1, Ut = function e(t) {
|
||||
@@ -10423,7 +10423,7 @@ function g1() {
|
||||
}), Ut;
|
||||
}
|
||||
var A1 = g1();
|
||||
const $1 = /* @__PURE__ */ Kr(A1);
|
||||
const W1 = /* @__PURE__ */ Kr(A1);
|
||||
/*! scure-base - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
function Ue(e) {
|
||||
return e instanceof Uint8Array || ArrayBuffer.isView(e) && e.constructor.name === "Uint8Array";
|
||||
@@ -10638,7 +10638,7 @@ function p1(e, t) {
|
||||
}
|
||||
};
|
||||
}
|
||||
const W1 = {
|
||||
const e2 = {
|
||||
alphabet: er,
|
||||
chain: Wt,
|
||||
checksum: p1,
|
||||
@@ -10654,7 +10654,7 @@ const W1 = {
|
||||
if (e.length > 0 && !n.test(e))
|
||||
throw new Error("invalid base64");
|
||||
return Uint8Array.fromBase64(e, { alphabet: r, lastChunkHandling: "strict" });
|
||||
}, e2 = I1 ? {
|
||||
}, t2 = I1 ? {
|
||||
encode(e) {
|
||||
return f1(e), e.toBase64();
|
||||
},
|
||||
@@ -10740,19 +10740,19 @@ function y1(e) {
|
||||
};
|
||||
}
|
||||
const nr = /* @__PURE__ */ y1("bech32");
|
||||
function t2(e) {
|
||||
function r2(e) {
|
||||
return nr.encodeFromBytes("npub", Je(e));
|
||||
}
|
||||
function r2(e) {
|
||||
function n2(e) {
|
||||
return nr.encodeFromBytes("nsec", e);
|
||||
}
|
||||
function n2(e) {
|
||||
function i2(e) {
|
||||
const { prefix: t, bytes: n } = nr.decodeToBytes(e);
|
||||
if (t !== "nsec" || n.length !== 32)
|
||||
throw new Error("Not a valid nsec.");
|
||||
return n;
|
||||
}
|
||||
function i2(e) {
|
||||
function a2(e) {
|
||||
return {
|
||||
schema: kn,
|
||||
recovery_public_key: e.recoveryPublicKeyHex,
|
||||
@@ -10767,7 +10767,7 @@ class oe extends Error {
|
||||
super(t), this.name = "RecoveryExportParseError";
|
||||
}
|
||||
}
|
||||
function a2(e) {
|
||||
function o2(e) {
|
||||
if (typeof e != "object" || e === null)
|
||||
throw new oe("Recovery export must be a JSON object.");
|
||||
const t = e;
|
||||
@@ -10792,68 +10792,69 @@ function a2(e) {
|
||||
return t;
|
||||
}
|
||||
export {
|
||||
Rr as $,
|
||||
ft as A,
|
||||
x1 as B,
|
||||
rt as C,
|
||||
L1 as D,
|
||||
q1 as $,
|
||||
bt as A,
|
||||
Q1 as B,
|
||||
ft as C,
|
||||
T1 as D,
|
||||
w1 as E,
|
||||
E1 as F,
|
||||
pt as G,
|
||||
Ce as H,
|
||||
D1 as I,
|
||||
pe as J,
|
||||
x1 as F,
|
||||
rt as G,
|
||||
E1 as H,
|
||||
pt as I,
|
||||
Ce as J,
|
||||
_1 as K,
|
||||
W1 as L,
|
||||
he as M,
|
||||
We as N,
|
||||
n2 as O,
|
||||
Je as P,
|
||||
i1 as Q,
|
||||
D1 as L,
|
||||
pe as M,
|
||||
e2 as N,
|
||||
he as O,
|
||||
We as P,
|
||||
i2 as Q,
|
||||
oe as R,
|
||||
Z1 as S,
|
||||
N1 as T,
|
||||
C1 as U,
|
||||
K1 as V,
|
||||
_r as W,
|
||||
O1 as X,
|
||||
v1 as Y,
|
||||
q1 as Z,
|
||||
jr as _,
|
||||
t2 as a,
|
||||
qr as a0,
|
||||
mn as a1,
|
||||
Rt as a2,
|
||||
$1 as S,
|
||||
Je as T,
|
||||
i1 as U,
|
||||
N1 as V,
|
||||
C1 as W,
|
||||
K1 as X,
|
||||
_r as Y,
|
||||
O1 as Z,
|
||||
v1 as _,
|
||||
qe as a,
|
||||
Rr as a0,
|
||||
qr as a1,
|
||||
mn as a2,
|
||||
mt as a3,
|
||||
e2 as a4,
|
||||
t2 as a4,
|
||||
G1 as a5,
|
||||
J1 as a6,
|
||||
i2 as a7,
|
||||
r2 as a8,
|
||||
Kr as a9,
|
||||
P1 as a6,
|
||||
J1 as a7,
|
||||
a2 as a8,
|
||||
n2 as a9,
|
||||
Kr as aa,
|
||||
jt as b,
|
||||
qe as c,
|
||||
M1 as d,
|
||||
Rt as c,
|
||||
S1 as d,
|
||||
b1 as e,
|
||||
j1 as f,
|
||||
r2 as f,
|
||||
V1 as g,
|
||||
R1 as h,
|
||||
jr as h,
|
||||
H1 as i,
|
||||
P1 as j,
|
||||
$1 as k,
|
||||
k1 as l,
|
||||
S1 as m,
|
||||
F1 as n,
|
||||
U1 as o,
|
||||
a2 as p,
|
||||
s1 as q,
|
||||
M1 as j,
|
||||
j1 as k,
|
||||
R1 as l,
|
||||
L1 as m,
|
||||
W1 as n,
|
||||
k1 as o,
|
||||
o2 as p,
|
||||
F1 as q,
|
||||
B1 as r,
|
||||
z1 as s,
|
||||
X1 as t,
|
||||
U1 as t,
|
||||
De as u,
|
||||
Fr as v,
|
||||
T1 as w,
|
||||
Y1 as x,
|
||||
bt as y,
|
||||
Q1 as z
|
||||
s1 as w,
|
||||
Z1 as x,
|
||||
X1 as y,
|
||||
Y1 as z
|
||||
};
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
import { a9 as D2 } from "./bitlogin-shared-DTz11m56.js";
|
||||
import { aa as D2 } from "./bitlogin-shared-CEfP5Eg9.js";
|
||||
const wl = {
|
||||
debug() {
|
||||
}
|
||||
@@ -7918,7 +7918,7 @@ let ke;
|
||||
async function l2() {
|
||||
try {
|
||||
if (ke) return ke;
|
||||
const e = (await import("./bitlogin-shared--Zs4uvXt.js").then((t) => t.i)).default;
|
||||
const e = (await import("./bitlogin-shared-DeDxZ3Jc.js").then((t) => t.i)).default;
|
||||
return ke = new e(), ke;
|
||||
} catch (e) {
|
||||
throw console.error(e), new Error("LNC is not available");
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { a9 as Ye } from "./bitlogin-shared-DTz11m56.js";
|
||||
import { aa as Ye } from "./bitlogin-shared-CEfP5Eg9.js";
|
||||
function We(we, je) {
|
||||
for (var Ie = 0; Ie < je.length; Ie++) {
|
||||
const Ae = je[Ie];
|
||||
Vendored
+2087
-218
File diff suppressed because it is too large.
Load diff
Vendored
+1405
-952
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user