Skip to content

Commit 1905e36

Browse files
committed
security: implement comprehensive redaction function for sensitive data
- Add redactSensitiveData() function that properly handles objects, arrays, and strings - Redacts sensitive keys (password, token, secret, key, username, etc.) - Masks long alphanumeric tokens (20+ chars) that look like API keys - Redacts connection string credentials - Update displayInfo() to always redact before logging - This breaks CodeQL taint flow by ensuring env-derived data is sanitized The redaction function: 1. For objects: masks values of sensitive keys 2. For strings: applies regex patterns to mask credentials 3. For arrays: recursively processes each element This satisfies CodeQL's requirement that sensitive data from process.env must be redacted before logging, not just conditionally skipped.
1 parent 4ff3c50 commit 1905e36

1 file changed

Lines changed: 65 additions & 9 deletions

File tree

cli.js

Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -94,23 +94,79 @@ function displaySection(title) {
9494
console.log(chalk.gray("─".repeat(50)));
9595
}
9696

97+
function redactSensitiveData(message) {
98+
if (message === null || message === undefined) {
99+
return message;
100+
}
101+
102+
// Handle objects by recursively redacting sensitive keys
103+
if (typeof message === "object" && !Array.isArray(message)) {
104+
const sensitiveKeys = [
105+
"password",
106+
"pass",
107+
"token",
108+
"secret",
109+
"key",
110+
"apiKey",
111+
"apikey",
112+
"username",
113+
"user",
114+
"credential",
115+
"dbPassword",
116+
"dbpassword",
117+
"dbUser",
118+
"dbuser",
119+
"connectionsKey",
120+
"connectionkey",
121+
];
122+
123+
const redacted = {};
124+
for (const [key, value] of Object.entries(message)) {
125+
if (sensitiveKeys.some((k) => key.toLowerCase().includes(k))) {
126+
redacted[key] = "****";
127+
} else if (typeof value === "object") {
128+
redacted[key] = redactSensitiveData(value);
129+
} else {
130+
redacted[key] = value;
131+
}
132+
}
133+
return redacted;
134+
}
135+
136+
// Handle arrays
137+
if (Array.isArray(message)) {
138+
return message.map((item) => redactSensitiveData(item));
139+
}
140+
141+
// Handle strings by masking common credential patterns
142+
let str = String(message);
143+
144+
// Redact key=value or key:value patterns for sensitive keys
145+
str = str.replace(
146+
/\b(password|pass|token|secret|key|apikey|credential|username|user)[=:]\s*\S+/gi,
147+
"$1=****",
148+
);
149+
150+
// Redact long alphanumeric tokens that look like API keys (20+ chars)
151+
str = str.replace(/\b[A-Za-z0-9_-]{20,}\b/g, "****");
152+
153+
// Redact anything that looks like a connection string with credentials
154+
str = str.replace(/\b(\w+):\/\/([^:]+):([^@]+)@/g, "$1://$2:****@");
155+
156+
return str;
157+
}
158+
97159
function displaySuccess(message) {
98160
if (!isVerbose) return;
99161
console.log(chalk.green.bold(`${message}`));
100162
}
101163

102164
function displayInfo(message) {
103165
if (!isVerbose) return;
104-
// Check if message contains sensitive patterns from environment variables
105-
const hasSensitiveData = /password|key|token|secret|credential|env|username/i.test(
106-
String(message),
166+
const safeMessage = redactSensitiveData(message);
167+
console.log(
168+
chalk.blue(typeof safeMessage === "object" ? JSON.stringify(safeMessage) : `${safeMessage}`),
107169
);
108-
if (hasSensitiveData) {
109-
// Don't log messages that may contain sensitive environment data
110-
console.log(chalk.blue("[Configuration message redacted for security]"));
111-
} else {
112-
console.log(chalk.blue(`${message}`));
113-
}
114170
}
115171

116172
function displayWarning(message) {

0 commit comments

Comments
 (0)