| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 |
- import { describe, expect, test } from "bun:test"
- import { HttpBody, HttpClientRequest } from "effect/unstable/http"
- import { redactedErrorRequest } from "../src/http/recorder"
- import { make, redactHeaders, redactUrl } from "../src/redaction/redactor"
- import { secretFindings } from "../src/redaction/secrets"
- describe("redaction", () => {
- test("redacts sensitive URL query parameters", () => {
- expect(
- redactUrl(
- "https://example.test/path?key=secret-google-key&api_key=secret-openai-key&safe=value&X-Amz-Signature=secret-signature",
- ),
- ).toBe(
- "https://example.test/path?key=%5BREDACTED%5D&api_key=%5BREDACTED%5D&safe=value&X-Amz-Signature=%5BREDACTED%5D",
- )
- })
- test("redacts URL credentials", () => {
- expect(redactUrl("https://user:password@example.test/path?safe=value")).toBe(
- "https://%5BREDACTED%5D:%5BREDACTED%5D@example.test/path?safe=value",
- )
- })
- test("applies custom URL redaction after built-in redaction", () => {
- expect(
- redactUrl("https://example.test/accounts/real-account/path?key=secret-key", undefined, (url) =>
- url.replace("/accounts/real-account/", "/accounts/{account}/"),
- ),
- ).toBe("https://example.test/accounts/{account}/path?key=%5BREDACTED%5D")
- })
- test("redacts sensitive headers when allow-listed", () => {
- expect(
- redactHeaders(
- {
- authorization: "Bearer secret-token",
- "content-type": "application/json",
- "x-custom-token": "custom-secret",
- "x-api-key": "secret-key",
- "x-goog-api-key": "secret-google-key",
- },
- ["authorization", "content-type", "x-api-key", "x-goog-api-key", "x-custom-token"],
- ["x-custom-token"],
- ),
- ).toEqual({
- authorization: "[REDACTED]",
- "content-type": "application/json",
- "x-api-key": "[REDACTED]",
- "x-custom-token": "[REDACTED]",
- "x-goog-api-key": "[REDACTED]",
- })
- })
- test("redacts error requests without retaining headers, params, or body", () => {
- const request = HttpClientRequest.post("https://example.test/path", {
- headers: { authorization: "Bearer super-secret" },
- body: HttpBody.text("super-secret-body", "text/plain"),
- }).pipe(HttpClientRequest.setUrlParam("api_key", "super-secret-key"))
- expect(redactedErrorRequest(request).toJSON()).toMatchObject({
- url: "https://example.test/path",
- urlParams: { params: [] },
- headers: {},
- body: { _tag: "Empty" },
- })
- })
- test("detects secret-looking values without returning the secret", () => {
- expect(
- secretFindings({
- version: 1,
- interactions: [
- {
- transport: "http",
- request: {
- method: "POST",
- url: "https://example.test/path?key=sk-123456789012345678901234",
- headers: {},
- body: JSON.stringify({
- nested: "AIzaSyDHibiBRvJZLsFnPYPoiTwxY4ztQ55yqCE",
- }),
- },
- response: {
- status: 200,
- headers: {},
- body: "Bearer abcdefghijklmnopqrstuvwxyz",
- },
- },
- ],
- }),
- ).toEqual([
- { path: "interactions[0].request.url", reason: "API key" },
- { path: "interactions[0].request.body", reason: "Google API key" },
- { path: "interactions[0].response.body", reason: "bearer token" },
- ])
- })
- test("detects secret-looking values inside metadata", () => {
- expect(
- secretFindings({
- version: 1,
- metadata: { token: "sk-123456789012345678901234" },
- interactions: [],
- }),
- ).toEqual([{ path: "metadata.token", reason: "API key" }])
- })
- test("redacts configured and common sensitive JSON fields", () => {
- const redactor = make({
- jsonFields: ["account_id"],
- })
- const request = redactor.request({
- method: "POST",
- url: "https://example.test/path",
- headers: { "content-type": "application/json" },
- body: JSON.stringify({
- password: "secret-password",
- accessToken: "access-token",
- nested: { account_id: "account-123", safe: "visible" },
- }),
- })
- expect(JSON.parse(request.body)).toEqual({
- password: "[REDACTED]",
- accessToken: "[REDACTED]",
- nested: { account_id: "[REDACTED]", safe: "visible" },
- })
- })
- test("preserves JSON text when no fields are redacted", () => {
- const body = '{\n "id": 9007199254740993,\n "safe": true\n}'
- expect(
- make().request({
- method: "POST",
- url: "https://example.test/path",
- headers: { "content-type": "application/json" },
- body,
- }).body,
- ).toBe(body)
- })
- test("extends default header redaction and allow lists", () => {
- const redactor = make({
- headers: ["x-custom-token"],
- allowRequestHeaders: ["anthropic-version", "x-custom-token"],
- })
- expect(
- redactor.request({
- method: "GET",
- url: "https://example.test/path",
- headers: {
- authorization: "Bearer secret",
- "content-type": "application/json",
- "anthropic-version": "2023-06-01",
- "x-custom-token": "secret",
- },
- body: "",
- }).headers,
- ).toEqual({
- "anthropic-version": "2023-06-01",
- "content-type": "application/json",
- "x-custom-token": "[REDACTED]",
- })
- })
- })
|