mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-21 08:22:20 +02:00
Redact custom provider credentials from OpenCode diagnostics
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
7292c79988
commit
8bded58b3e
2 files changed
+47
-12
No files matched your search
@@ -2262,16 +2262,20 @@ describe("OpenCodeServerDriver", () => {
|
||||
const exitingFixture = join(root, "exit-before-health.mjs");
|
||||
await writeFile(
|
||||
exitingFixture,
|
||||
"#!/usr/bin/env node\nprocess.stderr.write(`credential=${process.env.OPENROUTER_API_KEY}\\nauthorization=super-secret-opencode-token\\n`);\nprocess.exit(17);\n",
|
||||
"#!/usr/bin/env node\nimport { readFileSync } from 'node:fs';\nconst config = JSON.parse(readFileSync(`${process.env.XDG_CONFIG_HOME}/opencode/opencode.json`, 'utf8'));\nprocess.stderr.write(`credential=${process.env.OPENROUTER_API_KEY}\\ngateway=${config.provider.paperclip.options.apiKey}\\nauthorization=super-secret-opencode-token\\n`);\nprocess.exit(17);\n",
|
||||
{ mode: 0o755 },
|
||||
);
|
||||
const diagnostics: string[] = [];
|
||||
const driver = new OpenCodeServerDriver({
|
||||
model: "openrouter/deepseek/deepseek-v4-flash-0731",
|
||||
model: "paperclip/team/model-alias",
|
||||
runtimeDirectory: root,
|
||||
command: exitingFixture,
|
||||
onDiagnostic: (message) => { diagnostics.push(message); },
|
||||
environment: {
|
||||
PATH: process.env.PATH,
|
||||
OPENROUTER_API_KEY: "fixture-key",
|
||||
PAPERCLIP_AI_PROVIDER_KEY: "fixture-custom-gateway-key",
|
||||
PAPERCLIP_AI_PROVIDER_URL: "https://gateway.example/v1",
|
||||
},
|
||||
});
|
||||
const error = await driver
|
||||
@@ -2289,6 +2293,41 @@ describe("OpenCodeServerDriver", () => {
|
||||
expect(error).toContain("stage=health");
|
||||
expect(error).toContain("[REDACTED]");
|
||||
expect(error).not.toContain("fixture-key");
|
||||
expect(error).not.toContain("fixture-custom-gateway-key");
|
||||
expect(error).not.toContain("super-secret-opencode-token");
|
||||
expect(diagnostics.join("")).toContain("gateway=[REDACTED]");
|
||||
expect(diagnostics.join("")).not.toContain("fixture-custom-gateway-key");
|
||||
});
|
||||
|
||||
it.each(["network", "response"])("redacts custom gateway keys in %s errors", async (failure) => {
|
||||
await chmod(fixture, 0o755);
|
||||
const root = await mkdtemp(join(tmpdir(), "paperclip-opencode-driver-"));
|
||||
const workspace = await mkdtemp(join(tmpdir(), "paperclip-opencode-workspace-"));
|
||||
roots.push(root, workspace);
|
||||
const key = "fixture-custom-gateway-key";
|
||||
const driver = new OpenCodeServerDriver({
|
||||
model: "paperclip/team/model-alias",
|
||||
runtimeDirectory: root,
|
||||
command: fixture,
|
||||
environment: {
|
||||
PATH: process.env.PATH,
|
||||
PAPERCLIP_AI_PROVIDER_KEY: key,
|
||||
PAPERCLIP_AI_PROVIDER_URL: "https://gateway.example/v1",
|
||||
},
|
||||
fetch: async (input, init) => {
|
||||
if (String(input).endsWith("/session") && init?.method === "POST") {
|
||||
if (failure === "network") throw new Error(`Gateway rejected ${key}`);
|
||||
return new Response(`Gateway rejected ${key}`, { status: 400 });
|
||||
}
|
||||
return fetch(input, init);
|
||||
},
|
||||
});
|
||||
const error = await driver.openSession({
|
||||
runId: "run-gateway-error",
|
||||
normalizedSessionId: "gateway-error",
|
||||
workingDirectory: workspace,
|
||||
}).then(() => "provider unexpectedly started", (cause: unknown) => String(cause));
|
||||
expect(error).toContain("Gateway rejected [REDACTED]");
|
||||
expect(error).not.toContain(key);
|
||||
});
|
||||
});
|
||||
@@ -254,6 +254,7 @@ export class OpenCodeServerDriver implements HarnessDriver {
|
||||
recovered: false,
|
||||
reason: redact(String(error), [
|
||||
this.#options.environment?.OPENROUTER_API_KEY,
|
||||
this.#options.environment?.PAPERCLIP_AI_PROVIDER_KEY,
|
||||
]),
|
||||
};
|
||||
}
|
||||
@@ -1970,14 +1971,15 @@ async function startRuntime(input: {
|
||||
const assignedMcp = nativeMcpLaunchBinding(
|
||||
input.options.environment ?? process.env,
|
||||
);
|
||||
input.trace?.addSensitiveValues([
|
||||
const sensitiveValues = [
|
||||
password,
|
||||
authHeader,
|
||||
bridge.secret,
|
||||
assignedMcp?.token,
|
||||
input.options.environment?.OPENROUTER_API_KEY,
|
||||
input.options.environment?.PAPERCLIP_AI_PROVIDER_KEY,
|
||||
]);
|
||||
].filter((value): value is string => Boolean(value));
|
||||
input.trace?.addSensitiveValues(sensitiveValues);
|
||||
const instructionRoot =
|
||||
input.options.runtimeContext?.instructions.bundle.rootPath;
|
||||
const [modelProvider, ...modelIdParts] = input.options.model.split("/");
|
||||
@@ -2089,10 +2091,7 @@ async function startRuntime(input: {
|
||||
let diagnostics = "";
|
||||
child.stderr?.on("data", (chunk) => {
|
||||
const raw = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
|
||||
const redactedDiagnostic = redact(raw.toString("utf8"), [
|
||||
password,
|
||||
input.options.environment?.OPENROUTER_API_KEY,
|
||||
]);
|
||||
const redactedDiagnostic = redact(raw.toString("utf8"), sensitiveValues);
|
||||
diagnostics = `${diagnostics}${redactedDiagnostic}`.slice(-8_192);
|
||||
const frameId = input.trace?.frame({
|
||||
direction: "provider_stderr",
|
||||
@@ -2155,10 +2154,7 @@ async function startRuntime(input: {
|
||||
process: child,
|
||||
bridge,
|
||||
trace: input.trace,
|
||||
sensitiveValues: [
|
||||
password,
|
||||
input.options.environment?.OPENROUTER_API_KEY,
|
||||
].filter((value): value is string => Boolean(value)),
|
||||
sensitiveValues,
|
||||
close: async (closeInput = {}) => {
|
||||
await bridge.close().catch(() => {});
|
||||
if (child.exitCode === null && child.signalCode === null && child.pid) {
|
||||
|
||||
Reference in new issue
Block a user