});
});
-// Test data for new secret types
+// Test data for secret types
const openaiApiKey = "sk-proj-abc123def456ghi789jkl012mno345pqr678stu901vwx";
+const anthropicApiKey =
+ "sk-ant-api03-abc123def456ghi789jkl012mno345pqr678stu901vwxyz0123456789abcdefghijklmnopqrstuvwxyz";
+const stripeTestKey = "sk_test_abc123def456ghi789jkl012";
+const stripeLiveKey = "sk_live_xyz789abc123def456ghi789";
+const revenueCatKey = "sk_abcdefghijklmnopqrstuvwx";
const awsAccessKey = "AKIAIOSFODNN7EXAMPLE";
const githubToken = "ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx1234";
const githubOAuthToken = "gho_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx5678";
describe("detectSecrets - API Keys", () => {
const apiKeyConfig: SecretsDetectionConfig = {
...defaultConfig,
- entities: ["API_KEY_OPENAI", "API_KEY_AWS", "API_KEY_GITHUB"],
+ entities: ["API_KEY_SK", "API_KEY_AWS", "API_KEY_GITHUB"],
};
- test("detects OpenAI API key", () => {
+ test("detects OpenAI API key (sk-proj-...)", () => {
const text = `My API key is ${openaiApiKey}`;
const result = detectSecrets(text, apiKeyConfig);
expect(result.detected).toBe(true);
expect(result.matches).toHaveLength(1);
- expect(result.matches[0].type).toBe("API_KEY_OPENAI");
+ expect(result.matches[0].type).toBe("API_KEY_SK");
expect(result.matches[0].count).toBe(1);
expect(result.locations).toBeDefined();
- expect(result.locations?.[0].type).toBe("API_KEY_OPENAI");
+ expect(result.locations?.[0].type).toBe("API_KEY_SK");
+ });
+
+ test("detects Anthropic API key (sk-ant-...)", () => {
+ const text = `Anthropic key: ${anthropicApiKey}`;
+ const result = detectSecrets(text, apiKeyConfig);
+ expect(result.detected).toBe(true);
+ expect(result.matches).toHaveLength(1);
+ expect(result.matches[0].type).toBe("API_KEY_SK");
+ });
+
+ test("detects Stripe test key (sk_test_...)", () => {
+ const text = `STRIPE_SECRET_KEY=${stripeTestKey}`;
+ const result = detectSecrets(text, apiKeyConfig);
+ expect(result.detected).toBe(true);
+ expect(result.matches).toHaveLength(1);
+ expect(result.matches[0].type).toBe("API_KEY_SK");
+ });
+
+ test("detects Stripe live key (sk_live_...)", () => {
+ const text = `export STRIPE_KEY="${stripeLiveKey}"`;
+ const result = detectSecrets(text, apiKeyConfig);
+ expect(result.detected).toBe(true);
+ expect(result.matches).toHaveLength(1);
+ expect(result.matches[0].type).toBe("API_KEY_SK");
+ });
+
+ test("detects RevenueCat key (sk_...)", () => {
+ const text = `revenuecat_api_key: ${revenueCatKey}`;
+ const result = detectSecrets(text, apiKeyConfig);
+ expect(result.detected).toBe(true);
+ expect(result.matches).toHaveLength(1);
+ expect(result.matches[0].type).toBe("API_KEY_SK");
});
test("detects AWS access key", () => {
const result = detectSecrets(text, apiKeyConfig);
expect(result.detected).toBe(true);
expect(result.matches).toHaveLength(3);
- expect(result.matches.find((m) => m.type === "API_KEY_OPENAI")).toBeDefined();
+ expect(result.matches.find((m) => m.type === "API_KEY_SK")).toBeDefined();
expect(result.matches.find((m) => m.type === "API_KEY_AWS")).toBeDefined();
expect(result.matches.find((m) => m.type === "API_KEY_GITHUB")).toBeDefined();
});
expect(result.detected).toBe(false);
});
+ test("avoids false positive - sk_ prefix but too short", () => {
+ const text = "This sk_short is not valid";
+ const result = detectSecrets(text, apiKeyConfig);
+ expect(result.detected).toBe(false);
+ });
+
test("avoids false positive - AKIA prefix but wrong length", () => {
const text = "AKIA12345 is not valid";
const result = detectSecrets(text, apiKeyConfig);
entities: [
"OPENSSH_PRIVATE_KEY",
"PEM_PRIVATE_KEY",
- "API_KEY_OPENAI",
+ "API_KEY_SK",
"API_KEY_AWS",
"API_KEY_GITHUB",
"JWT_TOKEN",
} from "./mask";
const sampleSecret = "sk-proj-abc123def456ghi789jkl012mno345pqr678stu901vwx";
+const stripeSecret = "sk_live_abc123def456ghi789jkl012";
/** Helper to create a minimal request from messages */
function createRequest(messages: OpenAIMessage[]): OpenAIRequest {
test("uses [[TYPE_N]] format", () => {
const text = `My API key is ${sampleSecret}`;
const locations: SecretLocation[] = [
- { start: 14, end: 14 + sampleSecret.length, type: "API_KEY_OPENAI" },
+ { start: 14, end: 14 + sampleSecret.length, type: "API_KEY_SK" },
];
const result = maskSecrets(text, locations);
- expect(result.masked).toBe("My API key is [[API_KEY_OPENAI_1]]");
+ expect(result.masked).toBe("My API key is [[API_KEY_SK_1]]");
});
test("increments counter per secret type", () => {
const anotherSecret = "sk-proj-xyz789abc123def456ghi789jkl012mno345pqr678";
const text = `Key1: ${sampleSecret} Key2: ${anotherSecret}`;
const locations: SecretLocation[] = [
- { start: 6, end: 6 + sampleSecret.length, type: "API_KEY_OPENAI" },
+ { start: 6, end: 6 + sampleSecret.length, type: "API_KEY_SK" },
{
start: 6 + sampleSecret.length + 7,
end: 6 + sampleSecret.length + 7 + anotherSecret.length,
- type: "API_KEY_OPENAI",
+ type: "API_KEY_SK",
},
];
const result = maskSecrets(text, locations);
- expect(result.masked).toContain("[[API_KEY_OPENAI_1]]");
- expect(result.masked).toContain("[[API_KEY_OPENAI_2]]");
+ expect(result.masked).toContain("[[API_KEY_SK_1]]");
+ expect(result.masked).toContain("[[API_KEY_SK_2]]");
});
test("tracks different secret types separately", () => {
const awsKey = "AKIAIOSFODNN7EXAMPLE";
const text = `OpenAI: ${sampleSecret} AWS: ${awsKey}`;
const locations: SecretLocation[] = [
- { start: 8, end: 8 + sampleSecret.length, type: "API_KEY_OPENAI" },
+ { start: 8, end: 8 + sampleSecret.length, type: "API_KEY_SK" },
{
start: 8 + sampleSecret.length + 6,
end: 8 + sampleSecret.length + 6 + awsKey.length,
];
const result = maskSecrets(text, locations);
- expect(result.masked).toContain("[[API_KEY_OPENAI_1]]");
+ expect(result.masked).toContain("[[API_KEY_SK_1]]");
expect(result.masked).toContain("[[API_KEY_AWS_1]]");
});
+
+ test("masks sk_ prefix keys (Stripe)", () => {
+ const text = `Stripe key: ${stripeSecret}`;
+ const locations: SecretLocation[] = [
+ { start: 12, end: 12 + stripeSecret.length, type: "API_KEY_SK" },
+ ];
+ const result = maskSecrets(text, locations);
+
+ expect(result.masked).toBe("Stripe key: [[API_KEY_SK_1]]");
+ expect(result.context.mapping["[[API_KEY_SK_1]]"]).toBe(stripeSecret);
+ });
});
describe("maskRequest with MessageSecretsResult", () => {
]);
// spanLocations[0] = first message (user), spanLocations[1] = second message (assistant)
const detection = createSecretsResultFromSpans([
- [{ start: 10, end: 10 + sampleSecret.length, type: "API_KEY_OPENAI" }],
+ [{ start: 10, end: 10 + sampleSecret.length, type: "API_KEY_SK" }],
[],
]);
const { masked, context } = maskRequest(request, detection, openaiExtractor);
- expect(masked.messages[0].content).toContain("[[API_KEY_OPENAI_1]]");
+ expect(masked.messages[0].content).toContain("[[API_KEY_SK_1]]");
expect(masked.messages[0].content).not.toContain(sampleSecret);
expect(masked.messages[1].content).toBe("I'll help you with that.");
expect(Object.keys(context.mapping)).toHaveLength(1);
{ role: "user", content: `Key2: ${sampleSecret}` },
]);
const detection = createSecretsResultFromSpans([
- [{ start: 6, end: 6 + sampleSecret.length, type: "API_KEY_OPENAI" }],
- [{ start: 6, end: 6 + sampleSecret.length, type: "API_KEY_OPENAI" }],
+ [{ start: 6, end: 6 + sampleSecret.length, type: "API_KEY_SK" }],
+ [{ start: 6, end: 6 + sampleSecret.length, type: "API_KEY_SK" }],
]);
const { masked, context } = maskRequest(request, detection, openaiExtractor);
- expect(masked.messages[0].content).toBe("Key1: [[API_KEY_OPENAI_1]]");
- expect(masked.messages[1].content).toBe("Key2: [[API_KEY_OPENAI_1]]");
+ expect(masked.messages[0].content).toBe("Key1: [[API_KEY_SK_1]]");
+ expect(masked.messages[1].content).toBe("Key2: [[API_KEY_SK_1]]");
expect(Object.keys(context.mapping)).toHaveLength(1);
});
]);
// Two spans: text content at index 0, image is skipped
const detection = createSecretsResultFromSpans([
- [{ start: 5, end: 5 + sampleSecret.length, type: "API_KEY_OPENAI" }],
+ [{ start: 5, end: 5 + sampleSecret.length, type: "API_KEY_SK" }],
]);
const { masked } = maskRequest(request, detection, openaiExtractor);
const content = masked.messages[0].content as Array<{ type: string; text?: string }>;
- expect(content[0].text).toBe("Key: [[API_KEY_OPENAI_1]]");
+ expect(content[0].text).toBe("Key: [[API_KEY_SK_1]]");
expect(content[1].type).toBe("image_url");
});
});
describe("streaming with secrets placeholders", () => {
test("buffers partial [[ placeholder", () => {
const context = createSecretsMaskingContext();
- context.mapping["[[API_KEY_OPENAI_1]]"] = sampleSecret;
+ context.mapping["[[API_KEY_SK_1]]"] = sampleSecret;
const { output, remainingBuffer } = unmaskSecretsStreamChunk("", "Key: [[API_KEY", context);
test("completes buffered placeholder across chunks", () => {
const context = createSecretsMaskingContext();
- context.mapping["[[API_KEY_OPENAI_1]]"] = sampleSecret;
+ context.mapping["[[API_KEY_SK_1]]"] = sampleSecret;
const { output, remainingBuffer } = unmaskSecretsStreamChunk(
"[[API_KEY",
- "_OPENAI_1]] done",
+ "_SK_1]] done",
context,
);
{
start: originalText.indexOf(sampleSecret),
end: originalText.indexOf(sampleSecret) + sampleSecret.length,
- type: "API_KEY_OPENAI",
+ type: "API_KEY_SK",
},
];
const { masked, context } = maskSecrets(originalText, locations);
expect(masked).not.toContain(sampleSecret);
- expect(masked).toContain("[[API_KEY_OPENAI_1]]");
+ expect(masked).toContain("[[API_KEY_SK_1]]");
const restored = unmaskSecrets(masked, context);
expect(restored).toBe(originalText);
describe("unmaskSecretsResponse", () => {
test("unmasks all choices in response", () => {
const context = createSecretsMaskingContext();
- context.mapping["[[API_KEY_OPENAI_1]]"] = sampleSecret;
+ context.mapping["[[API_KEY_SK_1]]"] = sampleSecret;
const response: OpenAIResponse = {
id: "test",
index: 0,
message: {
role: "assistant",
- content: "Your key is [[API_KEY_OPENAI_1]]",
+ content: "Your key is [[API_KEY_SK_1]]",
},
finish_reason: "stop",
},
test("reuses placeholder for duplicate secret values", () => {
const text = `Key1: ${sampleSecret} Key2: ${sampleSecret}`;
const locations: SecretLocation[] = [
- { start: 6, end: 6 + sampleSecret.length, type: "API_KEY_OPENAI" },
+ { start: 6, end: 6 + sampleSecret.length, type: "API_KEY_SK" },
{
start: 6 + sampleSecret.length + 7,
end: 6 + sampleSecret.length * 2 + 7,
- type: "API_KEY_OPENAI",
+ type: "API_KEY_SK",
},
];
const result = maskSecrets(text, locations);
- expect(result.masked).toBe("Key1: [[API_KEY_OPENAI_1]] Key2: [[API_KEY_OPENAI_1]]");
+ expect(result.masked).toBe("Key1: [[API_KEY_SK_1]] Key2: [[API_KEY_SK_1]]");
expect(Object.keys(result.context.mapping)).toHaveLength(1);
});
maskSecrets(
`Key: ${sampleSecret}`,
- [{ start: 5, end: 5 + sampleSecret.length, type: "API_KEY_OPENAI" }],
+ [{ start: 5, end: 5 + sampleSecret.length, type: "API_KEY_SK" }],
context,
);
const anotherSecret = "sk-proj-xyz789abc123def456ghi789jkl012mno345pqr678";
const result2 = maskSecrets(
`Another: ${anotherSecret}`,
- [{ start: 9, end: 9 + anotherSecret.length, type: "API_KEY_OPENAI" }],
+ [{ start: 9, end: 9 + anotherSecret.length, type: "API_KEY_SK" }],
context,
);
- expect(result2.masked).toBe("Another: [[API_KEY_OPENAI_2]]");
+ expect(result2.masked).toBe("Another: [[API_KEY_SK_2]]");
expect(Object.keys(context.mapping)).toHaveLength(2);
});
});