- Add private_key, encrypted_credentials, tenant_id, client_email, serviceAccountKey to SENSITIVE_FIELDS array - Add comprehensive test cases for delegation credential sensitive fields - Prevents logging of sensitive delegation credential data including private keys Fixes security issue where delegation credential sensitive fields were not being redacted from logs Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: [email protected] <[email protected]> Co-authored-by: Morgan <[email protected]>
90 lines
2.2 KiB
TypeScript
90 lines
2.2 KiB
TypeScript
import logger from "./logger";
|
|
|
|
// Fields that might contain sensitive data
|
|
const SENSITIVE_FIELDS = [
|
|
"accessToken",
|
|
"api_key",
|
|
"apiKey",
|
|
"auth",
|
|
"authorization",
|
|
"client_email",
|
|
"client_id",
|
|
"client_secret",
|
|
"credential",
|
|
"encrypted_credentials",
|
|
"hash",
|
|
"key",
|
|
"password",
|
|
"private_key",
|
|
"refreshToken",
|
|
"secret",
|
|
"serviceAccountKey",
|
|
"tenant_id",
|
|
"token",
|
|
];
|
|
|
|
// Create a Set for efficient O(1) average time complexity lookups
|
|
const SENSITIVE_FIELDS_SET = new Set<string>(SENSITIVE_FIELDS);
|
|
|
|
/**
|
|
* Creates a replacer function for JSON.stringify that redacts sensitive fields.
|
|
* This approach is more efficient as it leverages native JSON serialization.
|
|
*/
|
|
function createReplacer(): (key: string, value: unknown) => unknown {
|
|
// Use a WeakSet to track circular references
|
|
const seen = new WeakSet();
|
|
|
|
return function replacer(key: string, value: unknown): unknown {
|
|
// Handle circular references
|
|
if (value && typeof value === "object") {
|
|
if (seen.has(value)) {
|
|
return "[Circular]";
|
|
}
|
|
seen.add(value);
|
|
}
|
|
|
|
// Redact sensitive fields
|
|
if (SENSITIVE_FIELDS_SET.has(key)) {
|
|
return "[REDACTED]";
|
|
}
|
|
|
|
// Handle Error objects specially to preserve their structure
|
|
if (value instanceof Error) {
|
|
const errorObj: Record<string, unknown> = {
|
|
name: value.name,
|
|
message: value.message,
|
|
};
|
|
|
|
if (value.stack) {
|
|
errorObj.stack = value.stack;
|
|
}
|
|
|
|
// Handle cause property safely
|
|
if ("cause" in value && value.cause !== undefined) {
|
|
errorObj.cause = value.cause;
|
|
}
|
|
|
|
return errorObj;
|
|
}
|
|
|
|
return value;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Redacts sensitive data from an object by replacing sensitive fields with a placeholder.
|
|
* Uses JSON.stringify's replacer function for efficient serialization and redaction.
|
|
*/
|
|
export function redactSensitiveData(data: unknown): unknown {
|
|
if (!data || typeof data !== "object") return data;
|
|
|
|
try {
|
|
const replacer = createReplacer();
|
|
const jsonString = JSON.stringify(data, replacer);
|
|
return JSON.parse(jsonString);
|
|
} catch (error) {
|
|
logger.error("Failed to redact sensitive data", error);
|
|
return { error: "Redaction failed" };
|
|
}
|
|
}
|