@zyraxon-ai/http-recorder 19.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +217 -0
- package/dist/cassette.d.ts +36 -0
- package/dist/cassette.d.ts.map +1 -0
- package/dist/cassette.js +105 -0
- package/dist/cassette.js.map +1 -0
- package/dist/effect.d.ts +11 -0
- package/dist/effect.d.ts.map +1 -0
- package/dist/effect.js +18 -0
- package/dist/effect.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/internal-effect.d.ts +19 -0
- package/dist/internal-effect.d.ts.map +1 -0
- package/dist/internal-effect.js +105 -0
- package/dist/internal-effect.js.map +1 -0
- package/dist/internal.d.ts +9 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +9 -0
- package/dist/internal.js.map +1 -0
- package/dist/matching.d.ts +14 -0
- package/dist/matching.d.ts.map +1 -0
- package/dist/matching.js +92 -0
- package/dist/matching.js.map +1 -0
- package/dist/recorder.d.ts +13 -0
- package/dist/recorder.d.ts.map +1 -0
- package/dist/recorder.js +33 -0
- package/dist/recorder.js.map +1 -0
- package/dist/redaction.d.ts +14 -0
- package/dist/redaction.d.ts.map +1 -0
- package/dist/redaction.js +100 -0
- package/dist/redaction.js.map +1 -0
- package/dist/redactor.d.ts +30 -0
- package/dist/redactor.d.ts.map +1 -0
- package/dist/redactor.js +87 -0
- package/dist/redactor.js.map +1 -0
- package/dist/schema.d.ts +330 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +53 -0
- package/dist/schema.js.map +1 -0
- package/dist/socket.d.ts +16 -0
- package/dist/socket.d.ts.map +1 -0
- package/dist/socket.js +194 -0
- package/dist/socket.js.map +1 -0
- package/dist/types.d.ts +101 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/websocket.d.ts +29 -0
- package/dist/websocket.d.ts.map +1 -0
- package/dist/websocket.js +101 -0
- package/dist/websocket.js.map +1 -0
- package/package.json +47 -0
package/dist/internal.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { CassetteNotFoundError, hasCassetteSync, UnsafeCassetteError } from "./cassette.js";
|
|
2
|
+
export { cassetteLayer, recordingLayer } from "./internal-effect.js";
|
|
3
|
+
export { redactHeaders, redactUrl, secretFindings } from "./redaction.js";
|
|
4
|
+
export { socketLayer } from "./socket.js";
|
|
5
|
+
export { makeWebSocketExecutor, } from "./websocket.js";
|
|
6
|
+
export * as Cassette from "./cassette.js";
|
|
7
|
+
export * as Redactor from "./redactor.js";
|
|
8
|
+
export * as HttpRecorderInternal from "./internal.js";
|
|
9
|
+
//# sourceMappingURL=internal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"internal.js","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AAC3F,OAAO,EAAE,aAAa,EAAE,cAAc,EAAmD,MAAM,sBAAsB,CAAA;AACrH,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,cAAc,EAAsB,MAAM,gBAAgB,CAAA;AAC7F,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EACL,qBAAqB,GAKtB,MAAM,gBAAgB,CAAA;AACvB,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AACzC,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AAEzC,OAAO,KAAK,oBAAoB,MAAM,eAAe,CAAA"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Option } from "effect";
|
|
2
|
+
import type { HttpInteraction, RequestMatcher, RequestSnapshot } from "./types.js";
|
|
3
|
+
export declare const decodeJson: (input: unknown, options?: import("effect/SchemaAST").ParseOptions) => Option.Option<unknown>;
|
|
4
|
+
export declare const canonicalizeJson: (value: unknown) => unknown;
|
|
5
|
+
export type { RequestMatcher } from "./types.js";
|
|
6
|
+
export declare const canonicalSnapshot: (snapshot: RequestSnapshot) => string;
|
|
7
|
+
export declare const defaultMatcher: RequestMatcher;
|
|
8
|
+
export declare const safeText: (value: unknown) => string;
|
|
9
|
+
export declare const requestDiff: (expected: RequestSnapshot, received: RequestSnapshot) => ReadonlyArray<string>;
|
|
10
|
+
export declare const selectSequential: (interactions: ReadonlyArray<HttpInteraction>, incoming: RequestSnapshot, match: RequestMatcher, index: number) => {
|
|
11
|
+
readonly interaction: HttpInteraction | undefined;
|
|
12
|
+
readonly detail: string;
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=matching.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matching.d.ts","sourceRoot":"","sources":["../src/matching.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAA;AAEvC,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAGlF,eAAO,MAAM,UAAU,+FAAwC,CAAA;AAK/D,eAAO,MAAM,gBAAgB,GAAI,OAAO,OAAO,KAAG,OAUjD,CAAA;AAED,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhD,eAAO,MAAM,iBAAiB,GAAI,UAAU,eAAe,KAAG,MAS1D,CAAA;AAEJ,eAAO,MAAM,cAAc,EAAE,cACgC,CAAA;AAE7D,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,WAMtC,CAAA;AA4BD,eAAO,MAAM,WAAW,GAAI,UAAU,eAAe,EAAE,UAAU,eAAe,KAAG,aAAa,CAAC,MAAM,CAoBtG,CAAA;AAED,eAAO,MAAM,gBAAgB,GAC3B,cAAc,aAAa,CAAC,eAAe,CAAC,EAC5C,UAAU,eAAe,EACzB,OAAO,cAAc,EACrB,OAAO,MAAM,KACZ;IAAE,QAAQ,CAAC,WAAW,EAAE,eAAe,GAAG,SAAS,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAM9E,CAAA"}
|
package/dist/matching.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { Option, Schema } from "effect";
|
|
2
|
+
import { REDACTED, secretFindings } from "./redaction.js";
|
|
3
|
+
const JsonValue = Schema.fromJsonString(Schema.Unknown);
|
|
4
|
+
export const decodeJson = Schema.decodeUnknownOption(JsonValue);
|
|
5
|
+
const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
6
|
+
export const canonicalizeJson = (value) => {
|
|
7
|
+
if (Array.isArray(value))
|
|
8
|
+
return value.map(canonicalizeJson);
|
|
9
|
+
if (isRecord(value)) {
|
|
10
|
+
return Object.fromEntries(Object.keys(value)
|
|
11
|
+
.toSorted()
|
|
12
|
+
.map((key) => [key, canonicalizeJson(value[key])]));
|
|
13
|
+
}
|
|
14
|
+
return value;
|
|
15
|
+
};
|
|
16
|
+
export const canonicalSnapshot = (snapshot) => JSON.stringify({
|
|
17
|
+
method: snapshot.method,
|
|
18
|
+
url: snapshot.url,
|
|
19
|
+
headers: canonicalizeJson(snapshot.headers),
|
|
20
|
+
body: Option.match(decodeJson(snapshot.body), {
|
|
21
|
+
onNone: () => snapshot.body,
|
|
22
|
+
onSome: canonicalizeJson,
|
|
23
|
+
}),
|
|
24
|
+
});
|
|
25
|
+
export const defaultMatcher = (incoming, recorded) => canonicalSnapshot(incoming) === canonicalSnapshot(recorded);
|
|
26
|
+
export const safeText = (value) => {
|
|
27
|
+
if (value === undefined)
|
|
28
|
+
return "undefined";
|
|
29
|
+
if (secretFindings(value).length > 0)
|
|
30
|
+
return JSON.stringify(REDACTED);
|
|
31
|
+
const text = JSON.stringify(value);
|
|
32
|
+
if (!text)
|
|
33
|
+
return typeof value;
|
|
34
|
+
return text.length > 300 ? `${text.slice(0, 300)}...` : text;
|
|
35
|
+
};
|
|
36
|
+
const jsonBody = (body) => Option.getOrUndefined(decodeJson(body));
|
|
37
|
+
const valueDiffs = (expected, received, base = "$", limit = 8) => {
|
|
38
|
+
if (Object.is(expected, received))
|
|
39
|
+
return [];
|
|
40
|
+
if (isRecord(expected) && isRecord(received)) {
|
|
41
|
+
return [...new Set([...Object.keys(expected), ...Object.keys(received)])]
|
|
42
|
+
.toSorted()
|
|
43
|
+
.flatMap((key) => valueDiffs(expected[key], received[key], `${base}.${key}`, limit))
|
|
44
|
+
.slice(0, limit);
|
|
45
|
+
}
|
|
46
|
+
if (Array.isArray(expected) && Array.isArray(received)) {
|
|
47
|
+
return Array.from({ length: Math.max(expected.length, received.length) }, (_, index) => index)
|
|
48
|
+
.flatMap((index) => valueDiffs(expected[index], received[index], `${base}[${index}]`, limit))
|
|
49
|
+
.slice(0, limit);
|
|
50
|
+
}
|
|
51
|
+
return [`${base} expected ${safeText(expected)}, received ${safeText(received)}`];
|
|
52
|
+
};
|
|
53
|
+
const headerDiffs = (expected, received) => [...new Set([...Object.keys(expected), ...Object.keys(received)])].toSorted().flatMap((key) => {
|
|
54
|
+
if (expected[key] === received[key])
|
|
55
|
+
return [];
|
|
56
|
+
if (expected[key] === undefined)
|
|
57
|
+
return [` ${key} unexpected ${safeText(received[key])}`];
|
|
58
|
+
if (received[key] === undefined)
|
|
59
|
+
return [` ${key} missing expected ${safeText(expected[key])}`];
|
|
60
|
+
return [` ${key} expected ${safeText(expected[key])}, received ${safeText(received[key])}`];
|
|
61
|
+
});
|
|
62
|
+
export const requestDiff = (expected, received) => {
|
|
63
|
+
const lines = [];
|
|
64
|
+
if (expected.method !== received.method) {
|
|
65
|
+
lines.push("method:", ` expected ${expected.method}, received ${received.method}`);
|
|
66
|
+
}
|
|
67
|
+
if (expected.url !== received.url) {
|
|
68
|
+
lines.push("url:", ` expected ${expected.url}`, ` received ${received.url}`);
|
|
69
|
+
}
|
|
70
|
+
const headers = headerDiffs(expected.headers, received.headers);
|
|
71
|
+
if (headers.length > 0)
|
|
72
|
+
lines.push("headers:", ...headers.slice(0, 8));
|
|
73
|
+
const expectedBody = jsonBody(expected.body);
|
|
74
|
+
const receivedBody = jsonBody(received.body);
|
|
75
|
+
const body = expectedBody !== undefined && receivedBody !== undefined
|
|
76
|
+
? valueDiffs(expectedBody, receivedBody).map((line) => ` ${line}`)
|
|
77
|
+
: expected.body === received.body
|
|
78
|
+
? []
|
|
79
|
+
: [` expected ${safeText(expected.body)}, received ${safeText(received.body)}`];
|
|
80
|
+
if (body.length > 0)
|
|
81
|
+
lines.push("body:", ...body);
|
|
82
|
+
return lines;
|
|
83
|
+
};
|
|
84
|
+
export const selectSequential = (interactions, incoming, match, index) => {
|
|
85
|
+
const interaction = interactions[index];
|
|
86
|
+
if (!interaction)
|
|
87
|
+
return { interaction, detail: `interaction ${index + 1} of ${interactions.length} not recorded` };
|
|
88
|
+
if (!match(incoming, interaction.request))
|
|
89
|
+
return { interaction: undefined, detail: requestDiff(interaction.request, incoming).join("\n") };
|
|
90
|
+
return { interaction, detail: "" };
|
|
91
|
+
};
|
|
92
|
+
//# sourceMappingURL=matching.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matching.js","sourceRoot":"","sources":["../src/matching.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Effect, Scope } from "effect";
|
|
2
|
+
import type * as CassetteService from "./cassette.js";
|
|
3
|
+
import type { CassetteNotFoundError } from "./cassette.js";
|
|
4
|
+
import type { Interaction } from "./schema.js";
|
|
5
|
+
export declare const resolveAutoMode: (cassette: CassetteService.Interface, name: string) => Effect.Effect<"record" | "replay" | "passthrough">;
|
|
6
|
+
export interface ReplayState<T> {
|
|
7
|
+
readonly claim: <E>(validate: (interaction: T | undefined, index: number, interactions: ReadonlyArray<T>) => Effect.Effect<void, E>) => Effect.Effect<{
|
|
8
|
+
readonly interaction: T;
|
|
9
|
+
readonly index: number;
|
|
10
|
+
}, CassetteNotFoundError | E>;
|
|
11
|
+
}
|
|
12
|
+
export declare const makeReplayState: <T>(cassette: CassetteService.Interface, name: string, project: (interactions: ReadonlyArray<Interaction>) => ReadonlyArray<T>) => Effect.Effect<ReplayState<T>, never, Scope.Scope>;
|
|
13
|
+
//# sourceMappingURL=recorder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../src/recorder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAmB,MAAM,QAAQ,CAAA;AACvD,OAAO,KAAK,KAAK,eAAe,MAAM,eAAe,CAAA;AACrD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAO9C,eAAO,MAAM,eAAe,GAC1B,UAAU,eAAe,CAAC,SAAS,EACnC,MAAM,MAAM,KACX,MAAM,CAAC,MAAM,CAAC,QAAQ,GAAG,QAAQ,GAAG,aAAa,CAIhD,CAAA;AAEJ,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,EAChB,QAAQ,EAAE,CAAC,WAAW,EAAE,CAAC,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,KAC5G,MAAM,CAAC,MAAM,CAAC;QAAE,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,qBAAqB,GAAG,CAAC,CAAC,CAAA;CACnG;AAED,eAAO,MAAM,eAAe,GAAI,CAAC,EAC/B,UAAU,eAAe,CAAC,SAAS,EACnC,MAAM,MAAM,EACZ,SAAS,CAAC,YAAY,EAAE,aAAa,CAAC,WAAW,CAAC,KAAK,aAAa,CAAC,CAAC,CAAC,KACtE,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAgC/C,CAAA"}
|
package/dist/recorder.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Effect, SynchronizedRef } from "effect";
|
|
2
|
+
const isCI = () => {
|
|
3
|
+
const value = process.env.CI;
|
|
4
|
+
return value !== undefined && value !== "" && value !== "false" && value !== "0";
|
|
5
|
+
};
|
|
6
|
+
export const resolveAutoMode = (cassette, name) => Effect.gen(function* () {
|
|
7
|
+
if (isCI())
|
|
8
|
+
return "replay";
|
|
9
|
+
return (yield* cassette.exists(name)) ? "replay" : "record";
|
|
10
|
+
});
|
|
11
|
+
export const makeReplayState = (cassette, name, project) => Effect.gen(function* () {
|
|
12
|
+
const load = yield* Effect.cached(cassette.read(name).pipe(Effect.map(project)));
|
|
13
|
+
const position = yield* SynchronizedRef.make(0);
|
|
14
|
+
yield* Effect.addFinalizer(() => Effect.gen(function* () {
|
|
15
|
+
const used = yield* SynchronizedRef.get(position);
|
|
16
|
+
if (used === 0)
|
|
17
|
+
return yield* Effect.void;
|
|
18
|
+
const interactions = yield* load.pipe(Effect.orDie);
|
|
19
|
+
if (used < interactions.length)
|
|
20
|
+
return yield* Effect.die(new Error(`Unused recorded interactions in ${name}: used ${used} of ${interactions.length}`));
|
|
21
|
+
return yield* Effect.void;
|
|
22
|
+
}));
|
|
23
|
+
return {
|
|
24
|
+
claim: (validate) => Effect.flatMap(load, (interactions) => SynchronizedRef.modifyEffect(position, (index) => Effect.gen(function* () {
|
|
25
|
+
const interaction = interactions[index];
|
|
26
|
+
yield* validate(interaction, index, interactions);
|
|
27
|
+
if (interaction === undefined)
|
|
28
|
+
return yield* Effect.die("Replay validation accepted a missing interaction");
|
|
29
|
+
return [{ interaction, index }, index + 1];
|
|
30
|
+
}))),
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
//# sourceMappingURL=recorder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recorder.js","sourceRoot":"","sources":["../src/recorder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAS,eAAe,EAAE,MAAM,QAAQ,CAAA;AAKvD,MAAM,IAAI,GAAG,GAAG,EAAE;IAChB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAA;IAC5B,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,IAAI,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,GAAG,CAAA;AAClF,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,QAAmC,EACnC,IAAY,EACwC,EAAE,CACtD,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,IAAI,IAAI,EAAE;QAAE,OAAO,QAAQ,CAAA;IAC3B,OAAO,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAA;AAC7D,CAAC,CAAC,CAAA;AAQJ,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,QAAmC,EACnC,IAAY,EACZ,OAAuE,EACpB,EAAE,CACrD,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;IAChF,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IAE/C,KAAK,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,CAC9B,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAClB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;QACjD,IAAI,IAAI,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAA;QACzC,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACnD,IAAI,IAAI,GAAG,YAAY,CAAC,MAAM;YAC5B,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CACtB,IAAI,KAAK,CAAC,mCAAmC,IAAI,UAAU,IAAI,OAAO,YAAY,CAAC,MAAM,EAAE,CAAC,CAC7F,CAAA;QACH,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAA;IAC3B,CAAC,CAAC,CACH,CAAA;IAED,OAAO;QACL,KAAK,EAAE,CAAC,QAAQ,EAAE,EAAE,CAClB,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,YAAY,EAAE,EAAE,CACpC,eAAe,CAAC,YAAY,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAC/C,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAClB,MAAM,WAAW,GAAG,YAAY,CAAC,KAAK,CAAC,CAAA;YACvC,KAAK,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,EAAE,YAAY,CAAC,CAAA;YACjD,IAAI,WAAW,KAAK,SAAS;gBAC3B,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,kDAAkD,CAAC,CAAA;YAC9E,OAAO,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,KAAK,GAAG,CAAC,CAAU,CAAA;QACrD,CAAC,CAAC,CACH,CACF;KACJ,CAAA;AACH,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
export declare const REDACTED = "[REDACTED]";
|
|
3
|
+
export type UrlRedactor = (url: string) => string;
|
|
4
|
+
export declare const redactUrl: (raw: string, query?: ReadonlyArray<string>, urlRedactor?: UrlRedactor) => string;
|
|
5
|
+
export declare const redactHeaders: (headers: Record<string, string>, allow: ReadonlyArray<string>, redact?: ReadonlyArray<string>) => {
|
|
6
|
+
[k: string]: string;
|
|
7
|
+
};
|
|
8
|
+
export declare const SecretFindingSchema: Schema.Struct<{
|
|
9
|
+
readonly path: Schema.String;
|
|
10
|
+
readonly reason: Schema.String;
|
|
11
|
+
}>;
|
|
12
|
+
export type SecretFinding = Schema.Schema.Type<typeof SecretFindingSchema>;
|
|
13
|
+
export declare const secretFindings: (value: unknown) => ReadonlyArray<SecretFinding>;
|
|
14
|
+
//# sourceMappingURL=redaction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redaction.d.ts","sourceRoot":"","sources":["../src/redaction.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAE/B,eAAO,MAAM,QAAQ,eAAe,CAAA;AA+DpC,MAAM,MAAM,WAAW,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;AAEjD,eAAO,MAAM,SAAS,GACpB,KAAK,MAAM,EACX,QAAO,aAAa,CAAC,MAAM,CAAwB,EACnD,cAAc,WAAW,WAW1B,CAAA;AAED,eAAO,MAAM,aAAa,GACxB,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,OAAO,aAAa,CAAC,MAAM,CAAC,EAC5B,SAAQ,aAAa,CAAC,MAAM,CAA0B;;CAWvD,CAAA;AAED,eAAO,MAAM,mBAAmB;;;EAG9B,CAAA;AACF,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAE1E,eAAO,MAAM,cAAc,GAAI,OAAO,OAAO,KAAG,aAAa,CAAC,aAAa,CAW1E,CAAA"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
export const REDACTED = "[REDACTED]";
|
|
3
|
+
const DEFAULT_REDACT_HEADERS = [
|
|
4
|
+
"authorization",
|
|
5
|
+
"cookie",
|
|
6
|
+
"proxy-authorization",
|
|
7
|
+
"set-cookie",
|
|
8
|
+
"x-api-key",
|
|
9
|
+
"x-amz-security-token",
|
|
10
|
+
"x-goog-api-key",
|
|
11
|
+
];
|
|
12
|
+
const DEFAULT_REDACT_QUERY = [
|
|
13
|
+
"access_token",
|
|
14
|
+
"api-key",
|
|
15
|
+
"api_key",
|
|
16
|
+
"apikey",
|
|
17
|
+
"code",
|
|
18
|
+
"key",
|
|
19
|
+
"signature",
|
|
20
|
+
"sig",
|
|
21
|
+
"token",
|
|
22
|
+
"x-amz-credential",
|
|
23
|
+
"x-amz-security-token",
|
|
24
|
+
"x-amz-signature",
|
|
25
|
+
];
|
|
26
|
+
const SECRET_PATTERNS = [
|
|
27
|
+
{ label: "bearer token", pattern: /\bBearer\s+[A-Za-z0-9._~+/=-]{16,}\b/i },
|
|
28
|
+
{ label: "API key", pattern: /\bsk-[A-Za-z0-9][A-Za-z0-9_-]{20,}\b/ },
|
|
29
|
+
{ label: "Anthropic API key", pattern: /\bsk-ant-[A-Za-z0-9_-]{20,}\b/ },
|
|
30
|
+
{ label: "Google API key", pattern: /\bAIza[0-9A-Za-z_-]{20,}\b/ },
|
|
31
|
+
{ label: "AWS access key", pattern: /\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/ },
|
|
32
|
+
{ label: "GitHub token", pattern: /\bgh[pousr]_[A-Za-z0-9_]{20,}\b/ },
|
|
33
|
+
{ label: "private key", pattern: /-----BEGIN [A-Z ]*PRIVATE KEY-----/ },
|
|
34
|
+
];
|
|
35
|
+
const ENV_SECRET_NAMES = /(?:API|AUTH|BEARER|CREDENTIAL|KEY|PASSWORD|SECRET|TOKEN)/i;
|
|
36
|
+
const SAFE_ENV_VALUES = new Set(["fixture", "test", "test-key"]);
|
|
37
|
+
const envSecrets = () => Object.entries(process.env).flatMap(([name, value]) => {
|
|
38
|
+
if (!value)
|
|
39
|
+
return [];
|
|
40
|
+
if (!ENV_SECRET_NAMES.test(name))
|
|
41
|
+
return [];
|
|
42
|
+
if (value.length < 12)
|
|
43
|
+
return [];
|
|
44
|
+
if (SAFE_ENV_VALUES.has(value.toLowerCase()))
|
|
45
|
+
return [];
|
|
46
|
+
return [{ name, value }];
|
|
47
|
+
});
|
|
48
|
+
const pathFor = (base, key) => (base ? `${base}.${key}` : key);
|
|
49
|
+
const stringEntries = (value, base = "") => {
|
|
50
|
+
if (typeof value === "string")
|
|
51
|
+
return [{ path: base, value }];
|
|
52
|
+
if (Array.isArray(value))
|
|
53
|
+
return value.flatMap((item, index) => stringEntries(item, `${base}[${index}]`));
|
|
54
|
+
if (value && typeof value === "object") {
|
|
55
|
+
return Object.entries(value).flatMap(([key, child]) => stringEntries(child, pathFor(base, key)));
|
|
56
|
+
}
|
|
57
|
+
return [];
|
|
58
|
+
};
|
|
59
|
+
const redactionSet = (values, defaults) => new Set([...defaults, ...(values ?? [])].map((value) => value.toLowerCase()));
|
|
60
|
+
export const redactUrl = (raw, query = DEFAULT_REDACT_QUERY, urlRedactor) => {
|
|
61
|
+
if (!URL.canParse(raw))
|
|
62
|
+
return urlRedactor?.(raw) ?? raw;
|
|
63
|
+
const url = new URL(raw);
|
|
64
|
+
if (url.username)
|
|
65
|
+
url.username = REDACTED;
|
|
66
|
+
if (url.password)
|
|
67
|
+
url.password = REDACTED;
|
|
68
|
+
const redacted = redactionSet(query, DEFAULT_REDACT_QUERY);
|
|
69
|
+
for (const key of url.searchParams.keys()) {
|
|
70
|
+
if (redacted.has(key.toLowerCase()))
|
|
71
|
+
url.searchParams.set(key, REDACTED);
|
|
72
|
+
}
|
|
73
|
+
return urlRedactor?.(url.toString()) ?? url.toString();
|
|
74
|
+
};
|
|
75
|
+
export const redactHeaders = (headers, allow, redact = DEFAULT_REDACT_HEADERS) => {
|
|
76
|
+
const allowed = new Set(allow.map((name) => name.toLowerCase()));
|
|
77
|
+
const redacted = redactionSet(redact, DEFAULT_REDACT_HEADERS);
|
|
78
|
+
return Object.fromEntries(Object.entries(headers)
|
|
79
|
+
.map(([name, value]) => [name.toLowerCase(), value])
|
|
80
|
+
.filter(([name]) => allowed.has(name))
|
|
81
|
+
.map(([name, value]) => [name, redacted.has(name) ? REDACTED : value])
|
|
82
|
+
.toSorted(([a], [b]) => a.localeCompare(b)));
|
|
83
|
+
};
|
|
84
|
+
export const SecretFindingSchema = Schema.Struct({
|
|
85
|
+
path: Schema.String,
|
|
86
|
+
reason: Schema.String,
|
|
87
|
+
});
|
|
88
|
+
export const secretFindings = (value) => {
|
|
89
|
+
const environment = envSecrets();
|
|
90
|
+
return stringEntries(value).flatMap((entry) => [
|
|
91
|
+
...SECRET_PATTERNS.filter((item) => item.pattern.test(entry.value)).map((item) => ({
|
|
92
|
+
path: entry.path,
|
|
93
|
+
reason: item.label,
|
|
94
|
+
})),
|
|
95
|
+
...environment
|
|
96
|
+
.filter((item) => entry.value.includes(item.value))
|
|
97
|
+
.map((item) => ({ path: entry.path, reason: `environment secret ${item.name}` })),
|
|
98
|
+
]);
|
|
99
|
+
};
|
|
100
|
+
//# sourceMappingURL=redaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redaction.js","sourceRoot":"","sources":["../src/redaction.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { RedactOptions, RequestSnapshot, ResponseSnapshot } from "./types.js";
|
|
2
|
+
export type { RedactOptions } from "./types.js";
|
|
3
|
+
export declare const DEFAULT_REQUEST_HEADERS: ReadonlyArray<string>;
|
|
4
|
+
export declare const DEFAULT_RESPONSE_HEADERS: ReadonlyArray<string>;
|
|
5
|
+
export interface Redactor {
|
|
6
|
+
readonly request: (snapshot: RequestSnapshot) => RequestSnapshot;
|
|
7
|
+
readonly response: (snapshot: ResponseSnapshot) => ResponseSnapshot;
|
|
8
|
+
}
|
|
9
|
+
export declare const compose: (...redactors: ReadonlyArray<Partial<Redactor>>) => Redactor;
|
|
10
|
+
export interface HeaderOptions {
|
|
11
|
+
readonly allow?: ReadonlyArray<string>;
|
|
12
|
+
readonly redact?: ReadonlyArray<string>;
|
|
13
|
+
}
|
|
14
|
+
export declare const requestHeaders: (options?: HeaderOptions) => Partial<Redactor>;
|
|
15
|
+
export declare const responseHeaders: (options?: HeaderOptions) => Partial<Redactor>;
|
|
16
|
+
export interface UrlOptions {
|
|
17
|
+
readonly query?: ReadonlyArray<string>;
|
|
18
|
+
readonly transform?: (url: string) => string;
|
|
19
|
+
}
|
|
20
|
+
export declare const url: (options?: UrlOptions) => Partial<Redactor>;
|
|
21
|
+
export declare const body: (transform: (parsed: unknown) => unknown) => Partial<Redactor>;
|
|
22
|
+
export interface DefaultRedactorOverrides {
|
|
23
|
+
readonly requestHeaders?: HeaderOptions;
|
|
24
|
+
readonly responseHeaders?: HeaderOptions;
|
|
25
|
+
readonly url?: UrlOptions;
|
|
26
|
+
readonly body?: (parsed: unknown) => unknown;
|
|
27
|
+
}
|
|
28
|
+
export declare const make: (options?: RedactOptions) => Redactor;
|
|
29
|
+
export declare const defaults: (overrides?: DefaultRedactorOverrides) => Redactor;
|
|
30
|
+
//# sourceMappingURL=redactor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redactor.d.ts","sourceRoot":"","sources":["../src/redactor.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAElF,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C,eAAO,MAAM,uBAAuB,EAAE,aAAa,CAAC,MAAM,CAA6C,CAAA;AACvG,eAAO,MAAM,wBAAwB,EAAE,aAAa,CAAC,MAAM,CAAoB,CAAA;AAI/E,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,eAAe,KAAK,eAAe,CAAA;IAChE,QAAQ,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,gBAAgB,CAAA;CACpE;AAED,eAAO,MAAM,OAAO,GAAI,GAAG,WAAW,aAAa,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,KAAG,QAOxE,CAAA;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;IACtC,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CACxC;AAED,eAAO,MAAM,cAAc,GAAI,UAAS,aAAkB,KAAG,OAAO,CAAC,QAAQ,CAK3E,CAAA;AAEF,eAAO,MAAM,eAAe,GAAI,UAAS,aAAkB,KAAG,OAAO,CAAC,QAAQ,CAK5E,CAAA;AAEF,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;IACtC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;CAC7C;AAED,eAAO,MAAM,GAAG,GAAI,UAAS,UAAe,KAAG,OAAO,CAAC,QAAQ,CAE7D,CAAA;AAEF,eAAO,MAAM,IAAI,GAAI,WAAW,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,KAAG,OAAO,CAAC,QAAQ,CAQ7E,CAAA;AAEF,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,cAAc,CAAC,EAAE,aAAa,CAAA;IACvC,QAAQ,CAAC,eAAe,CAAC,EAAE,aAAa,CAAA;IACxC,QAAQ,CAAC,GAAG,CAAC,EAAE,UAAU,CAAA;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAA;CAC7C;AAkCD,eAAO,MAAM,IAAI,GAAI,UAAS,aAAkB,KAAG,QAuBlD,CAAA;AAED,eAAO,MAAM,QAAQ,GAAI,YAAW,wBAA6B,KAAG,QAMjE,CAAA"}
|
package/dist/redactor.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Option } from "effect";
|
|
2
|
+
import { decodeJson } from "./matching.js";
|
|
3
|
+
import { REDACTED, redactHeaders, redactUrl } from "./redaction.js";
|
|
4
|
+
export const DEFAULT_REQUEST_HEADERS = ["content-type", "accept", "openai-beta"];
|
|
5
|
+
export const DEFAULT_RESPONSE_HEADERS = ["content-type"];
|
|
6
|
+
const identity = (value) => value;
|
|
7
|
+
export const compose = (...redactors) => {
|
|
8
|
+
const requests = redactors.map((r) => r.request).filter((fn) => fn !== undefined);
|
|
9
|
+
const responses = redactors.map((r) => r.response).filter((fn) => fn !== undefined);
|
|
10
|
+
return {
|
|
11
|
+
request: requests.length === 0 ? identity : (snapshot) => requests.reduce((acc, fn) => fn(acc), snapshot),
|
|
12
|
+
response: responses.length === 0 ? identity : (snapshot) => responses.reduce((acc, fn) => fn(acc), snapshot),
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
export const requestHeaders = (options = {}) => ({
|
|
16
|
+
request: (snapshot) => ({
|
|
17
|
+
...snapshot,
|
|
18
|
+
headers: redactHeaders(snapshot.headers, options.allow ?? DEFAULT_REQUEST_HEADERS, options.redact),
|
|
19
|
+
}),
|
|
20
|
+
});
|
|
21
|
+
export const responseHeaders = (options = {}) => ({
|
|
22
|
+
response: (snapshot) => ({
|
|
23
|
+
...snapshot,
|
|
24
|
+
headers: redactHeaders(snapshot.headers, options.allow ?? DEFAULT_RESPONSE_HEADERS, options.redact),
|
|
25
|
+
}),
|
|
26
|
+
});
|
|
27
|
+
export const url = (options = {}) => ({
|
|
28
|
+
request: (snapshot) => ({ ...snapshot, url: redactUrl(snapshot.url, options.query, options.transform) }),
|
|
29
|
+
});
|
|
30
|
+
export const body = (transform) => ({
|
|
31
|
+
request: (snapshot) => ({
|
|
32
|
+
...snapshot,
|
|
33
|
+
body: Option.match(decodeJson(snapshot.body), {
|
|
34
|
+
onNone: () => snapshot.body,
|
|
35
|
+
onSome: (parsed) => JSON.stringify(transform(parsed)),
|
|
36
|
+
}),
|
|
37
|
+
}),
|
|
38
|
+
});
|
|
39
|
+
const DEFAULT_REDACT_JSON_FIELDS = [
|
|
40
|
+
"access_token",
|
|
41
|
+
"api_key",
|
|
42
|
+
"apikey",
|
|
43
|
+
"client_secret",
|
|
44
|
+
"password",
|
|
45
|
+
"refresh_token",
|
|
46
|
+
"secret",
|
|
47
|
+
"token",
|
|
48
|
+
];
|
|
49
|
+
const normalizeField = (field) => field.replace(/[^a-z0-9]/gi, "").toLowerCase();
|
|
50
|
+
const redactJsonFields = (value, fields) => {
|
|
51
|
+
if (Array.isArray(value))
|
|
52
|
+
return value.map((item) => redactJsonFields(item, fields));
|
|
53
|
+
if (!value || typeof value !== "object")
|
|
54
|
+
return value;
|
|
55
|
+
return Object.fromEntries(Object.entries(value).map(([key, child]) => [
|
|
56
|
+
key,
|
|
57
|
+
fields.has(normalizeField(key)) ? REDACTED : redactJsonFields(child, fields),
|
|
58
|
+
]));
|
|
59
|
+
};
|
|
60
|
+
const redactBody = (value, fields, transform) => {
|
|
61
|
+
const redacted = Option.match(decodeJson(value), {
|
|
62
|
+
onNone: () => value,
|
|
63
|
+
onSome: (parsed) => JSON.stringify(redactJsonFields(parsed, fields)),
|
|
64
|
+
});
|
|
65
|
+
return transform?.(redacted) ?? redacted;
|
|
66
|
+
};
|
|
67
|
+
export const make = (options = {}) => {
|
|
68
|
+
const fields = new Set([...DEFAULT_REDACT_JSON_FIELDS, ...(options.jsonFields ?? [])].map(normalizeField));
|
|
69
|
+
return compose(requestHeaders({
|
|
70
|
+
allow: [...DEFAULT_REQUEST_HEADERS, ...(options.allowRequestHeaders ?? []), ...(options.headers ?? [])],
|
|
71
|
+
redact: options.headers,
|
|
72
|
+
}), responseHeaders({
|
|
73
|
+
allow: [...DEFAULT_RESPONSE_HEADERS, ...(options.allowResponseHeaders ?? []), ...(options.headers ?? [])],
|
|
74
|
+
redact: options.headers,
|
|
75
|
+
}), url({ query: options.queryParameters, transform: options.url }), {
|
|
76
|
+
request: (snapshot) => ({
|
|
77
|
+
...snapshot,
|
|
78
|
+
body: redactBody(snapshot.body, fields, options.body),
|
|
79
|
+
}),
|
|
80
|
+
response: (snapshot) => ({
|
|
81
|
+
...snapshot,
|
|
82
|
+
body: redactBody(snapshot.body, fields, options.body),
|
|
83
|
+
}),
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
export const defaults = (overrides = {}) => compose(requestHeaders(overrides.requestHeaders), responseHeaders(overrides.responseHeaders), url(overrides.url), ...(overrides.body ? [body(overrides.body)] : []));
|
|
87
|
+
//# sourceMappingURL=redactor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redactor.js","sourceRoot":"","sources":["../src/redactor.ts"],"names":[],"mappings":"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"}
|