// Per-host manifest entries: the key rule, the validation, and the signature // check that makes any of it trustworthy. // // Two things these tests exist to stop: // 1. An apex being governed by `hosts`. The main site's content comes from // the chain; if a signed blob could redirect it, the trust boundary for // the name would move without the owner ever spending a transaction. // 2. A manifest being trusted because pubkey recovery "worked". Recovery // always yields SOME key — only comparing the derived address to the // on-chain owner proves anything. import test from "node:test"; import assert from "node:assert/strict"; import { subLabelsOf, hostOverride, hostAction } from "../src/lib/record-picker.js"; import { validateHosts, verifyManifest, canonicalBytes, canonicalize } from "../src/lib/manifest.js"; import { sha256, utf8ToBin, secp256k1, ripemd160, encodeCashAddress, binToBase64 } from "@bitauth/libauth"; // ---------- subLabelsOf ---------- test("subLabelsOf: apex has no labels above the name", () => { assert.equal(subLabelsOf("aegis.x", "aegis.x"), ""); assert.equal(subLabelsOf("Aegis.X", "aegis.x"), ""); }); test("subLabelsOf: one and many labels, in DNS order", () => { assert.equal(subLabelsOf("www.aegis.x", "aegis.x"), "www"); assert.equal(subLabelsOf("chipnet.explorer.aegis.x", "aegis.x"), "chipnet.explorer"); }); test("subLabelsOf: a name that merely ends in the same text is not a subdomain", () => { // "notaegis.x" ends with "aegis.x" as a STRING but not as a label boundary. assert.equal(subLabelsOf("notaegis.x", "aegis.x"), ""); }); // ---------- hostOverride ---------- const manifest = { v: 2, name: "aegis.x", seq: 1, hosts: { www: { path: "" }, shop: { path: "store/v2/" }, blog: { u: "https://blog.example.com" }, old: { block: true }, "*": { u: "https://aegis.x/" }, }, }; test("hostOverride: the apex is never governed by hosts", () => { assert.equal(hostOverride(manifest, "aegis.x", "aegis.x"), null); }); test("hostOverride: exact key wins", () => { assert.deepEqual(hostOverride(manifest, "shop.aegis.x", "aegis.x"), { path: "store/v2/" }); assert.deepEqual(hostOverride(manifest, "old.aegis.x", "aegis.x"), { block: true }); }); test("hostOverride: the wildcard catches anything not named", () => { assert.deepEqual(hostOverride(manifest, "anything.aegis.x", "aegis.x"), { u: "https://aegis.x/" }); }); test("hostOverride: a named host opts out of the wildcard", () => { assert.deepEqual(hostOverride(manifest, "www.aegis.x", "aegis.x"), { path: "" }); }); test("hostOverride: no hosts block means no override, never a throw", () => { assert.equal(hostOverride({ v: 1, name: "aegis.x" }, "www.aegis.x", "aegis.x"), null); assert.equal(hostOverride(null, "www.aegis.x", "aegis.x"), null); }); test("hostOverride: inherited Object properties are not host entries", () => { // A manifest is attacker-supplied JSON; "constructor" must not resolve. assert.equal(hostOverride({ hosts: {} }, "constructor.aegis.x", "aegis.x"), null); assert.equal(hostOverride({ hosts: {} }, "toString.aegis.x", "aegis.x"), null); }); // ---------- hostAction ---------- test("hostAction: block beats redirect beats path", () => { assert.deepEqual(hostAction({ block: true, u: "https://x.test/", path: "a/" }), { kind: "block" }); assert.deepEqual(hostAction({ u: "https://x.test/", path: "a/" }), { kind: "redirect", url: "https://x.test/" }); assert.deepEqual(hostAction({ path: "a/" }), { kind: "path", path: "a/" }); }); test("hostAction: an empty path is a real instruction — serve the bucket root", () => { // Distinct from "no path at all": `{path:""}` points a subdomain at the apex // folder, which is how `www` is made to mirror the main site. assert.deepEqual(hostAction({ path: "" }), { kind: "path", path: "" }); assert.equal(hostAction({ dns: { A: ["1.2.3.4"] } }), null); }); // ---------- validateHosts ---------- test("validateHosts: accepts the shapes the dashboard writes", () => { assert.equal(validateHosts(undefined), null); assert.equal(validateHosts({ www: { path: "" }, "a.b": { path: "deep/" }, "*": { block: true } }), null); assert.equal(validateHosts({ mail: { dns: { A: ["1.2.3.4"] } } }), null); }); test("validateHosts: rejects path traversal and absolute paths", () => { assert.match(validateHosts({ x: { path: "../secrets/" } }), /\.\./); assert.match(validateHosts({ x: { path: "/etc/passwd" } }), /leading/); }); test("validateHosts: rejects a host key that is not a label path", () => { assert.match(validateHosts({ "has space": { path: "" } }), /invalid host key/); assert.match(validateHosts({ "UPPER": { path: "" } }), /invalid host key/); assert.match(validateHosts({ "-leading": { path: "" } }), /invalid host key/); }); test("validateHosts: rejects a non-http redirect target", () => { assert.match(validateHosts({ x: { u: "javascript:alert(1)" } }), /http\(s\) URL/); assert.match(validateHosts({ x: { u: "file:///etc/passwd" } }), /http\(s\) URL/); }); test("validateHosts: rejects an entry that declares nothing", () => { assert.match(validateHosts({ x: {} }), /declares nothing/); }); // ---------- verifyManifest ---------- // A throwaway key, and a manifest genuinely signed with it. const privKey = sha256.hash(utf8ToBin("manifest-hosts-test-key")); const pubKey = secp256k1.derivePublicKeyCompressed(privKey); const ownerEnc = encodeCashAddress({ prefix: "bchtest", type: "p2pkh", payload: ripemd160.hash(sha256.hash(pubKey)) }); const OWNER = typeof ownerEnc === "string" ? ownerEnc : ownerEnc.address; function sign(m) { const digest = sha256.hash(utf8ToBin(canonicalBytes(m))); const r = secp256k1.signMessageHashRecoverableCompact(privKey, digest); const sig = new Uint8Array(65); sig[0] = 27 + r.recoveryId + 4; // compressed-pubkey flag sig.set(r.signature, 1); return { ...m, sig: binToBase64(sig) }; } test("verifyManifest: accepts a v2 manifest signed by the owner", () => { const m = sign({ v: 2, name: "aegis.x", seq: 7, hosts: { www: { path: "" } } }); assert.deepEqual(verifyManifest(m, { name: "aegis.x", owner: OWNER }), { ok: true, seq: 7 }); }); test("verifyManifest: v1 stays valid — no migration is forced on anyone", () => { const m = sign({ v: 1, name: "aegis.x", seq: 1, dns: { A: ["1.2.3.4"] } }); assert.equal(verifyManifest(m, { name: "aegis.x", owner: OWNER }).ok, true); }); test("verifyManifest: a signature from the wrong key is refused, not merely recovered", () => { const m = sign({ v: 2, name: "aegis.x", seq: 7, hosts: { www: { path: "" } } }); const other = "bchtest:qzew3dqcsj0guwe5y3uxf9q2xs5ajgy9lca4z093cu"; const out = verifyManifest(m, { name: "aegis.x", owner: other }); assert.equal(out.ok, false); assert.match(out.error, /does not match the current NFT owner/); }); test("verifyManifest: any tampering after signing invalidates it", () => { const m = sign({ v: 2, name: "aegis.x", seq: 7, hosts: { www: { path: "" } } }); const tampered = { ...m, hosts: { www: { u: "https://evil.test/" } } }; assert.equal(verifyManifest(tampered, { name: "aegis.x", owner: OWNER }).ok, false); }); test("verifyManifest: a manifest cannot be replayed for another name", () => { const m = sign({ v: 2, name: "aegis.x", seq: 7 }); const out = verifyManifest(m, { name: "other.x", owner: OWNER }); assert.equal(out.ok, false); assert.match(out.error, /name field/); }); test("verifyManifest: an older seq is refused, an equal one is not", () => { const m = sign({ v: 2, name: "aegis.x", seq: 5 }); assert.match(verifyManifest(m, { name: "aegis.x", owner: OWNER, seenSeq: 9 }).error, /stale manifest/); assert.equal(verifyManifest(m, { name: "aegis.x", owner: OWNER, seenSeq: 5 }).ok, true); }); test("verifyManifest: a malformed hosts block is refused before the signature is trusted", () => { const m = sign({ v: 2, name: "aegis.x", seq: 7, hosts: { x: { path: "../../etc/" } } }); assert.equal(verifyManifest(m, { name: "aegis.x", owner: OWNER }).ok, false); }); // ---------- canonicalisation ---------- test("canonicalize: key order and dropped nulls are stable, so signing is reproducible", () => { const a = canonicalize({ b: 1, a: { d: null, c: [1, null, 2] } }); const b = canonicalize({ a: { c: [1, 2], d: undefined }, b: 1 }); assert.equal(JSON.stringify(a), JSON.stringify(b)); assert.equal(JSON.stringify(a), '{"a":{"c":[1,2]},"b":1}'); }); // ---------- subFolderFor ---------- // The derivation that decides which bytes a visitor gets. A mistake here does // not error — it silently serves the wrong folder, or the whole site under a // subdomain that was meant to show one page. import { subFolderFor } from "../src/lib/record-picker.js"; test("subFolderFor: the apex is the bucket root", () => { assert.equal(subFolderFor("aegis.x", "aegis.x", null), ""); }); test("subFolderFor: one label is one folder", () => { assert.equal(subFolderFor("brand.aegis.x", "aegis.x", null), "brand"); }); test("subFolderFor: labels reverse — DNS reads outward, a path reads inward", () => { // chipnet.explorer.aegis.x and aegis.x/explorer/chipnet are the same page. assert.equal(subFolderFor("chipnet.explorer.aegis.x", "aegis.x", null), "explorer/chipnet"); assert.equal(subFolderFor("d.c.b.a.aegis.x", "aegis.x", null), "a/b/c/d"); }); test("subFolderFor: an override replaces the derived folder", () => { const m = { hosts: { shop: { path: "store/v2/" } } }; assert.equal(subFolderFor("shop.aegis.x", "aegis.x", m), "store/v2"); }); test("subFolderFor: an empty override points a subdomain at the apex folder", () => { const m = { hosts: { www: { path: "" } } }; assert.equal(subFolderFor("www.aegis.x", "aegis.x", m), ""); }); test("subFolderFor: slashes are normalised so the caller can always append one", () => { assert.equal(subFolderFor("a.aegis.x", "aegis.x", { hosts: { a: { path: "/x/y/" } } }), "x/y"); }); test("subFolderFor: a redirect or block never yields a folder", () => { assert.equal(subFolderFor("a.aegis.x", "aegis.x", { hosts: { a: { u: "https://x.test/" } } }), "a"); assert.equal(subFolderFor("a.aegis.x", "aegis.x", { hosts: { a: { block: true } } }), "a"); }); test("subFolderFor: an unverified/absent manifest falls back to the convention", () => { assert.equal(subFolderFor("brand.aegis.x", "aegis.x", undefined), "brand"); assert.equal(subFolderFor("brand.aegis.x", "aegis.x", { hosts: {} }), "brand"); }); test("subFolderFor: .bch names index as