Archilyzer · Source

archilyzer

Archilyzer
git clone https://archilyzer.pages.dev/source/archilyzer.git
Log | Files | Refs | README | LICENSE

commit b69a6629c94ea5523aba3540e559a16b29f271c1
parent 129de94dcad57c0c2c16d5a84cbcf294a58e4453
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Tue, 22 Sep 2026 16:39:41 -0400

editor: /api/test/worker-token does not exist unless the harness started the server

It is an unauthenticated GET that SETS A CREDENTIAL. `?set=x` hands any caller a
token of its choosing for every `/api/ops/*` and `/api/worker/*` route; `?unset=1`
is a remote kill switch for both. Being a plain GET, both are reachable by CSRF
from any page the operator's browser loads — so binding Caddy to loopback is no
defence, because the browser is inside the loopback, and there is no path rule
for `/api/test` anyway.

The surface is opt-in now, the way the worker endpoint it manipulates is:
`EDITOR_TEST_ROUTES=1` is set by `dev:test` and `start:test`, the two scripts
Playwright's webServer, the sharded runner and Dockerfile.test all boot through,
and by nothing else. 404 rather than 403, so the answer is indistinguishable
from a route that was never built.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Diffstat:
Aeditor/app/api/test/_guard.ts | 26++++++++++++++++++++++++++
Meditor/app/api/test/worker-token/route.ts | 11+++++++++--
Meditor/package.json | 4++--
3 files changed, 37 insertions(+), 4 deletions(-)

diff --git a/editor/app/api/test/_guard.ts b/editor/app/api/test/_guard.ts @@ -0,0 +1,26 @@ +import { NextResponse } from "next/server"; + +// THE /api/test ROUTES EXIST ONLY WHEN THE E2E HARNESS STARTED THIS SERVER. +// +// Every route under this directory is UNAUTHENTICATED and every one of them +// mutates live process state: it drops the job registry and the runner +// singletons, fabricates a stuck job, restarts a lane — and `worker-token` +// SETS A CREDENTIAL. An unauthenticated `GET /api/test/worker-token?set=x` +// hands the caller a token of its own choosing for `/api/ops/*` and +// `/api/worker/*`; `?unset=1` is a remote kill switch for both. As plain GETs, +// all of that is reachable by CSRF from any page the operator's browser loads, +// which is why binding to loopback is not an answer — the browser is inside the +// loopback. The Caddyfile has no path rule for `/api/test` either. +// +// So the surface is opt-in, the way the worker endpoint is: `EDITOR_TEST_ROUTES=1` +// is set by `dev:test` and `start:test` (editor/package.json) — the two scripts +// Playwright's webServer, the sharded runner and Dockerfile.test all boot +// through — and by nothing else. A real editor never sets it. +// +// 404 AND NOT 403, deliberately: the answer must be indistinguishable from a +// route that was never built. A 403 advertises that the harness exists and that +// there is an env var worth guessing. +export function testRouteDenied(): NextResponse | null { + if (process.env.EDITOR_TEST_ROUTES === "1") return null; + return NextResponse.json({ error: "Not Found" }, { status: 404 }); +} diff --git a/editor/app/api/test/worker-token/route.ts b/editor/app/api/test/worker-token/route.ts @@ -1,5 +1,6 @@ import { NextResponse } from "next/server"; import { workerEndpointEnabled } from "yt-dlp-transcript-common/lib/workerToken"; +import { testRouteDenied } from "../_guard"; export const dynamic = "force-dynamic"; @@ -24,9 +25,15 @@ export const dynamic = "force-dynamic"; // later /api/ops and /api/worker spec answering 503 — a whole suite red from // one failure. `?set=` with no value is the restore, and it is idempotent. // -// Mounted unconditionally, like the other /api/test routes: the editor is a -// localhost admin tool, not a deployed service. +// GUARDED BY `EDITOR_TEST_ROUTES`, and it is the route that made the guard +// necessary: an unauthenticated GET that SETS a credential is a CSRF-able +// token grant, and "the editor is a localhost admin tool" does not help — the +// operator's browser is inside the loopback. See _guard.ts. export async function GET(req: Request) { + // FIRST LINE, BEFORE THE QUERY IS EVEN PARSED. This route sets a credential; + // off a harness-started server it does not exist. See _guard.ts. + const denied = testRouteDenied(); + if (denied) return denied; const url = new URL(req.url); // `?set=<token>` restores (or changes) it; `?unset=1` removes it entirely. // Exactly one of the two, so a typo cannot silently do nothing. diff --git a/editor/package.json b/editor/package.json @@ -5,8 +5,8 @@ "type": "module", "scripts": { "dev": "next dev --port ${EDITOR_PORT:-3001}", - "dev:test": "WORKER_TOKEN=test-worker-token TRANSCRIPTS_DIR=$(pwd)/test-transcripts EXPORT_PUBLIC_DIR=$(pwd)/test-transcripts/.export-public SETTINGS_FILE=$(pwd)/test-settings.json EDITOR_CHANGELOG_FILE=$(pwd)/test-changelog.md YTDLP_BIN=$(pwd)/e2e/fixtures/bin/fake-ytdlp.mjs GALLERY_DL_BIN=$(pwd)/e2e/fixtures/bin/fake-gallery-dl.mjs WHISPER_BIN=$(pwd)/e2e/fixtures/bin/fake-whisper.mjs WHISPER_MODEL=/dev/null CHOUGH_BIN=$(pwd)/e2e/fixtures/bin/fake-chough.mjs CHOUGH_MODEL=/dev/null PARAKEET_STITCH_BIN=$(pwd)/e2e/fixtures/bin/fake-parakeet-stitch.mjs PARAKEET_CLI=/dev/null PARAKEET_MODEL=/dev/null DIARIZE_BIN=$(pwd)/e2e/fixtures/bin/fake-diarize.mjs FFMPEG_BIN=$(pwd)/e2e/fixtures/bin/fake-ffmpeg.mjs FFPROBE_BIN=$(pwd)/e2e/fixtures/bin/fake-ffprobe.mjs OLLAMA_URL=http://127.0.0.1:${OLLAMA_STUB_PORT:-11435} CLAUDE_BIN=$(pwd)/e2e/fixtures/bin/fake-claude.mjs FINDMNT_BIN=$(pwd)/e2e/fixtures/bin/fake-findmnt.mjs UDISKSCTL_BIN=$(pwd)/e2e/fixtures/bin/fake-udisksctl.mjs AUDIO_CHECK_INTERVAL_MS_OVERRIDE=300 AUDIO_CHECK_SIZE_GATE_OVERRIDE=4096 AUDIO_CHECK_INTERVAL_FLOOR_MS_OVERRIDE=50 AUDIO_CHECK_RECOVER_STEP_MS_OVERRIDE=100 AUDIO_CHECK_RECOVER_AFTER_OVERRIDE=2 next dev --port ${PORT:-3011}", - "start:test": "WORKER_TOKEN=test-worker-token TRANSCRIPTS_DIR=$(pwd)/test-transcripts EXPORT_PUBLIC_DIR=$(pwd)/test-transcripts/.export-public SETTINGS_FILE=$(pwd)/test-settings.json EDITOR_CHANGELOG_FILE=$(pwd)/test-changelog.md YTDLP_BIN=$(pwd)/e2e/fixtures/bin/fake-ytdlp.mjs GALLERY_DL_BIN=$(pwd)/e2e/fixtures/bin/fake-gallery-dl.mjs WHISPER_BIN=$(pwd)/e2e/fixtures/bin/fake-whisper.mjs WHISPER_MODEL=/dev/null CHOUGH_BIN=$(pwd)/e2e/fixtures/bin/fake-chough.mjs CHOUGH_MODEL=/dev/null PARAKEET_STITCH_BIN=$(pwd)/e2e/fixtures/bin/fake-parakeet-stitch.mjs PARAKEET_CLI=/dev/null PARAKEET_MODEL=/dev/null DIARIZE_BIN=$(pwd)/e2e/fixtures/bin/fake-diarize.mjs FFMPEG_BIN=$(pwd)/e2e/fixtures/bin/fake-ffmpeg.mjs FFPROBE_BIN=$(pwd)/e2e/fixtures/bin/fake-ffprobe.mjs OLLAMA_URL=http://127.0.0.1:${OLLAMA_STUB_PORT:-11435} CLAUDE_BIN=$(pwd)/e2e/fixtures/bin/fake-claude.mjs FINDMNT_BIN=$(pwd)/e2e/fixtures/bin/fake-findmnt.mjs UDISKSCTL_BIN=$(pwd)/e2e/fixtures/bin/fake-udisksctl.mjs AUDIO_CHECK_INTERVAL_MS_OVERRIDE=300 AUDIO_CHECK_SIZE_GATE_OVERRIDE=4096 AUDIO_CHECK_INTERVAL_FLOOR_MS_OVERRIDE=50 AUDIO_CHECK_RECOVER_STEP_MS_OVERRIDE=100 AUDIO_CHECK_RECOVER_AFTER_OVERRIDE=2 next start --port ${PORT:-3011}", + "dev:test": "EDITOR_TEST_ROUTES=1 WORKER_TOKEN=test-worker-token TRANSCRIPTS_DIR=$(pwd)/test-transcripts EXPORT_PUBLIC_DIR=$(pwd)/test-transcripts/.export-public SETTINGS_FILE=$(pwd)/test-settings.json EDITOR_CHANGELOG_FILE=$(pwd)/test-changelog.md YTDLP_BIN=$(pwd)/e2e/fixtures/bin/fake-ytdlp.mjs GALLERY_DL_BIN=$(pwd)/e2e/fixtures/bin/fake-gallery-dl.mjs WHISPER_BIN=$(pwd)/e2e/fixtures/bin/fake-whisper.mjs WHISPER_MODEL=/dev/null CHOUGH_BIN=$(pwd)/e2e/fixtures/bin/fake-chough.mjs CHOUGH_MODEL=/dev/null PARAKEET_STITCH_BIN=$(pwd)/e2e/fixtures/bin/fake-parakeet-stitch.mjs PARAKEET_CLI=/dev/null PARAKEET_MODEL=/dev/null DIARIZE_BIN=$(pwd)/e2e/fixtures/bin/fake-diarize.mjs FFMPEG_BIN=$(pwd)/e2e/fixtures/bin/fake-ffmpeg.mjs FFPROBE_BIN=$(pwd)/e2e/fixtures/bin/fake-ffprobe.mjs OLLAMA_URL=http://127.0.0.1:${OLLAMA_STUB_PORT:-11435} CLAUDE_BIN=$(pwd)/e2e/fixtures/bin/fake-claude.mjs FINDMNT_BIN=$(pwd)/e2e/fixtures/bin/fake-findmnt.mjs UDISKSCTL_BIN=$(pwd)/e2e/fixtures/bin/fake-udisksctl.mjs AUDIO_CHECK_INTERVAL_MS_OVERRIDE=300 AUDIO_CHECK_SIZE_GATE_OVERRIDE=4096 AUDIO_CHECK_INTERVAL_FLOOR_MS_OVERRIDE=50 AUDIO_CHECK_RECOVER_STEP_MS_OVERRIDE=100 AUDIO_CHECK_RECOVER_AFTER_OVERRIDE=2 next dev --port ${PORT:-3011}", + "start:test": "EDITOR_TEST_ROUTES=1 WORKER_TOKEN=test-worker-token TRANSCRIPTS_DIR=$(pwd)/test-transcripts EXPORT_PUBLIC_DIR=$(pwd)/test-transcripts/.export-public SETTINGS_FILE=$(pwd)/test-settings.json EDITOR_CHANGELOG_FILE=$(pwd)/test-changelog.md YTDLP_BIN=$(pwd)/e2e/fixtures/bin/fake-ytdlp.mjs GALLERY_DL_BIN=$(pwd)/e2e/fixtures/bin/fake-gallery-dl.mjs WHISPER_BIN=$(pwd)/e2e/fixtures/bin/fake-whisper.mjs WHISPER_MODEL=/dev/null CHOUGH_BIN=$(pwd)/e2e/fixtures/bin/fake-chough.mjs CHOUGH_MODEL=/dev/null PARAKEET_STITCH_BIN=$(pwd)/e2e/fixtures/bin/fake-parakeet-stitch.mjs PARAKEET_CLI=/dev/null PARAKEET_MODEL=/dev/null DIARIZE_BIN=$(pwd)/e2e/fixtures/bin/fake-diarize.mjs FFMPEG_BIN=$(pwd)/e2e/fixtures/bin/fake-ffmpeg.mjs FFPROBE_BIN=$(pwd)/e2e/fixtures/bin/fake-ffprobe.mjs OLLAMA_URL=http://127.0.0.1:${OLLAMA_STUB_PORT:-11435} CLAUDE_BIN=$(pwd)/e2e/fixtures/bin/fake-claude.mjs FINDMNT_BIN=$(pwd)/e2e/fixtures/bin/fake-findmnt.mjs UDISKSCTL_BIN=$(pwd)/e2e/fixtures/bin/fake-udisksctl.mjs AUDIO_CHECK_INTERVAL_MS_OVERRIDE=300 AUDIO_CHECK_SIZE_GATE_OVERRIDE=4096 AUDIO_CHECK_INTERVAL_FLOOR_MS_OVERRIDE=50 AUDIO_CHECK_RECOVER_STEP_MS_OVERRIDE=100 AUDIO_CHECK_RECOVER_AFTER_OVERRIDE=2 next start --port ${PORT:-3011}", "build": "next build", "start": "next start --port ${EDITOR_PORT:-3001}", "lint": "eslint",