From 36b18bab3360591c608b2f5a6e3759a9476c2a44 Mon Sep 17 00:00:00 2001 From: Bishop Bettini Date: Sat, 8 Aug 2026 16:32:35 -0400 Subject: [PATCH] feat:demonstrate proper attestation --- pages/index/build.json | 2 +- pages/index/bundle-iife.json | 6 ++++++ pages/index/globals.d.ts | 11 +++++++++++ pages/index/logic.ts | 20 ++++++++++++++++++++ pages/index/page.html | 8 ++++---- pages/index/page.ts | 7 ++++++- pages/package.json | 2 +- pages/tests/index-logic.test.ts | 27 +++++++++++++++++++++++++++ pages/tests/index-page.test.ts | 32 ++++++++++++++++++++++++++++++++ pages/tests/index.test.ts | 4 ---- 10 files changed, 108 insertions(+), 11 deletions(-) create mode 100644 pages/index/bundle-iife.json create mode 100644 pages/index/globals.d.ts create mode 100644 pages/index/logic.ts create mode 100644 pages/tests/index-logic.test.ts create mode 100644 pages/tests/index-page.test.ts delete mode 100644 pages/tests/index.test.ts diff --git a/pages/index/build.json b/pages/index/build.json index 371b266..534a076 100644 --- a/pages/index/build.json +++ b/pages/index/build.json @@ -1,7 +1,7 @@ { "template": "page.html", "styles": ["../shared/footer.css", "page.css"], - "scripts": [], + "scripts": ["../../build/pages/index/bundle.js"], "footer": "../shared/footer.html", "output": "../../dist/index.html" } diff --git a/pages/index/bundle-iife.json b/pages/index/bundle-iife.json new file mode 100644 index 0000000..67ec35c --- /dev/null +++ b/pages/index/bundle-iife.json @@ -0,0 +1,6 @@ +{ + "packagesDir": "../../build/packages", + "output": "../../build/pages/index/bundle.js", + "files": ["../pages/index/logic.js", "../pages/index/page.js"], + "exports": [] +} diff --git a/pages/index/globals.d.ts b/pages/index/globals.d.ts new file mode 100644 index 0000000..425d9e6 --- /dev/null +++ b/pages/index/globals.d.ts @@ -0,0 +1,11 @@ +/** + * Ambient declarations for the globals bundle.js injects into the page. + * + * bundle-iife concatenates this page's own pure logic (logic.ts) and page.ts's + * own compiled output into one IIFE. This file exists only so page.ts can be + * type-checked against that surface; it declares only the members page.ts + * actually calls, mirroring the corresponding signatures in logic.ts. + */ + +declare function must(value: T | null | undefined): T; +declare function verifyCommand(protocol: string, origin: string): string; diff --git a/pages/index/logic.ts b/pages/index/logic.ts new file mode 100644 index 0000000..a1ec40b --- /dev/null +++ b/pages/index/logic.ts @@ -0,0 +1,20 @@ +// Pure logic for index: the verify command text and the must() guard — no DOM state, so unit-tested directly. + +// Unwrap a possibly-missing DOM lookup, or fail loudly; kept here so its throw branch is testable. +export function must(value: T | null | undefined): T { + if (value === null || value === undefined) { + throw new Error('expected element not found'); + } + return value; +} + +const REPO = 'keepass-web/source-application'; +const FILE = 'index.html'; + +// A file opened from disk (or a local build) is already there; anything else needs downloading first. +export function verifyCommand(protocol: string, origin: string): string { + if (protocol === 'file:') { + return `gh attestation verify ${FILE} --repo ${REPO}`; + } + return `curl -O ${origin}/${FILE}\ngh attestation verify ${FILE} --repo ${REPO}`; +} diff --git a/pages/index/page.html b/pages/index/page.html index dc2b698..23cd4da 100644 --- a/pages/index/page.html +++ b/pages/index/page.html @@ -23,10 +23,10 @@

Your passwords.
Your storage.
Your browser.

- Verify before you trust. Tampering would leave a public, permanent trace. -

Every release is cryptographically attested and logged to a public transparency log (Sigstore). Verify any downloaded file was built by this project's Release workflow, not hand-assembled:

-
gh attestation verify <file> --repo keepass-web/source-application
-

Requires the GitHub CLI. Prefer to build it yourself from source instead? See Reproducing a Build.

+ Verify before you trust. Every release is signed and publicly logged. +

It's backed by a public transparency log (Sigstore). Verify any downloaded file was built by this project's Release workflow, not hand-assembled:

+
gh attestation verify index.html --repo keepass-web/source-application
+

Requires the GitHub CLI. Prefer to run a local copy? Download the latest release. Prefer to build it yourself from source instead? See Reproducing a Build.

diff --git a/pages/index/page.ts b/pages/index/page.ts index cb0ff5c..ee1dfdd 100644 --- a/pages/index/page.ts +++ b/pages/index/page.ts @@ -1 +1,6 @@ -export {}; +/** Fills in the verify command shown in the trust disclosure: a file already +on disk (opened locally, or a local build) skips the download step a hosted +visit needs. */ + +const code = must(document.getElementById('verify-command')); +code.textContent = verifyCommand(window.location.protocol, window.location.origin); diff --git a/pages/package.json b/pages/package.json index d7b2c4b..f24ac6f 100644 --- a/pages/package.json +++ b/pages/package.json @@ -7,7 +7,7 @@ "scripts": { "typecheck": "tsc --noEmit", "test": "node --experimental-strip-types --experimental-test-coverage --test-coverage-lines=100 --test-coverage-branches=100 --test-coverage-functions=100 --test-coverage-include='0x67/**/*.ts' --test-coverage-include='index/**/*.ts' --test-coverage-include='local/**/*.ts' --test-coverage-include='cloud-google-drive/**/*.ts' --test-coverage-exclude='**/*.d.ts' --test 'tests/**/*.test.ts'", - "build": "tsc --project tsconfig.build.json && node --experimental-strip-types ../tools/build/bundle-iife/src/index.ts 0x67/bundle-iife.json && node --experimental-strip-types ../tools/build/bundle-iife/src/index.ts local/bundle-iife.json && node --experimental-strip-types ../tools/build/bundle-iife/src/index.ts cloud-google-drive/bundle-iife.json && node --experimental-strip-types ../tools/build/inliner/src/index.ts 0x67/build.json && node --experimental-strip-types ../tools/build/inliner/src/index.ts local/build.json && node --experimental-strip-types ../tools/build/inliner/src/index.ts index/build.json && node --experimental-strip-types ../tools/build/inliner/src/index.ts cloud-google-drive/build.json" + "build": "tsc --project tsconfig.build.json && node --experimental-strip-types ../tools/build/bundle-iife/src/index.ts 0x67/bundle-iife.json && node --experimental-strip-types ../tools/build/bundle-iife/src/index.ts local/bundle-iife.json && node --experimental-strip-types ../tools/build/bundle-iife/src/index.ts cloud-google-drive/bundle-iife.json && node --experimental-strip-types ../tools/build/bundle-iife/src/index.ts index/bundle-iife.json && node --experimental-strip-types ../tools/build/inliner/src/index.ts 0x67/build.json && node --experimental-strip-types ../tools/build/inliner/src/index.ts local/build.json && node --experimental-strip-types ../tools/build/inliner/src/index.ts index/build.json && node --experimental-strip-types ../tools/build/inliner/src/index.ts cloud-google-drive/build.json" }, "devDependencies": { "@types/jsdom": "^28.0.3", diff --git a/pages/tests/index-logic.test.ts b/pages/tests/index-logic.test.ts new file mode 100644 index 0000000..78724da --- /dev/null +++ b/pages/tests/index-logic.test.ts @@ -0,0 +1,27 @@ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import { must, verifyCommand } from '../index/logic.ts'; + +test('must passes a present value through unchanged', () => { + assert.equal(must(42), 42); + assert.equal(must('x'), 'x'); +}); + +test('must throws for null or undefined', () => { + assert.throws(() => must(null), /expected element not found/); + assert.throws(() => must(undefined), /expected element not found/); +}); + +test('verifyCommand skips the download step for a file already on disk', () => { + assert.equal( + verifyCommand('file:', 'null'), + 'gh attestation verify index.html --repo keepass-web/source-application', + ); +}); + +test('verifyCommand adds a download step for a hosted origin', () => { + assert.equal( + verifyCommand('https:', 'https://keepass-web.app'), + 'curl -O https://keepass-web.app/index.html\ngh attestation verify index.html --repo keepass-web/source-application', + ); +}); diff --git a/pages/tests/index-page.test.ts b/pages/tests/index-page.test.ts new file mode 100644 index 0000000..4fe3633 --- /dev/null +++ b/pages/tests/index-page.test.ts @@ -0,0 +1,32 @@ +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; +import { test } from 'node:test'; +import { fileURLToPath } from 'node:url'; +import { JSDOM } from 'jsdom'; +import { must, verifyCommand } from '../index/logic.ts'; + +const htmlPath = fileURLToPath(new URL('../index/page.html', import.meta.url)); +const html = readFileSync(htmlPath, 'utf8'); +const dom = new JSDOM(html, { url: 'https://keepass-web.app/index.html' }); + +Object.defineProperty(globalThis, 'document', { + value: dom.window.document as unknown as Document, + configurable: true, + writable: true, +}); +Object.defineProperty(globalThis, 'window', { + value: dom.window as unknown as Window & typeof globalThis, + configurable: true, + writable: true, +}); +Object.assign(globalThis, { must, verifyCommand }); + +await import('../index/page.ts'); + +test('fills in the verify command for the page origin', () => { + const code = dom.window.document.getElementById('verify-command'); + assert.equal( + code?.textContent, + 'curl -O https://keepass-web.app/index.html\ngh attestation verify index.html --repo keepass-web/source-application', + ); +}); diff --git a/pages/tests/index.test.ts b/pages/tests/index.test.ts deleted file mode 100644 index 1395893..0000000 --- a/pages/tests/index.test.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { test } from 'node:test'; - -// Placeholder — no page-level test coverage yet. -test('placeholder', () => {});