@agent-inspect/redact 2.0.0
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/dist/index.cjs +396 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +80 -0
- package/dist/index.d.ts +80 -0
- package/dist/index.mjs +384 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +42 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var crypto = require('crypto');
|
|
4
|
+
|
|
5
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
6
|
+
|
|
7
|
+
var crypto__default = /*#__PURE__*/_interopDefault(crypto);
|
|
8
|
+
|
|
9
|
+
// packages/redact/src/index.ts
|
|
10
|
+
var DEFAULT_REDACT_KEYS = [
|
|
11
|
+
"authorization",
|
|
12
|
+
"cookie",
|
|
13
|
+
"token",
|
|
14
|
+
"apiKey",
|
|
15
|
+
"password",
|
|
16
|
+
"secret",
|
|
17
|
+
"email"
|
|
18
|
+
];
|
|
19
|
+
var SHARE_PROFILE_EXTRA_KEYS = [
|
|
20
|
+
"userEmail",
|
|
21
|
+
"customerEmail",
|
|
22
|
+
"phone",
|
|
23
|
+
"phoneNumber",
|
|
24
|
+
"address",
|
|
25
|
+
"ip",
|
|
26
|
+
"ipAddress",
|
|
27
|
+
"sessionId",
|
|
28
|
+
"requestId",
|
|
29
|
+
"correlationId",
|
|
30
|
+
"decisionId",
|
|
31
|
+
"groupId",
|
|
32
|
+
"customerId",
|
|
33
|
+
"userId",
|
|
34
|
+
"accountId",
|
|
35
|
+
"tenantId",
|
|
36
|
+
"orgId",
|
|
37
|
+
"organizationId",
|
|
38
|
+
"traceId",
|
|
39
|
+
"spanId",
|
|
40
|
+
"parentSpanId"
|
|
41
|
+
];
|
|
42
|
+
var STRICT_PROFILE_EXTRA_KEYS = [
|
|
43
|
+
"prompt",
|
|
44
|
+
"completion",
|
|
45
|
+
"input",
|
|
46
|
+
"output",
|
|
47
|
+
"inputPreview",
|
|
48
|
+
"outputPreview",
|
|
49
|
+
"message",
|
|
50
|
+
"messages",
|
|
51
|
+
"transcript",
|
|
52
|
+
"context",
|
|
53
|
+
"document",
|
|
54
|
+
"documents",
|
|
55
|
+
"chunk",
|
|
56
|
+
"chunks",
|
|
57
|
+
"retrieval",
|
|
58
|
+
"query"
|
|
59
|
+
];
|
|
60
|
+
function isRecord(value) {
|
|
61
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
62
|
+
}
|
|
63
|
+
function toKey(key) {
|
|
64
|
+
return key.toLowerCase();
|
|
65
|
+
}
|
|
66
|
+
function stableHash(value) {
|
|
67
|
+
const hash = crypto__default.default.createHash("sha256").update(value, "utf8").digest("hex");
|
|
68
|
+
return hash.slice(0, 8);
|
|
69
|
+
}
|
|
70
|
+
function stringifyScalar(value) {
|
|
71
|
+
if (typeof value === "string") return value;
|
|
72
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
73
|
+
return String(value);
|
|
74
|
+
}
|
|
75
|
+
return void 0;
|
|
76
|
+
}
|
|
77
|
+
function patternDetector(options) {
|
|
78
|
+
return {
|
|
79
|
+
id: options.id,
|
|
80
|
+
severity: options.severity ?? "warning",
|
|
81
|
+
matchKind: "value",
|
|
82
|
+
detect(input) {
|
|
83
|
+
if (typeof input.value !== "string") return [];
|
|
84
|
+
options.pattern.lastIndex = 0;
|
|
85
|
+
return options.pattern.test(input.value) ? [{ action: "replace", severity: options.severity ?? "warning", matchKind: "value" }] : [];
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function digitsOnly(value) {
|
|
90
|
+
return value.replace(/\D/g, "");
|
|
91
|
+
}
|
|
92
|
+
function passesLuhn(value) {
|
|
93
|
+
const digits = digitsOnly(value);
|
|
94
|
+
if (digits.length < 13 || digits.length > 19) return false;
|
|
95
|
+
let sum = 0;
|
|
96
|
+
let double = false;
|
|
97
|
+
for (let i = digits.length - 1; i >= 0; i -= 1) {
|
|
98
|
+
let digit = Number(digits[i]);
|
|
99
|
+
if (double) {
|
|
100
|
+
digit *= 2;
|
|
101
|
+
if (digit > 9) digit -= 9;
|
|
102
|
+
}
|
|
103
|
+
sum += digit;
|
|
104
|
+
double = !double;
|
|
105
|
+
}
|
|
106
|
+
return sum % 10 === 0;
|
|
107
|
+
}
|
|
108
|
+
var credentialDetectors = [
|
|
109
|
+
patternDetector({
|
|
110
|
+
id: "value.authorizationHeader",
|
|
111
|
+
pattern: /^(?:basic|bearer|digest|apikey)\s+[a-z0-9._~+/=-]+$/i,
|
|
112
|
+
severity: "error"
|
|
113
|
+
}),
|
|
114
|
+
patternDetector({
|
|
115
|
+
id: "value.bearerToken",
|
|
116
|
+
pattern: /\bbearer\s+[a-z0-9._~+/=-]{12,}\b/i,
|
|
117
|
+
severity: "error"
|
|
118
|
+
}),
|
|
119
|
+
patternDetector({
|
|
120
|
+
id: "value.cookie",
|
|
121
|
+
pattern: /\b[a-z0-9_.-]+=[^;\s]+(?:;\s*[a-z0-9_.-]+=[^;\s]+)+/i,
|
|
122
|
+
severity: "error"
|
|
123
|
+
}),
|
|
124
|
+
patternDetector({
|
|
125
|
+
id: "value.jwt",
|
|
126
|
+
pattern: /\beyJ[a-zA-Z0-9_-]{8,}\.[a-zA-Z0-9_-]{8,}\.[a-zA-Z0-9_-]{8,}\b/,
|
|
127
|
+
severity: "error"
|
|
128
|
+
}),
|
|
129
|
+
patternDetector({
|
|
130
|
+
id: "value.providerApiKey",
|
|
131
|
+
pattern: /\b(?:sk-(?:proj-)?[a-zA-Z0-9_-]{16,}|sk-ant-[a-zA-Z0-9_-]{16,}|AIza[0-9A-Za-z_-]{20,})\b/,
|
|
132
|
+
severity: "error"
|
|
133
|
+
}),
|
|
134
|
+
patternDetector({
|
|
135
|
+
id: "value.githubToken",
|
|
136
|
+
pattern: /\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9_]{20,}\b/,
|
|
137
|
+
severity: "error"
|
|
138
|
+
}),
|
|
139
|
+
patternDetector({
|
|
140
|
+
id: "value.awsAccessKey",
|
|
141
|
+
pattern: /\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/,
|
|
142
|
+
severity: "error"
|
|
143
|
+
}),
|
|
144
|
+
patternDetector({
|
|
145
|
+
id: "value.privateKey",
|
|
146
|
+
pattern: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*-----END [A-Z ]*PRIVATE KEY-----/,
|
|
147
|
+
severity: "error"
|
|
148
|
+
}),
|
|
149
|
+
{
|
|
150
|
+
id: "value.creditCard",
|
|
151
|
+
severity: "error",
|
|
152
|
+
matchKind: "value",
|
|
153
|
+
detect(input) {
|
|
154
|
+
if (typeof input.value !== "string") return [];
|
|
155
|
+
const candidatePattern = /(?:\d[ -]?){13,19}/g;
|
|
156
|
+
for (const match of input.value.matchAll(candidatePattern)) {
|
|
157
|
+
const candidate = match[0] ?? "";
|
|
158
|
+
if (passesLuhn(candidate)) {
|
|
159
|
+
return [{ action: "replace", severity: "error", matchKind: "value" }];
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return [];
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
];
|
|
166
|
+
var identifierDetectors = [
|
|
167
|
+
patternDetector({
|
|
168
|
+
id: "value.email",
|
|
169
|
+
pattern: /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/i
|
|
170
|
+
}),
|
|
171
|
+
patternDetector({
|
|
172
|
+
id: "value.phone",
|
|
173
|
+
pattern: /\b(?:\+\d{1,3}[\s.-]?)?(?:\(?\d{3}\)?[\s.-])\d{3}[\s.-]\d{4}\b/
|
|
174
|
+
}),
|
|
175
|
+
patternDetector({
|
|
176
|
+
id: "value.ipv4",
|
|
177
|
+
pattern: /\b(?:(?:25[0-5]|2[0-4]\d|1?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|1?\d?\d)\b/
|
|
178
|
+
}),
|
|
179
|
+
patternDetector({
|
|
180
|
+
id: "value.ipv6",
|
|
181
|
+
pattern: /\b(?:[0-9a-f]{1,4}:){2,7}[0-9a-f]{1,4}\b/i
|
|
182
|
+
})
|
|
183
|
+
];
|
|
184
|
+
function builtInDetectorsForProfile(profile) {
|
|
185
|
+
if (profile === "local") return credentialDetectors;
|
|
186
|
+
return [...credentialDetectors, ...identifierDetectors];
|
|
187
|
+
}
|
|
188
|
+
function compileRules(rules, extraKeys) {
|
|
189
|
+
const out = /* @__PURE__ */ new Map();
|
|
190
|
+
const set = (rule) => {
|
|
191
|
+
const key = toKey(rule.key);
|
|
192
|
+
out.set(key, { ...rule, key });
|
|
193
|
+
};
|
|
194
|
+
for (const key of DEFAULT_REDACT_KEYS) {
|
|
195
|
+
set({ key, strategy: "full" });
|
|
196
|
+
}
|
|
197
|
+
for (const key of extraKeys ?? []) {
|
|
198
|
+
if (typeof key === "string" && key.length > 0) {
|
|
199
|
+
set({ key, strategy: "full" });
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
for (const rule of rules ?? []) {
|
|
203
|
+
if (typeof rule === "string") {
|
|
204
|
+
set({ key: rule, strategy: "full" });
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (rule.strategy === "full") set({ key: rule.key, strategy: "full" });
|
|
208
|
+
if (rule.strategy === "hash") set({ key: rule.key, strategy: "hash" });
|
|
209
|
+
if (rule.strategy === "prefix") {
|
|
210
|
+
set({
|
|
211
|
+
key: rule.key,
|
|
212
|
+
strategy: "prefix",
|
|
213
|
+
keep: typeof rule.keep === "number" ? rule.keep : 8
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return [...out.values()];
|
|
218
|
+
}
|
|
219
|
+
function actionForRule(rule) {
|
|
220
|
+
if (rule.strategy === "full") return "replace";
|
|
221
|
+
return rule.strategy;
|
|
222
|
+
}
|
|
223
|
+
function applyRule(rule, value, replacement) {
|
|
224
|
+
if (rule.strategy === "full") return replacement;
|
|
225
|
+
const asString = stringifyScalar(value);
|
|
226
|
+
if (rule.strategy === "prefix") {
|
|
227
|
+
if (asString === void 0) return replacement;
|
|
228
|
+
const keep = Math.max(0, Math.floor(rule.keep));
|
|
229
|
+
return asString.length <= keep ? `${asString}\u2026` : `${asString.slice(0, keep)}\u2026`;
|
|
230
|
+
}
|
|
231
|
+
if (rule.strategy === "hash") {
|
|
232
|
+
if (asString === void 0) return "[HASH:unknown]";
|
|
233
|
+
return `[HASH:${stableHash(asString)}]`;
|
|
234
|
+
}
|
|
235
|
+
return value;
|
|
236
|
+
}
|
|
237
|
+
function childPath(path, key) {
|
|
238
|
+
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key)) {
|
|
239
|
+
return path ? `${path}.${key}` : key;
|
|
240
|
+
}
|
|
241
|
+
return `${path || "$"}[${JSON.stringify(key)}]`;
|
|
242
|
+
}
|
|
243
|
+
function indexPath(path, index) {
|
|
244
|
+
return `${path || "$"}[${index}]`;
|
|
245
|
+
}
|
|
246
|
+
function makeFinding(path, detector, action, matchKind, severity = "warning", preview) {
|
|
247
|
+
return preview === void 0 ? { path, detector, action, severity, matchKind } : { path, detector, action, severity, matchKind, preview };
|
|
248
|
+
}
|
|
249
|
+
function createRedactionProfile(profile = "local") {
|
|
250
|
+
switch (profile) {
|
|
251
|
+
case "local":
|
|
252
|
+
return { profile: "local", extraKeys: [] };
|
|
253
|
+
case "share":
|
|
254
|
+
return {
|
|
255
|
+
profile: "share",
|
|
256
|
+
extraKeys: SHARE_PROFILE_EXTRA_KEYS,
|
|
257
|
+
maxMetadataValueLengthCap: 500,
|
|
258
|
+
maxPreviewLengthCap: 200
|
|
259
|
+
};
|
|
260
|
+
case "strict":
|
|
261
|
+
return {
|
|
262
|
+
profile: "strict",
|
|
263
|
+
extraKeys: [...SHARE_PROFILE_EXTRA_KEYS, ...STRICT_PROFILE_EXTRA_KEYS],
|
|
264
|
+
maxMetadataValueLengthCap: 200,
|
|
265
|
+
maxPreviewLengthCap: 80
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
var Redactor = class {
|
|
270
|
+
#rules;
|
|
271
|
+
#detectors;
|
|
272
|
+
#profile;
|
|
273
|
+
#replacement;
|
|
274
|
+
#maxDepth;
|
|
275
|
+
#collectFindings;
|
|
276
|
+
constructor(options) {
|
|
277
|
+
const resolved = createRedactionProfile(options?.profile ?? "local");
|
|
278
|
+
this.#profile = resolved.profile;
|
|
279
|
+
this.#rules = compileRules(options?.rules, [
|
|
280
|
+
...resolved.extraKeys,
|
|
281
|
+
...options?.extraKeys ?? []
|
|
282
|
+
]);
|
|
283
|
+
this.#detectors = [
|
|
284
|
+
...builtInDetectorsForProfile(this.#profile),
|
|
285
|
+
...options?.detectors ?? []
|
|
286
|
+
];
|
|
287
|
+
this.#replacement = options?.replacement ?? "[REDACTED]";
|
|
288
|
+
this.#maxDepth = options?.maxDepth ?? 32;
|
|
289
|
+
this.#collectFindings = options?.collectFindings ?? true;
|
|
290
|
+
}
|
|
291
|
+
redactValue(key, value) {
|
|
292
|
+
return this.#redactValue(value, key, key, 0, {
|
|
293
|
+
findings: [],
|
|
294
|
+
seen: /* @__PURE__ */ new WeakMap()
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
redactRecord(record) {
|
|
298
|
+
return this.redact(record).value;
|
|
299
|
+
}
|
|
300
|
+
redact(value) {
|
|
301
|
+
const state = {
|
|
302
|
+
findings: [],
|
|
303
|
+
seen: /* @__PURE__ */ new WeakMap()
|
|
304
|
+
};
|
|
305
|
+
const redacted = this.#redactValue(value, void 0, "$", 0, state);
|
|
306
|
+
return {
|
|
307
|
+
value: redacted,
|
|
308
|
+
findings: state.findings,
|
|
309
|
+
redacted: state.findings.some((finding) => finding.action !== "keep"),
|
|
310
|
+
profile: this.#profile
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
#recordFinding(state, finding) {
|
|
314
|
+
if (this.#collectFindings) state.findings.push(finding);
|
|
315
|
+
}
|
|
316
|
+
#redactValue(value, key, path, depth, state) {
|
|
317
|
+
if (depth > this.#maxDepth) {
|
|
318
|
+
this.#recordFinding(
|
|
319
|
+
state,
|
|
320
|
+
makeFinding(path, "structure.maxDepth", "truncate", "value", "warning")
|
|
321
|
+
);
|
|
322
|
+
return "[Truncated]";
|
|
323
|
+
}
|
|
324
|
+
if (key !== void 0) {
|
|
325
|
+
const rule = this.#rules.find((candidate) => candidate.key === toKey(key));
|
|
326
|
+
if (rule) {
|
|
327
|
+
this.#recordFinding(
|
|
328
|
+
state,
|
|
329
|
+
makeFinding(path, `key.${rule.key}`, actionForRule(rule), "key", "warning")
|
|
330
|
+
);
|
|
331
|
+
return applyRule(rule, value, this.#replacement);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
for (const detector of this.#detectors) {
|
|
335
|
+
const detections = detector.detect({ path, key, value });
|
|
336
|
+
for (const detection of detections) {
|
|
337
|
+
const action = detection.action ?? "replace";
|
|
338
|
+
this.#recordFinding(
|
|
339
|
+
state,
|
|
340
|
+
makeFinding(
|
|
341
|
+
path,
|
|
342
|
+
detector.id,
|
|
343
|
+
action,
|
|
344
|
+
detection.matchKind ?? detector.matchKind ?? "custom",
|
|
345
|
+
detection.severity ?? detector.severity ?? "warning",
|
|
346
|
+
detection.preview
|
|
347
|
+
)
|
|
348
|
+
);
|
|
349
|
+
if (action !== "keep") {
|
|
350
|
+
return detection.replacement ?? this.#replacement;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
if (Array.isArray(value)) {
|
|
355
|
+
if (state.seen.has(value)) return state.seen.get(value);
|
|
356
|
+
const out = [];
|
|
357
|
+
state.seen.set(value, out);
|
|
358
|
+
value.forEach((item, index) => {
|
|
359
|
+
out[index] = this.#redactValue(item, void 0, indexPath(path, index), depth + 1, state);
|
|
360
|
+
});
|
|
361
|
+
return out;
|
|
362
|
+
}
|
|
363
|
+
if (isRecord(value)) {
|
|
364
|
+
if (state.seen.has(value)) return state.seen.get(value);
|
|
365
|
+
const out = {};
|
|
366
|
+
state.seen.set(value, out);
|
|
367
|
+
for (const [entryKey, entryValue] of Object.entries(value)) {
|
|
368
|
+
out[entryKey] = this.#redactValue(
|
|
369
|
+
entryValue,
|
|
370
|
+
entryKey,
|
|
371
|
+
childPath(path === "$" ? "" : path, entryKey),
|
|
372
|
+
depth + 1,
|
|
373
|
+
state
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
return out;
|
|
377
|
+
}
|
|
378
|
+
return value;
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
function createRedactor(options) {
|
|
382
|
+
return new Redactor(options);
|
|
383
|
+
}
|
|
384
|
+
function redact(value, options) {
|
|
385
|
+
return createRedactor(options).redact(value);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
exports.DEFAULT_REDACT_KEYS = DEFAULT_REDACT_KEYS;
|
|
389
|
+
exports.Redactor = Redactor;
|
|
390
|
+
exports.SHARE_PROFILE_EXTRA_KEYS = SHARE_PROFILE_EXTRA_KEYS;
|
|
391
|
+
exports.STRICT_PROFILE_EXTRA_KEYS = STRICT_PROFILE_EXTRA_KEYS;
|
|
392
|
+
exports.createRedactionProfile = createRedactionProfile;
|
|
393
|
+
exports.createRedactor = createRedactor;
|
|
394
|
+
exports.redact = redact;
|
|
395
|
+
//# sourceMappingURL=index.cjs.map
|
|
396
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["crypto"],"mappings":"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","file":"index.cjs","sourcesContent":["import crypto from \"node:crypto\";\n\nexport type RedactionProfile = \"local\" | \"share\" | \"strict\";\n\nexport type RedactionAction = \"replace\" | \"hash\" | \"prefix\" | \"truncate\" | \"keep\";\n\nexport type RedactionSeverity = \"info\" | \"warning\" | \"error\";\n\nexport type RedactionMatchKind = \"key\" | \"value\" | \"custom\";\n\nexport type RedactionRule =\n | string\n | { key: string; strategy: \"full\" }\n | { key: string; strategy: \"prefix\"; keep?: number }\n | { key: string; strategy: \"hash\" };\n\nexport interface RedactionFinding {\n path: string;\n detector: string;\n action: RedactionAction;\n severity: RedactionSeverity;\n matchKind: RedactionMatchKind;\n preview?: string;\n}\n\nexport interface RedactionDetection {\n action?: RedactionAction;\n replacement?: unknown;\n severity?: RedactionSeverity;\n matchKind?: RedactionMatchKind;\n preview?: string;\n}\n\nexport interface RedactionDetectorInput {\n path: string;\n key?: string;\n value: unknown;\n}\n\nexport interface RedactionDetector {\n id: string;\n severity?: RedactionSeverity;\n matchKind?: RedactionMatchKind;\n detect(input: RedactionDetectorInput): RedactionDetection[];\n}\n\nexport interface RedactionResult<T = unknown> {\n value: T;\n findings: RedactionFinding[];\n redacted: boolean;\n profile: RedactionProfile;\n}\n\nexport interface ResolvedRedactionProfile {\n profile: RedactionProfile;\n extraKeys: readonly string[];\n maxMetadataValueLengthCap?: number;\n maxPreviewLengthCap?: number;\n}\n\nexport interface RedactOptions {\n profile?: RedactionProfile;\n detectors?: RedactionDetector[];\n rules?: RedactionRule[];\n replacement?: string;\n maxDepth?: number;\n maxStringLength?: number;\n collectFindings?: boolean;\n}\n\nexport interface RedactorOptions extends RedactOptions {\n extraKeys?: readonly string[];\n}\n\nexport const DEFAULT_REDACT_KEYS = [\n \"authorization\",\n \"cookie\",\n \"token\",\n \"apiKey\",\n \"password\",\n \"secret\",\n \"email\",\n] as const;\n\nexport const SHARE_PROFILE_EXTRA_KEYS = [\n \"userEmail\",\n \"customerEmail\",\n \"phone\",\n \"phoneNumber\",\n \"address\",\n \"ip\",\n \"ipAddress\",\n \"sessionId\",\n \"requestId\",\n \"correlationId\",\n \"decisionId\",\n \"groupId\",\n \"customerId\",\n \"userId\",\n \"accountId\",\n \"tenantId\",\n \"orgId\",\n \"organizationId\",\n \"traceId\",\n \"spanId\",\n \"parentSpanId\",\n] as const;\n\nexport const STRICT_PROFILE_EXTRA_KEYS = [\n \"prompt\",\n \"completion\",\n \"input\",\n \"output\",\n \"inputPreview\",\n \"outputPreview\",\n \"message\",\n \"messages\",\n \"transcript\",\n \"context\",\n \"document\",\n \"documents\",\n \"chunk\",\n \"chunks\",\n \"retrieval\",\n \"query\",\n] as const;\n\ntype CompiledRule =\n | { key: string; strategy: \"full\" }\n | { key: string; strategy: \"prefix\"; keep: number }\n | { key: string; strategy: \"hash\" };\n\ninterface RedactionState {\n findings: RedactionFinding[];\n seen: WeakMap<object, unknown>;\n}\n\ninterface PatternDetectorOptions {\n id: string;\n pattern: RegExp;\n severity?: RedactionSeverity;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction toKey(key: string): string {\n return key.toLowerCase();\n}\n\nfunction stableHash(value: string): string {\n const hash = crypto.createHash(\"sha256\").update(value, \"utf8\").digest(\"hex\");\n return hash.slice(0, 8);\n}\n\nfunction stringifyScalar(value: unknown): string | undefined {\n if (typeof value === \"string\") return value;\n if (typeof value === \"number\" || typeof value === \"boolean\" || typeof value === \"bigint\") {\n return String(value);\n }\n return undefined;\n}\n\nfunction patternDetector(options: PatternDetectorOptions): RedactionDetector {\n return {\n id: options.id,\n severity: options.severity ?? \"warning\",\n matchKind: \"value\",\n detect(input) {\n if (typeof input.value !== \"string\") return [];\n options.pattern.lastIndex = 0;\n return options.pattern.test(input.value)\n ? [{ action: \"replace\", severity: options.severity ?? \"warning\", matchKind: \"value\" }]\n : [];\n },\n };\n}\n\nfunction digitsOnly(value: string): string {\n return value.replace(/\\D/g, \"\");\n}\n\nfunction passesLuhn(value: string): boolean {\n const digits = digitsOnly(value);\n if (digits.length < 13 || digits.length > 19) return false;\n\n let sum = 0;\n let double = false;\n for (let i = digits.length - 1; i >= 0; i -= 1) {\n let digit = Number(digits[i]);\n if (double) {\n digit *= 2;\n if (digit > 9) digit -= 9;\n }\n sum += digit;\n double = !double;\n }\n return sum % 10 === 0;\n}\n\nconst credentialDetectors: readonly RedactionDetector[] = [\n patternDetector({\n id: \"value.authorizationHeader\",\n pattern: /^(?:basic|bearer|digest|apikey)\\s+[a-z0-9._~+/=-]+$/i,\n severity: \"error\",\n }),\n patternDetector({\n id: \"value.bearerToken\",\n pattern: /\\bbearer\\s+[a-z0-9._~+/=-]{12,}\\b/i,\n severity: \"error\",\n }),\n patternDetector({\n id: \"value.cookie\",\n pattern: /\\b[a-z0-9_.-]+=[^;\\s]+(?:;\\s*[a-z0-9_.-]+=[^;\\s]+)+/i,\n severity: \"error\",\n }),\n patternDetector({\n id: \"value.jwt\",\n pattern: /\\beyJ[a-zA-Z0-9_-]{8,}\\.[a-zA-Z0-9_-]{8,}\\.[a-zA-Z0-9_-]{8,}\\b/,\n severity: \"error\",\n }),\n patternDetector({\n id: \"value.providerApiKey\",\n pattern: /\\b(?:sk-(?:proj-)?[a-zA-Z0-9_-]{16,}|sk-ant-[a-zA-Z0-9_-]{16,}|AIza[0-9A-Za-z_-]{20,})\\b/,\n severity: \"error\",\n }),\n patternDetector({\n id: \"value.githubToken\",\n pattern: /\\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9_]{20,}\\b/,\n severity: \"error\",\n }),\n patternDetector({\n id: \"value.awsAccessKey\",\n pattern: /\\b(?:AKIA|ASIA)[A-Z0-9]{16}\\b/,\n severity: \"error\",\n }),\n patternDetector({\n id: \"value.privateKey\",\n pattern: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\\s\\S]*-----END [A-Z ]*PRIVATE KEY-----/,\n severity: \"error\",\n }),\n {\n id: \"value.creditCard\",\n severity: \"error\",\n matchKind: \"value\",\n detect(input) {\n if (typeof input.value !== \"string\") return [];\n const candidatePattern = /(?:\\d[ -]?){13,19}/g;\n for (const match of input.value.matchAll(candidatePattern)) {\n const candidate = match[0] ?? \"\";\n if (passesLuhn(candidate)) {\n return [{ action: \"replace\", severity: \"error\", matchKind: \"value\" }];\n }\n }\n return [];\n },\n },\n];\n\nconst identifierDetectors: readonly RedactionDetector[] = [\n patternDetector({\n id: \"value.email\",\n pattern: /\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}\\b/i,\n }),\n patternDetector({\n id: \"value.phone\",\n pattern: /\\b(?:\\+\\d{1,3}[\\s.-]?)?(?:\\(?\\d{3}\\)?[\\s.-])\\d{3}[\\s.-]\\d{4}\\b/,\n }),\n patternDetector({\n id: \"value.ipv4\",\n pattern: /\\b(?:(?:25[0-5]|2[0-4]\\d|1?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|1?\\d?\\d)\\b/,\n }),\n patternDetector({\n id: \"value.ipv6\",\n pattern: /\\b(?:[0-9a-f]{1,4}:){2,7}[0-9a-f]{1,4}\\b/i,\n }),\n];\n\nfunction builtInDetectorsForProfile(profile: RedactionProfile): readonly RedactionDetector[] {\n if (profile === \"local\") return credentialDetectors;\n return [...credentialDetectors, ...identifierDetectors];\n}\n\nfunction compileRules(\n rules?: RedactionRule[],\n extraKeys?: readonly string[],\n): CompiledRule[] {\n const out = new Map<string, CompiledRule>();\n\n const set = (rule: CompiledRule) => {\n const key = toKey(rule.key);\n out.set(key, { ...rule, key } as CompiledRule);\n };\n\n for (const key of DEFAULT_REDACT_KEYS) {\n set({ key, strategy: \"full\" });\n }\n\n for (const key of extraKeys ?? []) {\n if (typeof key === \"string\" && key.length > 0) {\n set({ key, strategy: \"full\" });\n }\n }\n\n for (const rule of rules ?? []) {\n if (typeof rule === \"string\") {\n set({ key: rule, strategy: \"full\" });\n continue;\n }\n\n if (rule.strategy === \"full\") set({ key: rule.key, strategy: \"full\" });\n if (rule.strategy === \"hash\") set({ key: rule.key, strategy: \"hash\" });\n if (rule.strategy === \"prefix\") {\n set({\n key: rule.key,\n strategy: \"prefix\",\n keep: typeof rule.keep === \"number\" ? rule.keep : 8,\n });\n }\n }\n\n return [...out.values()];\n}\n\nfunction actionForRule(rule: CompiledRule): RedactionAction {\n if (rule.strategy === \"full\") return \"replace\";\n return rule.strategy;\n}\n\nfunction applyRule(\n rule: CompiledRule,\n value: unknown,\n replacement: string,\n): unknown {\n if (rule.strategy === \"full\") return replacement;\n\n const asString = stringifyScalar(value);\n if (rule.strategy === \"prefix\") {\n if (asString === undefined) return replacement;\n const keep = Math.max(0, Math.floor(rule.keep));\n return asString.length <= keep ? `${asString}…` : `${asString.slice(0, keep)}…`;\n }\n\n if (rule.strategy === \"hash\") {\n if (asString === undefined) return \"[HASH:unknown]\";\n return `[HASH:${stableHash(asString)}]`;\n }\n\n return value;\n}\n\nfunction childPath(path: string, key: string): string {\n if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key)) {\n return path ? `${path}.${key}` : key;\n }\n return `${path || \"$\"}[${JSON.stringify(key)}]`;\n}\n\nfunction indexPath(path: string, index: number): string {\n return `${path || \"$\"}[${index}]`;\n}\n\nfunction makeFinding(\n path: string,\n detector: string,\n action: RedactionAction,\n matchKind: RedactionMatchKind,\n severity: RedactionSeverity = \"warning\",\n preview?: string,\n): RedactionFinding {\n return preview === undefined\n ? { path, detector, action, severity, matchKind }\n : { path, detector, action, severity, matchKind, preview };\n}\n\nexport function createRedactionProfile(profile: RedactionProfile = \"local\"): ResolvedRedactionProfile {\n switch (profile) {\n case \"local\":\n return { profile: \"local\", extraKeys: [] };\n case \"share\":\n return {\n profile: \"share\",\n extraKeys: SHARE_PROFILE_EXTRA_KEYS,\n maxMetadataValueLengthCap: 500,\n maxPreviewLengthCap: 200,\n };\n case \"strict\":\n return {\n profile: \"strict\",\n extraKeys: [...SHARE_PROFILE_EXTRA_KEYS, ...STRICT_PROFILE_EXTRA_KEYS],\n maxMetadataValueLengthCap: 200,\n maxPreviewLengthCap: 80,\n };\n }\n}\n\nexport class Redactor {\n readonly #rules: CompiledRule[];\n readonly #detectors: readonly RedactionDetector[];\n readonly #profile: RedactionProfile;\n readonly #replacement: string;\n readonly #maxDepth: number;\n readonly #collectFindings: boolean;\n\n constructor(options?: RedactorOptions) {\n const resolved = createRedactionProfile(options?.profile ?? \"local\");\n this.#profile = resolved.profile;\n this.#rules = compileRules(options?.rules, [\n ...resolved.extraKeys,\n ...(options?.extraKeys ?? []),\n ]);\n this.#detectors = [\n ...builtInDetectorsForProfile(this.#profile),\n ...(options?.detectors ?? []),\n ];\n this.#replacement = options?.replacement ?? \"[REDACTED]\";\n this.#maxDepth = options?.maxDepth ?? 32;\n this.#collectFindings = options?.collectFindings ?? true;\n }\n\n redactValue(key: string, value: unknown): unknown {\n return this.#redactValue(value, key, key, 0, {\n findings: [],\n seen: new WeakMap<object, unknown>(),\n });\n }\n\n redactRecord(record: Record<string, unknown>): Record<string, unknown> {\n return this.redact(record).value as Record<string, unknown>;\n }\n\n redact<T = unknown>(value: T): RedactionResult<T> {\n const state: RedactionState = {\n findings: [],\n seen: new WeakMap<object, unknown>(),\n };\n const redacted = this.#redactValue(value, undefined, \"$\", 0, state);\n return {\n value: redacted as T,\n findings: state.findings,\n redacted: state.findings.some((finding) => finding.action !== \"keep\"),\n profile: this.#profile,\n };\n }\n\n #recordFinding(state: RedactionState, finding: RedactionFinding): void {\n if (this.#collectFindings) state.findings.push(finding);\n }\n\n #redactValue(\n value: unknown,\n key: string | undefined,\n path: string,\n depth: number,\n state: RedactionState,\n ): unknown {\n if (depth > this.#maxDepth) {\n this.#recordFinding(\n state,\n makeFinding(path, \"structure.maxDepth\", \"truncate\", \"value\", \"warning\"),\n );\n return \"[Truncated]\";\n }\n\n if (key !== undefined) {\n const rule = this.#rules.find((candidate) => candidate.key === toKey(key));\n if (rule) {\n this.#recordFinding(\n state,\n makeFinding(path, `key.${rule.key}`, actionForRule(rule), \"key\", \"warning\"),\n );\n return applyRule(rule, value, this.#replacement);\n }\n }\n\n for (const detector of this.#detectors) {\n const detections = detector.detect({ path, key, value });\n for (const detection of detections) {\n const action = detection.action ?? \"replace\";\n this.#recordFinding(\n state,\n makeFinding(\n path,\n detector.id,\n action,\n detection.matchKind ?? detector.matchKind ?? \"custom\",\n detection.severity ?? detector.severity ?? \"warning\",\n detection.preview,\n ),\n );\n if (action !== \"keep\") {\n return detection.replacement ?? this.#replacement;\n }\n }\n }\n\n if (Array.isArray(value)) {\n if (state.seen.has(value)) return state.seen.get(value);\n const out: unknown[] = [];\n state.seen.set(value, out);\n value.forEach((item, index) => {\n out[index] = this.#redactValue(item, undefined, indexPath(path, index), depth + 1, state);\n });\n return out;\n }\n\n if (isRecord(value)) {\n if (state.seen.has(value)) return state.seen.get(value);\n const out: Record<string, unknown> = {};\n state.seen.set(value, out);\n for (const [entryKey, entryValue] of Object.entries(value)) {\n out[entryKey] = this.#redactValue(\n entryValue,\n entryKey,\n childPath(path === \"$\" ? \"\" : path, entryKey),\n depth + 1,\n state,\n );\n }\n return out;\n }\n\n return value;\n }\n}\n\nexport function createRedactor(options?: RedactorOptions): Redactor {\n return new Redactor(options);\n}\n\nexport function redact<T = unknown>(value: T, options?: RedactOptions): RedactionResult<T> {\n return createRedactor(options).redact(value);\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
type RedactionProfile = "local" | "share" | "strict";
|
|
2
|
+
type RedactionAction = "replace" | "hash" | "prefix" | "truncate" | "keep";
|
|
3
|
+
type RedactionSeverity = "info" | "warning" | "error";
|
|
4
|
+
type RedactionMatchKind = "key" | "value" | "custom";
|
|
5
|
+
type RedactionRule = string | {
|
|
6
|
+
key: string;
|
|
7
|
+
strategy: "full";
|
|
8
|
+
} | {
|
|
9
|
+
key: string;
|
|
10
|
+
strategy: "prefix";
|
|
11
|
+
keep?: number;
|
|
12
|
+
} | {
|
|
13
|
+
key: string;
|
|
14
|
+
strategy: "hash";
|
|
15
|
+
};
|
|
16
|
+
interface RedactionFinding {
|
|
17
|
+
path: string;
|
|
18
|
+
detector: string;
|
|
19
|
+
action: RedactionAction;
|
|
20
|
+
severity: RedactionSeverity;
|
|
21
|
+
matchKind: RedactionMatchKind;
|
|
22
|
+
preview?: string;
|
|
23
|
+
}
|
|
24
|
+
interface RedactionDetection {
|
|
25
|
+
action?: RedactionAction;
|
|
26
|
+
replacement?: unknown;
|
|
27
|
+
severity?: RedactionSeverity;
|
|
28
|
+
matchKind?: RedactionMatchKind;
|
|
29
|
+
preview?: string;
|
|
30
|
+
}
|
|
31
|
+
interface RedactionDetectorInput {
|
|
32
|
+
path: string;
|
|
33
|
+
key?: string;
|
|
34
|
+
value: unknown;
|
|
35
|
+
}
|
|
36
|
+
interface RedactionDetector {
|
|
37
|
+
id: string;
|
|
38
|
+
severity?: RedactionSeverity;
|
|
39
|
+
matchKind?: RedactionMatchKind;
|
|
40
|
+
detect(input: RedactionDetectorInput): RedactionDetection[];
|
|
41
|
+
}
|
|
42
|
+
interface RedactionResult<T = unknown> {
|
|
43
|
+
value: T;
|
|
44
|
+
findings: RedactionFinding[];
|
|
45
|
+
redacted: boolean;
|
|
46
|
+
profile: RedactionProfile;
|
|
47
|
+
}
|
|
48
|
+
interface ResolvedRedactionProfile {
|
|
49
|
+
profile: RedactionProfile;
|
|
50
|
+
extraKeys: readonly string[];
|
|
51
|
+
maxMetadataValueLengthCap?: number;
|
|
52
|
+
maxPreviewLengthCap?: number;
|
|
53
|
+
}
|
|
54
|
+
interface RedactOptions {
|
|
55
|
+
profile?: RedactionProfile;
|
|
56
|
+
detectors?: RedactionDetector[];
|
|
57
|
+
rules?: RedactionRule[];
|
|
58
|
+
replacement?: string;
|
|
59
|
+
maxDepth?: number;
|
|
60
|
+
maxStringLength?: number;
|
|
61
|
+
collectFindings?: boolean;
|
|
62
|
+
}
|
|
63
|
+
interface RedactorOptions extends RedactOptions {
|
|
64
|
+
extraKeys?: readonly string[];
|
|
65
|
+
}
|
|
66
|
+
declare const DEFAULT_REDACT_KEYS: readonly ["authorization", "cookie", "token", "apiKey", "password", "secret", "email"];
|
|
67
|
+
declare const SHARE_PROFILE_EXTRA_KEYS: readonly ["userEmail", "customerEmail", "phone", "phoneNumber", "address", "ip", "ipAddress", "sessionId", "requestId", "correlationId", "decisionId", "groupId", "customerId", "userId", "accountId", "tenantId", "orgId", "organizationId", "traceId", "spanId", "parentSpanId"];
|
|
68
|
+
declare const STRICT_PROFILE_EXTRA_KEYS: readonly ["prompt", "completion", "input", "output", "inputPreview", "outputPreview", "message", "messages", "transcript", "context", "document", "documents", "chunk", "chunks", "retrieval", "query"];
|
|
69
|
+
declare function createRedactionProfile(profile?: RedactionProfile): ResolvedRedactionProfile;
|
|
70
|
+
declare class Redactor {
|
|
71
|
+
#private;
|
|
72
|
+
constructor(options?: RedactorOptions);
|
|
73
|
+
redactValue(key: string, value: unknown): unknown;
|
|
74
|
+
redactRecord(record: Record<string, unknown>): Record<string, unknown>;
|
|
75
|
+
redact<T = unknown>(value: T): RedactionResult<T>;
|
|
76
|
+
}
|
|
77
|
+
declare function createRedactor(options?: RedactorOptions): Redactor;
|
|
78
|
+
declare function redact<T = unknown>(value: T, options?: RedactOptions): RedactionResult<T>;
|
|
79
|
+
|
|
80
|
+
export { DEFAULT_REDACT_KEYS, type RedactOptions, type RedactionAction, type RedactionDetection, type RedactionDetector, type RedactionDetectorInput, type RedactionFinding, type RedactionMatchKind, type RedactionProfile, type RedactionResult, type RedactionRule, type RedactionSeverity, Redactor, type RedactorOptions, type ResolvedRedactionProfile, SHARE_PROFILE_EXTRA_KEYS, STRICT_PROFILE_EXTRA_KEYS, createRedactionProfile, createRedactor, redact };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
type RedactionProfile = "local" | "share" | "strict";
|
|
2
|
+
type RedactionAction = "replace" | "hash" | "prefix" | "truncate" | "keep";
|
|
3
|
+
type RedactionSeverity = "info" | "warning" | "error";
|
|
4
|
+
type RedactionMatchKind = "key" | "value" | "custom";
|
|
5
|
+
type RedactionRule = string | {
|
|
6
|
+
key: string;
|
|
7
|
+
strategy: "full";
|
|
8
|
+
} | {
|
|
9
|
+
key: string;
|
|
10
|
+
strategy: "prefix";
|
|
11
|
+
keep?: number;
|
|
12
|
+
} | {
|
|
13
|
+
key: string;
|
|
14
|
+
strategy: "hash";
|
|
15
|
+
};
|
|
16
|
+
interface RedactionFinding {
|
|
17
|
+
path: string;
|
|
18
|
+
detector: string;
|
|
19
|
+
action: RedactionAction;
|
|
20
|
+
severity: RedactionSeverity;
|
|
21
|
+
matchKind: RedactionMatchKind;
|
|
22
|
+
preview?: string;
|
|
23
|
+
}
|
|
24
|
+
interface RedactionDetection {
|
|
25
|
+
action?: RedactionAction;
|
|
26
|
+
replacement?: unknown;
|
|
27
|
+
severity?: RedactionSeverity;
|
|
28
|
+
matchKind?: RedactionMatchKind;
|
|
29
|
+
preview?: string;
|
|
30
|
+
}
|
|
31
|
+
interface RedactionDetectorInput {
|
|
32
|
+
path: string;
|
|
33
|
+
key?: string;
|
|
34
|
+
value: unknown;
|
|
35
|
+
}
|
|
36
|
+
interface RedactionDetector {
|
|
37
|
+
id: string;
|
|
38
|
+
severity?: RedactionSeverity;
|
|
39
|
+
matchKind?: RedactionMatchKind;
|
|
40
|
+
detect(input: RedactionDetectorInput): RedactionDetection[];
|
|
41
|
+
}
|
|
42
|
+
interface RedactionResult<T = unknown> {
|
|
43
|
+
value: T;
|
|
44
|
+
findings: RedactionFinding[];
|
|
45
|
+
redacted: boolean;
|
|
46
|
+
profile: RedactionProfile;
|
|
47
|
+
}
|
|
48
|
+
interface ResolvedRedactionProfile {
|
|
49
|
+
profile: RedactionProfile;
|
|
50
|
+
extraKeys: readonly string[];
|
|
51
|
+
maxMetadataValueLengthCap?: number;
|
|
52
|
+
maxPreviewLengthCap?: number;
|
|
53
|
+
}
|
|
54
|
+
interface RedactOptions {
|
|
55
|
+
profile?: RedactionProfile;
|
|
56
|
+
detectors?: RedactionDetector[];
|
|
57
|
+
rules?: RedactionRule[];
|
|
58
|
+
replacement?: string;
|
|
59
|
+
maxDepth?: number;
|
|
60
|
+
maxStringLength?: number;
|
|
61
|
+
collectFindings?: boolean;
|
|
62
|
+
}
|
|
63
|
+
interface RedactorOptions extends RedactOptions {
|
|
64
|
+
extraKeys?: readonly string[];
|
|
65
|
+
}
|
|
66
|
+
declare const DEFAULT_REDACT_KEYS: readonly ["authorization", "cookie", "token", "apiKey", "password", "secret", "email"];
|
|
67
|
+
declare const SHARE_PROFILE_EXTRA_KEYS: readonly ["userEmail", "customerEmail", "phone", "phoneNumber", "address", "ip", "ipAddress", "sessionId", "requestId", "correlationId", "decisionId", "groupId", "customerId", "userId", "accountId", "tenantId", "orgId", "organizationId", "traceId", "spanId", "parentSpanId"];
|
|
68
|
+
declare const STRICT_PROFILE_EXTRA_KEYS: readonly ["prompt", "completion", "input", "output", "inputPreview", "outputPreview", "message", "messages", "transcript", "context", "document", "documents", "chunk", "chunks", "retrieval", "query"];
|
|
69
|
+
declare function createRedactionProfile(profile?: RedactionProfile): ResolvedRedactionProfile;
|
|
70
|
+
declare class Redactor {
|
|
71
|
+
#private;
|
|
72
|
+
constructor(options?: RedactorOptions);
|
|
73
|
+
redactValue(key: string, value: unknown): unknown;
|
|
74
|
+
redactRecord(record: Record<string, unknown>): Record<string, unknown>;
|
|
75
|
+
redact<T = unknown>(value: T): RedactionResult<T>;
|
|
76
|
+
}
|
|
77
|
+
declare function createRedactor(options?: RedactorOptions): Redactor;
|
|
78
|
+
declare function redact<T = unknown>(value: T, options?: RedactOptions): RedactionResult<T>;
|
|
79
|
+
|
|
80
|
+
export { DEFAULT_REDACT_KEYS, type RedactOptions, type RedactionAction, type RedactionDetection, type RedactionDetector, type RedactionDetectorInput, type RedactionFinding, type RedactionMatchKind, type RedactionProfile, type RedactionResult, type RedactionRule, type RedactionSeverity, Redactor, type RedactorOptions, type ResolvedRedactionProfile, SHARE_PROFILE_EXTRA_KEYS, STRICT_PROFILE_EXTRA_KEYS, createRedactionProfile, createRedactor, redact };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
|
|
3
|
+
// packages/redact/src/index.ts
|
|
4
|
+
var DEFAULT_REDACT_KEYS = [
|
|
5
|
+
"authorization",
|
|
6
|
+
"cookie",
|
|
7
|
+
"token",
|
|
8
|
+
"apiKey",
|
|
9
|
+
"password",
|
|
10
|
+
"secret",
|
|
11
|
+
"email"
|
|
12
|
+
];
|
|
13
|
+
var SHARE_PROFILE_EXTRA_KEYS = [
|
|
14
|
+
"userEmail",
|
|
15
|
+
"customerEmail",
|
|
16
|
+
"phone",
|
|
17
|
+
"phoneNumber",
|
|
18
|
+
"address",
|
|
19
|
+
"ip",
|
|
20
|
+
"ipAddress",
|
|
21
|
+
"sessionId",
|
|
22
|
+
"requestId",
|
|
23
|
+
"correlationId",
|
|
24
|
+
"decisionId",
|
|
25
|
+
"groupId",
|
|
26
|
+
"customerId",
|
|
27
|
+
"userId",
|
|
28
|
+
"accountId",
|
|
29
|
+
"tenantId",
|
|
30
|
+
"orgId",
|
|
31
|
+
"organizationId",
|
|
32
|
+
"traceId",
|
|
33
|
+
"spanId",
|
|
34
|
+
"parentSpanId"
|
|
35
|
+
];
|
|
36
|
+
var STRICT_PROFILE_EXTRA_KEYS = [
|
|
37
|
+
"prompt",
|
|
38
|
+
"completion",
|
|
39
|
+
"input",
|
|
40
|
+
"output",
|
|
41
|
+
"inputPreview",
|
|
42
|
+
"outputPreview",
|
|
43
|
+
"message",
|
|
44
|
+
"messages",
|
|
45
|
+
"transcript",
|
|
46
|
+
"context",
|
|
47
|
+
"document",
|
|
48
|
+
"documents",
|
|
49
|
+
"chunk",
|
|
50
|
+
"chunks",
|
|
51
|
+
"retrieval",
|
|
52
|
+
"query"
|
|
53
|
+
];
|
|
54
|
+
function isRecord(value) {
|
|
55
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
56
|
+
}
|
|
57
|
+
function toKey(key) {
|
|
58
|
+
return key.toLowerCase();
|
|
59
|
+
}
|
|
60
|
+
function stableHash(value) {
|
|
61
|
+
const hash = crypto.createHash("sha256").update(value, "utf8").digest("hex");
|
|
62
|
+
return hash.slice(0, 8);
|
|
63
|
+
}
|
|
64
|
+
function stringifyScalar(value) {
|
|
65
|
+
if (typeof value === "string") return value;
|
|
66
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
67
|
+
return String(value);
|
|
68
|
+
}
|
|
69
|
+
return void 0;
|
|
70
|
+
}
|
|
71
|
+
function patternDetector(options) {
|
|
72
|
+
return {
|
|
73
|
+
id: options.id,
|
|
74
|
+
severity: options.severity ?? "warning",
|
|
75
|
+
matchKind: "value",
|
|
76
|
+
detect(input) {
|
|
77
|
+
if (typeof input.value !== "string") return [];
|
|
78
|
+
options.pattern.lastIndex = 0;
|
|
79
|
+
return options.pattern.test(input.value) ? [{ action: "replace", severity: options.severity ?? "warning", matchKind: "value" }] : [];
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function digitsOnly(value) {
|
|
84
|
+
return value.replace(/\D/g, "");
|
|
85
|
+
}
|
|
86
|
+
function passesLuhn(value) {
|
|
87
|
+
const digits = digitsOnly(value);
|
|
88
|
+
if (digits.length < 13 || digits.length > 19) return false;
|
|
89
|
+
let sum = 0;
|
|
90
|
+
let double = false;
|
|
91
|
+
for (let i = digits.length - 1; i >= 0; i -= 1) {
|
|
92
|
+
let digit = Number(digits[i]);
|
|
93
|
+
if (double) {
|
|
94
|
+
digit *= 2;
|
|
95
|
+
if (digit > 9) digit -= 9;
|
|
96
|
+
}
|
|
97
|
+
sum += digit;
|
|
98
|
+
double = !double;
|
|
99
|
+
}
|
|
100
|
+
return sum % 10 === 0;
|
|
101
|
+
}
|
|
102
|
+
var credentialDetectors = [
|
|
103
|
+
patternDetector({
|
|
104
|
+
id: "value.authorizationHeader",
|
|
105
|
+
pattern: /^(?:basic|bearer|digest|apikey)\s+[a-z0-9._~+/=-]+$/i,
|
|
106
|
+
severity: "error"
|
|
107
|
+
}),
|
|
108
|
+
patternDetector({
|
|
109
|
+
id: "value.bearerToken",
|
|
110
|
+
pattern: /\bbearer\s+[a-z0-9._~+/=-]{12,}\b/i,
|
|
111
|
+
severity: "error"
|
|
112
|
+
}),
|
|
113
|
+
patternDetector({
|
|
114
|
+
id: "value.cookie",
|
|
115
|
+
pattern: /\b[a-z0-9_.-]+=[^;\s]+(?:;\s*[a-z0-9_.-]+=[^;\s]+)+/i,
|
|
116
|
+
severity: "error"
|
|
117
|
+
}),
|
|
118
|
+
patternDetector({
|
|
119
|
+
id: "value.jwt",
|
|
120
|
+
pattern: /\beyJ[a-zA-Z0-9_-]{8,}\.[a-zA-Z0-9_-]{8,}\.[a-zA-Z0-9_-]{8,}\b/,
|
|
121
|
+
severity: "error"
|
|
122
|
+
}),
|
|
123
|
+
patternDetector({
|
|
124
|
+
id: "value.providerApiKey",
|
|
125
|
+
pattern: /\b(?:sk-(?:proj-)?[a-zA-Z0-9_-]{16,}|sk-ant-[a-zA-Z0-9_-]{16,}|AIza[0-9A-Za-z_-]{20,})\b/,
|
|
126
|
+
severity: "error"
|
|
127
|
+
}),
|
|
128
|
+
patternDetector({
|
|
129
|
+
id: "value.githubToken",
|
|
130
|
+
pattern: /\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9_]{20,}\b/,
|
|
131
|
+
severity: "error"
|
|
132
|
+
}),
|
|
133
|
+
patternDetector({
|
|
134
|
+
id: "value.awsAccessKey",
|
|
135
|
+
pattern: /\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/,
|
|
136
|
+
severity: "error"
|
|
137
|
+
}),
|
|
138
|
+
patternDetector({
|
|
139
|
+
id: "value.privateKey",
|
|
140
|
+
pattern: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*-----END [A-Z ]*PRIVATE KEY-----/,
|
|
141
|
+
severity: "error"
|
|
142
|
+
}),
|
|
143
|
+
{
|
|
144
|
+
id: "value.creditCard",
|
|
145
|
+
severity: "error",
|
|
146
|
+
matchKind: "value",
|
|
147
|
+
detect(input) {
|
|
148
|
+
if (typeof input.value !== "string") return [];
|
|
149
|
+
const candidatePattern = /(?:\d[ -]?){13,19}/g;
|
|
150
|
+
for (const match of input.value.matchAll(candidatePattern)) {
|
|
151
|
+
const candidate = match[0] ?? "";
|
|
152
|
+
if (passesLuhn(candidate)) {
|
|
153
|
+
return [{ action: "replace", severity: "error", matchKind: "value" }];
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return [];
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
];
|
|
160
|
+
var identifierDetectors = [
|
|
161
|
+
patternDetector({
|
|
162
|
+
id: "value.email",
|
|
163
|
+
pattern: /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/i
|
|
164
|
+
}),
|
|
165
|
+
patternDetector({
|
|
166
|
+
id: "value.phone",
|
|
167
|
+
pattern: /\b(?:\+\d{1,3}[\s.-]?)?(?:\(?\d{3}\)?[\s.-])\d{3}[\s.-]\d{4}\b/
|
|
168
|
+
}),
|
|
169
|
+
patternDetector({
|
|
170
|
+
id: "value.ipv4",
|
|
171
|
+
pattern: /\b(?:(?:25[0-5]|2[0-4]\d|1?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|1?\d?\d)\b/
|
|
172
|
+
}),
|
|
173
|
+
patternDetector({
|
|
174
|
+
id: "value.ipv6",
|
|
175
|
+
pattern: /\b(?:[0-9a-f]{1,4}:){2,7}[0-9a-f]{1,4}\b/i
|
|
176
|
+
})
|
|
177
|
+
];
|
|
178
|
+
function builtInDetectorsForProfile(profile) {
|
|
179
|
+
if (profile === "local") return credentialDetectors;
|
|
180
|
+
return [...credentialDetectors, ...identifierDetectors];
|
|
181
|
+
}
|
|
182
|
+
function compileRules(rules, extraKeys) {
|
|
183
|
+
const out = /* @__PURE__ */ new Map();
|
|
184
|
+
const set = (rule) => {
|
|
185
|
+
const key = toKey(rule.key);
|
|
186
|
+
out.set(key, { ...rule, key });
|
|
187
|
+
};
|
|
188
|
+
for (const key of DEFAULT_REDACT_KEYS) {
|
|
189
|
+
set({ key, strategy: "full" });
|
|
190
|
+
}
|
|
191
|
+
for (const key of extraKeys ?? []) {
|
|
192
|
+
if (typeof key === "string" && key.length > 0) {
|
|
193
|
+
set({ key, strategy: "full" });
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
for (const rule of rules ?? []) {
|
|
197
|
+
if (typeof rule === "string") {
|
|
198
|
+
set({ key: rule, strategy: "full" });
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (rule.strategy === "full") set({ key: rule.key, strategy: "full" });
|
|
202
|
+
if (rule.strategy === "hash") set({ key: rule.key, strategy: "hash" });
|
|
203
|
+
if (rule.strategy === "prefix") {
|
|
204
|
+
set({
|
|
205
|
+
key: rule.key,
|
|
206
|
+
strategy: "prefix",
|
|
207
|
+
keep: typeof rule.keep === "number" ? rule.keep : 8
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return [...out.values()];
|
|
212
|
+
}
|
|
213
|
+
function actionForRule(rule) {
|
|
214
|
+
if (rule.strategy === "full") return "replace";
|
|
215
|
+
return rule.strategy;
|
|
216
|
+
}
|
|
217
|
+
function applyRule(rule, value, replacement) {
|
|
218
|
+
if (rule.strategy === "full") return replacement;
|
|
219
|
+
const asString = stringifyScalar(value);
|
|
220
|
+
if (rule.strategy === "prefix") {
|
|
221
|
+
if (asString === void 0) return replacement;
|
|
222
|
+
const keep = Math.max(0, Math.floor(rule.keep));
|
|
223
|
+
return asString.length <= keep ? `${asString}\u2026` : `${asString.slice(0, keep)}\u2026`;
|
|
224
|
+
}
|
|
225
|
+
if (rule.strategy === "hash") {
|
|
226
|
+
if (asString === void 0) return "[HASH:unknown]";
|
|
227
|
+
return `[HASH:${stableHash(asString)}]`;
|
|
228
|
+
}
|
|
229
|
+
return value;
|
|
230
|
+
}
|
|
231
|
+
function childPath(path, key) {
|
|
232
|
+
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key)) {
|
|
233
|
+
return path ? `${path}.${key}` : key;
|
|
234
|
+
}
|
|
235
|
+
return `${path || "$"}[${JSON.stringify(key)}]`;
|
|
236
|
+
}
|
|
237
|
+
function indexPath(path, index) {
|
|
238
|
+
return `${path || "$"}[${index}]`;
|
|
239
|
+
}
|
|
240
|
+
function makeFinding(path, detector, action, matchKind, severity = "warning", preview) {
|
|
241
|
+
return preview === void 0 ? { path, detector, action, severity, matchKind } : { path, detector, action, severity, matchKind, preview };
|
|
242
|
+
}
|
|
243
|
+
function createRedactionProfile(profile = "local") {
|
|
244
|
+
switch (profile) {
|
|
245
|
+
case "local":
|
|
246
|
+
return { profile: "local", extraKeys: [] };
|
|
247
|
+
case "share":
|
|
248
|
+
return {
|
|
249
|
+
profile: "share",
|
|
250
|
+
extraKeys: SHARE_PROFILE_EXTRA_KEYS,
|
|
251
|
+
maxMetadataValueLengthCap: 500,
|
|
252
|
+
maxPreviewLengthCap: 200
|
|
253
|
+
};
|
|
254
|
+
case "strict":
|
|
255
|
+
return {
|
|
256
|
+
profile: "strict",
|
|
257
|
+
extraKeys: [...SHARE_PROFILE_EXTRA_KEYS, ...STRICT_PROFILE_EXTRA_KEYS],
|
|
258
|
+
maxMetadataValueLengthCap: 200,
|
|
259
|
+
maxPreviewLengthCap: 80
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
var Redactor = class {
|
|
264
|
+
#rules;
|
|
265
|
+
#detectors;
|
|
266
|
+
#profile;
|
|
267
|
+
#replacement;
|
|
268
|
+
#maxDepth;
|
|
269
|
+
#collectFindings;
|
|
270
|
+
constructor(options) {
|
|
271
|
+
const resolved = createRedactionProfile(options?.profile ?? "local");
|
|
272
|
+
this.#profile = resolved.profile;
|
|
273
|
+
this.#rules = compileRules(options?.rules, [
|
|
274
|
+
...resolved.extraKeys,
|
|
275
|
+
...options?.extraKeys ?? []
|
|
276
|
+
]);
|
|
277
|
+
this.#detectors = [
|
|
278
|
+
...builtInDetectorsForProfile(this.#profile),
|
|
279
|
+
...options?.detectors ?? []
|
|
280
|
+
];
|
|
281
|
+
this.#replacement = options?.replacement ?? "[REDACTED]";
|
|
282
|
+
this.#maxDepth = options?.maxDepth ?? 32;
|
|
283
|
+
this.#collectFindings = options?.collectFindings ?? true;
|
|
284
|
+
}
|
|
285
|
+
redactValue(key, value) {
|
|
286
|
+
return this.#redactValue(value, key, key, 0, {
|
|
287
|
+
findings: [],
|
|
288
|
+
seen: /* @__PURE__ */ new WeakMap()
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
redactRecord(record) {
|
|
292
|
+
return this.redact(record).value;
|
|
293
|
+
}
|
|
294
|
+
redact(value) {
|
|
295
|
+
const state = {
|
|
296
|
+
findings: [],
|
|
297
|
+
seen: /* @__PURE__ */ new WeakMap()
|
|
298
|
+
};
|
|
299
|
+
const redacted = this.#redactValue(value, void 0, "$", 0, state);
|
|
300
|
+
return {
|
|
301
|
+
value: redacted,
|
|
302
|
+
findings: state.findings,
|
|
303
|
+
redacted: state.findings.some((finding) => finding.action !== "keep"),
|
|
304
|
+
profile: this.#profile
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
#recordFinding(state, finding) {
|
|
308
|
+
if (this.#collectFindings) state.findings.push(finding);
|
|
309
|
+
}
|
|
310
|
+
#redactValue(value, key, path, depth, state) {
|
|
311
|
+
if (depth > this.#maxDepth) {
|
|
312
|
+
this.#recordFinding(
|
|
313
|
+
state,
|
|
314
|
+
makeFinding(path, "structure.maxDepth", "truncate", "value", "warning")
|
|
315
|
+
);
|
|
316
|
+
return "[Truncated]";
|
|
317
|
+
}
|
|
318
|
+
if (key !== void 0) {
|
|
319
|
+
const rule = this.#rules.find((candidate) => candidate.key === toKey(key));
|
|
320
|
+
if (rule) {
|
|
321
|
+
this.#recordFinding(
|
|
322
|
+
state,
|
|
323
|
+
makeFinding(path, `key.${rule.key}`, actionForRule(rule), "key", "warning")
|
|
324
|
+
);
|
|
325
|
+
return applyRule(rule, value, this.#replacement);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
for (const detector of this.#detectors) {
|
|
329
|
+
const detections = detector.detect({ path, key, value });
|
|
330
|
+
for (const detection of detections) {
|
|
331
|
+
const action = detection.action ?? "replace";
|
|
332
|
+
this.#recordFinding(
|
|
333
|
+
state,
|
|
334
|
+
makeFinding(
|
|
335
|
+
path,
|
|
336
|
+
detector.id,
|
|
337
|
+
action,
|
|
338
|
+
detection.matchKind ?? detector.matchKind ?? "custom",
|
|
339
|
+
detection.severity ?? detector.severity ?? "warning",
|
|
340
|
+
detection.preview
|
|
341
|
+
)
|
|
342
|
+
);
|
|
343
|
+
if (action !== "keep") {
|
|
344
|
+
return detection.replacement ?? this.#replacement;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
if (Array.isArray(value)) {
|
|
349
|
+
if (state.seen.has(value)) return state.seen.get(value);
|
|
350
|
+
const out = [];
|
|
351
|
+
state.seen.set(value, out);
|
|
352
|
+
value.forEach((item, index) => {
|
|
353
|
+
out[index] = this.#redactValue(item, void 0, indexPath(path, index), depth + 1, state);
|
|
354
|
+
});
|
|
355
|
+
return out;
|
|
356
|
+
}
|
|
357
|
+
if (isRecord(value)) {
|
|
358
|
+
if (state.seen.has(value)) return state.seen.get(value);
|
|
359
|
+
const out = {};
|
|
360
|
+
state.seen.set(value, out);
|
|
361
|
+
for (const [entryKey, entryValue] of Object.entries(value)) {
|
|
362
|
+
out[entryKey] = this.#redactValue(
|
|
363
|
+
entryValue,
|
|
364
|
+
entryKey,
|
|
365
|
+
childPath(path === "$" ? "" : path, entryKey),
|
|
366
|
+
depth + 1,
|
|
367
|
+
state
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
return out;
|
|
371
|
+
}
|
|
372
|
+
return value;
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
function createRedactor(options) {
|
|
376
|
+
return new Redactor(options);
|
|
377
|
+
}
|
|
378
|
+
function redact(value, options) {
|
|
379
|
+
return createRedactor(options).redact(value);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
export { DEFAULT_REDACT_KEYS, Redactor, SHARE_PROFILE_EXTRA_KEYS, STRICT_PROFILE_EXTRA_KEYS, createRedactionProfile, createRedactor, redact };
|
|
383
|
+
//# sourceMappingURL=index.mjs.map
|
|
384
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import crypto from \"node:crypto\";\n\nexport type RedactionProfile = \"local\" | \"share\" | \"strict\";\n\nexport type RedactionAction = \"replace\" | \"hash\" | \"prefix\" | \"truncate\" | \"keep\";\n\nexport type RedactionSeverity = \"info\" | \"warning\" | \"error\";\n\nexport type RedactionMatchKind = \"key\" | \"value\" | \"custom\";\n\nexport type RedactionRule =\n | string\n | { key: string; strategy: \"full\" }\n | { key: string; strategy: \"prefix\"; keep?: number }\n | { key: string; strategy: \"hash\" };\n\nexport interface RedactionFinding {\n path: string;\n detector: string;\n action: RedactionAction;\n severity: RedactionSeverity;\n matchKind: RedactionMatchKind;\n preview?: string;\n}\n\nexport interface RedactionDetection {\n action?: RedactionAction;\n replacement?: unknown;\n severity?: RedactionSeverity;\n matchKind?: RedactionMatchKind;\n preview?: string;\n}\n\nexport interface RedactionDetectorInput {\n path: string;\n key?: string;\n value: unknown;\n}\n\nexport interface RedactionDetector {\n id: string;\n severity?: RedactionSeverity;\n matchKind?: RedactionMatchKind;\n detect(input: RedactionDetectorInput): RedactionDetection[];\n}\n\nexport interface RedactionResult<T = unknown> {\n value: T;\n findings: RedactionFinding[];\n redacted: boolean;\n profile: RedactionProfile;\n}\n\nexport interface ResolvedRedactionProfile {\n profile: RedactionProfile;\n extraKeys: readonly string[];\n maxMetadataValueLengthCap?: number;\n maxPreviewLengthCap?: number;\n}\n\nexport interface RedactOptions {\n profile?: RedactionProfile;\n detectors?: RedactionDetector[];\n rules?: RedactionRule[];\n replacement?: string;\n maxDepth?: number;\n maxStringLength?: number;\n collectFindings?: boolean;\n}\n\nexport interface RedactorOptions extends RedactOptions {\n extraKeys?: readonly string[];\n}\n\nexport const DEFAULT_REDACT_KEYS = [\n \"authorization\",\n \"cookie\",\n \"token\",\n \"apiKey\",\n \"password\",\n \"secret\",\n \"email\",\n] as const;\n\nexport const SHARE_PROFILE_EXTRA_KEYS = [\n \"userEmail\",\n \"customerEmail\",\n \"phone\",\n \"phoneNumber\",\n \"address\",\n \"ip\",\n \"ipAddress\",\n \"sessionId\",\n \"requestId\",\n \"correlationId\",\n \"decisionId\",\n \"groupId\",\n \"customerId\",\n \"userId\",\n \"accountId\",\n \"tenantId\",\n \"orgId\",\n \"organizationId\",\n \"traceId\",\n \"spanId\",\n \"parentSpanId\",\n] as const;\n\nexport const STRICT_PROFILE_EXTRA_KEYS = [\n \"prompt\",\n \"completion\",\n \"input\",\n \"output\",\n \"inputPreview\",\n \"outputPreview\",\n \"message\",\n \"messages\",\n \"transcript\",\n \"context\",\n \"document\",\n \"documents\",\n \"chunk\",\n \"chunks\",\n \"retrieval\",\n \"query\",\n] as const;\n\ntype CompiledRule =\n | { key: string; strategy: \"full\" }\n | { key: string; strategy: \"prefix\"; keep: number }\n | { key: string; strategy: \"hash\" };\n\ninterface RedactionState {\n findings: RedactionFinding[];\n seen: WeakMap<object, unknown>;\n}\n\ninterface PatternDetectorOptions {\n id: string;\n pattern: RegExp;\n severity?: RedactionSeverity;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction toKey(key: string): string {\n return key.toLowerCase();\n}\n\nfunction stableHash(value: string): string {\n const hash = crypto.createHash(\"sha256\").update(value, \"utf8\").digest(\"hex\");\n return hash.slice(0, 8);\n}\n\nfunction stringifyScalar(value: unknown): string | undefined {\n if (typeof value === \"string\") return value;\n if (typeof value === \"number\" || typeof value === \"boolean\" || typeof value === \"bigint\") {\n return String(value);\n }\n return undefined;\n}\n\nfunction patternDetector(options: PatternDetectorOptions): RedactionDetector {\n return {\n id: options.id,\n severity: options.severity ?? \"warning\",\n matchKind: \"value\",\n detect(input) {\n if (typeof input.value !== \"string\") return [];\n options.pattern.lastIndex = 0;\n return options.pattern.test(input.value)\n ? [{ action: \"replace\", severity: options.severity ?? \"warning\", matchKind: \"value\" }]\n : [];\n },\n };\n}\n\nfunction digitsOnly(value: string): string {\n return value.replace(/\\D/g, \"\");\n}\n\nfunction passesLuhn(value: string): boolean {\n const digits = digitsOnly(value);\n if (digits.length < 13 || digits.length > 19) return false;\n\n let sum = 0;\n let double = false;\n for (let i = digits.length - 1; i >= 0; i -= 1) {\n let digit = Number(digits[i]);\n if (double) {\n digit *= 2;\n if (digit > 9) digit -= 9;\n }\n sum += digit;\n double = !double;\n }\n return sum % 10 === 0;\n}\n\nconst credentialDetectors: readonly RedactionDetector[] = [\n patternDetector({\n id: \"value.authorizationHeader\",\n pattern: /^(?:basic|bearer|digest|apikey)\\s+[a-z0-9._~+/=-]+$/i,\n severity: \"error\",\n }),\n patternDetector({\n id: \"value.bearerToken\",\n pattern: /\\bbearer\\s+[a-z0-9._~+/=-]{12,}\\b/i,\n severity: \"error\",\n }),\n patternDetector({\n id: \"value.cookie\",\n pattern: /\\b[a-z0-9_.-]+=[^;\\s]+(?:;\\s*[a-z0-9_.-]+=[^;\\s]+)+/i,\n severity: \"error\",\n }),\n patternDetector({\n id: \"value.jwt\",\n pattern: /\\beyJ[a-zA-Z0-9_-]{8,}\\.[a-zA-Z0-9_-]{8,}\\.[a-zA-Z0-9_-]{8,}\\b/,\n severity: \"error\",\n }),\n patternDetector({\n id: \"value.providerApiKey\",\n pattern: /\\b(?:sk-(?:proj-)?[a-zA-Z0-9_-]{16,}|sk-ant-[a-zA-Z0-9_-]{16,}|AIza[0-9A-Za-z_-]{20,})\\b/,\n severity: \"error\",\n }),\n patternDetector({\n id: \"value.githubToken\",\n pattern: /\\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9_]{20,}\\b/,\n severity: \"error\",\n }),\n patternDetector({\n id: \"value.awsAccessKey\",\n pattern: /\\b(?:AKIA|ASIA)[A-Z0-9]{16}\\b/,\n severity: \"error\",\n }),\n patternDetector({\n id: \"value.privateKey\",\n pattern: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\\s\\S]*-----END [A-Z ]*PRIVATE KEY-----/,\n severity: \"error\",\n }),\n {\n id: \"value.creditCard\",\n severity: \"error\",\n matchKind: \"value\",\n detect(input) {\n if (typeof input.value !== \"string\") return [];\n const candidatePattern = /(?:\\d[ -]?){13,19}/g;\n for (const match of input.value.matchAll(candidatePattern)) {\n const candidate = match[0] ?? \"\";\n if (passesLuhn(candidate)) {\n return [{ action: \"replace\", severity: \"error\", matchKind: \"value\" }];\n }\n }\n return [];\n },\n },\n];\n\nconst identifierDetectors: readonly RedactionDetector[] = [\n patternDetector({\n id: \"value.email\",\n pattern: /\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}\\b/i,\n }),\n patternDetector({\n id: \"value.phone\",\n pattern: /\\b(?:\\+\\d{1,3}[\\s.-]?)?(?:\\(?\\d{3}\\)?[\\s.-])\\d{3}[\\s.-]\\d{4}\\b/,\n }),\n patternDetector({\n id: \"value.ipv4\",\n pattern: /\\b(?:(?:25[0-5]|2[0-4]\\d|1?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|1?\\d?\\d)\\b/,\n }),\n patternDetector({\n id: \"value.ipv6\",\n pattern: /\\b(?:[0-9a-f]{1,4}:){2,7}[0-9a-f]{1,4}\\b/i,\n }),\n];\n\nfunction builtInDetectorsForProfile(profile: RedactionProfile): readonly RedactionDetector[] {\n if (profile === \"local\") return credentialDetectors;\n return [...credentialDetectors, ...identifierDetectors];\n}\n\nfunction compileRules(\n rules?: RedactionRule[],\n extraKeys?: readonly string[],\n): CompiledRule[] {\n const out = new Map<string, CompiledRule>();\n\n const set = (rule: CompiledRule) => {\n const key = toKey(rule.key);\n out.set(key, { ...rule, key } as CompiledRule);\n };\n\n for (const key of DEFAULT_REDACT_KEYS) {\n set({ key, strategy: \"full\" });\n }\n\n for (const key of extraKeys ?? []) {\n if (typeof key === \"string\" && key.length > 0) {\n set({ key, strategy: \"full\" });\n }\n }\n\n for (const rule of rules ?? []) {\n if (typeof rule === \"string\") {\n set({ key: rule, strategy: \"full\" });\n continue;\n }\n\n if (rule.strategy === \"full\") set({ key: rule.key, strategy: \"full\" });\n if (rule.strategy === \"hash\") set({ key: rule.key, strategy: \"hash\" });\n if (rule.strategy === \"prefix\") {\n set({\n key: rule.key,\n strategy: \"prefix\",\n keep: typeof rule.keep === \"number\" ? rule.keep : 8,\n });\n }\n }\n\n return [...out.values()];\n}\n\nfunction actionForRule(rule: CompiledRule): RedactionAction {\n if (rule.strategy === \"full\") return \"replace\";\n return rule.strategy;\n}\n\nfunction applyRule(\n rule: CompiledRule,\n value: unknown,\n replacement: string,\n): unknown {\n if (rule.strategy === \"full\") return replacement;\n\n const asString = stringifyScalar(value);\n if (rule.strategy === \"prefix\") {\n if (asString === undefined) return replacement;\n const keep = Math.max(0, Math.floor(rule.keep));\n return asString.length <= keep ? `${asString}…` : `${asString.slice(0, keep)}…`;\n }\n\n if (rule.strategy === \"hash\") {\n if (asString === undefined) return \"[HASH:unknown]\";\n return `[HASH:${stableHash(asString)}]`;\n }\n\n return value;\n}\n\nfunction childPath(path: string, key: string): string {\n if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key)) {\n return path ? `${path}.${key}` : key;\n }\n return `${path || \"$\"}[${JSON.stringify(key)}]`;\n}\n\nfunction indexPath(path: string, index: number): string {\n return `${path || \"$\"}[${index}]`;\n}\n\nfunction makeFinding(\n path: string,\n detector: string,\n action: RedactionAction,\n matchKind: RedactionMatchKind,\n severity: RedactionSeverity = \"warning\",\n preview?: string,\n): RedactionFinding {\n return preview === undefined\n ? { path, detector, action, severity, matchKind }\n : { path, detector, action, severity, matchKind, preview };\n}\n\nexport function createRedactionProfile(profile: RedactionProfile = \"local\"): ResolvedRedactionProfile {\n switch (profile) {\n case \"local\":\n return { profile: \"local\", extraKeys: [] };\n case \"share\":\n return {\n profile: \"share\",\n extraKeys: SHARE_PROFILE_EXTRA_KEYS,\n maxMetadataValueLengthCap: 500,\n maxPreviewLengthCap: 200,\n };\n case \"strict\":\n return {\n profile: \"strict\",\n extraKeys: [...SHARE_PROFILE_EXTRA_KEYS, ...STRICT_PROFILE_EXTRA_KEYS],\n maxMetadataValueLengthCap: 200,\n maxPreviewLengthCap: 80,\n };\n }\n}\n\nexport class Redactor {\n readonly #rules: CompiledRule[];\n readonly #detectors: readonly RedactionDetector[];\n readonly #profile: RedactionProfile;\n readonly #replacement: string;\n readonly #maxDepth: number;\n readonly #collectFindings: boolean;\n\n constructor(options?: RedactorOptions) {\n const resolved = createRedactionProfile(options?.profile ?? \"local\");\n this.#profile = resolved.profile;\n this.#rules = compileRules(options?.rules, [\n ...resolved.extraKeys,\n ...(options?.extraKeys ?? []),\n ]);\n this.#detectors = [\n ...builtInDetectorsForProfile(this.#profile),\n ...(options?.detectors ?? []),\n ];\n this.#replacement = options?.replacement ?? \"[REDACTED]\";\n this.#maxDepth = options?.maxDepth ?? 32;\n this.#collectFindings = options?.collectFindings ?? true;\n }\n\n redactValue(key: string, value: unknown): unknown {\n return this.#redactValue(value, key, key, 0, {\n findings: [],\n seen: new WeakMap<object, unknown>(),\n });\n }\n\n redactRecord(record: Record<string, unknown>): Record<string, unknown> {\n return this.redact(record).value as Record<string, unknown>;\n }\n\n redact<T = unknown>(value: T): RedactionResult<T> {\n const state: RedactionState = {\n findings: [],\n seen: new WeakMap<object, unknown>(),\n };\n const redacted = this.#redactValue(value, undefined, \"$\", 0, state);\n return {\n value: redacted as T,\n findings: state.findings,\n redacted: state.findings.some((finding) => finding.action !== \"keep\"),\n profile: this.#profile,\n };\n }\n\n #recordFinding(state: RedactionState, finding: RedactionFinding): void {\n if (this.#collectFindings) state.findings.push(finding);\n }\n\n #redactValue(\n value: unknown,\n key: string | undefined,\n path: string,\n depth: number,\n state: RedactionState,\n ): unknown {\n if (depth > this.#maxDepth) {\n this.#recordFinding(\n state,\n makeFinding(path, \"structure.maxDepth\", \"truncate\", \"value\", \"warning\"),\n );\n return \"[Truncated]\";\n }\n\n if (key !== undefined) {\n const rule = this.#rules.find((candidate) => candidate.key === toKey(key));\n if (rule) {\n this.#recordFinding(\n state,\n makeFinding(path, `key.${rule.key}`, actionForRule(rule), \"key\", \"warning\"),\n );\n return applyRule(rule, value, this.#replacement);\n }\n }\n\n for (const detector of this.#detectors) {\n const detections = detector.detect({ path, key, value });\n for (const detection of detections) {\n const action = detection.action ?? \"replace\";\n this.#recordFinding(\n state,\n makeFinding(\n path,\n detector.id,\n action,\n detection.matchKind ?? detector.matchKind ?? \"custom\",\n detection.severity ?? detector.severity ?? \"warning\",\n detection.preview,\n ),\n );\n if (action !== \"keep\") {\n return detection.replacement ?? this.#replacement;\n }\n }\n }\n\n if (Array.isArray(value)) {\n if (state.seen.has(value)) return state.seen.get(value);\n const out: unknown[] = [];\n state.seen.set(value, out);\n value.forEach((item, index) => {\n out[index] = this.#redactValue(item, undefined, indexPath(path, index), depth + 1, state);\n });\n return out;\n }\n\n if (isRecord(value)) {\n if (state.seen.has(value)) return state.seen.get(value);\n const out: Record<string, unknown> = {};\n state.seen.set(value, out);\n for (const [entryKey, entryValue] of Object.entries(value)) {\n out[entryKey] = this.#redactValue(\n entryValue,\n entryKey,\n childPath(path === \"$\" ? \"\" : path, entryKey),\n depth + 1,\n state,\n );\n }\n return out;\n }\n\n return value;\n }\n}\n\nexport function createRedactor(options?: RedactorOptions): Redactor {\n return new Redactor(options);\n}\n\nexport function redact<T = unknown>(value: T, options?: RedactOptions): RedactionResult<T> {\n return createRedactor(options).redact(value);\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agent-inspect/redact",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "Reusable deterministic redaction utilities for local AgentInspect workflows",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/rajudandigam/agent-inspect.git",
|
|
10
|
+
"directory": "packages/redact"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/rajudandigam/agent-inspect/issues"
|
|
14
|
+
},
|
|
15
|
+
"homepage": "https://github.com/rajudandigam/agent-inspect#readme",
|
|
16
|
+
"sideEffects": false,
|
|
17
|
+
"main": "./dist/index.cjs",
|
|
18
|
+
"module": "./dist/index.mjs",
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"import": {
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"default": "./dist/index.mjs"
|
|
25
|
+
},
|
|
26
|
+
"require": {
|
|
27
|
+
"types": "./dist/index.d.cts",
|
|
28
|
+
"default": "./dist/index.cjs"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist"
|
|
34
|
+
],
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "pnpm --workspace-root exec tsup --config tsup.redact.config.ts",
|
|
37
|
+
"test": "vitest run -c ../../vitest.config.ts"
|
|
38
|
+
},
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
}
|
|
42
|
+
}
|