@ogcio/o11y-sdk-node
Version:
Opentelemetry standard instrumentation SDK for NodeJS based project
142 lines (119 loc) • 4.08 kB
text/typescript
import { describe, expect, it, vi, beforeEach } from "vitest";
import {
_cleanStringPII,
_cleanLogBodyPII,
} from "../../lib/internals/pii-detection.js";
import * as sharedMetrics from "../../lib/internals/shared-metrics.js";
describe("PII Detection Utils", () => {
const mockMetricAdd = vi.fn();
beforeEach(() => {
vi.restoreAllMocks();
vi.spyOn(sharedMetrics, "_getPIICounterRedactionMetric").mockReturnValue({
add: mockMetricAdd,
});
});
describe("_cleanStringPII", () => {
it("redacts plain email", () => {
const input = "admin@example.com";
const output = _cleanStringPII(input, "log");
expect(output).toBe("[REDACTED EMAIL]");
expect(mockMetricAdd).toHaveBeenCalledWith(
1,
expect.objectContaining({
pii_email_domain: "example.com",
pii_type: "email",
redaction_source: "log",
}),
);
});
it("redacts email in URL-encoded string", () => {
const input = "user%40gmail.com";
const output = _cleanStringPII(input, "log");
expect(output).toBe("[REDACTED EMAIL]");
expect(mockMetricAdd).toHaveBeenCalledWith(
1,
expect.objectContaining({
pii_format: "url",
pii_email_domain: "gmail.com",
}),
);
});
it("handles strings without email unchanged", () => {
const input = "hello world";
const output = _cleanStringPII(input, "log");
expect(output).toBe("hello world");
expect(mockMetricAdd).not.toHaveBeenCalled();
});
it("handles array of strings", () => {
const input = ["one@gmail.com", "two@example.com"];
const output = _cleanStringPII(input, "log");
expect(output).toEqual(["[REDACTED EMAIL]", "[REDACTED EMAIL]"]);
expect(mockMetricAdd).toHaveBeenCalledTimes(2);
});
it("ignores non-string input", () => {
expect(_cleanStringPII(1234, "trace")).toBe(1234);
expect(_cleanStringPII(true, "trace")).toBe(true);
expect(_cleanStringPII(undefined, "trace")).toBeUndefined();
expect(mockMetricAdd).not.toHaveBeenCalled();
});
});
describe("_cleanLogBodyPII", () => {
it("cleans string email", () => {
const result = _cleanLogBodyPII("demo@abc.com");
expect(result).toBe("[REDACTED EMAIL]");
});
it("cleans deeply nested object", () => {
const input = {
user: {
email: "test@gmail.com",
profile: {
contact: "foo@example.com",
},
},
status: "active",
};
const result = _cleanLogBodyPII(input);
expect(result).toEqual({
user: {
email: "[REDACTED EMAIL]",
profile: {
contact: "[REDACTED EMAIL]",
},
},
status: "active",
});
});
it("cleans Uint8Array input", () => {
const str = "admin@gmail.com";
const buffer = new TextEncoder().encode(str);
const result = _cleanLogBodyPII(buffer);
const decoded = new TextDecoder().decode(result as Uint8Array);
expect(decoded).toBe("[REDACTED EMAIL]");
});
it("skips malformed Uint8Array decode", () => {
const corrupted = new Uint8Array([0xff, 0xfe, 0xfd]);
const result = _cleanLogBodyPII(corrupted);
// Should return a Uint8Array, but unmodified/redaction should not happen
expect(result).toBeInstanceOf(Uint8Array);
expect(result).not.toEqual(expect.arrayContaining([91, 82, 69]));
});
it("cleans arrays of values", () => {
const result = _cleanLogBodyPII([
"bob@abc.com",
123,
{ nested: "jane@example.com" },
]);
expect(result).toEqual([
"[REDACTED EMAIL]",
123,
{ nested: "[REDACTED EMAIL]" },
]);
});
it("passes null and boolean through", () => {
expect(_cleanLogBodyPII(null)).toBeNull();
expect(_cleanLogBodyPII(undefined)).toBeUndefined();
expect(_cleanLogBodyPII(true)).toBe(true);
expect(_cleanLogBodyPII(false)).toBe(false);
});
});
});