Oregami
Repositories/oxedyne/daimond

oxedyne/daimond/dev/verify_harness.mjs

13.4 KiB, 1 run

created by r2519314175:477, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1// verify_harness.mjs — the harness's own rules about where a browser is allowed
2// to paint, put to it without launching one.
3//
4// `dev/harness.mjs` has named this file since `displayFault` was made "separate
5// and pure so `dev/verify_harness.mjs` can put the cases to it". The file did not
6// exist. Nothing in the tree tested that function, and on 2026-08-24 it was
7// measured wrong -- it refused a forwarded `DISPLAY`, correctly, and had nothing
8// to say about `WAYLAND_DISPLAY`, so a verifier started under `xvfb-run` opened a
9// real window on the owner's desktop while he was working. A doc comment naming a
10// test is not a test.
11//
12// Nothing here starts a browser, a server or a model. It imports the two pure
13// functions and puts environments to them, so it costs a second and can be run
14// before anything headed.
15//
16// node dev/verify_harness.mjs
17// node dev/verify_harness.mjs --prove # and show each check failing against
18// # the code as it stood before the fix
19// node dev/verify_harness.mjs --break oldguard # 1: display.mjs before it could
20// # tell an unattended run from a watched one
21//
22// ── The break, and why it is a patched copy of the module ────────────
23//
24// `--prove` above is a paraphrase of the old function written out by hand, and a
25// paraphrase proves that the paraphrase disagrees. The break below is not: it
26// reads `dev/display.mjs` off the disk, cuts the unattended rule out of it, writes
27// the result to the scratch directory and imports THAT. So the red is the module
28// as it stood, in the layer the change was made in -- which is the standard the
29// rest of this tree's breaks are held to, and this file was the exception.
30//
31// It can be done here and almost nowhere else because `display.mjs` has no imports
32// at all, so a copy of it anywhere on the disk is the same module. That property
33// is stated at the top of that file for a different reason and pays twice.
34
35import path from 'node:path';
36import fs from 'node:fs';
37import os from 'node:os';
38import { pathToFileURL } from 'node:url';
39
40import * as GUARD from './harness.mjs';
41
42const { cleanDisplayEnv, WAYLAND_VARS } = GUARD;
43
44const PROVE = process.argv.includes('--prove');
45
46/// The one break this file declares, or `''` for the clean run.
47const BREAK = (() => {
48 const i = process.argv.indexOf('--break');
49 return i >= 0 && process.argv[i + 1] ? String(process.argv[i + 1]) : '';
50})();
51if (BREAK && BREAK !== 'oldguard') {
52 console.log(`no such break: ${BREAK}. This file declares: oldguard.`);
53 process.exit(1);
54}
55
56/// The two lines inside `displayFault` that ask the question at all.
57///
58/// A marker rather than a line number: the file is edited by hand and a number
59/// would rot into a break that silently removed the wrong thing -- or nothing,
60/// which is the failure `normalise()` in the hand calls the most damning.
61const CALL_MARK = '\tconst nobody = unattendedFault(e);';
62
63/// The banner the rule and the three names it is spelled with all sit under.
64const BLOCK_MARK = '// │ A display nobody is looking at, and the seat that is │';
65
66/// The last line of that block, so the cut ends where the old file ended.
67const BLOCK_END = '\nexport function unattendedFault(e) {';
68
69/// `dev/display.mjs` as it stood before the unattended rule, as a module.
70///
71/// BOTH halves go -- the rule and its three names, and the two lines in
72/// `displayFault` that call it -- because the file before this change had neither,
73/// and a break that left the names behind would leave one check unreddenable and
74/// therefore unmeasured. What is left is byte for byte the old `displayFault`:
75/// nothing is paraphrased, which is what `--prove` below does and why it is not
76/// enough on its own. Refused if either cut changes nothing, so a break that has
77/// stopped reproducing anything says so rather than passing quietly.
78async function guardBefore() {
79 const src = fs.readFileSync(path.join(HERE, 'display.mjs'), 'utf8');
80 let cut = src;
81 const at = cut.indexOf(CALL_MARK);
82 if (at < 0) {
83 throw new Error('the call to unattendedFault was not found in dev/display.mjs, so '
84 + 'this break reproduces nothing. Fix CALL_MARK here.');
85 }
86 // Back over the comment the call sits under, and forward past the `if`.
87 const top = cut.lastIndexOf('\t// UNATTENDED FIRST', at);
88 const after = cut.indexOf('\n', cut.indexOf('if (nobody) return nobody;', at)) + 1;
89 cut = cut.slice(0, top < 0 ? at : top) + cut.slice(after);
90 const nb = cut.indexOf(BLOCK_MARK);
91 const ne = cut.indexOf(BLOCK_END);
92 if (nb < 0 || ne < nb) {
93 throw new Error('the unattended block was not found in dev/display.mjs; fix '
94 + 'BLOCK_MARK and BLOCK_END here.');
95 }
96 // Back to the top of the box-drawn banner, and forward to the end of the
97 // function the banner introduces.
98 const bt = cut.lastIndexOf('// ┌', nb);
99 const be = cut.indexOf('\n}\n', ne) + 4; // and the blank line after it
100 cut = cut.slice(0, bt < 0 ? nb : bt) + cut.slice(be);
101 if (cut === src) throw new Error('the cut changed nothing.');
102 if (cut.includes('UNATTENDED_VAR')) {
103 throw new Error('the cut left the rule behind, so this break reproduces nothing.');
104 }
105 const dir = path.join(process.env.DAIMOND_SCRATCH
106 || path.join(os.homedir(), '.cache/daimond'), 'harness-break');
107 fs.mkdirSync(dir, { recursive: true });
108 const f = path.join(dir, `display.before.${process.pid}.mjs`);
109 fs.writeFileSync(f, cut);
110 const m = await import(pathToFileURL(f).href);
111 fs.rmSync(f, { force: true });
112 return m;
113}
114
115const HERE = path.dirname(new URL(import.meta.url).pathname);
116
117/// The module the checks below are put to: this tree's, or this tree's as it stood.
118const UNDER = BREAK === 'oldguard' ? await guardBefore() : GUARD;
119const displayFault = UNDER.displayFault;
120const { UNATTENDED_VAR, OWNED_VAR, SEAT_DISPLAY } = UNDER;
121if (BREAK) {
122 console.log(`\n── BREAK ${BREAK}: dev/display.mjs with the unattended rule cut out ──`);
123}
124
125let ok = 0, bad = 0;
126
127/// One check, named as a property rather than as a step.
128function check(name, pass, detail) {
129 if (pass) { ok++; console.log(` ok ${name}${detail ? ` — ${detail}` : ''}`); }
130 else { bad++; console.log(` FAIL ${name}${detail ? ` — ${detail}` : ''}`); }
131}
132
133// ── The seat this machine actually has ──────────────────────────
134//
135// Every case below is spelled the way argonaut spells it, because that is the
136// machine the fault happened on: a Wayland session, `WAYLAND_DISPLAY=wayland-0`
137// and `XDG_SESSION_TYPE=wayland` in every rc session, and `xvfb-run` adding a
138// `DISPLAY` beside them rather than instead of them.
139const UNDER_XVFB = { DISPLAY: ':99', WAYLAND_DISPLAY: 'wayland-0', XDG_SESSION_TYPE: 'wayland' };
140const OWN_SEAT = { DISPLAY: ':0', WAYLAND_DISPLAY: 'wayland-0', XDG_SESSION_TYPE: 'wayland' };
141const FORWARDED = { DISPLAY: 'localhost:10.0' };
142const NOTHING = {};
143const WAYLAND_ONLY = { WAYLAND_DISPLAY: 'wayland-0', XDG_SESSION_TYPE: 'wayland' };
144
145console.log('\n── What is taken out of a launch environment ─────────');
146
147{
148 const cleaned = cleanDisplayEnv(UNDER_XVFB);
149 check('the Wayland variables are taken out, so Chromium takes the X path',
150 WAYLAND_VARS.every((v) => cleaned[v] === undefined),
151 JSON.stringify(cleaned));
152 check('and the display xvfb provided is left alone, or there is nowhere to paint',
153 cleaned.DISPLAY === ':99');
154}
155
156{
157 // The variable Chromium needs for reasons that have nothing to do with the
158 // screen. Taking it out breaks a headless run, so a check says it is kept.
159 const cleaned = cleanDisplayEnv({ ...UNDER_XVFB, XDG_RUNTIME_DIR: '/run/user/1000' });
160 check('XDG_RUNTIME_DIR survives, since it is not about the display at all',
161 cleaned.XDG_RUNTIME_DIR === '/run/user/1000');
162}
163
164{
165 const src = { ...UNDER_XVFB };
166 cleanDisplayEnv(src);
167 check('the environment handed in is not edited, only the copy handed back',
168 src.WAYLAND_DISPLAY === 'wayland-0');
169}
170
171console.log('\n── What a headed run is refused ──────────────────────');
172
173check('a run under xvfb is allowed, Wayland variables and all',
174 displayFault(UNDER_XVFB) === null, JSON.stringify(displayFault(UNDER_XVFB)));
175
176check('argonaut\'s own seat is allowed, because watching a run is a thing people do',
177 displayFault(OWN_SEAT) === null);
178
179{
180 const said = displayFault(FORWARDED);
181 check('a display forwarded from another machine is refused, and named',
182 typeof said === 'string' && /localhost/.test(said) && /somebody else/.test(said));
183}
184
185{
186 // The case the fix is for: a headed run with no xvfb on a Wayland seat. Before
187 // the fix this returned the bare "DISPLAY is unset" sentence, which is true and
188 // says nothing about the compositor the browser would have found instead.
189 const said = displayFault(WAYLAND_ONLY);
190 check('a Wayland seat with no X display is refused, and the refusal says why',
191 typeof said === 'string' && /WAYLAND_DISPLAY/.test(said) && /owner's own screen/.test(said),
192 JSON.stringify(said));
193}
194
195check('an environment with no display at all is refused',
196 typeof displayFault(NOTHING) === 'string');
197
198check('a bare DISPLAY string is still read the way it always was',
199 displayFault(':99') === null && typeof displayFault('gilgamesh:0') === 'string');
200
201check('and so is nothing at all, rather than throwing',
202 typeof displayFault(undefined) === 'string');
203
204console.log('\n── A display nobody is looking at, and the seat that is ─');
205
206// B13: a daimon reaches a headed instrument through `verify`, which runs a tracked
207// script OUTSIDE the command fence -- so the script inherits the hand's own
208// environment. Start the hand from a desktop session and that environment carries
209// `DISPLAY=:0`, and the check above says yes to it, correctly, because a person
210// watching their own run is the case it was written for. Nobody is watching this one.
211const UNATT = { DAIMOND_UNATTENDED: '1', DAIMOND_OWNED_DISPLAY: ':99' };
212
213{
214 const said = displayFault({ ...OWN_SEAT, ...UNATT });
215 check('an unattended run is refused the seat, which an attended one is given',
216 typeof said === 'string' && said.includes('OWN SEAT'),
217 JSON.stringify(said));
218}
219
220check('and the same environment without the mark is still allowed, so watching still works',
221 displayFault(OWN_SEAT) === null);
222
223check('an unattended run on the display it started for itself is allowed',
224 displayFault({ ...UNDER_XVFB, ...UNATT }) === null,
225 JSON.stringify(displayFault({ ...UNDER_XVFB, ...UNATT })));
226
227{
228 // The ordinary way a wrong display arrives: inherited from whatever started the
229 // process, never chosen. `:98` is somebody else's xvfb, which is not this run's
230 // to paint on either.
231 const said = displayFault({ DISPLAY: ':98', ...UNATT });
232 check('an unattended run on a display it did not start is refused',
233 typeof said === 'string' && said.includes(':98') && said.includes(':99'),
234 JSON.stringify(said));
235}
236
237{
238 const said = displayFault({ DISPLAY: ':99', DAIMOND_UNATTENDED: '1' });
239 check('an unattended run that claims no display of its own is refused',
240 typeof said === 'string' && said.includes(String(OWNED_VAR)),
241 JSON.stringify(said));
242}
243
244{
245 // A launcher that names the seat AS its own must not be believed. This is the
246 // belt beside the brace: the comparison above would pass a launcher whose two
247 // names agree, and agreeing on the wrong display is exactly the accident.
248 const said = displayFault({ DISPLAY: ':0', DAIMOND_UNATTENDED: '1', DAIMOND_OWNED_DISPLAY: ':0' });
249 check('a launcher that claims the seat as its own display is refused anyway',
250 typeof said === 'string' && said.includes('OWN SEAT'),
251 JSON.stringify(said));
252}
253
254{
255 const said = displayFault({ ...FORWARDED, ...UNATT });
256 check('a forwarded display is refused for an unattended run as it always was, and named',
257 typeof said === 'string' && /localhost/.test(said) && /somebody else/.test(said));
258}
259
260check('the two names and the seat are exported, so a launcher spells none of them itself',
261 UNATTENDED_VAR === 'DAIMOND_UNATTENDED' && OWNED_VAR === 'DAIMOND_OWNED_DISPLAY'
262 && SEAT_DISPLAY === ':0',
263 `${UNATTENDED_VAR} ${OWNED_VAR} ${SEAT_DISPLAY}`);
264
265// ── Proved against the code as it stood ─────────────────────────
266//
267// The old `displayFault` in one line: it saw a DISPLAY string and nothing else.
268// Every check above that the fix is FOR must fail against it, and every check
269// that was already right must still pass -- otherwise the fix is doing something
270// other than what it says.
271if (PROVE) {
272 console.log('\n── The same cases, against displayFault as it stood ───');
273 const before = (display) => {
274 const d = (display || '').trim();
275 if (!d) return 'A headed run needs a display and DISPLAY is unset.';
276 const host = d.slice(0, d.indexOf(':') < 0 ? d.length : d.indexOf(':'));
277 if (host) return `DISPLAY is "${d}" ... on somebody else's screen`;
278 return null;
279 };
280 // It was handed `env.DISPLAY`, so that is what it is handed here.
281 const said = before(WAYLAND_ONLY.DISPLAY);
282 console.log(` ${/WAYLAND_DISPLAY/.test(String(said)) ? 'ok ' : 'FAIL'} a Wayland seat with no X display is refused, and the refusal says why`
283 + ` — ${JSON.stringify(said)}`);
284 console.log(` ${typeof before !== 'function' ? 'ok ' : 'FAIL'} the Wayland variables are taken out, so Chromium takes the X path`
285 + ' — there was no such function to call');
286 console.log(` ok a display forwarded from another machine is refused, and named`
287 + ' — this half was always right, and is unmoved');
288}
289
290console.log(`\n${ok} ok, ${bad} failed.`);
291if (bad) { console.log('failed checks above.'); process.exit(1); }