Skip to content

Commit 559dc0f

Browse files
fix(security): redact command artifact arguments
1 parent e22c7c7 commit 559dc0f

2 files changed

Lines changed: 22 additions & 4 deletions

File tree

src/adapter.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4520,12 +4520,14 @@ function codexTokenCandidates(config: AdapterConfig): string[] {
45204520
return candidates;
45214521
}
45224522

4523-
function redactedCommandResult(result: CommandResult): JsonObject {
4523+
export function redactedCommandResult(result: CommandResult): JsonObject {
45244524
const diagnostic = runtimeDiagnostic(result);
45254525
return {
45264526
...result,
4527+
args: result.args.map((arg) => redactTokenish(arg)),
45274528
stdout: redactTokenish(result.stdout),
45284529
stderr: redactTokenish(result.stderr),
4530+
spawn_error: redactTokenish(result.spawn_error),
45294531
...(diagnostic ? {runtime_diagnostic: diagnostic} : {}),
45304532
};
45314533
}
@@ -4582,14 +4584,14 @@ export function redactTokenish(text: string): string {
45824584
}
45834585
return text
45844586
.replace(/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g, "[redacted private key]")
4587+
.replace(/(https?:\/\/)[^/\s:@]+:[^@\s/]+@/gi, "$1[redacted]@")
4588+
.replace(/x-access-token:[^@\s'\"]+/gi, "x-access-token:[redacted]")
45854589
.replace(/\b(?:gh[pousr]_|github_pat_)[A-Za-z0-9_-]{6,}/g, "[redacted github token]")
45864590
.replace(/\bsk-(?:proj-)?[A-Za-z0-9_-]{8,}/g, "[redacted OpenAI token]")
45874591
.replace(/\bBearer\s+[^\s'\"]+/gi, "Bearer [redacted]")
45884592
.replace(/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi, "[redacted email]")
45894593
.replace(/\bon account\s+`?[^`\n.]+`?/gi, "on the configured account")
4590-
.replace(/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g, "[redacted JWT]")
4591-
.replace(/x-access-token:[^@\s'\"]+/gi, "x-access-token:[redacted]")
4592-
.replace(/(https?:\/\/)[^/\s:@]+:[^@\s/]+@/gi, "$1[redacted]@");
4594+
.replace(/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g, "[redacted JWT]");
45934595
}
45944596

45954597
function failTask(path: string, task: JsonObject, reason: string, detail: string): JsonObject {

tests/webhook-adapter.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
publishResultIfConfigured,
2828
readBoundedRuntimeResult,
2929
recoverPendingPublications,
30+
redactedCommandResult,
3031
redactTokenish,
3132
reviewContextInstallationTokenRequest,
3233
resumeTaskPublication,
@@ -3261,6 +3262,21 @@ test("redacts credentials and passes only allowlisted ambient environment keys",
32613262
].join("\n");
32623263
const redacted = redactTokenish(secretText);
32633264
assert.doesNotMatch(redacted, /1234567890|topsecret|password|private-data|eyJabc|reviewer@example\.com|reviewer \(/);
3265+
const artifact = redactedCommandResult({
3266+
args: ["git", "-c", "user.email=reviewer@example.com", "https://x-access-token:ghs_1234567890@github.com/OpenCoven/example.git"],
3267+
returncode: 1,
3268+
stdout: "Bearer topsecret",
3269+
stderr: "reviewer@example.com",
3270+
signal: null,
3271+
timed_out: false,
3272+
duration_ms: 1,
3273+
stdout_truncated: false,
3274+
stderr_truncated: false,
3275+
output_limit_bytes: 1024,
3276+
spawn_error: "failed for reviewer@example.com",
3277+
});
3278+
assert.doesNotMatch(JSON.stringify(artifact), /1234567890|topsecret|reviewer@example\.com/);
3279+
assert.deepEqual(artifact.args, ["git", "-c", "user.email=[redacted email]", "https://[redacted]@github.com/OpenCoven/example.git"]);
32643280
const env = sanitizedRuntimeEnvironment({
32653281
PATH: "/bin", LANG: "C.UTF-8", SSH_AUTH_SOCK: "/tmp/agent.sock",
32663282
DATABASE_URL: "postgres://user:pass@db", AWS_ACCESS_KEY_ID: "AKIASECRET",

0 commit comments

Comments
 (0)