Skip to content
Merged
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
16 changes: 16 additions & 0 deletions src/lib/local/query.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -410,4 +410,20 @@ describe("searchFileContent", () => {
expect(searchFileContent("needlecomparison", {}, db)[0]!.lineText).toBe(comparisonLine);
expect(searchFileContent("needletype", {}, db)[0]!.lineText).toBe(typeLine);
});

test("redacts DB_PASS assignments and Basic authorization credentials from emitted matches", () => {
const value = ["synthetic", "review", "value", "731"].join("-");
setup({
"db-config.txt": `needledb DB_PASS=${value}`,
"request.txt": `needlebasic Authorization: Basic ${value}`,
});

const dbPassHit = searchFileContent("needledb", {}, db)[0]!;
expect(dbPassHit.lineText.includes(value)).toBe(false);
expect(dbPassHit.lineText).toBe("needledb DB_PASS=[REDACTED]");

const basicHit = searchFileContent("needlebasic", {}, db)[0]!;
expect(basicHit.lineText.includes(value)).toBe(false);
expect(basicHit.lineText).toBe("needlebasic Authorization: Basic [REDACTED]");
});
});
14 changes: 14 additions & 0 deletions src/lib/redaction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,20 @@ describe("redactCredentialBearingText", () => {
);
});

test("redacts DB_PASS assignments and Basic authorization credentials", () => {
const value = ["synthetic", "review", "value", "731"].join("-");

expect(redactCredentialBearingText(`DB_PASS=${value}`)).toBe(
`DB_PASS=${REDACTION_PLACEHOLDER}`,
);
expect(redactCredentialBearingText(`Authorization: Basic ${value}`)).toBe(
`Authorization: Basic ${REDACTION_PLACEHOLDER}`,
);
expect(redactCredentialBearingText(`authorization: basic ${value}`)).toBe(
`authorization: basic ${REDACTION_PLACEHOLDER}`,
);
});

test("redacts common standalone credential token shapes", () => {
const values = [
["sk-", "synthetic_token_123456789"].join(""),
Expand Down
6 changes: 3 additions & 3 deletions src/lib/redaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { SearchResult } from "../types/index.js";
export const REDACTION_PLACEHOLDER = "[REDACTED]";

const SENSITIVE_KEY_SOURCE =
String.raw`\b[a-z0-9_-]*(?:api[_-]?key|access[_-]?key|secret(?:[_-]?key)?|client[_-]?secret|(?:auth|access|refresh)[_-]?token|token|password|passwd|pwd|passphrase|private[_-]?key)\b`;
String.raw`\b[a-z0-9_-]*(?:api[_-]?key|access[_-]?key|secret(?:[_-]?key)?|client[_-]?secret|(?:auth|access|refresh)[_-]?token|token|password|passwd|pwd|pass|passphrase|private[_-]?key)\b`;

const SENSITIVE_EQUALS_ASSIGNMENT_PATTERN = new RegExp(
`(${SENSITIVE_KEY_SOURCE}["'\\x60]?[\\s]*=[\\s]*)(?![=>])(["'\\x60]?).*$`,
Expand All @@ -25,7 +25,7 @@ const SENSITIVE_ASSIGNMENT_PATTERNS = [
] as const;

const CREDENTIAL_URL_PATTERN = /([a-z][a-z0-9+.-]*:\/\/[^/\s:@]+:)([^@\s/]+)(@)/gi;
const BEARER_TOKEN_PATTERN = /(\bBearer\s+)[a-z0-9._~+/-]{8,}=*/gi;
const AUTHORIZATION_CREDENTIAL_PATTERN = /(\b(?:Bearer|Basic)\s+)[a-z0-9._~+/-]{8,}=*/gi;

const INLINE_CREDENTIAL_PATTERNS: readonly RegExp[] = [
/\bsk-[a-z0-9_-]{10,}\b/gi,
Expand Down Expand Up @@ -57,7 +57,7 @@ export function redactCredentialBearingText(text: string): string {
`${prefix}${REDACTION_PLACEHOLDER}${suffix}`,
);
redacted = redacted.replace(
BEARER_TOKEN_PATTERN,
AUTHORIZATION_CREDENTIAL_PATTERN,
(_match: string, prefix: string) => `${prefix}${REDACTION_PLACEHOLDER}`,
);

Expand Down
Loading