@ogcio/o11y-sdk-node 0.3.1 → 0.4.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/CHANGELOG.md +14 -0
- package/dist/lib/exporter/pii-exporter-decorator.d.ts +2 -0
- package/dist/lib/exporter/pii-exporter-decorator.js +27 -12
- package/dist/lib/index.d.ts +5 -0
- package/dist/lib/instrumentation.node.js +0 -8
- package/dist/lib/internals/redaction/pii-detection.d.ts +23 -0
- package/dist/lib/internals/redaction/pii-detection.js +87 -0
- package/dist/lib/internals/redaction/redactors/email.d.ts +8 -0
- package/dist/lib/internals/redaction/redactors/email.js +48 -0
- package/dist/lib/internals/redaction/redactors/index.d.ts +4 -0
- package/dist/lib/internals/redaction/redactors/index.js +6 -0
- package/dist/lib/internals/redaction/redactors/ip.d.ts +10 -0
- package/dist/lib/internals/redaction/redactors/ip.js +54 -0
- package/dist/lib/processor/enrich-logger-processor.d.ts +2 -2
- package/dist/package.json +14 -14
- package/dist/vitest.config.js +4 -4
- package/lib/exporter/pii-exporter-decorator.ts +48 -16
- package/lib/index.ts +5 -0
- package/lib/instrumentation.node.ts +0 -10
- package/lib/internals/redaction/pii-detection.ts +126 -0
- package/lib/internals/redaction/redactors/email.ts +58 -0
- package/lib/internals/redaction/redactors/index.ts +12 -0
- package/lib/internals/redaction/redactors/ip.ts +68 -0
- package/lib/internals/shared-metrics.ts +1 -1
- package/lib/processor/enrich-logger-processor.ts +2 -2
- package/package.json +14 -14
- package/test/internals/pii-detection.test.ts +27 -25
- package/test/internals/redactors/email.test.ts +81 -0
- package/test/internals/redactors/ip.test.ts +89 -0
- package/test/traces/active-span.test.ts +1 -1
- package/vitest.config.ts +4 -4
- package/dist/lib/internals/pii-detection.d.ts +0 -17
- package/dist/lib/internals/pii-detection.js +0 -116
- package/lib/internals/pii-detection.ts +0 -145
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { AnyValue } from "@opentelemetry/api-logs";
|
|
2
|
-
export type PIISource = "trace" | "log" | "metric";
|
|
3
|
-
/**
|
|
4
|
-
* Cleans a string by redacting email addresses and emitting metrics for PII.
|
|
5
|
-
*
|
|
6
|
-
* If the string is URL-encoded, it will be decoded before redaction.
|
|
7
|
-
* Metrics are emitted for each domain found in redacted email addresses.
|
|
8
|
-
*
|
|
9
|
-
* @param {string} value - The input string to sanitize.
|
|
10
|
-
* @param {"trace" | "log"} source - The source context of the input, used in metrics.
|
|
11
|
-
* @returns {string} The cleaned string with any email addresses replaced by `[REDACTED EMAIL]`.
|
|
12
|
-
*/
|
|
13
|
-
export declare function _cleanStringPII(value: AnyValue, source: PIISource): AnyValue;
|
|
14
|
-
export declare function _cleanObjectPII(entry: object, source: PIISource): {
|
|
15
|
-
[k: string]: AnyValue;
|
|
16
|
-
};
|
|
17
|
-
export declare function _cleanLogBodyPII(value: AnyValue): AnyValue;
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { _getPIICounterRedactionMetric } from "./shared-metrics.js";
|
|
2
|
-
const EMAIL_REGEX = /[a-zA-Z0-9._%+-]+@([a-zA-Z0-9.-]+\.[a-z]{2,})/gi;
|
|
3
|
-
const decoder = new TextDecoder();
|
|
4
|
-
const encoder = new TextEncoder();
|
|
5
|
-
/**
|
|
6
|
-
* Redacts all email addresses in the input string and collects metadata.
|
|
7
|
-
*
|
|
8
|
-
* @param {string} value The input string potentially containing email addresses.
|
|
9
|
-
* @returns {{
|
|
10
|
-
* redacted: string,
|
|
11
|
-
* count: number,
|
|
12
|
-
* domains: Record<string, number>
|
|
13
|
-
* }}
|
|
14
|
-
*
|
|
15
|
-
* An object containing:
|
|
16
|
-
* - `redacted`: the string with email addresses replaced by `[REDACTED EMAIL]`
|
|
17
|
-
* - `count`: total number of email addresses redacted
|
|
18
|
-
* - `domains`: a map of domain names to the number of times they were redacted
|
|
19
|
-
*/
|
|
20
|
-
function _redactEmails(value) {
|
|
21
|
-
let count = 0;
|
|
22
|
-
const domains = {};
|
|
23
|
-
const redacted = value.replace(EMAIL_REGEX, (_, domain) => {
|
|
24
|
-
count++;
|
|
25
|
-
domains[domain] = (domains[domain] || 0) + 1;
|
|
26
|
-
return "[REDACTED EMAIL]";
|
|
27
|
-
});
|
|
28
|
-
return { redacted, count, domains };
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Checks whether a string contains URI-encoded components.
|
|
32
|
-
*
|
|
33
|
-
* @param {string} value - The string to inspect.
|
|
34
|
-
* @returns {boolean} `true` if the string is encoded, `false` otherwise.
|
|
35
|
-
*/
|
|
36
|
-
function _containsEncodedComponents(value) {
|
|
37
|
-
try {
|
|
38
|
-
return decodeURI(value) !== decodeURIComponent(value);
|
|
39
|
-
}
|
|
40
|
-
catch {
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Cleans a string by redacting email addresses and emitting metrics for PII.
|
|
46
|
-
*
|
|
47
|
-
* If the string is URL-encoded, it will be decoded before redaction.
|
|
48
|
-
* Metrics are emitted for each domain found in redacted email addresses.
|
|
49
|
-
*
|
|
50
|
-
* @param {string} value - The input string to sanitize.
|
|
51
|
-
* @param {"trace" | "log"} source - The source context of the input, used in metrics.
|
|
52
|
-
* @returns {string} The cleaned string with any email addresses replaced by `[REDACTED EMAIL]`.
|
|
53
|
-
*/
|
|
54
|
-
export function _cleanStringPII(value, source) {
|
|
55
|
-
if (Array.isArray(value)) {
|
|
56
|
-
return value.map((v) => _cleanStringPII(v, source));
|
|
57
|
-
}
|
|
58
|
-
if (typeof value !== "string") {
|
|
59
|
-
return value;
|
|
60
|
-
}
|
|
61
|
-
let kind = "string";
|
|
62
|
-
let decodedValue = value;
|
|
63
|
-
if (_containsEncodedComponents(value)) {
|
|
64
|
-
decodedValue = decodeURIComponent(value);
|
|
65
|
-
kind = "url";
|
|
66
|
-
}
|
|
67
|
-
const { redacted, count, domains } = _redactEmails(decodedValue);
|
|
68
|
-
if (count > 0) {
|
|
69
|
-
for (const [domain, domainCount] of Object.entries(domains)) {
|
|
70
|
-
_getPIICounterRedactionMetric().add(domainCount, {
|
|
71
|
-
pii_type: "email",
|
|
72
|
-
redaction_source: source,
|
|
73
|
-
pii_email_domain: domain,
|
|
74
|
-
pii_format: kind,
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
return redacted;
|
|
79
|
-
}
|
|
80
|
-
export function _cleanObjectPII(entry, source) {
|
|
81
|
-
if (!entry) {
|
|
82
|
-
return entry;
|
|
83
|
-
}
|
|
84
|
-
return Object.fromEntries(Object.entries(entry).map(([k, v]) => [k, _cleanStringPII(v, source)]));
|
|
85
|
-
}
|
|
86
|
-
export function _cleanLogBodyPII(value) {
|
|
87
|
-
if (typeof value === "string") {
|
|
88
|
-
return _cleanStringPII(value, "log");
|
|
89
|
-
}
|
|
90
|
-
if (typeof value === "number" ||
|
|
91
|
-
typeof value === "boolean" ||
|
|
92
|
-
value == null) {
|
|
93
|
-
return value;
|
|
94
|
-
}
|
|
95
|
-
if (value instanceof Uint8Array) {
|
|
96
|
-
try {
|
|
97
|
-
const decoded = decoder.decode(value);
|
|
98
|
-
const sanitized = _cleanStringPII(decoded, "log");
|
|
99
|
-
return encoder.encode(sanitized);
|
|
100
|
-
}
|
|
101
|
-
catch {
|
|
102
|
-
return value;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
if (Array.isArray(value)) {
|
|
106
|
-
return value.map(_cleanLogBodyPII);
|
|
107
|
-
}
|
|
108
|
-
if (typeof value === "object") {
|
|
109
|
-
const sanitized = {};
|
|
110
|
-
for (const [key, val] of Object.entries(value)) {
|
|
111
|
-
sanitized[key] = _cleanLogBodyPII(val);
|
|
112
|
-
}
|
|
113
|
-
return sanitized;
|
|
114
|
-
}
|
|
115
|
-
return value;
|
|
116
|
-
}
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import type { AnyValue, AnyValueMap } from "@opentelemetry/api-logs";
|
|
2
|
-
import { _getPIICounterRedactionMetric } from "./shared-metrics.js";
|
|
3
|
-
|
|
4
|
-
const EMAIL_REGEX = /[a-zA-Z0-9._%+-]+@([a-zA-Z0-9.-]+\.[a-z]{2,})/gi;
|
|
5
|
-
|
|
6
|
-
const decoder = new TextDecoder();
|
|
7
|
-
const encoder = new TextEncoder();
|
|
8
|
-
|
|
9
|
-
export type PIISource = "trace" | "log" | "metric";
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Redacts all email addresses in the input string and collects metadata.
|
|
13
|
-
*
|
|
14
|
-
* @param {string} value The input string potentially containing email addresses.
|
|
15
|
-
* @returns {{
|
|
16
|
-
* redacted: string,
|
|
17
|
-
* count: number,
|
|
18
|
-
* domains: Record<string, number>
|
|
19
|
-
* }}
|
|
20
|
-
*
|
|
21
|
-
* An object containing:
|
|
22
|
-
* - `redacted`: the string with email addresses replaced by `[REDACTED EMAIL]`
|
|
23
|
-
* - `count`: total number of email addresses redacted
|
|
24
|
-
* - `domains`: a map of domain names to the number of times they were redacted
|
|
25
|
-
*/
|
|
26
|
-
function _redactEmails(value: string): {
|
|
27
|
-
redacted: string;
|
|
28
|
-
count: number;
|
|
29
|
-
domains: Record<string, number>;
|
|
30
|
-
} {
|
|
31
|
-
let count = 0;
|
|
32
|
-
const domains: Record<string, number> = {};
|
|
33
|
-
|
|
34
|
-
const redacted = value.replace(EMAIL_REGEX, (_, domain) => {
|
|
35
|
-
count++;
|
|
36
|
-
domains[domain] = (domains[domain] || 0) + 1;
|
|
37
|
-
return "[REDACTED EMAIL]";
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
return { redacted, count, domains };
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Checks whether a string contains URI-encoded components.
|
|
45
|
-
*
|
|
46
|
-
* @param {string} value - The string to inspect.
|
|
47
|
-
* @returns {boolean} `true` if the string is encoded, `false` otherwise.
|
|
48
|
-
*/
|
|
49
|
-
function _containsEncodedComponents(value: string) {
|
|
50
|
-
try {
|
|
51
|
-
return decodeURI(value) !== decodeURIComponent(value);
|
|
52
|
-
} catch {
|
|
53
|
-
return false;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Cleans a string by redacting email addresses and emitting metrics for PII.
|
|
59
|
-
*
|
|
60
|
-
* If the string is URL-encoded, it will be decoded before redaction.
|
|
61
|
-
* Metrics are emitted for each domain found in redacted email addresses.
|
|
62
|
-
*
|
|
63
|
-
* @param {string} value - The input string to sanitize.
|
|
64
|
-
* @param {"trace" | "log"} source - The source context of the input, used in metrics.
|
|
65
|
-
* @returns {string} The cleaned string with any email addresses replaced by `[REDACTED EMAIL]`.
|
|
66
|
-
*/
|
|
67
|
-
export function _cleanStringPII(value: AnyValue, source: PIISource): AnyValue {
|
|
68
|
-
if (Array.isArray(value)) {
|
|
69
|
-
return value.map((v) => _cleanStringPII(v, source));
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (typeof value !== "string") {
|
|
73
|
-
return value;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
let kind: "string" | "url" = "string";
|
|
77
|
-
let decodedValue = value;
|
|
78
|
-
|
|
79
|
-
if (_containsEncodedComponents(value)) {
|
|
80
|
-
decodedValue = decodeURIComponent(value);
|
|
81
|
-
kind = "url";
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const { redacted, count, domains } = _redactEmails(decodedValue);
|
|
85
|
-
|
|
86
|
-
if (count > 0) {
|
|
87
|
-
for (const [domain, domainCount] of Object.entries(domains)) {
|
|
88
|
-
_getPIICounterRedactionMetric().add(domainCount, {
|
|
89
|
-
pii_type: "email",
|
|
90
|
-
redaction_source: source,
|
|
91
|
-
pii_email_domain: domain,
|
|
92
|
-
pii_format: kind,
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
return redacted;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export function _cleanObjectPII(entry: object, source: PIISource) {
|
|
100
|
-
if (!entry) {
|
|
101
|
-
return entry;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return Object.fromEntries(
|
|
105
|
-
Object.entries(entry).map(([k, v]) => [k, _cleanStringPII(v, source)]),
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export function _cleanLogBodyPII(value: AnyValue): AnyValue {
|
|
110
|
-
if (typeof value === "string") {
|
|
111
|
-
return _cleanStringPII(value, "log");
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (
|
|
115
|
-
typeof value === "number" ||
|
|
116
|
-
typeof value === "boolean" ||
|
|
117
|
-
value == null
|
|
118
|
-
) {
|
|
119
|
-
return value;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (value instanceof Uint8Array) {
|
|
123
|
-
try {
|
|
124
|
-
const decoded = decoder.decode(value);
|
|
125
|
-
const sanitized = _cleanStringPII(decoded, "log") as string;
|
|
126
|
-
return encoder.encode(sanitized);
|
|
127
|
-
} catch {
|
|
128
|
-
return value;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (Array.isArray(value)) {
|
|
133
|
-
return value.map(_cleanLogBodyPII);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (typeof value === "object") {
|
|
137
|
-
const sanitized: AnyValueMap = {};
|
|
138
|
-
for (const [key, val] of Object.entries(value)) {
|
|
139
|
-
sanitized[key] = _cleanLogBodyPII(val);
|
|
140
|
-
}
|
|
141
|
-
return sanitized;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return value;
|
|
145
|
-
}
|