1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
import { describe, expect, it } from "bun:test";
import { readFileSync } from "fs";
const SECRET_NAMES = [
"ANTHROPIC_API_KEY",
"GLUECRON_PAT",
"DATABASE_URL",
"WORKFLOW_SECRETS_KEY",
"SERVER_TARGETS_KEY",
"GOOGLE_OAUTH_CLIENT_SECRET",
"STRIPE_SECRET_KEY",
"EMERGENCY_PAT_SECRET",
];
const USER_CODE_PATHS = [
"src/lib/workflow-runner.ts",
"src/lib/preview-builder.ts",
"src/lib/hosted-claude-loop.ts",
];
describe("user-code paths do not inherit the process environment", () => {
for (const path of USER_CODE_PATHS) {
it(`${path} never spreads process.env into a spawn`, () => {
const src = readFileSync(path, "utf8")
.replace(/\/\*[\s\S]*?\*\//g, "")
.replace(/^\s*\/\/.*$/gm, "");
expect(src).not.toMatch(/env:\s*\{\s*\.\.\.process\.env/);
});
}
});
describe("no secret is named in a user-code path's env construction", () => {
for (const path of USER_CODE_PATHS) {
const src = readFileSync(path, "utf8")
.replace(/\/\*[\s\S]*?\*\//g, "")
.replace(/^\s*\/\/.*$/gm, "");
for (const secret of SECRET_NAMES) {
it(`${path} does not read process.env.${secret}`, () => {
if (path.includes("hosted-claude-loop") && secret === "ANTHROPIC_API_KEY") return;
expect(src).not.toMatch(new RegExp(`process\\.env\\.${secret}\\b`));
expect(src).not.toMatch(new RegExp(`process\\.env\\[["'\`]${secret}["'\`]\\]`));
});
}
}
});
describe("the shared allowlist is the only way user code gets an env", () => {
it("preview-builder uses buildRunnerEnv rather than its own env object", () => {
const src = readFileSync("src/lib/preview-builder.ts", "utf8");
expect(src).toContain("buildRunnerEnv(");
});
it("workflow-runner still filters through allowlist AND denylist", () => {
const src = readFileSync("src/lib/workflow-runner.ts", "utf8");
expect(src).toContain("RUNNER_ENV_ALLOWLIST");
expect(src).toContain("RUNNER_ENV_DENYLIST");
expect(src).toMatch(/RUNNER_ENV_DENYLIST\.test\(key\)/);
});
it("the allowlist contains no secret-shaped names", () => {
const src = readFileSync("src/lib/workflow-runner.ts", "utf8");
const list = src.slice(
src.indexOf("const RUNNER_ENV_ALLOWLIST"),
src.indexOf("];", src.indexOf("const RUNNER_ENV_ALLOWLIST"))
);
for (const secret of SECRET_NAMES) expect(list).not.toContain(secret);
expect(list).not.toMatch(/KEY|SECRET|TOKEN|PASSWORD|CREDENTIAL/);
});
});
describe("hosted loops keep the platform key opt-in", () => {
const src = readFileSync("src/lib/hosted-claude-loop.ts", "utf8")
.replace(/\/\*[\s\S]*?\*\//g, "")
.replace(/^\s*\/\/.*$/gm, "");
it("the operator PAT is never injected", () => {
expect(src).not.toMatch(/env\.GLUECRON_PAT\s*=/);
expect(src).not.toMatch(/GLUECRON_PAT:/);
});
it("the Anthropic key requires an explicit opt-in flag", () => {
expect(src).toContain('process.env.HOSTED_LOOPS_PLATFORM_KEY === "1"');
expect(src).not.toMatch(/^\s*ANTHROPIC_API_KEY:/m);
});
});
describe("no unlisted shell-exec site", () => {
it("every `sh -c` / `bash -c` spawn lives in a declared user-code path", () => {
const { readdirSync, statSync } = require("fs") as typeof import("fs");
const { join } = require("path") as typeof import("path");
const walk = (d: string): string[] =>
readdirSync(d).flatMap((e) => {
const p = join(d, e);
return statSync(p).isDirectory() ? walk(p) : [p];
});
const found: string[] = [];
for (const dir of ["src/lib", "src/routes", "src/hooks"]) {
for (const f of walk(dir)) {
if (!/\.tsx?$/.test(f)) continue;
const src = readFileSync(f, "utf8").replace(/^\s*\/\/.*$/gm, "");
if (/\[\s*["'`](?:ba)?sh["'`]\s*,\s*["'`]-c["'`]/.test(src)) {
found.push(f.replace(/\\/g, "/"));
}
}
}
expect(found.sort()).toEqual(
USER_CODE_PATHS.filter((p) => found.includes(p)).sort()
);
});
});
|