autotel 4.0.0 → 4.2.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/README.md +26 -1
- package/dist/auto.cjs +2 -2
- package/dist/auto.js +1 -1
- package/dist/correlation-id.cjs +1 -1
- package/dist/correlation-id.js +1 -1
- package/dist/decorators.cjs +1 -1
- package/dist/decorators.js +1 -1
- package/dist/{event-Dlqr4ZNL.cjs → event-BhHREDJk.cjs} +3 -3
- package/dist/{event-Dlqr4ZNL.cjs.map → event-BhHREDJk.cjs.map} +1 -1
- package/dist/{event-_58ryBjh.js → event-ByBTV9M2.js} +3 -3
- package/dist/{event-_58ryBjh.js.map → event-ByBTV9M2.js.map} +1 -1
- package/dist/event.cjs +1 -1
- package/dist/event.js +1 -1
- package/dist/{functional-BGkT8J-h.js → functional-DtI0u4vx.js} +19 -19
- package/dist/functional-DtI0u4vx.js.map +1 -0
- package/dist/{functional-C4CzoVrX.cjs → functional-zpzNLhky.cjs} +4 -4
- package/dist/{functional-C4CzoVrX.cjs.map → functional-zpzNLhky.cjs.map} +1 -1
- package/dist/functional.cjs +1 -1
- package/dist/functional.js +1 -1
- package/dist/http.cjs +1 -1
- package/dist/http.js +1 -1
- package/dist/index.cjs +5 -5
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5 -5
- package/dist/{init-DJQOdVlN.d.ts → init-B7u-DjxM.d.ts} +57 -2
- package/dist/init-B7u-DjxM.d.ts.map +1 -0
- package/dist/{init-DvapOXCc.cjs → init-BX7AmFRl.cjs} +40 -21
- package/dist/init-BX7AmFRl.cjs.map +1 -0
- package/dist/{init-Ch6t7MNI.js → init-D-jnNMix.js} +39 -20
- package/dist/init-D-jnNMix.js.map +1 -0
- package/dist/{init-CNp-ee80.d.cts → init-DSrRmVnz.d.cts} +57 -2
- package/dist/init-DSrRmVnz.d.cts.map +1 -0
- package/dist/instrumentation.cjs +1 -1
- package/dist/instrumentation.js +1 -1
- package/dist/logger-D3Ej3DII.js +446 -0
- package/dist/logger-D3Ej3DII.js.map +1 -0
- package/dist/logger-thMPLpOG.cjs +487 -0
- package/dist/logger-thMPLpOG.cjs.map +1 -0
- package/dist/logger.cjs +8 -236
- package/dist/logger.js +2 -204
- package/dist/messaging.cjs +1 -1
- package/dist/messaging.js +1 -1
- package/dist/semantic-helpers.cjs +1 -1
- package/dist/semantic-helpers.js +1 -1
- package/dist/{track-3HY4NGV-.cjs → track-D59FfpL0.cjs} +2 -2
- package/dist/{track-3HY4NGV-.cjs.map → track-D59FfpL0.cjs.map} +1 -1
- package/dist/{track-nsKVy-pj.js → track-wc0HafS_.js} +6 -6
- package/dist/track-wc0HafS_.js.map +1 -0
- package/dist/webhook.cjs +1 -1
- package/dist/webhook.js +1 -1
- package/dist/workflow-distributed.cjs +1 -1
- package/dist/workflow-distributed.js +1 -1
- package/dist/workflow.cjs +1 -1
- package/dist/workflow.js +1 -1
- package/dist/{yaml-config-B3dQ82GR.cjs → yaml-config-Ck2uB0Dp.cjs} +2 -1
- package/dist/yaml-config-Ck2uB0Dp.cjs.map +1 -0
- package/dist/yaml-config.cjs +1 -1
- package/dist/yaml-config.d.cts +7 -1
- package/dist/yaml-config.d.cts.map +1 -1
- package/dist/yaml-config.d.ts +7 -1
- package/dist/yaml-config.d.ts.map +1 -1
- package/dist/yaml-config.js +1 -0
- package/dist/yaml-config.js.map +1 -1
- package/package.json +1 -2
- package/skills/autotel-core/SKILL.md +2 -0
- package/skills/autotel-instrumentation/SKILL.md +25 -0
- package/skills/debug-missing-spans/SKILL.md +3 -1
- package/skills/migrate-to-autotel/SKILL.md +24 -23
- package/skills/review-otel-patterns/SKILL.md +5 -4
- package/dist/functional-BGkT8J-h.js.map +0 -1
- package/dist/init-CNp-ee80.d.cts.map +0 -1
- package/dist/init-Ch6t7MNI.js.map +0 -1
- package/dist/init-DJQOdVlN.d.ts.map +0 -1
- package/dist/init-DvapOXCc.cjs.map +0 -1
- package/dist/logger.cjs.map +0 -1
- package/dist/logger.js.map +0 -1
- package/dist/track-nsKVy-pj.js.map +0 -1
- package/dist/yaml-config-B3dQ82GR.cjs.map +0 -1
- package/src/attribute-redacting-processor.test.ts +0 -763
- package/src/attribute-redacting-processor.ts +0 -621
- package/src/attributes/attachers.ts +0 -161
- package/src/attributes/builders.ts +0 -529
- package/src/attributes/domains.ts +0 -42
- package/src/attributes/index.ts +0 -81
- package/src/attributes/registry.ts +0 -323
- package/src/attributes/types.ts +0 -211
- package/src/attributes/utils.ts +0 -64
- package/src/attributes/validators.ts +0 -266
- package/src/attributes.test.ts +0 -292
- package/src/auto.ts +0 -67
- package/src/autotel-logger.test.ts +0 -548
- package/src/autotel-logger.ts +0 -364
- package/src/baggage-span-processor.test.ts +0 -202
- package/src/baggage-span-processor.ts +0 -100
- package/src/business-baggage.test.ts +0 -500
- package/src/business-baggage.ts +0 -669
- package/src/circuit-breaker.test.ts +0 -341
- package/src/circuit-breaker.ts +0 -184
- package/src/config.test.ts +0 -94
- package/src/config.ts +0 -172
- package/src/correlated-events.test.ts +0 -151
- package/src/correlated-events.ts +0 -47
- package/src/correlation-id.test.ts +0 -163
- package/src/correlation-id.ts +0 -206
- package/src/db.test.ts +0 -252
- package/src/db.ts +0 -447
- package/src/decorators.test.ts +0 -153
- package/src/decorators.ts +0 -188
- package/src/define-event.test.ts +0 -41
- package/src/define-event.ts +0 -58
- package/src/devtools.ts +0 -60
- package/src/drain-pipeline.test.ts +0 -68
- package/src/drain-pipeline.ts +0 -199
- package/src/drain-toolkit.test.ts +0 -113
- package/src/drain-toolkit.ts +0 -129
- package/src/enricher-toolkit.test.ts +0 -67
- package/src/enricher-toolkit.ts +0 -79
- package/src/enrichers.test.ts +0 -150
- package/src/enrichers.ts +0 -145
- package/src/env-config.test.ts +0 -323
- package/src/env-config.ts +0 -309
- package/src/error-catalog.test.ts +0 -133
- package/src/error-catalog.ts +0 -262
- package/src/event-queue.test.ts +0 -864
- package/src/event-queue.ts +0 -699
- package/src/event-subscriber.ts +0 -262
- package/src/event-testing.ts +0 -197
- package/src/event.test.ts +0 -1104
- package/src/event.ts +0 -988
- package/src/events-config.ts +0 -235
- package/src/exporters.ts +0 -165
- package/src/filtering-span-processor.test.ts +0 -281
- package/src/filtering-span-processor.ts +0 -111
- package/src/flatten-attributes.test.ts +0 -76
- package/src/flatten-attributes.ts +0 -80
- package/src/functional.strict-types.typecheck.ts +0 -53
- package/src/functional.test.ts +0 -1464
- package/src/functional.ts +0 -2539
- package/src/functional.types.test.ts +0 -135
- package/src/hook.mjs +0 -15
- package/src/http.test.ts +0 -485
- package/src/http.ts +0 -424
- package/src/index.ts +0 -433
- package/src/init-auto-redactor.test.ts +0 -53
- package/src/init-redactor.test.ts +0 -8
- package/src/init.customization.test.ts +0 -594
- package/src/init.integrations.test.ts +0 -399
- package/src/init.openllmetry.test.ts +0 -194
- package/src/init.protocol.test.ts +0 -215
- package/src/init.ts +0 -2312
- package/src/instrumentation.test.ts +0 -108
- package/src/instrumentation.ts +0 -319
- package/src/logger.test.ts +0 -125
- package/src/logger.ts +0 -341
- package/src/messaging-adapters.test.ts +0 -595
- package/src/messaging-adapters.ts +0 -583
- package/src/messaging-testing.test.ts +0 -573
- package/src/messaging-testing.ts +0 -935
- package/src/messaging.test.ts +0 -1646
- package/src/messaging.ts +0 -2245
- package/src/metric-helpers.ts +0 -47
- package/src/metric-testing.ts +0 -197
- package/src/metric.ts +0 -446
- package/src/metrics.test.ts +0 -241
- package/src/node-require.ts +0 -123
- package/src/operation-context.ts +0 -93
- package/src/parse-error.test.ts +0 -73
- package/src/parse-error.ts +0 -112
- package/src/posthog-logs.test.ts +0 -115
- package/src/posthog-logs.ts +0 -77
- package/src/pretty-console-exporter.test.ts +0 -545
- package/src/pretty-console-exporter.ts +0 -413
- package/src/pretty-log-formatter.test.ts +0 -123
- package/src/pretty-log-formatter.ts +0 -210
- package/src/processors/canonical-log-line-processor.test.ts +0 -523
- package/src/processors/canonical-log-line-processor.ts +0 -396
- package/src/processors.ts +0 -152
- package/src/rate-limiter.test.ts +0 -199
- package/src/rate-limiter.ts +0 -98
- package/src/redact-values.test.ts +0 -90
- package/src/redact-values.ts +0 -34
- package/src/register.ts +0 -37
- package/src/request-logger.test.ts +0 -545
- package/src/request-logger.ts +0 -342
- package/src/sampling.test.ts +0 -1060
- package/src/sampling.ts +0 -737
- package/src/security-schema.test.ts +0 -45
- package/src/security-schema.ts +0 -107
- package/src/semantic-conventions.ts +0 -15
- package/src/semantic-helpers.test.ts +0 -226
- package/src/semantic-helpers.ts +0 -438
- package/src/shutdown.test.ts +0 -364
- package/src/shutdown.ts +0 -246
- package/src/span-name-normalizer.test.ts +0 -377
- package/src/span-name-normalizer.ts +0 -213
- package/src/stable-hash.ts +0 -27
- package/src/structured-error.test.ts +0 -191
- package/src/structured-error.ts +0 -157
- package/src/stub.integration.test.ts +0 -361
- package/src/tail-sampling-processor.test.ts +0 -230
- package/src/tail-sampling-processor.ts +0 -55
- package/src/test-span-collector.test.ts +0 -234
- package/src/test-span-collector.ts +0 -150
- package/src/testing.ts +0 -705
- package/src/trace-context.test.ts +0 -73
- package/src/trace-context.ts +0 -567
- package/src/trace-helpers.new.test.ts +0 -278
- package/src/trace-helpers.test.ts +0 -290
- package/src/trace-helpers.ts +0 -710
- package/src/trace-hybrid.test.ts +0 -42
- package/src/trace-hybrid.ts +0 -37
- package/src/tracer-provider.test.ts +0 -183
- package/src/tracer-provider.ts +0 -266
- package/src/track.test.ts +0 -154
- package/src/track.ts +0 -216
- package/src/validate.test.ts +0 -287
- package/src/validate.ts +0 -307
- package/src/validation-attributes.ts +0 -43
- package/src/validation.test.ts +0 -330
- package/src/validation.ts +0 -246
- package/src/variable-name-inference.test.ts +0 -178
- package/src/variable-name-inference.ts +0 -242
- package/src/webhook.test.ts +0 -649
- package/src/webhook.ts +0 -637
- package/src/workflow-distributed.test.ts +0 -786
- package/src/workflow-distributed.ts +0 -916
- package/src/workflow.async-safety.integration.test.ts +0 -345
- package/src/workflow.test.ts +0 -647
- package/src/workflow.ts +0 -810
- package/src/yaml-config.test.ts +0 -337
- package/src/yaml-config.ts +0 -342
|
@@ -1,763 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for AttributeRedactingProcessor
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
6
|
-
import {
|
|
7
|
-
AttributeRedactingProcessor,
|
|
8
|
-
REDACTOR_PATTERNS,
|
|
9
|
-
REDACTOR_PRESETS,
|
|
10
|
-
createRedactedSpan,
|
|
11
|
-
normalizeAttributeRedactorConfig,
|
|
12
|
-
type AttributeRedactorFn,
|
|
13
|
-
type AttributeRedactorConfig,
|
|
14
|
-
} from './attribute-redacting-processor';
|
|
15
|
-
import type {
|
|
16
|
-
SpanProcessor,
|
|
17
|
-
ReadableSpan,
|
|
18
|
-
} from '@opentelemetry/sdk-trace-base';
|
|
19
|
-
import type { Context } from '@opentelemetry/api';
|
|
20
|
-
import type { Span } from '@opentelemetry/sdk-trace-base';
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Mock span processor to capture forwarded spans
|
|
24
|
-
*/
|
|
25
|
-
class MockSpanProcessor implements SpanProcessor {
|
|
26
|
-
public startedSpans: Span[] = [];
|
|
27
|
-
public endedSpans: ReadableSpan[] = [];
|
|
28
|
-
public flushed = false;
|
|
29
|
-
public shutdownCalled = false;
|
|
30
|
-
|
|
31
|
-
onStart(span: Span): void {
|
|
32
|
-
this.startedSpans.push(span);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
onEnd(span: ReadableSpan): void {
|
|
36
|
-
this.endedSpans.push(span);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
async forceFlush(): Promise<void> {
|
|
40
|
-
this.flushed = true;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async shutdown(): Promise<void> {
|
|
44
|
-
this.shutdownCalled = true;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Create a mock ReadableSpan with given attributes
|
|
50
|
-
*/
|
|
51
|
-
function createMockReadableSpan(
|
|
52
|
-
attributes: Record<string, unknown>,
|
|
53
|
-
): ReadableSpan {
|
|
54
|
-
return {
|
|
55
|
-
name: 'test-span',
|
|
56
|
-
kind: 0,
|
|
57
|
-
spanContext: () => ({
|
|
58
|
-
traceId: 'trace123',
|
|
59
|
-
spanId: 'span123',
|
|
60
|
-
traceFlags: 1,
|
|
61
|
-
}),
|
|
62
|
-
startTime: [0, 0],
|
|
63
|
-
endTime: [1, 0],
|
|
64
|
-
status: { code: 0 },
|
|
65
|
-
attributes,
|
|
66
|
-
links: [],
|
|
67
|
-
events: [],
|
|
68
|
-
duration: [1, 0],
|
|
69
|
-
ended: true,
|
|
70
|
-
resource: { attributes: {} },
|
|
71
|
-
instrumentationScope: { name: 'test', version: '1.0.0' },
|
|
72
|
-
droppedAttributesCount: 0,
|
|
73
|
-
droppedEventsCount: 0,
|
|
74
|
-
droppedLinksCount: 0,
|
|
75
|
-
} as unknown as ReadableSpan;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Create a mock mutable Span
|
|
80
|
-
*/
|
|
81
|
-
function createMockSpan(): Span {
|
|
82
|
-
return {
|
|
83
|
-
name: 'test-span',
|
|
84
|
-
spanContext: () => ({
|
|
85
|
-
traceId: 'trace123',
|
|
86
|
-
spanId: 'span123',
|
|
87
|
-
traceFlags: 1,
|
|
88
|
-
}),
|
|
89
|
-
} as unknown as Span;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
describe('AttributeRedactingProcessor', () => {
|
|
93
|
-
let mockProcessor: MockSpanProcessor;
|
|
94
|
-
|
|
95
|
-
beforeEach(() => {
|
|
96
|
-
mockProcessor = new MockSpanProcessor();
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
describe('custom redactor function', () => {
|
|
100
|
-
it('should redact attributes using custom function', () => {
|
|
101
|
-
const redactor: AttributeRedactorFn = (key, value) => {
|
|
102
|
-
if (key === 'password') return '[REDACTED]';
|
|
103
|
-
return value;
|
|
104
|
-
};
|
|
105
|
-
const processor = new AttributeRedactingProcessor(mockProcessor, {
|
|
106
|
-
redactor: { redactor },
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
const span = createMockReadableSpan({
|
|
110
|
-
password: 'secret123',
|
|
111
|
-
username: 'john',
|
|
112
|
-
});
|
|
113
|
-
processor.onEnd(span);
|
|
114
|
-
|
|
115
|
-
expect(mockProcessor.endedSpans).toHaveLength(1);
|
|
116
|
-
expect(mockProcessor.endedSpans[0]!.attributes.password).toBe(
|
|
117
|
-
'[REDACTED]',
|
|
118
|
-
);
|
|
119
|
-
expect(mockProcessor.endedSpans[0]!.attributes.username).toBe('john');
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it('should forward span to wrapped processor', () => {
|
|
123
|
-
const processor = new AttributeRedactingProcessor(mockProcessor, {
|
|
124
|
-
redactor: 'default',
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
const span = createMockReadableSpan({ test: 'value' });
|
|
128
|
-
processor.onEnd(span);
|
|
129
|
-
|
|
130
|
-
expect(mockProcessor.endedSpans).toHaveLength(1);
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it('should pass through onStart unchanged', () => {
|
|
134
|
-
const processor = new AttributeRedactingProcessor(mockProcessor, {
|
|
135
|
-
redactor: 'default',
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
const span = createMockSpan();
|
|
139
|
-
processor.onStart(span, {} as Context);
|
|
140
|
-
|
|
141
|
-
expect(mockProcessor.startedSpans).toHaveLength(1);
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
describe('built-in presets', () => {
|
|
146
|
-
describe('default preset', () => {
|
|
147
|
-
it('should redact email addresses with smart masking', () => {
|
|
148
|
-
const processor = new AttributeRedactingProcessor(mockProcessor, {
|
|
149
|
-
redactor: 'default',
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
const span = createMockReadableSpan({
|
|
153
|
-
'user.email': 'john.doe@example.com',
|
|
154
|
-
});
|
|
155
|
-
processor.onEnd(span);
|
|
156
|
-
|
|
157
|
-
expect(mockProcessor.endedSpans[0]!.attributes['user.email']).toBe(
|
|
158
|
-
'j***@***.com',
|
|
159
|
-
);
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
it('should redact phone numbers with smart masking', () => {
|
|
163
|
-
const processor = new AttributeRedactingProcessor(mockProcessor, {
|
|
164
|
-
redactor: 'default',
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
const span = createMockReadableSpan({
|
|
168
|
-
'user.phone': '555-123-4567',
|
|
169
|
-
});
|
|
170
|
-
processor.onEnd(span);
|
|
171
|
-
|
|
172
|
-
expect(mockProcessor.endedSpans[0]!.attributes['user.phone']).toBe(
|
|
173
|
-
'********67',
|
|
174
|
-
);
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
it('should redact SSNs', () => {
|
|
178
|
-
const processor = new AttributeRedactingProcessor(mockProcessor, {
|
|
179
|
-
redactor: 'default',
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
const span = createMockReadableSpan({
|
|
183
|
-
'user.ssn': '123-45-6789',
|
|
184
|
-
});
|
|
185
|
-
processor.onEnd(span);
|
|
186
|
-
|
|
187
|
-
// SSN has no smart mask; falls back to the default replacement.
|
|
188
|
-
expect(mockProcessor.endedSpans[0]!.attributes['user.ssn']).toBe(
|
|
189
|
-
'[REDACTED]',
|
|
190
|
-
);
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
it('should redact credit card numbers with smart masking', () => {
|
|
194
|
-
const processor = new AttributeRedactingProcessor(mockProcessor, {
|
|
195
|
-
redactor: 'default',
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
const span = createMockReadableSpan({
|
|
199
|
-
'payment.card': '4111-1111-1111-1111',
|
|
200
|
-
});
|
|
201
|
-
processor.onEnd(span);
|
|
202
|
-
|
|
203
|
-
// PCI-DSS compliant: last 4 digits preserved.
|
|
204
|
-
expect(mockProcessor.endedSpans[0]!.attributes['payment.card']).toBe(
|
|
205
|
-
'****1111',
|
|
206
|
-
);
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
it('should redact sensitive keys by name', () => {
|
|
210
|
-
const processor = new AttributeRedactingProcessor(mockProcessor, {
|
|
211
|
-
redactor: 'default',
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
const span = createMockReadableSpan({
|
|
215
|
-
password: 'mypassword123',
|
|
216
|
-
secret: 'mysecret',
|
|
217
|
-
token: 'abc123token',
|
|
218
|
-
apiKey: 'my-api-key',
|
|
219
|
-
'db.password': 'dbpass',
|
|
220
|
-
});
|
|
221
|
-
processor.onEnd(span);
|
|
222
|
-
|
|
223
|
-
expect(mockProcessor.endedSpans[0]!.attributes.password).toBe(
|
|
224
|
-
'[REDACTED]',
|
|
225
|
-
);
|
|
226
|
-
expect(mockProcessor.endedSpans[0]!.attributes.secret).toBe(
|
|
227
|
-
'[REDACTED]',
|
|
228
|
-
);
|
|
229
|
-
expect(mockProcessor.endedSpans[0]!.attributes.token).toBe(
|
|
230
|
-
'[REDACTED]',
|
|
231
|
-
);
|
|
232
|
-
expect(mockProcessor.endedSpans[0]!.attributes.apiKey).toBe(
|
|
233
|
-
'[REDACTED]',
|
|
234
|
-
);
|
|
235
|
-
// db.password doesn't match the pattern (not exact match)
|
|
236
|
-
expect(mockProcessor.endedSpans[0]!.attributes['db.password']).toBe(
|
|
237
|
-
'dbpass',
|
|
238
|
-
);
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
it('should not redact non-sensitive fields', () => {
|
|
242
|
-
const processor = new AttributeRedactingProcessor(mockProcessor, {
|
|
243
|
-
redactor: 'default',
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
const span = createMockReadableSpan({
|
|
247
|
-
'user.id': '12345',
|
|
248
|
-
'http.method': 'GET',
|
|
249
|
-
'http.url': '/api/users',
|
|
250
|
-
});
|
|
251
|
-
processor.onEnd(span);
|
|
252
|
-
|
|
253
|
-
expect(mockProcessor.endedSpans[0]!.attributes['user.id']).toBe(
|
|
254
|
-
'12345',
|
|
255
|
-
);
|
|
256
|
-
expect(mockProcessor.endedSpans[0]!.attributes['http.method']).toBe(
|
|
257
|
-
'GET',
|
|
258
|
-
);
|
|
259
|
-
expect(mockProcessor.endedSpans[0]!.attributes['http.url']).toBe(
|
|
260
|
-
'/api/users',
|
|
261
|
-
);
|
|
262
|
-
});
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
describe('strict preset', () => {
|
|
266
|
-
it('should redact Bearer tokens with smart masking', () => {
|
|
267
|
-
const processor = new AttributeRedactingProcessor(mockProcessor, {
|
|
268
|
-
redactor: 'strict',
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
const span = createMockReadableSpan({
|
|
272
|
-
'http.header.authorization':
|
|
273
|
-
'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9',
|
|
274
|
-
});
|
|
275
|
-
processor.onEnd(span);
|
|
276
|
-
|
|
277
|
-
expect(
|
|
278
|
-
mockProcessor.endedSpans[0]!.attributes['http.header.authorization'],
|
|
279
|
-
).toBe('Bearer ***');
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
it('should redact JWTs with smart masking', () => {
|
|
283
|
-
const processor = new AttributeRedactingProcessor(mockProcessor, {
|
|
284
|
-
redactor: 'strict',
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
const span = createMockReadableSpan({
|
|
288
|
-
'auth.token':
|
|
289
|
-
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U',
|
|
290
|
-
});
|
|
291
|
-
processor.onEnd(span);
|
|
292
|
-
|
|
293
|
-
expect(mockProcessor.endedSpans[0]!.attributes['auth.token']).toBe(
|
|
294
|
-
'eyJ***.***',
|
|
295
|
-
);
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
it('should redact API keys in values', () => {
|
|
299
|
-
const processor = new AttributeRedactingProcessor(mockProcessor, {
|
|
300
|
-
redactor: 'strict',
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
const span = createMockReadableSpan({
|
|
304
|
-
'request.query': 'apiKey=abc123def456',
|
|
305
|
-
});
|
|
306
|
-
processor.onEnd(span);
|
|
307
|
-
|
|
308
|
-
expect(mockProcessor.endedSpans[0]!.attributes['request.query']).toBe(
|
|
309
|
-
'[REDACTED]',
|
|
310
|
-
);
|
|
311
|
-
});
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
describe('pci-dss preset', () => {
|
|
315
|
-
it('should redact credit card numbers', () => {
|
|
316
|
-
const processor = new AttributeRedactingProcessor(mockProcessor, {
|
|
317
|
-
redactor: 'pci-dss',
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
const span = createMockReadableSpan({
|
|
321
|
-
'payment.cardNumber': '4111111111111111',
|
|
322
|
-
});
|
|
323
|
-
processor.onEnd(span);
|
|
324
|
-
|
|
325
|
-
expect(
|
|
326
|
-
mockProcessor.endedSpans[0]!.attributes['payment.cardNumber'],
|
|
327
|
-
).toBe('[REDACTED]');
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
it('should redact card-related keys', () => {
|
|
331
|
-
const processor = new AttributeRedactingProcessor(mockProcessor, {
|
|
332
|
-
redactor: 'pci-dss',
|
|
333
|
-
});
|
|
334
|
-
|
|
335
|
-
const span = createMockReadableSpan({
|
|
336
|
-
cardNumber: '4111111111111111',
|
|
337
|
-
cvv: '123',
|
|
338
|
-
pan: '4111111111111111',
|
|
339
|
-
});
|
|
340
|
-
processor.onEnd(span);
|
|
341
|
-
|
|
342
|
-
expect(mockProcessor.endedSpans[0]!.attributes.cardNumber).toBe(
|
|
343
|
-
'[REDACTED]',
|
|
344
|
-
);
|
|
345
|
-
expect(mockProcessor.endedSpans[0]!.attributes.cvv).toBe('[REDACTED]');
|
|
346
|
-
expect(mockProcessor.endedSpans[0]!.attributes.pan).toBe('[REDACTED]');
|
|
347
|
-
});
|
|
348
|
-
});
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
describe('custom configuration', () => {
|
|
352
|
-
it('should use custom key patterns', () => {
|
|
353
|
-
const config: AttributeRedactorConfig = {
|
|
354
|
-
keyPatterns: [/internal_id/i],
|
|
355
|
-
replacement: '***',
|
|
356
|
-
};
|
|
357
|
-
const processor = new AttributeRedactingProcessor(mockProcessor, {
|
|
358
|
-
redactor: config,
|
|
359
|
-
});
|
|
360
|
-
|
|
361
|
-
const span = createMockReadableSpan({
|
|
362
|
-
internal_id: 'secret-internal-123',
|
|
363
|
-
public_id: 'public-456',
|
|
364
|
-
});
|
|
365
|
-
processor.onEnd(span);
|
|
366
|
-
|
|
367
|
-
expect(mockProcessor.endedSpans[0]!.attributes.internal_id).toBe('***');
|
|
368
|
-
expect(mockProcessor.endedSpans[0]!.attributes.public_id).toBe(
|
|
369
|
-
'public-456',
|
|
370
|
-
);
|
|
371
|
-
});
|
|
372
|
-
|
|
373
|
-
it('should use custom value patterns', () => {
|
|
374
|
-
const config: AttributeRedactorConfig = {
|
|
375
|
-
builtins: false,
|
|
376
|
-
valuePatterns: [
|
|
377
|
-
{
|
|
378
|
-
name: 'customerId',
|
|
379
|
-
pattern: /CUST-\d{8}/g,
|
|
380
|
-
replacement: 'CUST-***',
|
|
381
|
-
},
|
|
382
|
-
],
|
|
383
|
-
};
|
|
384
|
-
const processor = new AttributeRedactingProcessor(mockProcessor, {
|
|
385
|
-
redactor: config,
|
|
386
|
-
});
|
|
387
|
-
|
|
388
|
-
const span = createMockReadableSpan({
|
|
389
|
-
'order.customer': 'CUST-12345678',
|
|
390
|
-
});
|
|
391
|
-
processor.onEnd(span);
|
|
392
|
-
|
|
393
|
-
expect(mockProcessor.endedSpans[0]!.attributes['order.customer']).toBe(
|
|
394
|
-
'CUST-***',
|
|
395
|
-
);
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
it('should use custom replacement string', () => {
|
|
399
|
-
const config: AttributeRedactorConfig = {
|
|
400
|
-
keyPatterns: [/^secret$/],
|
|
401
|
-
replacement: '<<HIDDEN>>',
|
|
402
|
-
};
|
|
403
|
-
const processor = new AttributeRedactingProcessor(mockProcessor, {
|
|
404
|
-
redactor: config,
|
|
405
|
-
});
|
|
406
|
-
|
|
407
|
-
const span = createMockReadableSpan({
|
|
408
|
-
secret: 'my-secret-value',
|
|
409
|
-
});
|
|
410
|
-
processor.onEnd(span);
|
|
411
|
-
|
|
412
|
-
expect(mockProcessor.endedSpans[0]!.attributes.secret).toBe('<<HIDDEN>>');
|
|
413
|
-
});
|
|
414
|
-
|
|
415
|
-
it('should redact exact dot-path matches from paths config', () => {
|
|
416
|
-
const config: AttributeRedactorConfig = {
|
|
417
|
-
builtins: false,
|
|
418
|
-
paths: ['user.password'],
|
|
419
|
-
};
|
|
420
|
-
const processor = new AttributeRedactingProcessor(mockProcessor, {
|
|
421
|
-
redactor: config,
|
|
422
|
-
});
|
|
423
|
-
|
|
424
|
-
const span = createMockReadableSpan({
|
|
425
|
-
'user.password': 'super-secret',
|
|
426
|
-
'billing.password': 'keep-this',
|
|
427
|
-
});
|
|
428
|
-
processor.onEnd(span);
|
|
429
|
-
|
|
430
|
-
expect(mockProcessor.endedSpans[0]!.attributes['user.password']).toBe(
|
|
431
|
-
'[REDACTED]',
|
|
432
|
-
);
|
|
433
|
-
expect(mockProcessor.endedSpans[0]!.attributes['billing.password']).toBe(
|
|
434
|
-
'keep-this',
|
|
435
|
-
);
|
|
436
|
-
});
|
|
437
|
-
});
|
|
438
|
-
|
|
439
|
-
describe('array handling', () => {
|
|
440
|
-
it('should redact PII in string arrays with smart masking', () => {
|
|
441
|
-
const processor = new AttributeRedactingProcessor(mockProcessor, {
|
|
442
|
-
redactor: 'default',
|
|
443
|
-
});
|
|
444
|
-
|
|
445
|
-
const span = createMockReadableSpan({
|
|
446
|
-
'user.emails': ['john@example.com', 'jane@example.org'],
|
|
447
|
-
});
|
|
448
|
-
processor.onEnd(span);
|
|
449
|
-
|
|
450
|
-
const redactedEmails = mockProcessor.endedSpans[0]!.attributes[
|
|
451
|
-
'user.emails'
|
|
452
|
-
] as string[];
|
|
453
|
-
expect(redactedEmails[0]).toBe('j***@***.com');
|
|
454
|
-
expect(redactedEmails[1]).toBe('j***@***.org');
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
it('should preserve non-string array elements', () => {
|
|
458
|
-
const processor = new AttributeRedactingProcessor(mockProcessor, {
|
|
459
|
-
redactor: 'default',
|
|
460
|
-
});
|
|
461
|
-
|
|
462
|
-
const span = createMockReadableSpan({
|
|
463
|
-
'request.ids': [1, 2, 3],
|
|
464
|
-
});
|
|
465
|
-
processor.onEnd(span);
|
|
466
|
-
|
|
467
|
-
const ids = mockProcessor.endedSpans[0]!.attributes[
|
|
468
|
-
'request.ids'
|
|
469
|
-
] as number[];
|
|
470
|
-
expect(ids).toEqual([1, 2, 3]);
|
|
471
|
-
});
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
describe('non-string values', () => {
|
|
475
|
-
it('should preserve numeric values', () => {
|
|
476
|
-
const processor = new AttributeRedactingProcessor(mockProcessor, {
|
|
477
|
-
redactor: 'default',
|
|
478
|
-
});
|
|
479
|
-
|
|
480
|
-
const span = createMockReadableSpan({
|
|
481
|
-
'http.status_code': 200,
|
|
482
|
-
'request.duration_ms': 150.5,
|
|
483
|
-
});
|
|
484
|
-
processor.onEnd(span);
|
|
485
|
-
|
|
486
|
-
expect(mockProcessor.endedSpans[0]!.attributes['http.status_code']).toBe(
|
|
487
|
-
200,
|
|
488
|
-
);
|
|
489
|
-
expect(
|
|
490
|
-
mockProcessor.endedSpans[0]!.attributes['request.duration_ms'],
|
|
491
|
-
).toBe(150.5);
|
|
492
|
-
});
|
|
493
|
-
|
|
494
|
-
it('should preserve boolean values', () => {
|
|
495
|
-
const processor = new AttributeRedactingProcessor(mockProcessor, {
|
|
496
|
-
redactor: 'default',
|
|
497
|
-
});
|
|
498
|
-
|
|
499
|
-
const span = createMockReadableSpan({
|
|
500
|
-
'request.authenticated': true,
|
|
501
|
-
'cache.hit': false,
|
|
502
|
-
});
|
|
503
|
-
processor.onEnd(span);
|
|
504
|
-
|
|
505
|
-
expect(
|
|
506
|
-
mockProcessor.endedSpans[0]!.attributes['request.authenticated'],
|
|
507
|
-
).toBe(true);
|
|
508
|
-
expect(mockProcessor.endedSpans[0]!.attributes['cache.hit']).toBe(false);
|
|
509
|
-
});
|
|
510
|
-
});
|
|
511
|
-
|
|
512
|
-
describe('error handling', () => {
|
|
513
|
-
it('should forward original span if redactor throws (fail-open)', () => {
|
|
514
|
-
const redactor: AttributeRedactorFn = () => {
|
|
515
|
-
throw new Error('Redactor error');
|
|
516
|
-
};
|
|
517
|
-
const processor = new AttributeRedactingProcessor(mockProcessor, {
|
|
518
|
-
redactor: { redactor },
|
|
519
|
-
});
|
|
520
|
-
|
|
521
|
-
const span = createMockReadableSpan({
|
|
522
|
-
'user.email': 'john@example.com',
|
|
523
|
-
});
|
|
524
|
-
processor.onEnd(span);
|
|
525
|
-
|
|
526
|
-
// Should not throw and should forward original span
|
|
527
|
-
expect(mockProcessor.endedSpans).toHaveLength(1);
|
|
528
|
-
expect(mockProcessor.endedSpans[0]!.attributes['user.email']).toBe(
|
|
529
|
-
'john@example.com',
|
|
530
|
-
);
|
|
531
|
-
});
|
|
532
|
-
|
|
533
|
-
it('should throw for unknown preset', () => {
|
|
534
|
-
expect(() => {
|
|
535
|
-
new AttributeRedactingProcessor(mockProcessor, {
|
|
536
|
-
redactor: 'unknown-preset' as 'default',
|
|
537
|
-
});
|
|
538
|
-
}).toThrow('Unknown attribute redactor preset');
|
|
539
|
-
});
|
|
540
|
-
});
|
|
541
|
-
|
|
542
|
-
describe('lifecycle methods', () => {
|
|
543
|
-
it('should forward forceFlush to wrapped processor', async () => {
|
|
544
|
-
const processor = new AttributeRedactingProcessor(mockProcessor, {
|
|
545
|
-
redactor: 'default',
|
|
546
|
-
});
|
|
547
|
-
|
|
548
|
-
await processor.forceFlush();
|
|
549
|
-
|
|
550
|
-
expect(mockProcessor.flushed).toBe(true);
|
|
551
|
-
});
|
|
552
|
-
|
|
553
|
-
it('should forward shutdown to wrapped processor', async () => {
|
|
554
|
-
const processor = new AttributeRedactingProcessor(mockProcessor, {
|
|
555
|
-
redactor: 'default',
|
|
556
|
-
});
|
|
557
|
-
|
|
558
|
-
await processor.shutdown();
|
|
559
|
-
|
|
560
|
-
expect(mockProcessor.shutdownCalled).toBe(true);
|
|
561
|
-
});
|
|
562
|
-
});
|
|
563
|
-
});
|
|
564
|
-
|
|
565
|
-
describe('createRedactedSpan', () => {
|
|
566
|
-
it('should create a proxy that intercepts attributes', () => {
|
|
567
|
-
const span = createMockReadableSpan({
|
|
568
|
-
password: 'secret123',
|
|
569
|
-
username: 'john',
|
|
570
|
-
});
|
|
571
|
-
|
|
572
|
-
const redactor: AttributeRedactorFn = (key, value) => {
|
|
573
|
-
if (key === 'password') return '[REDACTED]';
|
|
574
|
-
return value;
|
|
575
|
-
};
|
|
576
|
-
|
|
577
|
-
const redactedSpan = createRedactedSpan(span, redactor);
|
|
578
|
-
|
|
579
|
-
expect(redactedSpan.attributes.password).toBe('[REDACTED]');
|
|
580
|
-
expect(redactedSpan.attributes.username).toBe('john');
|
|
581
|
-
});
|
|
582
|
-
|
|
583
|
-
it('should preserve other span properties', () => {
|
|
584
|
-
const span = createMockReadableSpan({ test: 'value' });
|
|
585
|
-
const redactor: AttributeRedactorFn = (_, value) => value;
|
|
586
|
-
|
|
587
|
-
const redactedSpan = createRedactedSpan(span, redactor);
|
|
588
|
-
|
|
589
|
-
expect(redactedSpan.name).toBe('test-span');
|
|
590
|
-
expect(redactedSpan.duration).toEqual([1, 0]);
|
|
591
|
-
expect(redactedSpan.spanContext().traceId).toBe('trace123');
|
|
592
|
-
});
|
|
593
|
-
|
|
594
|
-
it('should handle methods correctly', () => {
|
|
595
|
-
const span = createMockReadableSpan({ test: 'value' });
|
|
596
|
-
const redactor: AttributeRedactorFn = (_, value) => value;
|
|
597
|
-
|
|
598
|
-
const redactedSpan = createRedactedSpan(span, redactor);
|
|
599
|
-
|
|
600
|
-
// spanContext is a method that should still work
|
|
601
|
-
const context = redactedSpan.spanContext();
|
|
602
|
-
expect(context.traceId).toBe('trace123');
|
|
603
|
-
expect(context.spanId).toBe('span123');
|
|
604
|
-
});
|
|
605
|
-
});
|
|
606
|
-
|
|
607
|
-
describe('REDACTOR_PATTERNS', () => {
|
|
608
|
-
it('should export regex patterns for advanced users', () => {
|
|
609
|
-
expect(REDACTOR_PATTERNS.email).toBeInstanceOf(RegExp);
|
|
610
|
-
expect(REDACTOR_PATTERNS.phone).toBeInstanceOf(RegExp);
|
|
611
|
-
expect(REDACTOR_PATTERNS.ssn).toBeInstanceOf(RegExp);
|
|
612
|
-
expect(REDACTOR_PATTERNS.creditCard).toBeInstanceOf(RegExp);
|
|
613
|
-
expect(REDACTOR_PATTERNS.bearerToken).toBeInstanceOf(RegExp);
|
|
614
|
-
expect(REDACTOR_PATTERNS.sensitiveKey).toBeInstanceOf(RegExp);
|
|
615
|
-
});
|
|
616
|
-
|
|
617
|
-
describe('pattern matching', () => {
|
|
618
|
-
it('email should match email addresses', () => {
|
|
619
|
-
REDACTOR_PATTERNS.email.lastIndex = 0;
|
|
620
|
-
expect(REDACTOR_PATTERNS.email.test('john@example.com')).toBe(true);
|
|
621
|
-
REDACTOR_PATTERNS.email.lastIndex = 0;
|
|
622
|
-
expect(
|
|
623
|
-
REDACTOR_PATTERNS.email.test('john.doe+test@sub.example.org'),
|
|
624
|
-
).toBe(true);
|
|
625
|
-
});
|
|
626
|
-
|
|
627
|
-
it('creditCard should match credit card numbers', () => {
|
|
628
|
-
// Reset lastIndex due to global flag
|
|
629
|
-
REDACTOR_PATTERNS.creditCard.lastIndex = 0;
|
|
630
|
-
expect(REDACTOR_PATTERNS.creditCard.test('4111111111111111')).toBe(true);
|
|
631
|
-
REDACTOR_PATTERNS.creditCard.lastIndex = 0;
|
|
632
|
-
expect(REDACTOR_PATTERNS.creditCard.test('4111-1111-1111-1111')).toBe(
|
|
633
|
-
true,
|
|
634
|
-
);
|
|
635
|
-
REDACTOR_PATTERNS.creditCard.lastIndex = 0;
|
|
636
|
-
expect(REDACTOR_PATTERNS.creditCard.test('4111 1111 1111 1111')).toBe(
|
|
637
|
-
true,
|
|
638
|
-
);
|
|
639
|
-
});
|
|
640
|
-
|
|
641
|
-
it('ssn should match SSN patterns', () => {
|
|
642
|
-
REDACTOR_PATTERNS.ssn.lastIndex = 0;
|
|
643
|
-
expect(REDACTOR_PATTERNS.ssn.test('123-45-6789')).toBe(true);
|
|
644
|
-
REDACTOR_PATTERNS.ssn.lastIndex = 0;
|
|
645
|
-
expect(REDACTOR_PATTERNS.ssn.test('123456789')).toBe(true);
|
|
646
|
-
});
|
|
647
|
-
|
|
648
|
-
it('phone should match US phone numbers', () => {
|
|
649
|
-
REDACTOR_PATTERNS.phone.lastIndex = 0;
|
|
650
|
-
expect(REDACTOR_PATTERNS.phone.test('555-123-4567')).toBe(true);
|
|
651
|
-
REDACTOR_PATTERNS.phone.lastIndex = 0;
|
|
652
|
-
expect(REDACTOR_PATTERNS.phone.test('555.123.4567')).toBe(true);
|
|
653
|
-
REDACTOR_PATTERNS.phone.lastIndex = 0;
|
|
654
|
-
expect(REDACTOR_PATTERNS.phone.test('5551234567')).toBe(true);
|
|
655
|
-
});
|
|
656
|
-
});
|
|
657
|
-
});
|
|
658
|
-
|
|
659
|
-
describe('REDACTOR_PRESETS', () => {
|
|
660
|
-
it('should export preset configurations for advanced users', () => {
|
|
661
|
-
expect(REDACTOR_PRESETS['default']).toBeDefined();
|
|
662
|
-
expect(REDACTOR_PRESETS['strict']).toBeDefined();
|
|
663
|
-
expect(REDACTOR_PRESETS['pci-dss']).toBeDefined();
|
|
664
|
-
});
|
|
665
|
-
|
|
666
|
-
it('presets should have required properties', () => {
|
|
667
|
-
expect(REDACTOR_PRESETS['default'].replacement).toBe('[REDACTED]');
|
|
668
|
-
expect(REDACTOR_PRESETS['default'].keyPatterns).toBeDefined();
|
|
669
|
-
expect(REDACTOR_PRESETS['default'].valuePatterns).toBeDefined();
|
|
670
|
-
});
|
|
671
|
-
});
|
|
672
|
-
|
|
673
|
-
describe('edge cases', () => {
|
|
674
|
-
let mockProcessor: MockSpanProcessor;
|
|
675
|
-
|
|
676
|
-
beforeEach(() => {
|
|
677
|
-
mockProcessor = new MockSpanProcessor();
|
|
678
|
-
});
|
|
679
|
-
|
|
680
|
-
it('should handle empty attributes', () => {
|
|
681
|
-
const processor = new AttributeRedactingProcessor(mockProcessor, {
|
|
682
|
-
redactor: 'default',
|
|
683
|
-
});
|
|
684
|
-
|
|
685
|
-
const span = createMockReadableSpan({});
|
|
686
|
-
processor.onEnd(span);
|
|
687
|
-
|
|
688
|
-
expect(mockProcessor.endedSpans).toHaveLength(1);
|
|
689
|
-
expect(Object.keys(mockProcessor.endedSpans[0]!.attributes)).toHaveLength(
|
|
690
|
-
0,
|
|
691
|
-
);
|
|
692
|
-
});
|
|
693
|
-
|
|
694
|
-
it('should handle partial email redaction in mixed content', () => {
|
|
695
|
-
const processor = new AttributeRedactingProcessor(mockProcessor, {
|
|
696
|
-
redactor: 'default',
|
|
697
|
-
});
|
|
698
|
-
|
|
699
|
-
const span = createMockReadableSpan({
|
|
700
|
-
message: 'User john@example.com signed up',
|
|
701
|
-
});
|
|
702
|
-
processor.onEnd(span);
|
|
703
|
-
|
|
704
|
-
expect(mockProcessor.endedSpans[0]!.attributes.message).toBe(
|
|
705
|
-
'User j***@***.com signed up',
|
|
706
|
-
);
|
|
707
|
-
});
|
|
708
|
-
|
|
709
|
-
it('should handle multiple PII in same value', () => {
|
|
710
|
-
const processor = new AttributeRedactingProcessor(mockProcessor, {
|
|
711
|
-
redactor: 'default',
|
|
712
|
-
});
|
|
713
|
-
|
|
714
|
-
const span = createMockReadableSpan({
|
|
715
|
-
contacts: 'Email: john@example.com, Phone: +1 555-123-4567',
|
|
716
|
-
});
|
|
717
|
-
processor.onEnd(span);
|
|
718
|
-
|
|
719
|
-
expect(mockProcessor.endedSpans[0]!.attributes.contacts).toBe(
|
|
720
|
-
'Email: j***@***.com, Phone: +1******67',
|
|
721
|
-
);
|
|
722
|
-
});
|
|
723
|
-
});
|
|
724
|
-
|
|
725
|
-
describe('normalizeAttributeRedactorConfig', () => {
|
|
726
|
-
it('should preserve preset strings', () => {
|
|
727
|
-
expect(normalizeAttributeRedactorConfig('default')).toBe('default');
|
|
728
|
-
});
|
|
729
|
-
|
|
730
|
-
it('should normalize regex-like values from serialized config', () => {
|
|
731
|
-
const normalized = normalizeAttributeRedactorConfig({
|
|
732
|
-
keyPatterns: ['password'],
|
|
733
|
-
patterns: [{ source: 'Bearer\\s+\\w+', flags: 'gi' }],
|
|
734
|
-
valuePatterns: [
|
|
735
|
-
{
|
|
736
|
-
name: 'customerId',
|
|
737
|
-
pattern: { source: 'CUST-\\d{4}', flags: 'g' },
|
|
738
|
-
replacement: 'CUST-***',
|
|
739
|
-
},
|
|
740
|
-
],
|
|
741
|
-
paths: ['user.token'],
|
|
742
|
-
builtins: ['email', 'jwt'],
|
|
743
|
-
replacement: '[MASKED]',
|
|
744
|
-
});
|
|
745
|
-
|
|
746
|
-
expect(typeof normalized).toBe('object');
|
|
747
|
-
if (!normalized || typeof normalized === 'string') {
|
|
748
|
-
throw new Error('Expected object config');
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
expect(normalized.keyPatterns?.[0]).toBeInstanceOf(RegExp);
|
|
752
|
-
expect(normalized.patterns?.[0]).toBeInstanceOf(RegExp);
|
|
753
|
-
expect(normalized.valuePatterns?.[0]?.pattern).toBeInstanceOf(RegExp);
|
|
754
|
-
expect(normalized.paths).toEqual(['user.token']);
|
|
755
|
-
expect(normalized.builtins).toEqual(['email', 'jwt']);
|
|
756
|
-
expect(normalized.replacement).toBe('[MASKED]');
|
|
757
|
-
});
|
|
758
|
-
|
|
759
|
-
it('should return undefined for unsupported raw values', () => {
|
|
760
|
-
expect(normalizeAttributeRedactorConfig(42)).toBeUndefined();
|
|
761
|
-
expect(normalizeAttributeRedactorConfig(null)).toBeUndefined();
|
|
762
|
-
});
|
|
763
|
-
});
|