@ogcio/o11y-sdk-react
Version:
Opentelemetry standard instrumentation SDK for React based project
97 lines (87 loc) • 2.68 kB
text/typescript
import { faro, TransportItemType } from "@grafana/faro-web-sdk";
const EMAIL_REGEX = /[a-zA-Z0-9._%+-]+@([a-zA-Z0-9.-]+\.[a-z]{2,})/gi;
/**
* Redacts all email addresses in the input string and collects metadata.
*
* @param {string} value The input string potentially containing email addresses.
* @returns {{
* redacted: string,
* count: number,
* domains: Record<string, number>
* }}
*
* An object containing:
* - `redacted`: the string with email addresses replaced by `[REDACTED EMAIL]`
* - `count`: total number of email addresses redacted
* - `domains`: a map of domain names to the number of times they were redacted
*/
function _redactEmails(value: string): {
redacted: string;
count: number;
domains: Record<string, number>;
} {
let count = 0;
const domains: Record<string, number> = {};
const redacted = value.replace(EMAIL_REGEX, (_, domain) => {
count++;
domains[domain] = (domains[domain] || 0) + 1;
return "[REDACTED EMAIL]";
});
return { redacted, count, domains };
}
/**
* Checks whether a string contains URI-encoded components.
*
* @param {string} value - The string to inspect.
* @returns {boolean} `true` if the string is encoded, `false` otherwise.
*/
function _containsEncodedComponents(value: string) {
try {
return decodeURI(value) !== decodeURIComponent(value);
} catch {
return false;
}
}
/**
* Cleans a string by redacting email addresses and emitting metrics for PII.
*
* If the string is URL-encoded, it will be decoded before redaction.
* Metrics are emitted for each domain found in redacted email addresses.
*
* @param {string} value - The input string to sanitize.
* @param {TransportItemType} source - The source context of the input.
* @returns {string} The cleaned string with any email addresses replaced by `[REDACTED EMAIL]`.
*/
export function _cleanStringPII(
value: string,
source: TransportItemType,
): string {
let kind: "string" | "url" = "string";
let decodedValue = value;
if (_containsEncodedComponents(value)) {
decodedValue = decodeURIComponent(value);
kind = "url";
}
const { redacted, count, domains } = _redactEmails(decodedValue);
if (count > 0) {
for (const [domain, domainCount] of Object.entries(domains)) {
faro.api.pushMeasurement(
{
type: "faro_o11y_pii_redaction",
values: {
redacted: domainCount,
},
},
{
context: {
pii_type: "email",
redaction_source: source,
pii_email_domain: domain,
pii_format: kind,
},
},
);
}
}
return redacted;
}