Files
stack/apps/api/src/common/utils/redact.util.spec.ts
T
jason.woltjeandClaude Sonnet 4.5 e151d09531 feat(#287): Add redaction utility for sensitive data in logs
Security improvements:
- Create redaction utility to prevent PII leakage in logs
- Redact sensitive fields: privateKey, tokens, passwords, metadata, payloads
- Redact user IDs: convert to "user-***"
- Redact instance IDs: convert to "instance-***"
- Support recursive redaction for nested objects and arrays

Changes:
- Add redact.util.ts with redaction functions
- Add comprehensive test coverage for redaction
- Support for:
  - Sensitive field detection (privateKey, token, etc.)
  - User ID redaction (userId, remoteUserId, localUserId, user.id)
  - Instance ID redaction (instanceId, remoteInstanceId, instance.id)
  - Nested object and array redaction
  - Primitive and null/undefined handling

Next steps:
- Apply redactSensitiveData() to all logger calls in federation services
- Use debug level for detailed logs with sensitive data

Part of M7.1 Remediation Sprint P1 security fixes.

Refs #287

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
2026-02-03 21:52:08 -06:00

143 lines
4.2 KiB
TypeScript

/**
* Redaction Utility Tests
*
* Tests for sensitive data redaction in logs.
*/
import { describe, it, expect } from "vitest";
import { redactSensitiveData, redactUserId, redactInstanceId } from "./redact.util";
describe("Redaction Utilities", () => {
describe("redactUserId", () => {
it("should redact user IDs", () => {
expect(redactUserId("user-12345")).toBe("user-***");
});
it("should handle null/undefined", () => {
expect(redactUserId(null)).toBe("user-***");
expect(redactUserId(undefined)).toBe("user-***");
});
});
describe("redactInstanceId", () => {
it("should redact instance IDs", () => {
expect(redactInstanceId("instance-abc-def")).toBe("instance-***");
});
it("should handle null/undefined", () => {
expect(redactInstanceId(null)).toBe("instance-***");
expect(redactInstanceId(undefined)).toBe("instance-***");
});
});
describe("redactSensitiveData", () => {
it("should redact user IDs", () => {
const data = { userId: "user-123", name: "Test" };
const redacted = redactSensitiveData(data);
expect(redacted.userId).toBe("user-***");
expect(redacted.name).toBe("Test");
});
it("should redact instance IDs", () => {
const data = { instanceId: "instance-456", url: "https://example.com" };
const redacted = redactSensitiveData(data);
expect(redacted.instanceId).toBe("instance-***");
expect(redacted.url).toBe("https://example.com");
});
it("should redact metadata objects", () => {
const data = {
metadata: { secret: "value", public: "data" },
other: "field",
};
const redacted = redactSensitiveData(data);
expect(redacted.metadata).toBe("[REDACTED]");
expect(redacted.other).toBe("field");
});
it("should redact payloads", () => {
const data = {
payload: { command: "execute", args: ["secret"] },
type: "command",
};
const redacted = redactSensitiveData(data);
expect(redacted.payload).toBe("[REDACTED]");
expect(redacted.type).toBe("command");
});
it("should redact private keys", () => {
const data = {
privateKey: "-----BEGIN PRIVATE KEY-----\n...",
publicKey: "-----BEGIN PUBLIC KEY-----\n...",
};
const redacted = redactSensitiveData(data);
expect(redacted.privateKey).toBe("[REDACTED]");
expect(redacted.publicKey).toBe("-----BEGIN PUBLIC KEY-----\n...");
});
it("should redact tokens", () => {
const data = {
token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
oidcToken: "token-value",
};
const redacted = redactSensitiveData(data);
expect(redacted.token).toBe("[REDACTED]");
expect(redacted.oidcToken).toBe("[REDACTED]");
});
it("should handle nested objects", () => {
const data = {
user: {
id: "user-789",
email: "[email protected]",
},
metadata: { nested: "data" },
};
const redacted = redactSensitiveData(data);
expect(redacted.user.id).toBe("user-***");
expect(redacted.user.email).toBe("[email protected]");
expect(redacted.metadata).toBe("[REDACTED]");
});
it("should handle arrays", () => {
const data = {
users: [{ userId: "user-1" }, { userId: "user-2" }],
};
const redacted = redactSensitiveData(data);
expect(redacted.users[0].userId).toBe("user-***");
expect(redacted.users[1].userId).toBe("user-***");
});
it("should preserve non-sensitive data", () => {
const data = {
id: "conn-123",
status: "active",
createdAt: "2024-01-01",
remoteUrl: "https://remote.com",
};
const redacted = redactSensitiveData(data);
expect(redacted).toEqual(data);
});
it("should handle null/undefined", () => {
expect(redactSensitiveData(null)).toBeNull();
expect(redactSensitiveData(undefined)).toBeUndefined();
});
it("should handle primitives", () => {
expect(redactSensitiveData("string")).toBe("string");
expect(redactSensitiveData(123)).toBe(123);
expect(redactSensitiveData(true)).toBe(true);
});
});
});