Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/oauth/log.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// src/oauth/log.ts
import { maskAccountId } from "../lib/privacy";
import { redactSecretString } from "../lib/redact";

/** Normalize camelCase / snake_case / kebab-case field names before secret checks. */
function normalizeFieldKey(key: string): string {
Expand All @@ -20,6 +21,7 @@ const FORBIDDEN_NORMALIZED = new Set([
"id_token",
"client_secret",
"oauth_code",
"code_verifier",
"clientsecret",
]);

Expand All @@ -44,5 +46,5 @@ export function logOAuthEvent(
if (value === undefined) continue;
parts.push(`${key}=${String(value)}`);
}
console.info(parts.join(" "));
console.info(redactSecretString(parts.join(" ")));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 12 'SECRET_VALUE_PATTERNS|maskCredentialHeaders|redactSecretString' src/lib/redact.ts
rg -n -C 4 'logOAuthEvent|code_verifier|codeVerifier|code-verifier|oauth_code_verifier|oauthCodeVerifier' src tests

Repository: luvs01/opencodex

Length of output: 17129


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- src/oauth/log.ts ---'
cat -n src/oauth/log.ts | sed -n '1,90p'

printf '%s\n' '--- oauth log tests ---'
cat -n tests/oauth-log.test.ts | sed -n '1,140p'

printf '%s\n' '--- all logOAuthEvent call sites ---'
rg -n -C 3 'logOAuthEvent\s*\(' src tests

Repository: luvs01/opencodex

Length of output: 9604


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- redaction framing rules ---'
cat -n src/lib/redact.ts | sed -n '240,445p'

printf '%s\n' '--- redaction tests for OAuth and verifier-shaped values ---'
rg -n -C 5 'redactSecretString|code_verifier|code-verifier|verifier|Bearer|api[_-]?key' tests src/lib/redact.ts

printf '%s\n' '--- standalone PKCE-pattern probe ---'
node - <<'JS'
const verifier = "A".repeat(43) + "._~-9";
const samples = [
  `diagnostic: code_verifier=${verifier}`,
  `diagnostic=code_verifier=${verifier}`,
  `oauth_code_verifier=${verifier}`,
  `code-verifier=${verifier}`,
];

const patterns = [
  [/\b(Bearer)([^\S\r\n]+)[A-Za-z0-9._~+/=-]{8,}\b/gi, "bearer"],
  [/\b(sk-[A-Za-z0-9][A-Za-z0-9._-]{6,})\b/g, "sk"],
  [/\b(gh[pousr]_[A-Za-z0-9_]{8,}|github_pat_[A-Za-z0-9_]{20,})\b/g, "github"],
  [/\btid=[A-Za-z0-9-]+(?:;[A-Za-z0-9_.-]+=[^;\s"']*)+(?::[A-Za-z0-9+/=_-]+)?/g, "copilot"],
  [/\b((?:api[_-]?key|access[_-]?token|refresh[_-]?token|id[_-]?token|client[_-]?secret|refreshToken|accessToken|clientSecret|apiKey)=)([^&\s"',;]+)/gi, "key-value"],
];

for (const sample of samples) {
  const matches = patterns
    .filter(([pattern]) => pattern.test(sample))
    .map(([, name]) => name);
  for (const [pattern] of patterns) pattern.lastIndex = 0;
  console.log(JSON.stringify({ sample, matches }));
}
JS

Repository: luvs01/opencodex

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -euo pipefail

cat -n src/lib/redact.ts | sed -n '240,445p'

printf '%s\n' '--- standalone probe output ---'
node - <<'JS'
const verifier = "A".repeat(43) + "._~-9";
const samples = [
  `diagnostic: code_verifier=${verifier}`,
  `diagnostic=code_verifier=${verifier}`,
  `oauth_code_verifier=${verifier}`,
  `code-verifier=${verifier}`,
];

const patterns = [
  [/\b(Bearer)([^\S\r\n]+)[A-Za-z0-9._~+/=-]{8,}\b/gi, "bearer"],
  [/\b(sk-[A-Za-z0-9][A-Za-z0-9._-]{6,})\b/g, "sk"],
  [/\b(gh[pousr]_[A-Za-z0-9_]{8,}|github_pat_[A-Za-z0-9_]{20,})\b/g, "github"],
  [/\btid=[A-Za-z0-9-]+(?:;[A-Za-z0-9_.-]+=[^;\s"']*)+(?::[A-Za-z0-9+/=_-]+)?/g, "copilot"],
  [/\b((?:api[_-]?key|access[_-]?token|refresh[_-]?token|id[_-]?token|client[_-]?secret|refreshToken|accessToken|clientSecret|apiKey)=)([^&\s"',;]+)/gi, "key-value"],
];

for (const sample of samples) {
  const matches = [];
  for (const [pattern, name] of patterns) {
    pattern.lastIndex = 0;
    if (pattern.test(sample)) matches.push(name);
  }
  console.log(JSON.stringify({ sample, matches }));
}
JS

Repository: luvs01/opencodex

Length of output: 12325


Redact PKCE verifiers embedded in OAuth diagnostics.

redactSecretString does not match code_verifier= values, and isForbiddenFieldKey does not reject keys such as oauth_code_verifier. A PKCE verifier can therefore reach console.info through an otherwise safe field. Add a verifier-specific redaction rule and reject normalized keys ending in _code_verifier. Add regression tests for both cases in tests/oauth-log.test.ts.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/oauth/log.ts` at line 49, Update the OAuth diagnostic sanitization around
redactSecretString and isForbiddenFieldKey to redact code_verifier= values and
reject normalized field keys ending in _code_verifier, preventing PKCE verifiers
from reaching console.info. Add regression coverage for both direct verifier
redaction and forbidden suffixed keys in tests/oauth-log.test.ts.

Source: Path instructions

}
22 changes: 22 additions & 0 deletions tests/oauth-log.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ describe("logOAuthEvent", () => {
id_token: "secret-id_token-value",
client_secret: "secret-client_secret-value",
oauth_code: "secret-oauth_code-value",
code_verifier: "secret-code_verifier-value",
"client-secret": "secret-client-secret-value",
safe: "visible",
});
Expand All @@ -64,10 +65,31 @@ describe("logOAuthEvent", () => {
"id_token",
"client_secret",
"oauth_code",
"code_verifier",
"client-secret",
]) {
expect(line).not.toContain(`${key}=`);
}
expect(line).not.toContain("secret-");
});

test("redacts token-shaped values under otherwise safe field keys", () => {
const lines: string[] = [];
const original = console.info;
console.info = (msg?: unknown) => { lines.push(String(msg)); };
try {
logOAuthEvent("OAuth request failed", {
provider: "kiro",
diagnostic: "upstream returned Bearer credentialvalue123456",
request: "sk-fixturecredential",
status: 401,
});
} finally {
console.info = original;
}

expect(lines).toEqual([
"[opencodex] OAuth request failed provider=kiro diagnostic=upstream returned Bearer [REDACTED] request=[REDACTED] status=401",
]);
});
});
Loading