@redocly/respect-core
Version:
API testing framework core
101 lines • 3.79 kB
JavaScript
import { deepCopy } from '../../utils/deep-copy.js';
export const POTENTIALLY_SECRET_FIELDS = [
'token',
'access_token',
'id_token',
'password',
'client_secret',
];
export function maskSecrets(target, secretsSet) {
const maskValue = (value, secret) => {
return value.replace(secret, '*'.repeat(8));
};
if (typeof target === 'string') {
let maskedString = target;
secretsSet.forEach((secret) => {
maskedString = maskedString.split(secret).join('*'.repeat(8));
});
return maskedString;
}
const masked = deepCopy(target);
const maskIfContainsSecret = (value) => {
let maskedValue = value;
for (const secret of secretsSet) {
if (maskedValue.includes(secret)) {
maskedValue = maskValue(maskedValue, secret);
}
}
return maskedValue;
};
const maskRecursive = (current) => {
for (const key in current) {
if (typeof current[key] === 'string') {
current[key] = maskIfContainsSecret(current[key]);
}
else if (typeof current[key] === 'object' && current[key] !== null) {
// Skip special objects that should not be modified
if (!(current[key] instanceof File) &&
!(current[key] instanceof ArrayBuffer) &&
!(current[key] instanceof Blob) &&
!(current[key] instanceof FormData) &&
!(current[key] instanceof Date) &&
!(current[key] instanceof RegExp) &&
!(current[key] instanceof Map) &&
!(current[key] instanceof Set) &&
!(current[key] instanceof URL) &&
!(current[key] instanceof Error)) {
maskRecursive(current[key]);
}
}
}
};
maskRecursive(masked);
return masked;
}
export function containsSecret(value, secretsSet) {
return Array.from(secretsSet).some((secret) => value.includes(secret));
}
export function findPotentiallySecretObjectFields(obj, tokenKeys = POTENTIALLY_SECRET_FIELDS) {
const foundTokens = [];
if (!obj || typeof obj !== 'object') {
return foundTokens;
}
const searchInObject = (currentObj) => {
if (!currentObj || typeof currentObj !== 'object') {
return;
}
if (Array.isArray(currentObj)) {
for (const item of currentObj) {
searchInObject(item);
}
return;
}
for (const key in currentObj) {
const value = currentObj[key];
// Check if the key matches any of the token keys (case-insensitive)
if (tokenKeys.some((tokenKey) => tokenKey.toLowerCase() === key.toLowerCase())) {
if (typeof value === 'string' && value.trim()) {
foundTokens.push(value);
}
}
if (typeof value === 'string' && value.trim()) {
for (const tokenKey of tokenKeys) {
const match = value.match(new RegExp(`${tokenKey}=([^;\\s]+)`, 'i'));
const [, secretValue] = match || [];
if (secretValue) {
foundTokens.push(secretValue);
}
}
}
if (value && typeof value === 'object') {
searchInObject(value);
}
}
};
searchInObject(obj);
return foundTokens;
}
export function conditionallyMaskSecrets({ value, noSecretsMasking, secretsSet, }) {
return noSecretsMasking ? value : maskSecrets(value, secretsSet);
}
//# sourceMappingURL=mask-secrets.js.map