diff --git a/dor-tools-builtin/viewer/image/pixels.ts b/dor-tools-builtin/viewer/image/pixels.ts index 4facc0d79..289e1e53a 100644 --- a/dor-tools-builtin/viewer/image/pixels.ts +++ b/dor-tools-builtin/viewer/image/pixels.ts @@ -241,7 +241,7 @@ const selected = (r: Raster, c: Rect, mask: Mask | undefined, x: number, y: numb x >= c.x && y >= c.y && x < c.x + c.width && y < c.y + c.height && (!mask || mask[y * r.width + x] !== 0); /** The most common color among unselected pixels 8-adjacent to the selection - * (`rect`, narrowed by `mask`); ties go to the first in row order. Null when the + * (`rect`, narrowed by `mask`); a tie goes to the color that reached the top count first. Null when the * selection has no such ring, as when it covers the image. */ export function dominantBorderColor(r: Raster, rect: Rect, mask?: Mask): Rgba | null { const c = clipRect(r, rect); diff --git a/lib/src/host/agent-browser-host.ts b/lib/src/host/agent-browser-host.ts index e62ab0bd4..cd029ed28 100644 --- a/lib/src/host/agent-browser-host.ts +++ b/lib/src/host/agent-browser-host.ts @@ -77,8 +77,6 @@ function cliError(result: CliResult): string { return result.stderr.trim() || result.stdout.trim() || `agent-browser exited ${result.exitCode}`; } -// A capture can queue behind a page-loading `open` for the CLI's whole 25s -// action timeout; past the host's capture bound it is wedged, and killed. const STREAM_PORT_READ_ATTEMPTS = 4; const STREAM_PORT_READ_DELAY_MS = 150; const PORT_PROBE_TIMEOUT_MS = 500; diff --git a/lib/src/host/browser-host.ts b/lib/src/host/browser-host.ts index 59d38f2c8..c3b80de03 100644 --- a/lib/src/host/browser-host.ts +++ b/lib/src/host/browser-host.ts @@ -148,7 +148,7 @@ const VIEWPORT_MEASURE_SCRIPT = '({width:innerWidth,height:innerHeight,dpr:devic * and would walk straight past a caller's rejection into the page. * `hasOwnProperty.call` keeps the table's own three names the only ones that * select a script. Same guard, same reason as `own()` in - * `RemoteControlSection.tsx`. */ + * `lib/src/components/remote-control-shared.ts`. */ function editScript(op: unknown): string | undefined { return typeof op === 'string' && Object.prototype.hasOwnProperty.call(EDIT_SCRIPTS, op) ? EDIT_SCRIPTS[op as BrowserEditOp] diff --git a/lib/src/host/iframe-proxy.ts b/lib/src/host/iframe-proxy.ts index 773198fff..c7bb8372a 100644 --- a/lib/src/host/iframe-proxy.ts +++ b/lib/src/host/iframe-proxy.ts @@ -16,12 +16,10 @@ * this file is the `http`/`net` plumbing. The logger is injected so neither host * has to depend on the other's logging. * - * Sibling to the agent-browser stream relay: same loopback-only bind and - * per-surface isolation, but this one speaks HTTP (parses and rewrites - * responses) and passes WebSocket upgrades through (dev-server HMR, - * openvscode-server). + * It binds loopback only, speaks HTTP (parses and rewrites responses), and + * passes WebSocket upgrades through (dev-server HMR, openvscode-server). * - * Per-surface isolation, two deliberate notes vs the spec's sketch: + * Per-surface isolation: * - Each grant gets its OWN ephemeral loopback server, so the grant's *origin* * is the grant. That makes root-relative sub-resources (`/assets/x.js`) and * absolute paths resolve and proxy transparently with zero body rewriting. @@ -224,13 +222,14 @@ export async function createIframeProxyUrl( } // v1 proxies http:// upstreams only — loopback dev servers are overwhelmingly // plain http, and rewriting authenticated https pages is the agent-browser's - // job (spec → Target policy). + // job (docs/specs/dor-browser.md → "Iframe Renderer"). if (upstream.protocol !== 'http:') { return { ok: false, reason: 'scheme', detail: IFRAME_HTTP_ONLY }; } // SSRF guard: the proxy fetches a user-supplied URL, so refuse the link-local // / cloud-metadata ranges (169.254.169.254 and friends). Other private ranges - // are trusted — the boundary is the user's own `dor iframe` (spec → Security). + // are trusted — the boundary is the user's own `dor iframe` + // (docs/specs/dor-browser.md → "Iframe Host Capability And CSP"). if (isBlockedAddress(upstream.hostname)) { return { ok: false, reason: 'scheme', detail: 'link-local / metadata addresses are refused' }; } @@ -580,8 +579,8 @@ function rewriteOrigin(value: string, from: string, to: string): string { // --- WebSocket upgrade passthrough (dev-server HMR, openvscode-server) ------- -// Mirrors the stream relay: once the upgrade head is rewritten (Host/Origin -// pointed at the upstream) the proxy is a dumb byte pipe. +// Once the upgrade head is rewritten (Host/Origin pointed at the upstream) the +// proxy is a dumb byte pipe. function handleUpgrade(grant: Grant, req: http.IncomingMessage, socket: net.Socket, head: Buffer): void { // The upgrade path is where the Origin rewrite costs the most, so it gets the // same two tests as handleRequest. WebSockets are not subject to CORS, so a diff --git a/relay/test/static.test.mjs b/relay/test/static.test.mjs index 76b1ac996..7ce386f42 100644 --- a/relay/test/static.test.mjs +++ b/relay/test/static.test.mjs @@ -161,7 +161,7 @@ test('the policy is same-origin everywhere, unframeable, and loosens only what p assert.deepEqual(policy, { 'default-src': ["'self'"], // No *script* exception: the build emits none inline and loads nothing - // off-origin, which the built-output case below pins. `wasm-unsafe-eval` + // off-origin, which `lib/scripts/assert-pocket-worker.mjs` pins at build time. `wasm-unsafe-eval` // is the addon's SIXEL decoder and permits WebAssembly compilation only — // pinned so a widening to `'unsafe-eval'` cannot pass as the same thing. 'script-src': ["'self'", "'wasm-unsafe-eval'"], @@ -180,9 +180,9 @@ test('the policy is same-origin everywhere, unframeable, and loosens only what p }); }); -test('connect-src names this deployment own relay and no other burrow', async () => { +test("connect-src names this deployment's own relay and no other host", async () => { // `'self'` alone leaves ws/wss to browsers that have disagreed about whether - // it covers them, and a bare `ws:` would admit every burrow on the network. + // it covers them, and a bare `ws:` would admit every host on the network. for (const [origin, expected] of [ ['https://dormouse.tailnet.ts.net', 'wss://dormouse.tailnet.ts.net'], ['http://localhost:3000', 'ws://localhost:3000'], diff --git a/remote-lib-common/test/pairing-invitation.test.mjs b/remote-lib-common/test/pairing-invitation.test.mjs index 9e0f70edb..455ad72c2 100644 --- a/remote-lib-common/test/pairing-invitation.test.mjs +++ b/remote-lib-common/test/pairing-invitation.test.mjs @@ -134,9 +134,9 @@ const MAX_ORIGIN_LENGTH = PAIRING_QR_URL_MAX_LENGTH - 1 - PAIRING_HASH_PREFIX.le /** A real origin of exactly `length` characters, with DNS-legal labels. */ function originOfLength(length) { - const burrow = length - 'https://'.length - '.dev'.length; - const first = Math.min(burrow, 63); - const rest = burrow - first - 1; + const host = length - 'https://'.length - '.dev'.length; + const first = Math.min(host, 63); + const rest = host - first - 1; return `https://${'a'.repeat(first)}${rest >= 0 ? `.${'b'.repeat(rest)}` : ''}.dev`; } @@ -203,7 +203,7 @@ test('plain HTTP is accepted on a loopback host, and nowhere else', async () => const parsed = await parsePairingInvitationUrl(url, origin, NOW); assert.equal(parsed?.burrowId, BURROW_ID, origin); } - // By burrow, never by suffix: a name that merely ends in `localhost` is an + // By host name, never by suffix: a name that merely ends in `localhost` is an // ordinary remote origin a self-hoster could be pointed at. for (const origin of ['http://evil.localhost', 'http://127.0.0.1.evil.example', 'http://pocket.example']) { const url = `${origin}/${PAIRING_HASH_PREFIX}${FIELDS.join('.')}`; diff --git a/scripts/github-state-check.test.mjs b/scripts/github-state-check.test.mjs index da9542d4f..841cc8704 100644 --- a/scripts/github-state-check.test.mjs +++ b/scripts/github-state-check.test.mjs @@ -78,7 +78,8 @@ test('the recorded live state passes every clause', () => { // The reporting step computes this fragment's verdict from its lines against // the rules the specs pin to this script. Every rule gets a line, every line // is in the grammar and names a pinned rule, so the computed verdict is the -// script's own — a mismatch between the two PRs' formats goes red here. +// script's own — a mismatch between this script's fragment format and +// `security-audit-report.mjs`'s parser goes red here. test('the reporting step reads the fragment as the script judged it', () => { const clean = judgeFragment(repoRoot, FRAGMENT, replay().text); assert.equal(clean.verdict, 'PASS', [...clean.doubts, ...clean.missing, ...clean.malformed.map((m) => m.line)].join('\n')); diff --git a/website/scripts/docs-parser.js b/website/scripts/docs-parser.js index cf59ead38..2c1702190 100644 --- a/website/scripts/docs-parser.js +++ b/website/scripts/docs-parser.js @@ -28,10 +28,6 @@ export class UnsupportedMarkdownError extends Error { /** Attributes an `` may carry. Everything else is rejected. */ export const IMG_ALLOWED_ATTRS = new Set(['src', 'alt', 'width', 'height', 'title']); -// --------------------------------------------------------------------------- -// Slugger — mirrors github-slugger so /docs anchors match GitHub's own. -// --------------------------------------------------------------------------- - /** * Whether a URL carries a scheme (`https:`, `mailto:`, `vscode:`). * @@ -50,6 +46,10 @@ export function isProtocolRelative(url) { return url.startsWith('//'); } +// --------------------------------------------------------------------------- +// Slugger — mirrors github-slugger so /docs anchors match GitHub's own. +// --------------------------------------------------------------------------- + export function slugify(text) { return text .trim()