Files
STCourses/packages/feature-studio/test/helpers.ts
T
Lucas WintherandClaude Opus 5.5 2d162a4282 fix(studio): review round 1 (symlink-safe writes, remote allowlist, upload cap/types/no-overwrite, owner rule at publish/sync/resolve, studio/repos layout and read-only imports, side-effect-free list)
C1: writes lstat every path component and refuse symlinks (dangling included),
check the parent's real path after mkdir, and open with O_NOFOLLOW.
I1: only https://, ssh:// and git@host: remotes; protocol.file/ext/fd=never on
every fetch and push; global/system git config ignored.
I2: STC_UPLOAD_MAX_MB cap (default 100) by Content-Length and while streaming;
extension allowlist (415); stcourse.yaml refused; 409 unless overwrite=true;
sandboxing CSP on draft raw files.
I3: owner-only visibility/source.kind rule checked on the merged result at
publish and sync, and at resolve; publish also validates the merged tree.
I4: bare repos in <dataDir>/studio/repos/<id>.git; imported courses read-only.
I5: list status comes from the bare repo; drafts are created lazily.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 04:43:53 +01:00

26 lines
1.2 KiB
TypeScript

import { mkdtempSync, realpathSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { $ } from "bun";
import { createStudioStore, type Identity } from "../src/server/store";
export const ada: Identity & { id: string } = { id: "u-ada", name: "Ada Lovelace", email: "[email protected]" };
export const bob: Identity & { id: string } = { id: "u-bob", name: "Bob Ext", email: "[email protected]" };
export function tempDir(prefix = "stc-studio-"): string {
return realpathSync(mkdtempSync(join(tmpdir(), prefix)));
}
export async function newStore(opts: { allowLocalRemotes?: boolean } = {}) {
const dataDir = tempDir();
const store = createStudioStore({ dataDir, ...opts });
return { dataDir, store };
}
/** Runs git as an outside tool (VS Code, a terminal) would: plain identity, in the given dir. */
export async function extGit(cwd: string, ...args: string[]): Promise<string> {
const r = await $`git -c user.name=${bob.name} -c user.email=${bob.email} -c commit.gpgsign=false -c core.hooksPath=/dev/null ${args}`.cwd(cwd).quiet().nothrow();
if (r.exitCode !== 0) throw new Error(`git ${args.join(" ")} failed: ${r.stderr.toString()}`);
return r.stdout.toString().trim();
}