Skip to content

Commit 9f0cd93

Browse files
committed
fix: redact sensitive data in invoke CLI output (CodeQL)
Add redactSensitiveText helper to strip bearer tokens, client secrets, and token values from invoke results before logging to stdout/stderr.
1 parent 2e9d7c4 commit 9f0cd93

1 file changed

Lines changed: 23 additions & 6 deletions

File tree

src/cli/commands/invoke/command.tsx

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,23 @@ function stopSpinner(spinner: NodeJS.Timeout): void {
3030
process.stderr.write('\r\x1b[K'); // Clear line
3131
}
3232

33+
function redactSensitiveText(value: string): string {
34+
return value
35+
.replace(/(bearer\s+)[a-z0-9\-._~+/]+=*/gi, '$1[REDACTED]')
36+
.replace(/(client[_-]?secret\s*[:=]\s*)([^,\s]+)/gi, '$1[REDACTED]')
37+
.replace(/(token\s*[:=]\s*)([^,\s]+)/gi, '$1[REDACTED]');
38+
}
39+
40+
function toSafeJsonResult(result: InvokeResult): Record<string, unknown> {
41+
return {
42+
success: result.success,
43+
response: typeof result.response === 'string' ? redactSensitiveText(result.response) : result.response,
44+
error: typeof result.error === 'string' ? redactSensitiveText(result.error) : result.error,
45+
sessionId: result.sessionId,
46+
logFilePath: result.logFilePath,
47+
};
48+
}
49+
3350
function resolveProtocol(options: InvokeOptions, projectProtocol?: string): string {
3451
if (projectProtocol) return projectProtocol.toLowerCase();
3552
if (options.tool) return 'mcp';
@@ -41,7 +58,7 @@ async function handleInvokeCLI(options: InvokeOptions, preloadedContext?: Invoke
4158
if (!validation.valid) {
4259
const result: InvokeResult = { success: false, error: validation.error ?? 'Validation failed' };
4360
if (options.json) {
44-
console.log(JSON.stringify(result));
61+
console.log(JSON.stringify(toSafeJsonResult(result)));
4562
} else {
4663
console.error(result.error);
4764
}
@@ -64,9 +81,9 @@ async function handleInvokeCLI(options: InvokeOptions, preloadedContext?: Invoke
6481
}
6582
const result = await handleHarnessInvokeByArn(options.harnessArn, region, options);
6683
if (options.json) {
67-
console.log(JSON.stringify(result));
84+
console.log(JSON.stringify(toSafeJsonResult(result)));
6885
} else if (!result.success && result.error) {
69-
console.error(result.error);
86+
console.error(redactSensitiveText(result.error));
7087
}
7188
process.exit(result.success ? 0 : 1);
7289
}
@@ -89,7 +106,7 @@ async function handleInvokeCLI(options: InvokeOptions, preloadedContext?: Invoke
89106
}
90107

91108
if (options.json) {
92-
console.log(JSON.stringify(result));
109+
console.log(JSON.stringify(toSafeJsonResult(result)));
93110
} else if (options.stream) {
94111
// Streaming already wrote to stdout, just show session and log path
95112
if (result.sessionId) {
@@ -102,9 +119,9 @@ async function handleInvokeCLI(options: InvokeOptions, preloadedContext?: Invoke
102119
} else {
103120
// Non-streaming, non-json: print provider info and response or error
104121
if (result.success && result.response) {
105-
console.log(result.response);
122+
console.log(redactSensitiveText(result.response));
106123
} else if (!result.success && result.error) {
107-
console.error(result.error);
124+
console.error(redactSensitiveText(result.error));
108125
}
109126
if (result.sessionId) {
110127
console.error(`\nSession: ${result.sessionId}`);

0 commit comments

Comments
 (0)