import { describe, expect, it } from
"vitest" ;
import {
getDefaultRedactPatterns,
redactSensitiveLines,
redactSensitiveText,
resolveRedactOptions,
} from
"./redact.js" ;
const defaults = getDefaultRedactPatterns();
describe(
"redactSensitiveText" , () => {
it(
"masks env assignments while keeping the key" , () => {
const input =
"OPENAI_API_KEY=sk-1234567890abcdef" ;
const output = redactSensitiveText(input, {
mode:
"tools" ,
patterns: defaults,
});
expect(output).toBe(
"OPENAI_API_KEY=sk-123…cdef" );
});
it(
"masks CLI flags" , () => {
const input =
"curl --token abcdef1234567890ghij https://api.test ";
const output = redactSensitiveText(input, {
mode:
"tools" ,
patterns: defaults,
});
expect(output).toBe(
"curl --token abcdef…ghij https://api.test ");
});
it(
"masks hook token CLI flags" , () => {
const input =
"gog gmail watch serve --hook-token abcdef1234567890ghij" ;
const output = redactSensitiveText(input, {
mode:
"tools" ,
patterns: defaults,
});
expect(output).toBe(
"gog gmail watch serve --hook-token abcdef…ghij" );
});
it(
"masks JSON fields" , () => {
const input =
'{"token":"abcdef1234567890ghij"}' ;
const output = redactSensitiveText(input, {
mode:
"tools" ,
patterns: defaults,
});
expect(output).toBe(
'{"token":"abcdef…ghij"}' );
});
it(
"masks bearer tokens" , () => {
const input =
"Authorization: Bearer abcdef1234567890ghij" ;
const output = redactSensitiveText(input, {
mode:
"tools" ,
patterns: defaults,
});
expect(output).toBe(
"Authorization: Bearer abcdef…ghij" );
});
it(
"masks bot-style tokens" , () => {
const input =
"123456:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef" ;
const output = redactSensitiveText(input, {
mode:
"tools" ,
patterns: defaults,
});
expect(output).toBe(
"123456…cdef" );
});
it(
"masks bot API URL tokens" , () => {
const input =
"GET https://api.example.test/bot123456:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef/getMe HTTP/1.1";
const output = redactSensitiveText(input, {
mode:
"tools" ,
patterns: defaults,
});
expect(output).toBe(
"GET https://api.example.test/bot123456 …cdef/getMe HTTP/1.1");
});
it(
"redacts short tokens fully" , () => {
const input =
"TOKEN=shortvalue" ;
const output = redactSensitiveText(input, {
mode:
"tools" ,
patterns: defaults,
});
expect(output).toBe(
"TOKEN=***" );
});
it(
"redacts private key blocks" , () => {
const input = [
"-----BEGIN PRIVATE KEY-----" ,
"ABCDEF1234567890" ,
"ZYXWVUT987654321" ,
"-----END PRIVATE KEY-----" ,
].join(
"\n" );
const output = redactSensitiveText(input, {
mode:
"tools" ,
patterns: defaults,
});
expect(output).toBe(
[
"-----BEGIN PRIVATE KEY-----" ,
"…redacted…" ,
"-----END PRIVATE KEY-----" ].join(
"\n" ),
);
});
it(
"honors custom patterns with flags" , () => {
const input =
"token=abcdef1234567890ghij" ;
const output = redactSensitiveText(input, {
mode:
"tools" ,
patterns: [
"/token=([A-Za-z0-9]+)/i" ],
});
expect(output).toBe(
"token=abcdef…ghij" );
});
it(
"ignores unsafe nested-repetition custom patterns" , () => {
const input = `${
"a" .repeat(
28 )}!`;
const output = redactSensitiveText(input, {
mode:
"tools" ,
patterns: [
"(a+)+$" ],
});
expect(output).toBe(input);
});
it(
"redacts large payloads with bounded regex passes" , () => {
const input = `${
"x" .repeat(
40 _
000 )} OPENAI_API_KEY=sk-
1234567890 abcdef ${
"y" .repeat(
40 _
000 )}`;
const output = redactSensitiveText(input, {
mode:
"tools" ,
patterns: defaults,
});
expect(output).toContain(
"OPENAI_API_KEY=sk-123…cdef" );
});
it(
"skips redaction when mode is off" , () => {
const input =
"OPENAI_API_KEY=sk-1234567890abcdef" ;
const output = redactSensitiveText(input, {
mode:
"off" ,
patterns: defaults,
});
expect(output).toBe(input);
});
it(
"does not resolve patterns when mode is off" , () => {
const options = {
mode:
"off" as
const ,
get patterns(): never {
throw new Error(
"patterns should not be read when redaction is off" );
},
};
expect(resolveRedactOptions(options)).toEqual({
mode:
"off" ,
patterns: [],
});
expect(redactSensitiveText(
"OPENAI_API_KEY=sk-1234567890abcdef" , options)).toBe(
"OPENAI_API_KEY=sk-1234567890abcdef" ,
);
});
it(
"reuses compiled global regex patterns" , () => {
const pattern = /token=([A-Za-z0-
9 ]+)/g;
const resolved = resolveRedactOptions({
mode:
"tools" ,
patterns: [pattern],
});
expect(resolved.patterns).toHaveLength(
1 );
expect(resolved.patterns[
0 ]).toBe(pattern);
});
});
describe(
"redactSensitiveLines" , () => {
it(
"redacts matching content across all lines" , () => {
const resolved = resolveRedactOptions({ mode:
"tools" , patterns: defaults });
const lines = [
"curl --token abcdef1234567890ghij https://api.test ", "normal log line"];
const result = redactSensitiveLines(lines, resolved);
expect(result[
0 ]).toBe(
"curl --token abcdef…ghij https://api.test ");
expect(result[
1 ]).toBe(
"normal log line" );
});
it(
"returns lines unmodified when mode is off" , () => {
const resolved = resolveRedactOptions({ mode:
"off" , patterns: defaults });
const lines = [
"TOKEN=abcdef1234567890ghij" ];
expect(redactSensitiveLines(lines, resolved)).toEqual(lines);
});
it(
"returns lines unmodified when resolved patterns is empty — does not fall back to defaults" ,
() => {
// Simulates the case where all user-configured patterns fail to compile.
// The pre-resolved empty array must be honored, not silently replaced with defaults.
const resolved = { mode: "tools" as const , patterns: [] };
const lines = ["TOKEN=abcdef1234567890ghij" ];
expect(redactSensitiveLines(lines, resolved)).toEqual(lines);
});
it("returns empty array unchanged — does not produce a synthetic blank line" , () => {
const resolved = resolveRedactOptions({ mode: "tools" , patterns: defaults });
expect(redactSensitiveLines([], resolved)).toEqual([]);
});
it("redacts a PEM block spanning multiple lines in the array" , () => {
const resolved = resolveRedactOptions({ mode: "tools" , patterns: defaults });
const lines = [
"log: key follows" ,
"-----BEGIN PRIVATE KEY-----" ,
"ABCDEF1234567890" ,
"ZYXWVUT987654321" ,
"-----END PRIVATE KEY-----" ,
"log: key done" ,
];
const result = redactSensitiveLines(lines, resolved);
const joined = result.join("\n" );
expect(joined).toContain("-----BEGIN PRIVATE KEY-----" );
expect(joined).toContain("-----END PRIVATE KEY-----" );
expect(joined).toContain("…redacted…" );
expect(joined).not.toContain("ABCDEF1234567890" );
});
});
Messung V0.5 in Prozent C=97 H=100 G=98
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-06-09)
¤
*© Formatika GbR, Deutschland