commit 801b25407fcfd5cb11fb086e885d92493a351117
parent fd96bcd46b033cc35a8da55e5e381b9dd7dc6aa7
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Sun, 20 Sep 2026 17:52:10 -0400
Refuse a traversing slug on the read route at the door
readChannelConfig swallows its own errors, so a "../" segment would fail
silently rather than loudly. isValidChannelSlug (CHANNEL_SLUG_RE) already
forbids "/" and ".." and is what every other slug-taking surface uses.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Diffstat:
1 file changed, 10 insertions(+), 0 deletions(-)
diff --git a/editor/app/api/ops/channel/[slug]/route.ts b/editor/app/api/ops/channel/[slug]/route.ts
@@ -1,6 +1,7 @@
import { NextResponse } from "next/server";
import { getPaths } from "yt-dlp-transcript-common/lib/paths";
import {
+ isValidChannelSlug,
readChannelConfig,
readChannelSnapshot,
readChannelStat,
@@ -43,6 +44,15 @@ export async function GET(
const denied = opsAuth(request);
if (denied) return denied;
const { slug } = await params;
+ // The slug reaches three path joins below. readChannelConfig swallows its own
+ // errors, so a traversing segment would fail silently rather than loudly —
+ // refuse it at the door instead (CHANNEL_SLUG_RE forbids "/" and "..").
+ if (!isValidChannelSlug(slug)) {
+ return NextResponse.json(
+ { ok: false, error: `"${slug}" is not a valid channel slug` },
+ { status: 400 },
+ );
+ }
const paths = getPaths();
const config = await readChannelConfig(paths, slug);
if (!config) {