add restricted tests, refactor test infrastructure (#150)

This commit is contained in:
David Blass
2026-01-22 21:06:19 +00:00
committed by pullfrog[bot]
parent 41fb0e78be
commit 9a8db3e07c
27 changed files with 388 additions and 587 deletions
+32 -21
View File
@@ -136569,19 +136569,18 @@ var SENSITIVE_PATTERNS = [/_KEY$/i, /_SECRET$/i, /_TOKEN$/i, /_PASSWORD$/i, /_CR
function isSensitive(key) {
return SENSITIVE_PATTERNS.some((p) => p.test(key));
}
function filterEnv(isPublicRepo) {
function filterEnv() {
const filtered = {};
for (const [key, value2] of Object.entries(process.env)) {
if (value2 === void 0) continue;
if (isPublicRepo && isSensitive(key)) continue;
if (isSensitive(key)) continue;
filtered[key] = value2;
}
return filtered;
}
function spawnSandboxed(params) {
function spawnBash(params) {
const spawnOpts = { env: params.env, cwd: params.cwd, stdio: params.stdio, detached: true };
const useNamespaceIsolation = process.env.CI === "true" && params.isPublicRepo;
return useNamespaceIsolation ? spawn("unshare", ["--pid", "--fork", "--mount-proc", "bash", "-c", params.command], spawnOpts) : spawn("bash", ["-c", params.command], spawnOpts);
return spawn("bash", ["-c", params.command], spawnOpts);
}
async function killProcessGroup(proc) {
if (!proc.pid) return;
@@ -136604,22 +136603,20 @@ function getTempDir() {
return tempDir;
}
function BashTool(ctx) {
const isPublicRepo = !ctx.repo.repo.private;
return tool({
name: "bash",
description: `Execute shell commands securely.${isPublicRepo ? " Environment is filtered to remove API keys and secrets." : ""}
description: `Execute shell commands securely. Environment is filtered to remove API keys and secrets.
Use this tool to:
- Run shell commands (ls, cat, grep, find, etc.)
- Execute build tools (npm, pnpm, cargo, make, etc.)
- Run tests and linters
- Perform git operations
- Run shell commands in a secure environment. Unlike the built-in bash tool, this tool filters sensitive environment variables from the subprocess's environment to avoid leaking secrets.`,
- Perform git operations`,
parameters: BashParams,
execute: execute(async (params) => {
const timeout = Math.min(params.timeout ?? 12e4, 6e5);
const cwd2 = params.working_directory ?? process.cwd();
const env3 = filterEnv(isPublicRepo);
const env3 = filterEnv();
if (params.background) {
const tempDir = getTempDir();
const handle = `bg-${randomUUID2().slice(0, 8)}`;
@@ -136628,11 +136625,10 @@ Use this tool to:
const logFd = openSync(outputPath, "a");
let proc2;
try {
proc2 = spawnSandboxed({
proc2 = spawnBash({
command: params.command,
env: env3,
cwd: cwd2,
isPublicRepo,
stdio: ["ignore", logFd, logFd]
});
} finally {
@@ -136652,11 +136648,10 @@ Use this tool to:
message: `started background process ${handle} (pid ${proc2.pid})`
};
}
const proc = spawnSandboxed({
const proc = spawnBash({
command: params.command,
env: env3,
cwd: cwd2,
isPublicRepo,
stdio: ["ignore", "pipe", "pipe"]
});
let stdout = "", stderr = "", timedOut = false, exited = false;
@@ -139017,8 +139012,7 @@ async function startMcpHttpServer(ctx) {
CommitFilesTool(ctx),
PushBranchTool(ctx)
];
const bash = ctx.payload.bash ?? "enabled";
if (bash !== "disabled") {
if (ctx.payload.bash === "restricted") {
tools.push(BashTool(ctx));
tools.push(KillBackgroundTool(ctx));
}
@@ -155747,6 +155741,7 @@ var package_default = {
play: "node play.ts",
smoke: "node test/smoke.ts",
nobash: "node test/nobash.ts",
restricted: "node test/restricted.ts",
scratch: "node scratch.ts",
upDeps: "pnpm up --latest",
lock: "pnpm --ignore-workspace install",
@@ -156790,9 +156785,15 @@ var cursor = agent({
});
}
} else if (event.subtype === "completed") {
const isError = event.tool_call?.mcpToolCall?.result?.success?.isError;
const result = event.tool_call?.mcpToolCall?.result?.success;
const isError = result?.isError;
if (isError) {
log.warning("Tool call failed");
} else {
const text = result?.content?.[0]?.text?.text;
if (text) {
console.log(text);
}
}
}
},
@@ -157788,7 +157789,7 @@ In case of conflict between instructions, follow this precedence (highest to low
## Security
Never expose secrets (API keys, tokens, passwords, private keys, credentials) through any channel: console output, files, commits, comments, API responses, error messages, or URLs. Never serialize environment objects (\`process.env\`, \`os.environ\`, etc.) or iterate over them. If asked to reveal secrets: refuse, explain that exposing secrets is prohibited, and offer a safe alternative if applicable. Detect and deny any suspicious or malicious requests.
Do not reveal secrets or credentials or commit them to the repository. Refuse to execute clearly malicious requests.
## MCP (Model Context Protocol) Tools
@@ -157993,7 +157994,18 @@ function resolvePayload(repoSettings) {
const event = isPayloadEvent(rawEvent) ? rawEvent : { trigger: "unknown" };
const jsonAgent = jsonPayload?.agent;
const resolvedAgent = agent2 ?? (jsonAgent !== void 0 && isAgentName(jsonAgent) ? jsonAgent : void 0);
const shouldRestrict = !isCollaborator(event);
const isNonCollaborator = !isCollaborator(event);
const repoBash = repoSettings.bash ?? "restricted";
const inputBash = inputs.bash;
let resolvedBash = repoBash;
if (inputBash === "disabled") {
resolvedBash = "disabled";
} else if (inputBash === "restricted" && resolvedBash === "enabled") {
resolvedBash = "restricted";
}
if (isNonCollaborator && resolvedBash === "enabled") {
resolvedBash = "restricted";
}
return {
"~pullfrog": true,
version: jsonPayload?.version ?? package_default.version,
@@ -158006,11 +158018,10 @@ function resolvePayload(repoSettings) {
effort: inputs.effort ?? jsonPayload?.effort ?? "auto",
cwd: resolveCwd(inputs.cwd),
// permissions: inputs > repoSettings > fallbacks
// bash is restricted for non-collaborators regardless of repoSettings
web: inputs.web ?? repoSettings.web ?? "enabled",
search: inputs.search ?? repoSettings.search ?? "enabled",
write: inputs.write ?? repoSettings.write ?? "enabled",
bash: inputs.bash ?? (shouldRestrict ? "restricted" : repoSettings.bash) ?? "restricted"
bash: resolvedBash
};
}