From 667f3e814bf0320209789b6700d684fdc0ff8cda Mon Sep 17 00:00:00 2001 From: Lucas Winther Date: Fri, 30 Oct 2026 03:27:30 +0100 Subject: [PATCH] fix(studio): forbid ext:: remotes; type the per-dialect test table Co-Authored-By: Claude Opus 5.5 --- packages/feature-studio/src/server/git.ts | 1 + packages/feature-studio/test/feature.test.ts | 2 +- packages/feature-studio/test/publish.test.ts | 9 +++++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/feature-studio/src/server/git.ts b/packages/feature-studio/src/server/git.ts index 9ce4dc4..ee8d222 100644 --- a/packages/feature-studio/src/server/git.ts +++ b/packages/feature-studio/src/server/git.ts @@ -11,6 +11,7 @@ const BASE = [ "-c", "merge.conflictstyle=merge", "-c", "pull.rebase=false", "-c", "init.defaultBranch=main", + "-c", "protocol.ext.allow=never", // author-supplied remote URLs must never run commands ]; function env(who?: Identity): Record { diff --git a/packages/feature-studio/test/feature.test.ts b/packages/feature-studio/test/feature.test.ts index d788c73..4636333 100644 --- a/packages/feature-studio/test/feature.test.ts +++ b/packages/feature-studio/test/feature.test.ts @@ -11,7 +11,7 @@ test("anonymous gets 401, learners 403", async () => { expect((await env.call("lea", "POST", "/api/studio/courses", { id: "x", title: "X" })).status).toBe(403); }); -const DBS = [["sqlite", "sqlite://:memory:"], ...(process.env.STC_TEST_PG_URL ? [["postgres", process.env.STC_TEST_PG_URL]] : [])] as const; +const DBS: [string, string][] = [["sqlite", "sqlite://:memory:"], ...(process.env.STC_TEST_PG_URL ? [["postgres", process.env.STC_TEST_PG_URL] as [string, string]] : [])]; test.each(DBS)("creating a course records the creator in course_authors; a per-course author without the role can edit it [%s]", async (_name, url) => { env = await setupApp([], url); diff --git a/packages/feature-studio/test/publish.test.ts b/packages/feature-studio/test/publish.test.ts index cc5c33e..a32d481 100644 --- a/packages/feature-studio/test/publish.test.ts +++ b/packages/feature-studio/test/publish.test.ts @@ -144,3 +144,12 @@ describe("sync", () => { void bob; }); }); + +test("ext:: remotes cannot run commands", async () => { + const { store } = await courseWithEdit(); + const marker = join(tempDir(), "pwned"); + await store.setRemote("c1", `ext::touch%s${marker}`); + const r = await store.sync("c1", ada); + expect(r.ok).toBe(false); + expect(await Bun.file(marker).exists()).toBe(false); +});