Remove SECRET_MASKED_ prefix from secrets placeholders
authorStefan Gasser <redacted>
Mon, 26 Jan 2026 07:15:59 +0000 (08:15 +0100)
committerStefan Gasser <redacted>
Mon, 26 Jan 2026 07:20:19 +0000 (08:20 +0100)
Simplify placeholder format by using [[TYPE_N]] for both PII and secrets.
This makes the format consistent and shorter.

Before: [[SECRET_MASKED_API_KEY_OPENAI_1]]
After:  [[API_KEY_OPENAI_1]]

src/masking/placeholders.test.ts
src/masking/placeholders.ts
src/routes/api.test.ts
src/routes/api.ts
src/secrets/mask.test.ts

index 39d4bb965e99158ab5670427fc6c2801a83cb7f2..d46dee2cf49a2b63ff50042fb28b532775c3e9e4 100644 (file)
@@ -23,7 +23,7 @@ describe("placeholder constants", () => {
   test("secret format uses correct delimiters", () => {
     expect(SECRET_PLACEHOLDER_FORMAT).toContain(PLACEHOLDER_DELIMITERS.start);
     expect(SECRET_PLACEHOLDER_FORMAT).toContain(PLACEHOLDER_DELIMITERS.end);
-    expect(SECRET_PLACEHOLDER_FORMAT).toBe("[[SECRET_MASKED_{N}]]");
+    expect(SECRET_PLACEHOLDER_FORMAT).toBe("[[{N}]]");
   });
 });
 
@@ -42,12 +42,12 @@ describe("generatePlaceholder", () => {
 describe("generateSecretPlaceholder", () => {
   test("generates secret placeholder", () => {
     const result = generateSecretPlaceholder("API_KEY_OPENAI", 1);
-    expect(result).toBe("[[SECRET_MASKED_API_KEY_OPENAI_1]]");
+    expect(result).toBe("[[API_KEY_OPENAI_1]]");
   });
 
   test("generates secret placeholder with different type and count", () => {
     const result = generateSecretPlaceholder("PEM_PRIVATE_KEY", 2);
-    expect(result).toBe("[[SECRET_MASKED_PEM_PRIVATE_KEY_2]]");
+    expect(result).toBe("[[PEM_PRIVATE_KEY_2]]");
   });
 });
 
index 149c37d84b111ab2b3d4e69c41cb7bfcb5dc04b7..66f38b233e24b5fb44b08968f5c948fe3726d63f 100644 (file)
@@ -10,8 +10,8 @@ export const PLACEHOLDER_DELIMITERS = {
 /** PII placeholder format: [[TYPE_N]] e.g. [[PERSON_1]], [[EMAIL_ADDRESS_2]] */
 export const PII_PLACEHOLDER_FORMAT = "[[{TYPE}_{N}]]";
 
-/** Secrets placeholder format: [[SECRET_MASKED_TYPE_N]] e.g. [[SECRET_MASKED_API_KEY_OPENAI_1]] */
-export const SECRET_PLACEHOLDER_FORMAT = "[[SECRET_MASKED_{N}]]";
+/** Secrets placeholder format: [[TYPE_N]] e.g. [[API_KEY_OPENAI_1]] */
+export const SECRET_PLACEHOLDER_FORMAT = "[[{N}]]";
 
 /**
  * Generates a placeholder string from the format
index 589e241e27321b0e485664d6394fc5929b426195..2e0718752ffe9e3bf298db006506f86a456c5a2f 100644 (file)
@@ -163,7 +163,7 @@ describe("POST /api/mask", () => {
       masked: string;
       entities: { type: string }[];
     };
-    expect(body.masked).toContain("[[SECRET_MASKED_PEM_PRIVATE_KEY_1]]");
+    expect(body.masked).toContain("[[PEM_PRIVATE_KEY_1]]");
     expect(body.entities.some((e) => e.type === "PEM_PRIVATE_KEY")).toBe(true);
   });
 
@@ -209,11 +209,11 @@ describe("POST /api/mask", () => {
 
     // Both PII and secrets should be masked
     expect(body.masked).toContain("[[EMAIL_ADDRESS_1]]");
-    expect(body.masked).toContain("[[SECRET_MASKED_PEM_PRIVATE_KEY_1]]");
+    expect(body.masked).toContain("[[PEM_PRIVATE_KEY_1]]");
 
     // Context should contain mappings for both
     expect(body.context["[[EMAIL_ADDRESS_1]]"]).toBe("john@example.com");
-    expect(body.context["[[SECRET_MASKED_PEM_PRIVATE_KEY_1]]"]).toBeDefined();
+    expect(body.context["[[PEM_PRIVATE_KEY_1]]"]).toBeDefined();
 
     // Entities should include both types
     expect(body.entities.some((e) => e.type === "EMAIL_ADDRESS")).toBe(true);
index fe4205de7dfa4f44ae37d6d71e817941fd7aa888..4c79863adeea96609d16e44c5e2715dc711101e6 100644 (file)
@@ -50,7 +50,6 @@ interface MaskResponse {
 function extractEntities(
   countersBefore: Record<string, number>,
   context: PlaceholderContext,
-  isSecret: boolean,
 ): MaskEntity[] {
   const entities: MaskEntity[] = [];
 
@@ -58,8 +57,8 @@ function extractEntities(
     const startCount = countersBefore[type] || 0;
     // Add entities for each new placeholder created
     for (let i = startCount + 1; i <= count; i++) {
-      // Build placeholder directly using known format
-      const placeholder = isSecret ? `[[SECRET_MASKED_${type}_${i}]]` : `[[${type}_${i}]]`;
+      // Build placeholder directly using known format (same for PII and secrets)
+      const placeholder = `[[${type}_${i}]]`;
 
       if (context.mapping[placeholder]) {
         entities.push({ type, placeholder });
@@ -152,7 +151,7 @@ apiRoutes.post("/mask", async (c) => {
       const countersBefore = { ...context.counters };
       const piiResult = maskPII(maskedText, filteredEntities, context);
       maskedText = piiResult.masked;
-      allEntities.push(...extractEntities(countersBefore, piiResult.context, false));
+      allEntities.push(...extractEntities(countersBefore, piiResult.context));
 
       // Collect unique entity types for logging
       for (const entity of filteredEntities) {
@@ -206,7 +205,7 @@ apiRoutes.post("/mask", async (c) => {
         const countersBefore = { ...context.counters };
         const secretsMaskResult = maskSecrets(maskedText, secretsResult.locations, context);
         maskedText = secretsMaskResult.masked;
-        allEntities.push(...extractEntities(countersBefore, secretsMaskResult.context, true));
+        allEntities.push(...extractEntities(countersBefore, secretsMaskResult.context));
 
         // Collect unique secret types for logging
         for (const match of secretsResult.matches) {
index 2fc7e3d5136800a60e30671f6d6ca8748802f874..3bd85646627e484ad027001771683ef4b35da330 100644 (file)
@@ -21,14 +21,14 @@ function createRequest(messages: OpenAIMessage[]): OpenAIRequest {
 }
 
 describe("secrets placeholder format", () => {
-  test("uses [[SECRET_MASKED_TYPE_N]] format", () => {
+  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" },
     ];
     const result = maskSecrets(text, locations);
 
-    expect(result.masked).toBe("My API key is [[SECRET_MASKED_API_KEY_OPENAI_1]]");
+    expect(result.masked).toBe("My API key is [[API_KEY_OPENAI_1]]");
   });
 
   test("increments counter per secret type", () => {
@@ -44,8 +44,8 @@ describe("secrets placeholder format", () => {
     ];
     const result = maskSecrets(text, locations);
 
-    expect(result.masked).toContain("[[SECRET_MASKED_API_KEY_OPENAI_1]]");
-    expect(result.masked).toContain("[[SECRET_MASKED_API_KEY_OPENAI_2]]");
+    expect(result.masked).toContain("[[API_KEY_OPENAI_1]]");
+    expect(result.masked).toContain("[[API_KEY_OPENAI_2]]");
   });
 
   test("tracks different secret types separately", () => {
@@ -61,8 +61,8 @@ describe("secrets placeholder format", () => {
     ];
     const result = maskSecrets(text, locations);
 
-    expect(result.masked).toContain("[[SECRET_MASKED_API_KEY_OPENAI_1]]");
-    expect(result.masked).toContain("[[SECRET_MASKED_API_KEY_AWS_1]]");
+    expect(result.masked).toContain("[[API_KEY_OPENAI_1]]");
+    expect(result.masked).toContain("[[API_KEY_AWS_1]]");
   });
 });
 
@@ -80,7 +80,7 @@ describe("maskRequest with MessageSecretsResult", () => {
 
     const { masked, context } = maskRequest(request, detection, openaiExtractor);
 
-    expect(masked.messages[0].content).toContain("[[SECRET_MASKED_API_KEY_OPENAI_1]]");
+    expect(masked.messages[0].content).toContain("[[API_KEY_OPENAI_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);
@@ -98,8 +98,8 @@ describe("maskRequest with MessageSecretsResult", () => {
 
     const { masked, context } = maskRequest(request, detection, openaiExtractor);
 
-    expect(masked.messages[0].content).toBe("Key1: [[SECRET_MASKED_API_KEY_OPENAI_1]]");
-    expect(masked.messages[1].content).toBe("Key2: [[SECRET_MASKED_API_KEY_OPENAI_1]]");
+    expect(masked.messages[0].content).toBe("Key1: [[API_KEY_OPENAI_1]]");
+    expect(masked.messages[1].content).toBe("Key2: [[API_KEY_OPENAI_1]]");
     expect(Object.keys(context.mapping)).toHaveLength(1);
   });
 
@@ -121,29 +121,29 @@ describe("maskRequest with MessageSecretsResult", () => {
     const { masked } = maskRequest(request, detection, openaiExtractor);
 
     const content = masked.messages[0].content as Array<{ type: string; text?: string }>;
-    expect(content[0].text).toBe("Key: [[SECRET_MASKED_API_KEY_OPENAI_1]]");
+    expect(content[0].text).toBe("Key: [[API_KEY_OPENAI_1]]");
     expect(content[1].type).toBe("image_url");
   });
 });
 
 describe("streaming with secrets placeholders", () => {
-  test("buffers partial [[SECRET_MASKED placeholder", () => {
+  test("buffers partial [[ placeholder", () => {
     const context = createSecretsMaskingContext();
-    context.mapping["[[SECRET_MASKED_API_KEY_OPENAI_1]]"] = sampleSecret;
+    context.mapping["[[API_KEY_OPENAI_1]]"] = sampleSecret;
 
-    const { output, remainingBuffer } = unmaskSecretsStreamChunk("", "Key: [[SECRET_MAS", context);
+    const { output, remainingBuffer } = unmaskSecretsStreamChunk("", "Key: [[API_KEY", context);
 
     expect(output).toBe("Key: ");
-    expect(remainingBuffer).toBe("[[SECRET_MAS");
+    expect(remainingBuffer).toBe("[[API_KEY");
   });
 
   test("completes buffered placeholder across chunks", () => {
     const context = createSecretsMaskingContext();
-    context.mapping["[[SECRET_MASKED_API_KEY_OPENAI_1]]"] = sampleSecret;
+    context.mapping["[[API_KEY_OPENAI_1]]"] = sampleSecret;
 
     const { output, remainingBuffer } = unmaskSecretsStreamChunk(
-      "[[SECRET_MAS",
-      "KED_API_KEY_OPENAI_1]] done",
+      "[[API_KEY",
+      "_OPENAI_1]] done",
       context,
     );
 
@@ -153,8 +153,8 @@ describe("streaming with secrets placeholders", () => {
 
   test("flushes incomplete buffer as-is", () => {
     const context = createSecretsMaskingContext();
-    const result = flushSecretsMaskingBuffer("[[SECRET_MAS", context);
-    expect(result).toBe("[[SECRET_MAS");
+    const result = flushSecretsMaskingBuffer("[[API_KEY", context);
+    expect(result).toBe("[[API_KEY");
   });
 });
 
@@ -176,7 +176,7 @@ Please store them securely.
     const { masked, context } = maskSecrets(originalText, locations);
 
     expect(masked).not.toContain(sampleSecret);
-    expect(masked).toContain("[[SECRET_MASKED_API_KEY_OPENAI_1]]");
+    expect(masked).toContain("[[API_KEY_OPENAI_1]]");
 
     const restored = unmaskSecrets(masked, context);
     expect(restored).toBe(originalText);
@@ -186,7 +186,7 @@ Please store them securely.
 describe("unmaskSecretsResponse", () => {
   test("unmasks all choices in response", () => {
     const context = createSecretsMaskingContext();
-    context.mapping["[[SECRET_MASKED_API_KEY_OPENAI_1]]"] = sampleSecret;
+    context.mapping["[[API_KEY_OPENAI_1]]"] = sampleSecret;
 
     const response: OpenAIResponse = {
       id: "test",
@@ -198,7 +198,7 @@ describe("unmaskSecretsResponse", () => {
           index: 0,
           message: {
             role: "assistant",
-            content: "Your key is [[SECRET_MASKED_API_KEY_OPENAI_1]]",
+            content: "Your key is [[API_KEY_OPENAI_1]]",
           },
           finish_reason: "stop",
         },
@@ -253,9 +253,7 @@ describe("edge cases", () => {
     ];
     const result = maskSecrets(text, locations);
 
-    expect(result.masked).toBe(
-      "Key1: [[SECRET_MASKED_API_KEY_OPENAI_1]] Key2: [[SECRET_MASKED_API_KEY_OPENAI_1]]",
-    );
+    expect(result.masked).toBe("Key1: [[API_KEY_OPENAI_1]] Key2: [[API_KEY_OPENAI_1]]");
     expect(Object.keys(result.context.mapping)).toHaveLength(1);
   });
 
@@ -275,7 +273,7 @@ describe("edge cases", () => {
       context,
     );
 
-    expect(result2.masked).toBe("Another: [[SECRET_MASKED_API_KEY_OPENAI_2]]");
+    expect(result2.masked).toBe("Another: [[API_KEY_OPENAI_2]]");
     expect(Object.keys(context.mapping)).toHaveLength(2);
   });
 });
git clone https://git.99rst.org/PROJECT