@ogcio/o11y-sdk-node 0.4.1 → 0.6.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/sdk-core/index.d.ts +1 -0
- package/dist/sdk-core/index.js +1 -0
- package/dist/sdk-core/lib/index.d.ts +2 -0
- package/dist/sdk-core/lib/index.js +2 -0
- package/dist/sdk-core/lib/redaction/basic-redactor.d.ts +7 -0
- package/dist/sdk-core/lib/redaction/basic-redactor.js +18 -0
- package/dist/sdk-core/lib/redaction/email-redactor.d.ts +8 -0
- package/dist/sdk-core/lib/redaction/email-redactor.js +17 -0
- package/dist/sdk-core/lib/redaction/index.d.ts +9 -0
- package/dist/sdk-core/lib/redaction/index.js +4 -0
- package/dist/sdk-core/lib/redaction/ip-redactor.d.ts +9 -0
- package/dist/sdk-core/lib/redaction/ip-redactor.js +23 -0
- package/dist/sdk-core/lib/redaction/ppsn-redactor.d.ts +8 -0
- package/dist/sdk-core/lib/redaction/ppsn-redactor.js +17 -0
- package/dist/sdk-core/lib/utils/data-structures.d.ts +15 -0
- package/dist/{lib/internals/redaction/pii-detection.js → sdk-core/lib/utils/data-structures.js} +4 -27
- package/dist/sdk-core/lib/utils/index.d.ts +2 -0
- package/dist/sdk-core/lib/utils/index.js +2 -0
- package/dist/sdk-core/lib/utils/string-decoding.d.ts +7 -0
- package/dist/sdk-core/lib/utils/string-decoding.js +22 -0
- package/dist/{index.d.ts → sdk-node/index.d.ts} +1 -0
- package/dist/{index.js → sdk-node/index.js} +1 -0
- package/dist/sdk-node/lib/config-manager.d.ts +3 -0
- package/dist/{lib → sdk-node/lib}/config-manager.js +1 -4
- package/dist/sdk-node/lib/exporter/console.d.ts +3 -0
- package/dist/sdk-node/lib/exporter/grpc.d.ts +3 -0
- package/dist/sdk-node/lib/exporter/http.d.ts +3 -0
- package/dist/{lib → sdk-node/lib}/exporter/index.d.ts +2 -2
- package/dist/{lib → sdk-node/lib}/exporter/pii-exporter-decorator.d.ts +6 -6
- package/dist/{lib → sdk-node/lib}/exporter/pii-exporter-decorator.js +7 -3
- package/dist/sdk-node/lib/exporter/processor-config.d.ts +7 -0
- package/dist/{lib → sdk-node/lib}/exporter/processor-config.js +4 -0
- package/dist/{lib → sdk-node/lib}/index.d.ts +11 -0
- package/dist/{lib → sdk-node/lib}/instrumentation.node.js +1 -1
- package/dist/sdk-node/lib/internals/redaction/redactors/email.d.ts +8 -0
- package/dist/sdk-node/lib/internals/redaction/redactors/email.js +19 -0
- package/dist/sdk-node/lib/internals/redaction/redactors/index.d.ts +16 -0
- package/dist/sdk-node/lib/internals/redaction/redactors/index.js +13 -0
- package/dist/sdk-node/lib/internals/redaction/redactors/ip.d.ts +8 -0
- package/dist/sdk-node/lib/internals/redaction/redactors/ip.js +20 -0
- package/dist/sdk-node/lib/internals/redaction/redactors/ppsn.d.ts +8 -0
- package/dist/sdk-node/lib/internals/redaction/redactors/ppsn.js +18 -0
- package/dist/{lib → sdk-node/lib}/metrics.d.ts +1 -1
- package/dist/{lib → sdk-node/lib}/processor/enrich-logger-processor.d.ts +3 -3
- package/dist/{lib → sdk-node/lib}/processor/enrich-span-processor.d.ts +3 -3
- package/dist/sdk-node/lib/processor/nextjs-logger-processor.d.ts +7 -0
- package/dist/sdk-node/lib/processor/nextjs-logger-processor.js +30 -0
- package/dist/sdk-node/lib/processor/nextjs-span-processor.d.ts +8 -0
- package/dist/sdk-node/lib/processor/nextjs-span-processor.js +25 -0
- package/dist/{lib → sdk-node/lib}/resource.d.ts +2 -2
- package/dist/{lib → sdk-node/lib}/traces.d.ts +1 -1
- package/dist/{lib → sdk-node/lib}/traces.js +2 -2
- package/dist/{lib → sdk-node/lib}/url-sampler.d.ts +3 -3
- package/dist/{lib → sdk-node/lib}/utils.d.ts +1 -1
- package/dist/sdk-node/package.json +62 -0
- package/package.json +25 -22
- package/CHANGELOG.md +0 -226
- package/dist/lib/config-manager.d.ts +0 -3
- package/dist/lib/exporter/console.d.ts +0 -3
- package/dist/lib/exporter/grpc.d.ts +0 -3
- package/dist/lib/exporter/http.d.ts +0 -3
- package/dist/lib/exporter/processor-config.d.ts +0 -5
- package/dist/lib/internals/redaction/pii-detection.d.ts +0 -25
- package/dist/lib/internals/redaction/redactors/email.d.ts +0 -8
- package/dist/lib/internals/redaction/redactors/email.js +0 -48
- package/dist/lib/internals/redaction/redactors/index.d.ts +0 -4
- package/dist/lib/internals/redaction/redactors/index.js +0 -6
- package/dist/lib/internals/redaction/redactors/ip.d.ts +0 -10
- package/dist/lib/internals/redaction/redactors/ip.js +0 -54
- package/dist/lib/internals/shared-metrics.d.ts +0 -7
- package/dist/lib/internals/shared-metrics.js +0 -18
- package/dist/package.json +0 -59
- package/dist/vitest.config.d.ts +0 -2
- package/dist/vitest.config.js +0 -45
- package/index.ts +0 -9
- package/lib/config-manager.ts +0 -16
- package/lib/exporter/console.ts +0 -33
- package/lib/exporter/grpc.ts +0 -65
- package/lib/exporter/http.ts +0 -56
- package/lib/exporter/index.ts +0 -9
- package/lib/exporter/pii-exporter-decorator.ts +0 -187
- package/lib/exporter/processor-config.ts +0 -23
- package/lib/index.ts +0 -118
- package/lib/instrumentation.node.ts +0 -115
- package/lib/internals/hooks.ts +0 -14
- package/lib/internals/redaction/pii-detection.ts +0 -113
- package/lib/internals/redaction/redactors/email.ts +0 -58
- package/lib/internals/redaction/redactors/index.ts +0 -12
- package/lib/internals/redaction/redactors/ip.ts +0 -68
- package/lib/internals/shared-metrics.ts +0 -34
- package/lib/metrics.ts +0 -75
- package/lib/processor/enrich-logger-processor.ts +0 -34
- package/lib/processor/enrich-span-processor.ts +0 -39
- package/lib/resource.ts +0 -30
- package/lib/traces.ts +0 -78
- package/lib/url-sampler.ts +0 -52
- package/lib/utils.ts +0 -22
- package/test/config-manager.test.ts +0 -34
- package/test/exporter/pii-exporter-decorator.test.ts +0 -88
- package/test/index.test.ts +0 -70
- package/test/integration/README.md +0 -26
- package/test/integration/http-tracing.integration.test.ts +0 -56
- package/test/integration/pii.integration.test.ts +0 -68
- package/test/integration/run.sh +0 -88
- package/test/internals/hooks.test.ts +0 -45
- package/test/internals/pii-detection.test.ts +0 -265
- package/test/internals/redactors/email.test.ts +0 -81
- package/test/internals/redactors/ip.test.ts +0 -93
- package/test/internals/shared-metrics.test.ts +0 -34
- package/test/metrics.test.ts +0 -142
- package/test/node-config.test.ts +0 -190
- package/test/processor/enrich-logger-processor.test.ts +0 -58
- package/test/processor/enrich-span-processor.test.ts +0 -52
- package/test/resource.test.ts +0 -33
- package/test/traces/active-span.test.ts +0 -28
- package/test/traces/with-span.test.ts +0 -340
- package/test/url-sampler.test.ts +0 -215
- package/test/utils/alloy-log-parser.ts +0 -53
- package/test/utils/mock-signals.ts +0 -144
- package/test/validation.test.ts +0 -103
- package/tsconfig.json +0 -15
- package/vitest.config.ts +0 -46
- /package/dist/{lib → sdk-node/lib}/exporter/console.js +0 -0
- /package/dist/{lib → sdk-node/lib}/exporter/grpc.js +0 -0
- /package/dist/{lib → sdk-node/lib}/exporter/http.js +0 -0
- /package/dist/{lib → sdk-node/lib}/exporter/index.js +0 -0
- /package/dist/{lib → sdk-node/lib}/index.js +0 -0
- /package/dist/{lib → sdk-node/lib}/instrumentation.node.d.ts +0 -0
- /package/dist/{lib → sdk-node/lib}/internals/hooks.d.ts +0 -0
- /package/dist/{lib → sdk-node/lib}/internals/hooks.js +0 -0
- /package/dist/{lib → sdk-node/lib}/metrics.js +0 -0
- /package/dist/{lib → sdk-node/lib}/processor/enrich-logger-processor.js +0 -0
- /package/dist/{lib → sdk-node/lib}/processor/enrich-span-processor.js +0 -0
- /package/dist/{lib → sdk-node/lib}/resource.js +0 -0
- /package/dist/{lib → sdk-node/lib}/url-sampler.js +0 -0
- /package/dist/{lib → sdk-node/lib}/utils.js +0 -0
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi, beforeEach } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
_cleanStringPII,
|
|
4
|
-
_containsEncodedComponents,
|
|
5
|
-
_recursiveObjectClean,
|
|
6
|
-
} from "../../lib/internals/redaction/pii-detection.js";
|
|
7
|
-
import * as sharedMetrics from "../../lib/internals/shared-metrics.js";
|
|
8
|
-
import { emailRedactor } from "../../lib/internals/redaction/redactors/email";
|
|
9
|
-
import { ipRedactor } from "../../lib/internals/redaction/redactors/ip";
|
|
10
|
-
|
|
11
|
-
describe("PII Detection Utils", () => {
|
|
12
|
-
const mockMetricAdd = vi.fn();
|
|
13
|
-
|
|
14
|
-
beforeEach(() => {
|
|
15
|
-
vi.restoreAllMocks();
|
|
16
|
-
vi.spyOn(sharedMetrics, "_getPIICounterRedactionMetric").mockReturnValue({
|
|
17
|
-
add: mockMetricAdd,
|
|
18
|
-
});
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
describe("_cleanStringPII", () => {
|
|
22
|
-
it("redacts plain PII", () => {
|
|
23
|
-
const input = "admin@example.com";
|
|
24
|
-
const output = _cleanStringPII(input, "log", [emailRedactor]);
|
|
25
|
-
|
|
26
|
-
expect(output).toBe("[REDACTED EMAIL]");
|
|
27
|
-
expect(mockMetricAdd).toHaveBeenCalledWith(
|
|
28
|
-
1,
|
|
29
|
-
expect.objectContaining({
|
|
30
|
-
pii_email_domain: "example.com",
|
|
31
|
-
pii_type: "email",
|
|
32
|
-
redaction_source: "log",
|
|
33
|
-
}),
|
|
34
|
-
);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it("redacts email in URL-encoded string", () => {
|
|
38
|
-
const input = "user%40gmail.com";
|
|
39
|
-
const output = _cleanStringPII(input, "log", [emailRedactor]);
|
|
40
|
-
|
|
41
|
-
expect(output).toBe("[REDACTED EMAIL]");
|
|
42
|
-
expect(mockMetricAdd).toHaveBeenCalledWith(
|
|
43
|
-
1,
|
|
44
|
-
expect.objectContaining({
|
|
45
|
-
pii_format: "url",
|
|
46
|
-
pii_email_domain: "gmail.com",
|
|
47
|
-
}),
|
|
48
|
-
);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it("redacts ip in URL-encoded string", () => {
|
|
52
|
-
const input = "%20127.0.0.1";
|
|
53
|
-
const output = _cleanStringPII(input, "log", [ipRedactor]);
|
|
54
|
-
|
|
55
|
-
expect(output).toBe(" [REDACTED IPV4]");
|
|
56
|
-
expect(mockMetricAdd).toHaveBeenCalledWith(
|
|
57
|
-
1,
|
|
58
|
-
expect.objectContaining({
|
|
59
|
-
pii_format: "url",
|
|
60
|
-
pii_type: "IPv4",
|
|
61
|
-
redaction_source: "log",
|
|
62
|
-
}),
|
|
63
|
-
);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it("handles strings without PII unchanged", () => {
|
|
67
|
-
const input = "hello world";
|
|
68
|
-
const output = _cleanStringPII(input, "log", [emailRedactor]);
|
|
69
|
-
|
|
70
|
-
expect(output).toBe("hello world");
|
|
71
|
-
expect(mockMetricAdd).not.toHaveBeenCalled();
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it("ignores non-string input", () => {
|
|
75
|
-
// @ts-expect-error
|
|
76
|
-
expect(_cleanStringPII(1234, "trace", [emailRedactor])).toBe(1234);
|
|
77
|
-
// @ts-expect-error
|
|
78
|
-
expect(_cleanStringPII(true, "trace", [emailRedactor])).toBe(true);
|
|
79
|
-
expect(
|
|
80
|
-
_cleanStringPII(undefined, "trace", [emailRedactor]),
|
|
81
|
-
).toBeUndefined();
|
|
82
|
-
expect(mockMetricAdd).not.toHaveBeenCalled();
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
describe("_recursiveObjectClean", () => {
|
|
87
|
-
it("cleans string PII", () => {
|
|
88
|
-
const result = _recursiveObjectClean("demo@abc.com", "log", [
|
|
89
|
-
emailRedactor,
|
|
90
|
-
]);
|
|
91
|
-
expect(result).toBe("[REDACTED EMAIL]");
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it("cleans array of strings", () => {
|
|
95
|
-
const input = ["one@gmail.com", "two@example.com"];
|
|
96
|
-
const output = _recursiveObjectClean(input, "log", [emailRedactor]);
|
|
97
|
-
|
|
98
|
-
expect(output).toEqual(["[REDACTED EMAIL]", "[REDACTED EMAIL]"]);
|
|
99
|
-
expect(mockMetricAdd).toHaveBeenCalledTimes(2);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it("cleans deeply nested object", () => {
|
|
103
|
-
const input = {
|
|
104
|
-
user: {
|
|
105
|
-
email: "test@gmail.com",
|
|
106
|
-
profile: {
|
|
107
|
-
contact: "foo@example.com",
|
|
108
|
-
},
|
|
109
|
-
},
|
|
110
|
-
status: "active",
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
const result = _recursiveObjectClean(input, "log", [emailRedactor]);
|
|
114
|
-
|
|
115
|
-
expect(result).toEqual({
|
|
116
|
-
user: {
|
|
117
|
-
email: "[REDACTED EMAIL]",
|
|
118
|
-
profile: {
|
|
119
|
-
contact: "[REDACTED EMAIL]",
|
|
120
|
-
},
|
|
121
|
-
},
|
|
122
|
-
status: "active",
|
|
123
|
-
});
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it("cleans Uint8Array input", () => {
|
|
127
|
-
const str = "admin@gmail.com";
|
|
128
|
-
const buffer = new TextEncoder().encode(str);
|
|
129
|
-
const result = _recursiveObjectClean(buffer, "log", [emailRedactor]);
|
|
130
|
-
const decoded = new TextDecoder().decode(result as Uint8Array);
|
|
131
|
-
|
|
132
|
-
expect(decoded).toBe("[REDACTED EMAIL]");
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
it("skips malformed Uint8Array decode", () => {
|
|
136
|
-
const corrupted = new Uint8Array([0xff, 0xfe, 0xfd]);
|
|
137
|
-
const result = _recursiveObjectClean(corrupted, "log", [emailRedactor]);
|
|
138
|
-
|
|
139
|
-
// Should return a Uint8Array, but unmodified/redaction should not happen
|
|
140
|
-
expect(result).toBeInstanceOf(Uint8Array);
|
|
141
|
-
expect(result).not.toEqual(expect.arrayContaining([91, 82, 69]));
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it("cleans arrays of values", () => {
|
|
145
|
-
const result = _recursiveObjectClean(
|
|
146
|
-
["bob@abc.com", 123, { nested: "jane@example.com" }],
|
|
147
|
-
"log",
|
|
148
|
-
[emailRedactor],
|
|
149
|
-
);
|
|
150
|
-
|
|
151
|
-
expect(result).toEqual([
|
|
152
|
-
"[REDACTED EMAIL]",
|
|
153
|
-
123,
|
|
154
|
-
{ nested: "[REDACTED EMAIL]" },
|
|
155
|
-
]);
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
it("passes null and boolean through", () => {
|
|
159
|
-
expect(_recursiveObjectClean(null, "log", [emailRedactor])).toBeNull();
|
|
160
|
-
expect(
|
|
161
|
-
_recursiveObjectClean(undefined, "log", [emailRedactor]),
|
|
162
|
-
).toBeUndefined();
|
|
163
|
-
expect(_recursiveObjectClean(true, "log", [emailRedactor])).toBe(true);
|
|
164
|
-
expect(_recursiveObjectClean(false, "log", [emailRedactor])).toBe(false);
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
describe("_containsEncodedComponents", () => {
|
|
169
|
-
describe("should return true for properly URL encoded strings", () => {
|
|
170
|
-
it.each([
|
|
171
|
-
["hello%20world", "Space encoded as %20"],
|
|
172
|
-
["test%2Bvalue", "Plus sign encoded as %2B"],
|
|
173
|
-
["path%2Fto%2Ffile", "Forward slashes encoded"],
|
|
174
|
-
["user%40domain.com", "@ symbol encoded"],
|
|
175
|
-
["100%25%20off", "Percent and space encoded"],
|
|
176
|
-
["a%3Db%26c%3Dd", "Query parameters (a=b&c=d)"],
|
|
177
|
-
["caf%C3%A9", "UTF-8 encoded (café)"],
|
|
178
|
-
["price%3A%20%2410", "Colon, space, dollar ($10)"],
|
|
179
|
-
["%22quoted%22", "Double quotes encoded"],
|
|
180
|
-
["https%3A%2F%2Fexample.com", "Full URL encoded"],
|
|
181
|
-
["file%20name.txt", "filename encoded"],
|
|
182
|
-
["search%3Fq%3Dhello%20world", "Query string encoded"],
|
|
183
|
-
["%3C%3E%26%22%27", "HTML special chars encoded (<>&\"')"],
|
|
184
|
-
["%E2%9C%93", "UTF-8 checkmark (✓) encoded"],
|
|
185
|
-
["test%2b", "Lowercase hex"],
|
|
186
|
-
["%E4%B8%AD%E6%96%87", "Chinese characters encoded"],
|
|
187
|
-
])('should detect "%s" as URL encoded (%s)', (input, description) => {
|
|
188
|
-
expect(_containsEncodedComponents(input)).toBe(true);
|
|
189
|
-
});
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
describe("should return false for non-URL encoded strings", () => {
|
|
193
|
-
it.each([
|
|
194
|
-
["test", "Simple ASCII string"],
|
|
195
|
-
["hello world", "Unencoded space"],
|
|
196
|
-
["user@domain.com", "Unencoded email"],
|
|
197
|
-
["simple123", "Alphanumeric only"],
|
|
198
|
-
["", "Empty string"],
|
|
199
|
-
["25%%", "Literal percent signs"],
|
|
200
|
-
["100% off", "Percent without hex digits"],
|
|
201
|
-
["test%2", "Incomplete percent encoding"],
|
|
202
|
-
["hello%ZZ", "Invalid hex digits"],
|
|
203
|
-
["test%2G", "Invalid hex digit G"],
|
|
204
|
-
["bad%encoding%here", "Percent without hex pairs"],
|
|
205
|
-
["hello%20world and more", "Partially encoded string"],
|
|
206
|
-
["hello%20world%21%20how%20are%20you", "Overly encoded string"],
|
|
207
|
-
["café", "Unicode characters (unencoded)"],
|
|
208
|
-
["hello+world", "Plus sign (form encoding style)"],
|
|
209
|
-
["test%", "Trailing percent"],
|
|
210
|
-
["hello%20world%", "Encoded content with trailing percent"],
|
|
211
|
-
["%", "Single percent"],
|
|
212
|
-
["%%", "Double percent"],
|
|
213
|
-
["normal text with % symbols", "Text with percent but no encoding"],
|
|
214
|
-
["price: $100%", "Currency with percent"],
|
|
215
|
-
["file.txt", "Simple filename"],
|
|
216
|
-
["path/to/file", "Unencoded path"],
|
|
217
|
-
["query?param=value", "Unencoded query string"],
|
|
218
|
-
["hello%world", "Percent without following hex"],
|
|
219
|
-
["test%1", "Single hex digit after percent"],
|
|
220
|
-
["hello%zz", "Non-hex characters after percent"],
|
|
221
|
-
])('should not detect "%s" as URL encoded (%s)', (input, description) => {
|
|
222
|
-
expect(_containsEncodedComponents(input)).toBe(false);
|
|
223
|
-
});
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
describe("Error handling", () => {
|
|
227
|
-
it.each([
|
|
228
|
-
["%C0%80", "Overlong UTF-8 encoding (security concern)"],
|
|
229
|
-
["%ED%A0%80", "UTF-8 surrogate (invalid)"],
|
|
230
|
-
["%FF%FE", "Invalid UTF-8 sequence"],
|
|
231
|
-
["test%20%ZZ", "Mix of valid and invalid encoding"],
|
|
232
|
-
])('should handle "%s" (%s)', (input, description) => {
|
|
233
|
-
// These should not throw errors
|
|
234
|
-
expect(() => _containsEncodedComponents(input)).not.toThrow();
|
|
235
|
-
|
|
236
|
-
// Most of these should return false due to invalid sequences
|
|
237
|
-
// or security-related encoding issues
|
|
238
|
-
const result = _containsEncodedComponents(input);
|
|
239
|
-
expect(typeof result).toBe("boolean");
|
|
240
|
-
});
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
describe("real-world url examples", () => {
|
|
244
|
-
it.each([
|
|
245
|
-
[
|
|
246
|
-
"https%3A%2F%2Fgoogle.com%2Fsearch%3Fq%3Djavascript",
|
|
247
|
-
"Encoded Google search URL",
|
|
248
|
-
],
|
|
249
|
-
["The%20quick%20brown%20fox", "Sentence with spaces encoded"],
|
|
250
|
-
[
|
|
251
|
-
"/api/user?body=here%20are%20all%20my%20secrets",
|
|
252
|
-
"contextually encoded API path",
|
|
253
|
-
],
|
|
254
|
-
["redirect_uri=https%3A%2F%2Fapp.com%2Fcallback", "OAuth redirect URI"],
|
|
255
|
-
["data%3Atext%2Fplain%3Bbase64%2CSGVsbG8%3D", "Data URL encoded"],
|
|
256
|
-
])('real-world case: "%s" (%s)', (input, description) => {
|
|
257
|
-
const result = _containsEncodedComponents(input);
|
|
258
|
-
|
|
259
|
-
// Verify the function doesn't crash
|
|
260
|
-
expect(typeof result).toBe("boolean");
|
|
261
|
-
expect(result).toBe(true);
|
|
262
|
-
});
|
|
263
|
-
});
|
|
264
|
-
});
|
|
265
|
-
});
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
describe,
|
|
3
|
-
expect,
|
|
4
|
-
it,
|
|
5
|
-
vi,
|
|
6
|
-
beforeEach,
|
|
7
|
-
beforeAll,
|
|
8
|
-
afterAll,
|
|
9
|
-
} from "vitest";
|
|
10
|
-
|
|
11
|
-
import * as sharedMetrics from "../../../lib/internals/shared-metrics.js";
|
|
12
|
-
import { ipRedactor } from "../../../lib/internals/redaction/redactors/ip";
|
|
13
|
-
import { emailRedactor } from "../../../lib/internals/redaction/redactors/email";
|
|
14
|
-
|
|
15
|
-
describe("Email Redaction utils", () => {
|
|
16
|
-
describe("tracks metrics", () => {
|
|
17
|
-
const mockMetricAdd = vi.fn();
|
|
18
|
-
|
|
19
|
-
beforeEach(() => {
|
|
20
|
-
vi.restoreAllMocks();
|
|
21
|
-
vi.spyOn(sharedMetrics, "_getPIICounterRedactionMetric").mockReturnValue({
|
|
22
|
-
add: mockMetricAdd,
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it("redacts plain PII and tracks redaction with metric", () => {
|
|
27
|
-
const input = "admin@example.com";
|
|
28
|
-
const output = emailRedactor(input, "log", "string");
|
|
29
|
-
|
|
30
|
-
expect(output).toBe("[REDACTED EMAIL]");
|
|
31
|
-
expect(mockMetricAdd).toHaveBeenCalledWith(
|
|
32
|
-
1,
|
|
33
|
-
expect.objectContaining({
|
|
34
|
-
pii_email_domain: "example.com",
|
|
35
|
-
pii_type: "email",
|
|
36
|
-
redaction_source: "log",
|
|
37
|
-
}),
|
|
38
|
-
);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it("handles strings without PII unchanged", () => {
|
|
42
|
-
const input = "hello world";
|
|
43
|
-
const output = ipRedactor(input, "log", "string");
|
|
44
|
-
|
|
45
|
-
expect(output).toBe("hello world");
|
|
46
|
-
expect(mockMetricAdd).not.toHaveBeenCalled();
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
describe("Redacts email addresses", () => {
|
|
51
|
-
beforeAll(() => {
|
|
52
|
-
vi.spyOn(sharedMetrics, "_getPIICounterRedactionMetric").mockReturnValue({
|
|
53
|
-
add: vi.fn(),
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
afterAll(() => {
|
|
58
|
-
vi.restoreAllMocks();
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it.each`
|
|
62
|
-
value | expectedRedactedValue
|
|
63
|
-
${"user+tag@example.com"} | ${"[REDACTED EMAIL]"}
|
|
64
|
-
${"user.name+tag+sorting@example.com"} | ${"[REDACTED EMAIL]"}
|
|
65
|
-
${"x@example.museum"} | ${"[REDACTED EMAIL]"}
|
|
66
|
-
${"a.b-c_d@example.co.uk"} | ${"[REDACTED EMAIL]"}
|
|
67
|
-
${"üser@example.de"} | ${"[REDACTED EMAIL]"}
|
|
68
|
-
${"john.doe@xn--exmple-cua.com"} | ${"[REDACTED EMAIL]"}
|
|
69
|
-
${"üser@example.de"} | ${"[REDACTED EMAIL]"}
|
|
70
|
-
${"plainaddress"} | ${"plainaddress"}
|
|
71
|
-
${"@missinglocal.org"} | ${"@missinglocal.org"}
|
|
72
|
-
${"user@invalid_domain.com"} | ${"user@invalid_domain.com"}
|
|
73
|
-
`(
|
|
74
|
-
"returns $expectedRedactedValue for value '$value'",
|
|
75
|
-
async ({ value, expectedRedactedValue }: Record<string, string>) => {
|
|
76
|
-
const result = emailRedactor(value, "log", "string");
|
|
77
|
-
expect(result).toBe(expectedRedactedValue);
|
|
78
|
-
},
|
|
79
|
-
);
|
|
80
|
-
});
|
|
81
|
-
});
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
describe,
|
|
3
|
-
expect,
|
|
4
|
-
it,
|
|
5
|
-
vi,
|
|
6
|
-
beforeEach,
|
|
7
|
-
beforeAll,
|
|
8
|
-
afterAll,
|
|
9
|
-
} from "vitest";
|
|
10
|
-
|
|
11
|
-
import * as sharedMetrics from "../../../lib/internals/shared-metrics.js";
|
|
12
|
-
import { ipRedactor } from "../../../lib/internals/redaction/redactors/ip";
|
|
13
|
-
|
|
14
|
-
describe("IP Redaction utils", () => {
|
|
15
|
-
describe("tracks metrics", () => {
|
|
16
|
-
const mockMetricAdd = vi.fn();
|
|
17
|
-
|
|
18
|
-
beforeEach(() => {
|
|
19
|
-
vi.restoreAllMocks();
|
|
20
|
-
vi.spyOn(sharedMetrics, "_getPIICounterRedactionMetric").mockReturnValue({
|
|
21
|
-
add: mockMetricAdd,
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it("redacts plain PII and tracks redaction with metric", () => {
|
|
26
|
-
const input = "255.255.255.255";
|
|
27
|
-
const output = ipRedactor(input, "log", "string");
|
|
28
|
-
|
|
29
|
-
expect(output).toBe("[REDACTED IPV4]");
|
|
30
|
-
expect(mockMetricAdd).toHaveBeenCalledWith(
|
|
31
|
-
1,
|
|
32
|
-
expect.objectContaining({
|
|
33
|
-
pii_format: "string",
|
|
34
|
-
pii_type: "IPv4",
|
|
35
|
-
redaction_source: "log",
|
|
36
|
-
}),
|
|
37
|
-
);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it("handles strings without PII unchanged", () => {
|
|
41
|
-
const input = "hello world";
|
|
42
|
-
const output = ipRedactor(input, "log", "string");
|
|
43
|
-
|
|
44
|
-
expect(output).toBe("hello world");
|
|
45
|
-
expect(mockMetricAdd).not.toHaveBeenCalled();
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
describe("Redacts IPv4 and IPv6", () => {
|
|
50
|
-
beforeAll(() => {
|
|
51
|
-
vi.spyOn(sharedMetrics, "_getPIICounterRedactionMetric").mockReturnValue({
|
|
52
|
-
add: vi.fn(),
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
afterAll(() => {
|
|
57
|
-
vi.restoreAllMocks();
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it.each`
|
|
61
|
-
value | expectedRedactedValue
|
|
62
|
-
${"hello world"} | ${"hello world"}
|
|
63
|
-
${"hello 127.0.0.1"} | ${"hello [REDACTED IPV4]"}
|
|
64
|
-
${"127.0.0.1, hello!"} | ${"[REDACTED IPV4], hello!"}
|
|
65
|
-
${"127.0.0.1,127.0.0.1"} | ${"[REDACTED IPV4],[REDACTED IPV4]"}
|
|
66
|
-
${"127.0.0.1127.0.0.1"} | ${"127.0.0.1127.0.0.1"}
|
|
67
|
-
${"256.1.1.1"} | ${"256.1.1.1"}
|
|
68
|
-
${"0.0.0.0!"} | ${"[REDACTED IPV4]!"}
|
|
69
|
-
${"text0.0.0.0"} | ${"text[REDACTED IPV4]"}
|
|
70
|
-
${"%A00.0.0.0"} | ${"[REDACTED IPV4]"}
|
|
71
|
-
${"0.0.0.0%20"} | ${"[REDACTED IPV4]"}
|
|
72
|
-
${"0.0.text0.0"} | ${"0.0.text0.0"}
|
|
73
|
-
${"2001:0db8::1"} | ${"[REDACTED IPV6]"}
|
|
74
|
-
${"::1"} | ${"[REDACTED IPV6]"}
|
|
75
|
-
${"%20::1"} | ${"[REDACTED IPV6]"}
|
|
76
|
-
${"::1%A0"} | ${"[REDACTED IPV6]"}
|
|
77
|
-
${"text::1"} | ${"text[REDACTED IPV6]"}
|
|
78
|
-
${"::1text"} | ${"[REDACTED IPV6]text"}
|
|
79
|
-
${"sentence ending with f::1"} | ${"sentence ending with [REDACTED IPV6]"}
|
|
80
|
-
${"sentence ending with :::1"} | ${"sentence ending with :::1"}
|
|
81
|
-
${"2001:0DB8:85A3:0000:0000:8A2E:0370:7334::1"} | ${"2001:0DB8:85A3:0000:0000:8A2E:0370:7334::1"}
|
|
82
|
-
${"2001:0DB8:85A3:0000:text:8A2E:0370:7334"} | ${"2001:0DB8:85A3:0000:text:8A2E:0370:7334"}
|
|
83
|
-
${"2001:0db8::12001:0db8::1"} | ${"2001:0db8::12001:0db8::1"}
|
|
84
|
-
${"2001:0db8::1,2001:0db8::1"} | ${"[REDACTED IPV6],[REDACTED IPV6]"}
|
|
85
|
-
`(
|
|
86
|
-
"returns $expectedRedactedValue for value '$value'",
|
|
87
|
-
async ({ value, expectedRedactedValue }: Record<string, string>) => {
|
|
88
|
-
const result = ipRedactor(value, "log", "string");
|
|
89
|
-
expect(result).toBe(expectedRedactedValue);
|
|
90
|
-
},
|
|
91
|
-
);
|
|
92
|
-
});
|
|
93
|
-
});
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
vi.mock("../../lib/metrics", () => {
|
|
2
|
-
return {
|
|
3
|
-
getMetric: vi.fn(),
|
|
4
|
-
};
|
|
5
|
-
});
|
|
6
|
-
|
|
7
|
-
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
8
|
-
import { _getPIICounterRedactionMetric } from "../../lib/internals/shared-metrics.js";
|
|
9
|
-
import { getMetric } from "../../lib/metrics"; // Get the mocked function
|
|
10
|
-
|
|
11
|
-
describe("shared metrics", () => {
|
|
12
|
-
const mockMetric = { add: vi.fn() };
|
|
13
|
-
|
|
14
|
-
beforeEach(() => {
|
|
15
|
-
vi.resetModules(); // Clear module-level cache
|
|
16
|
-
vi.restoreAllMocks();
|
|
17
|
-
(getMetric as vi.Mock).mockClear(); // clear call history
|
|
18
|
-
(getMetric as vi.Mock).mockReturnValue(mockMetric);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it("calls getMetric with correct arguments and caches result", () => {
|
|
22
|
-
const metric1 = _getPIICounterRedactionMetric();
|
|
23
|
-
const metric2 = _getPIICounterRedactionMetric();
|
|
24
|
-
|
|
25
|
-
expect(getMetric).toHaveBeenCalledOnce();
|
|
26
|
-
expect(getMetric).toHaveBeenCalledWith("counter", {
|
|
27
|
-
meterName: "o11y",
|
|
28
|
-
metricName: "o11y_pii_redaction",
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
expect(metric1).toBe(mockMetric);
|
|
32
|
-
expect(metric2).toBe(mockMetric); // should be cached
|
|
33
|
-
});
|
|
34
|
-
});
|
package/test/metrics.test.ts
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import { describe, test, expect, vi, beforeEach, assert } from "vitest";
|
|
2
|
-
import { getMetric, MetricsParams } from "../lib/metrics";
|
|
3
|
-
|
|
4
|
-
const mockMeter = {
|
|
5
|
-
createGauge: vi.fn(),
|
|
6
|
-
createHistogram: vi.fn(),
|
|
7
|
-
createCounter: vi.fn(),
|
|
8
|
-
createUpDownCounter: vi.fn(),
|
|
9
|
-
createObservableCounter: vi.fn(),
|
|
10
|
-
createObservableUpDownCounter: vi.fn(),
|
|
11
|
-
createObservableGauge: vi.fn(),
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
vi.mock("@opentelemetry/api", async () => {
|
|
15
|
-
const { createNoopMeter } = await import("@opentelemetry/api");
|
|
16
|
-
|
|
17
|
-
return {
|
|
18
|
-
metrics: {
|
|
19
|
-
getMeter: vi.fn(() => mockMeter),
|
|
20
|
-
},
|
|
21
|
-
createNoopMeter: createNoopMeter,
|
|
22
|
-
};
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
describe("MetricsFactoryMap", () => {
|
|
26
|
-
beforeEach(() => {
|
|
27
|
-
vi.clearAllMocks();
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
const validMetricParams: MetricsParams = {
|
|
31
|
-
metricName: "test-metric",
|
|
32
|
-
meterName: "test-meter",
|
|
33
|
-
options: { description: "A test metric" },
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
test("should call createGauge when type is 'gauge'", () => {
|
|
37
|
-
mockMeter.createGauge.mockReturnValue("mocked-gauge");
|
|
38
|
-
|
|
39
|
-
const result = getMetric("gauge", validMetricParams);
|
|
40
|
-
|
|
41
|
-
expect(result).toBe("mocked-gauge");
|
|
42
|
-
expect(mockMeter.createGauge).toHaveBeenCalledWith(
|
|
43
|
-
validMetricParams.metricName,
|
|
44
|
-
validMetricParams.options,
|
|
45
|
-
);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
test("should call createHistogram when type is 'histogram'", () => {
|
|
49
|
-
mockMeter.createHistogram.mockReturnValue("mocked-histogram");
|
|
50
|
-
|
|
51
|
-
const result = getMetric("histogram", validMetricParams);
|
|
52
|
-
|
|
53
|
-
expect(result).toBe("mocked-histogram");
|
|
54
|
-
expect(mockMeter.createHistogram).toHaveBeenCalledWith(
|
|
55
|
-
validMetricParams.metricName,
|
|
56
|
-
validMetricParams.options,
|
|
57
|
-
);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
test("should call createCounter when type is 'counter'", () => {
|
|
61
|
-
mockMeter.createCounter.mockReturnValue("mocked-counter");
|
|
62
|
-
|
|
63
|
-
const result = getMetric("counter", validMetricParams);
|
|
64
|
-
|
|
65
|
-
expect(result).toBe("mocked-counter");
|
|
66
|
-
expect(mockMeter.createCounter).toHaveBeenCalledWith(
|
|
67
|
-
validMetricParams.metricName,
|
|
68
|
-
validMetricParams.options,
|
|
69
|
-
);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
test("should call createUpDownCounter when type is 'updowncounter'", () => {
|
|
73
|
-
mockMeter.createUpDownCounter.mockReturnValue("mocked-updowncounter");
|
|
74
|
-
|
|
75
|
-
const result = getMetric("updowncounter", validMetricParams);
|
|
76
|
-
|
|
77
|
-
expect(result).toBe("mocked-updowncounter");
|
|
78
|
-
expect(mockMeter.createUpDownCounter).toHaveBeenCalledWith(
|
|
79
|
-
validMetricParams.metricName,
|
|
80
|
-
validMetricParams.options,
|
|
81
|
-
);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
test("should call createObservableCounter when type is 'async-counter'", () => {
|
|
85
|
-
mockMeter.createObservableCounter.mockReturnValue("mocked-async-counter");
|
|
86
|
-
|
|
87
|
-
const result = getMetric("async-counter", validMetricParams);
|
|
88
|
-
|
|
89
|
-
expect(result).toBe("mocked-async-counter");
|
|
90
|
-
expect(mockMeter.createObservableCounter).toHaveBeenCalledWith(
|
|
91
|
-
validMetricParams.metricName,
|
|
92
|
-
validMetricParams.options,
|
|
93
|
-
);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
test("should call createObservableUpDownCounter when type is 'async-updowncounter'", () => {
|
|
97
|
-
mockMeter.createObservableUpDownCounter.mockReturnValue(
|
|
98
|
-
"mocked-async-updowncounter",
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
const result = getMetric("async-updowncounter", validMetricParams);
|
|
102
|
-
|
|
103
|
-
expect(result).toBe("mocked-async-updowncounter");
|
|
104
|
-
expect(mockMeter.createObservableUpDownCounter).toHaveBeenCalledWith(
|
|
105
|
-
validMetricParams.metricName,
|
|
106
|
-
validMetricParams.options,
|
|
107
|
-
);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
test("should call createObservableGauge when type is 'async-gauge'", () => {
|
|
111
|
-
mockMeter.createObservableGauge.mockReturnValue("mocked-async-gauge");
|
|
112
|
-
|
|
113
|
-
const result = getMetric("async-gauge", validMetricParams);
|
|
114
|
-
|
|
115
|
-
expect(result).toBe("mocked-async-gauge");
|
|
116
|
-
expect(mockMeter.createObservableGauge).toHaveBeenCalledWith(
|
|
117
|
-
validMetricParams.metricName,
|
|
118
|
-
validMetricParams.options,
|
|
119
|
-
);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
test("should throw an error for unsupported metric types", () => {
|
|
123
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
124
|
-
const invalidMetricType = "invalid-type" as any;
|
|
125
|
-
|
|
126
|
-
expect(() => getMetric(invalidMetricType, validMetricParams)).toThrow(
|
|
127
|
-
`Unsupported metric type: ${invalidMetricType}`,
|
|
128
|
-
);
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
test("should return noop metric fallback for null config", async () => {
|
|
132
|
-
const nullMetricParams: MetricsParams = {
|
|
133
|
-
metricName: null!,
|
|
134
|
-
meterName: "",
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
const result = getMetric("async-gauge", nullMetricParams);
|
|
138
|
-
|
|
139
|
-
assert.isNotNull(result);
|
|
140
|
-
assert.equal(result.constructor.name, "NoopObservableGaugeMetric");
|
|
141
|
-
});
|
|
142
|
-
});
|