SPB Git

spb/autoscholar

Public

Reproduction sandbox: canonical (/private/var) paths so reproduce.sh can execute

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Simon-Pierre Boucher committed (Oct 30, 2026) parent 7be2efb Browse files

1 changed file +16 −1

Unified Split
modified services/research-runner/src/runner.ts +16 −1
@@ -479,7 +479,22 @@ export class ResearchRunner {
479 await mkdir(join(reproDir, "results/tables"), { recursive: true }); 479 await mkdir(join(reproDir, "results/tables"), { recursive: true });
480 await mkdir(join(reproDir, "results/figures"), { recursive: true }); 480 await mkdir(join(reproDir, "results/figures"), { recursive: true });
481 await mkdir(join(reproDir, "home"), { recursive: true }); 481 await mkdir(join(reproDir, "home"), { recursive: true });
482− const profile = `(version 1)\n(allow default)\n(deny file-write*)\n(allow file-write* (subpath "${reproDir}") (subpath "/private/tmp") (subpath "/tmp") (subpath "/private/var/folders") (literal "/dev/null") (regex #"^/dev/tty") (subpath "${this.host.tectonicCache ?? join(this.ws, ".cache/tectonic")}"))\n(deny file-read* (subpath "/Users") (subpath "/etc/autoscholar") (subpath "/var/autoscholar/runs") (subpath "/private/var/autoscholar/runs"))\n(allow file-read* (subpath "${reproDir}") (subpath "${this.ws}/.cache"))\n`; 482+ // Seatbelt matches canonical paths (/private/var/...): resolve every path and allow both spellings
483+ const real = async (p: string) => realpath(p).catch(() => p);
484+ const reproReal = await real(reproDir);
485+ const wsReal = await real(this.ws);
486+ const cacheDir = this.host.tectonicCache ?? join(this.ws, ".cache/tectonic");
487+ const cacheReal = await real(cacheDir);
488+ const both = (p: string, q: string) => [...new Set([p, q])].map((x) => `(subpath "${x}")`).join(" ");
489+ const profile = [
490+ "(version 1)",
491+ "(allow default)",
492+ "(deny file-write*)",
493+ `(allow file-write* ${both(reproDir, reproReal)} (subpath "/private/tmp") (subpath "/tmp") (subpath "/private/var/folders") (literal "/dev/null") (regex #"^/dev/tty") ${both(cacheDir, cacheReal)} ${both(join(this.ws, ".cache"), join(wsReal, ".cache"))})`,
494+ `(deny file-read* (subpath "/Users") (subpath "/etc/autoscholar") (subpath "/private/etc/autoscholar") (subpath "/var/autoscholar/runs") (subpath "/private/var/autoscholar/runs"))`,
495+ `(allow file-read* ${both(reproDir, reproReal)} ${both(join(this.ws, ".cache"), join(wsReal, ".cache"))} ${both(cacheDir, cacheReal)})`,
496+ "",
497+ ].join("\n");
483 const profilePath = join(this.ws, "_repro/repro.sb"); 498 const profilePath = join(this.ws, "_repro/repro.sb");
484 await writeFile(profilePath, profile); 499 await writeFile(profilePath, profile);
485 const t0 = Date.now(); 500 const t0 = Date.now();
486 501
Menu

Theme