Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pages/index/build.json
Original file line number Diff line number Diff line change
@@ -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"
}
6 changes: 6 additions & 0 deletions pages/index/bundle-iife.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"packagesDir": "../../build/packages",
"output": "../../build/pages/index/bundle.js",
"files": ["../pages/index/logic.js", "../pages/index/page.js"],
"exports": []
}
11 changes: 11 additions & 0 deletions pages/index/globals.d.ts
Original file line number Diff line number Diff line change
@@ -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<T>(value: T | null | undefined): T;
declare function verifyCommand(protocol: string, origin: string): string;
20 changes: 20 additions & 0 deletions pages/index/logic.ts
Original file line number Diff line number Diff line change
@@ -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<T>(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}`;
}
8 changes: 4 additions & 4 deletions pages/index/page.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ <h1>Your passwords.<br>Your storage.<br>Your browser.</h1>

<div class="trust">
<details>
<summary><strong>Verify before you trust.</strong> Tampering would leave a public, permanent trace.</summary>
<p>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:</p>
<pre><code>gh attestation verify &lt;file&gt; --repo keepass-web/source-application</code></pre>
<p>Requires the <a href="https://cli.github.com/">GitHub CLI</a>. Prefer to build it yourself from source instead? See <a href="https://github.com/keepass-web/source-application/blob/main/docs/REPRODUCING.md">Reproducing a Build</a>.</p>
<summary><strong>Verify before you trust.</strong> Every release is signed and publicly logged.</summary>
<p>It's backed by a public transparency log (Sigstore). Verify any downloaded file was built by this project's Release workflow, not hand-assembled:</p>
<pre><code id="verify-command">gh attestation verify index.html --repo keepass-web/source-application</code></pre>
<p>Requires the <a href="https://cli.github.com/">GitHub CLI</a>. Prefer to run a local copy? <a href="https://github.com/keepass-web/source-application/releases">Download the latest release</a>. Prefer to build it yourself from source instead? See <a href="https://github.com/keepass-web/source-application/blob/main/docs/REPRODUCING.md">Reproducing a Build</a>.</p>
</details>
</div>
</main>
Expand Down
7 changes: 6 additions & 1 deletion pages/index/page.ts
Original file line number Diff line number Diff line change
@@ -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);
2 changes: 1 addition & 1 deletion pages/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
27 changes: 27 additions & 0 deletions pages/tests/index-logic.test.ts
Original file line number Diff line number Diff line change
@@ -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',
);
});
32 changes: 32 additions & 0 deletions pages/tests/index-page.test.ts
Original file line number Diff line number Diff line change
@@ -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',
);
});
4 changes: 0 additions & 4 deletions pages/tests/index.test.ts

This file was deleted.