Files
openswarm/frontend/src/shared/exp1Invariants.stress.test.ts
T

186 lines
10 KiB
TypeScript

// Run: node --test (via frontend/scripts/run-tests.mjs)
//
// The 1.7.8-exp.1 invariant suite. Every fix in that release that is expressible as a pure function
// gets its ENTIRE input space enumerated here, not sampled, because a sampled test cannot tell you
// which case it missed and a passing sample is the most confident kind of wrong.
//
// Three habits this file enforces on itself:
// 1. Expectations are computed INDEPENDENTLY of the implementation. Re-deriving the answer with
// the same expression the code uses proves only that the expression equals itself.
// 2. Hostile inputs are in the enumeration, not a separate afterthought section.
// 3. The suite asserts its own size at the end. A test that silently stops running reads exactly
// like a test that passed, and this repo has been burned by that three times (unmarked async
// tests, the shutdown fuse killing the runner, four linter gates switched off while printing
// "0 errors"). Counting is the only defence against a false pass on a false pass.
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { notifyWanted, notifyAllowedNow, notifyOn, type NotifyPrefs } from './notifyWanted.ts';
import { googleStartUrl, isUsableInstallId } from './googleStartUrl.ts';
import { focusGuestForKeys, guestKeyTakeoverCount, resetGuestKeyTakeoverCount } from './focusGuestForKeys.ts';
let checks = 0;
const check = (cond: boolean, msg: string): void => { checks += 1; assert.ok(cond, msg); };
const eq = (a: unknown, b: unknown, msg: string): void => { checks += 1; assert.equal(a, b, msg); };
// ---------------------------------------------------------------- ENG-276, notifications
const TRI = [undefined, true, false] as const;
test('ENG-276: all 64 toggle combinations, agent and workflow gates are fully independent', () => {
let cases = 0;
for (const ac of TRI) for (const ae of TRI) for (const wr of TRI) for (const wf of TRI) {
const p: NotifyPrefs = {
notify_agent_completion: ac, notify_agent_errors: ae,
notify_workflow_runs: wr, notify_workflow_failures: wf,
};
// Independently derived: absent means on, so the answer is "this specific field is not false".
const wantAgentOk = ac !== false;
const wantAgentBad = ae !== false;
const wantWfOk = wr !== false;
const wantWfBad = wf !== false;
eq(notifyWanted(p, 'agent', false), wantAgentOk, `agent-ok ${JSON.stringify(p)}`);
eq(notifyWanted(p, 'agent', true), wantAgentBad, `agent-bad ${JSON.stringify(p)}`);
eq(notifyWanted(p, 'workflow', false), wantWfOk, `wf-ok ${JSON.stringify(p)}`);
eq(notifyWanted(p, 'workflow', true), wantWfBad, `wf-bad ${JSON.stringify(p)}`);
// The cross-gate bug this shipped to kill: an agent field must never move a workflow answer.
const agentsSilenced: NotifyPrefs = { ...p, notify_agent_completion: false, notify_agent_errors: false };
eq(notifyWanted(agentsSilenced, 'workflow', false), wantWfOk, `agents-off changed wf-ok ${JSON.stringify(p)}`);
eq(notifyWanted(agentsSilenced, 'workflow', true), wantWfBad, `agents-off changed wf-bad ${JSON.stringify(p)}`);
const wfSilenced: NotifyPrefs = { ...p, notify_workflow_runs: false, notify_workflow_failures: false };
eq(notifyWanted(wfSilenced, 'agent', false), wantAgentOk, `wf-off changed agent-ok ${JSON.stringify(p)}`);
eq(notifyWanted(wfSilenced, 'agent', true), wantAgentBad, `wf-off changed agent-bad ${JSON.stringify(p)}`);
cases += 1;
}
eq(cases, 81, 'the enumeration itself changed size, so the coverage claim is stale');
});
test('ENG-276: focus gating is exhaustive and defaults to holding notifications back', () => {
for (const whenFocused of TRI) for (const hidden of [true, false]) {
const got = notifyAllowedNow({ notify_when_focused: whenFocused }, hidden);
// Independently derived: allowed when the window is hidden, or when explicitly opted in.
eq(got, hidden || whenFocused === true, `whenFocused=${whenFocused} hidden=${hidden}`);
}
// Absent must behave as off here, the opposite of every other notify field, because a notification
// for the window you are already looking at is noise.
eq(notifyAllowedNow({}, false), false, 'absent notify_when_focused leaked as on');
eq(notifyOn(undefined), true, 'absent should mean ON for the ordinary fields');
});
// ---------------------------------------------------------------- sign-in guard, incl. attacks
test('install id: every length from 0 to 200 agrees with the cloud bound, no off-by-one', () => {
for (let n = 0; n <= 200; n++) {
const id = 'a'.repeat(n);
const usable = n >= 8 && n <= 128;
eq(isUsableInstallId(id), usable, `length ${n}`);
eq(googleStartUrl('https://api.openswarm.com', id, 8324) !== null, usable, `url at length ${n}`);
}
});
test('install id: hostile values cannot inject extra query parameters into the OAuth URL', () => {
// The real attack shape: if the id were concatenated rather than encoded, an `&` would let a
// crafted id append its own parameters (a redirect_uri, say) to a URL the user is sent to.
const hostile = [
'aaaaaaaa&redirect_uri=https://evil.example',
'aaaaaaaa#fragment',
'aaaaaaaa?x=1',
'aaaaaaaa https://evil.example',
'aaaaaaaa\nlocal_port=1',
'aaaaaaaa%26redirect_uri%3Dx',
'../../aaaaaaaa',
'aaaaaaaanull',
];
for (const id of hostile) {
const url = googleStartUrl('https://api.openswarm.com', id, 8324);
if (url === null) continue; // refused outright is also a correct answer
const q = new URL(url).searchParams;
eq([...q.keys()].sort().join(','), 'install_id,local_port', `extra params from ${JSON.stringify(id)}`);
eq(q.get('install_id'), id, `install_id was mangled for ${JSON.stringify(id)}`);
eq(q.get('local_port'), '8324', `local_port overwritten by ${JSON.stringify(id)}`);
}
});
test('install id: a hostile proxy base cannot smuggle a second path or host', () => {
const id = 'faec918d-6bda-42d5-9de9-f274eb49a8bc';
for (const base of ['https://api.openswarm.com', 'https://api.openswarm.com/', 'https://api.openswarm.com///']) {
const url = googleStartUrl(base, id, 8324);
check(url !== null, `base ${base} refused`);
check(!(url as string).includes('.com//'), `double slash in ${url}`);
eq(new URL(url as string).host, 'api.openswarm.com', `host drifted for base ${base}`);
eq(new URL(url as string).pathname, '/api/auth/google/start', `path drifted for base ${base}`);
}
});
test('install id: every port shape survives intact, since the bearer posts back to it', () => {
for (const port of [1, 80, 3000, 8324, 8424, 65535]) {
const url = googleStartUrl('https://api.openswarm.com', 'aaaaaaaa', port);
eq(new URL(url as string).searchParams.get('local_port'), String(port), `port ${port}`);
}
});
// ---------------------------------------------------------------- ENG-274, keystroke targeting
function fakeWv(): { focus: (o?: FocusOptions) => void; focused: number; lastOpts?: FocusOptions } {
const wv = { focused: 0, lastOpts: undefined as FocusOptions | undefined,
focus(o?: FocusOptions): void { wv.focused += 1; wv.lastOpts = o; } };
return wv;
}
function withActive(el: unknown, fn: () => void): void {
const doc = globalThis.document as unknown as { activeElement: unknown };
const prev = doc.activeElement; doc.activeElement = el;
try { fn(); } finally { doc.activeElement = prev; }
}
test('ENG-274: the guest is focused for EVERY possible prior activeElement, with no exception', () => {
const surfaces: Array<[unknown, boolean]> = [
[{ tagName: 'INPUT', isContentEditable: false }, true],
[{ tagName: 'TEXTAREA', isContentEditable: false }, true],
[{ tagName: 'DIV', isContentEditable: true }, true],
[{ tagName: 'BODY', isContentEditable: false }, false],
[{ tagName: 'DIV', isContentEditable: false }, false],
[{ tagName: 'BUTTON', isContentEditable: false }, false],
[{ tagName: 'WEBVIEW', isContentEditable: false }, false],
[null, false],
[undefined, false],
];
for (const [el, shouldCount] of surfaces) {
resetGuestKeyTakeoverCount();
const wv = fakeWv();
withActive(el, () => focusGuestForKeys(wv as never));
// The invariant that actually protects the user: the guest is focused NO MATTER WHAT, because a
// keystroke follows host focus and any miss puts it in whatever the user was typing in.
eq(wv.focused, 1, `guest not focused when activeElement was ${JSON.stringify(el)}`);
eq(wv.lastOpts?.preventScroll, true, `scroll not suppressed for ${JSON.stringify(el)}`);
eq(guestKeyTakeoverCount(), shouldCount ? 1 : 0, `takeover miscounted for ${JSON.stringify(el)}`);
}
});
test('ENG-274: 500 consecutive dispatches never miss and never double-count', () => {
resetGuestKeyTakeoverCount();
const wv = fakeWv();
const userInput = { tagName: 'INPUT', isContentEditable: false };
for (let i = 0; i < 500; i++) withActive(userInput, () => focusGuestForKeys(wv as never));
eq(wv.focused, 500, 'a dispatch skipped focusing the guest under repetition');
eq(guestKeyTakeoverCount(), 500, 'takeover count drifted from the number of dispatches');
});
test('ENG-274: a webview that throws on focus never breaks the run, at any prior focus', () => {
const dead = { focus(): void { throw new Error('detached'); } };
for (const el of [{ tagName: 'INPUT', isContentEditable: false }, null, { tagName: 'BODY' }]) {
withActive(el, () => { assert.doesNotThrow(() => focusGuestForKeys(dead as never)); checks += 1; });
}
});
// ---------------------------------------------------------------- the suite checks itself
test('META: the suite actually ran its full assertion budget', () => {
// A silent skip and a pass are indistinguishable without this. The floor is deliberately just
// under the real count so ordinary additions do not trip it, while a whole block vanishing does.
// Measured at 1133 (648 from the toggle matrix + 402 from the length sweep + the rest). The floor
// sits just under that: losing either large block drops hundreds and trips this immediately.
assert.ok(checks >= 1100, `only ${checks} assertions executed; a block did not run`);
console.log(` [exp1 invariants] ${checks} assertions executed`);
});