All files / configuration secretCleaner.ts

95.45% Statements 21/22
90% Branches 9/10
100% Functions 7/7
95% Lines 19/20

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 431x 1x   1x 1x         27x           27x 106x         1x 73x       27x 27x 27x 266x   598x   52x   214x 1x     27x   1x  
import loadash from 'lodash';
import ConfigurationError from '../errors/ConfigurationError';
import { NonEmptyArray } from '../interfaces/NonEmptyArray';
import { createHash } from 'crypto';
export class SecretCleaner {
	private matches: NonEmptyArray<RegExp>;
	private static flags: 'gi';
 
	constructor(matches: NonEmptyArray<string>) {
		Iif (!Array.isArray(matches) || matches.length === 0) {
			throw new ConfigurationError(
				'matches should contain a non empty list of expressions',
				{ matches }
			);
		}
		this.matches = matches.map(
			(match) => new RegExp(match, SecretCleaner.flags)
		) as NonEmptyArray<RegExp>;
	}
 
	/** simple hashing to consider secrets differ or are equal */
	static hashCode(value: string): string {
		return createHash('sha256').update(value).digest('base64');
	}
 
	/** filters an object against secret values in strings and returns a cleaned deep copy */
	filterSecretValues(data: any): any {
		const filteredData = loadash.cloneDeep(data);
		Object.keys(filteredData).forEach((key) => {
			if (
				typeof filteredData[key] === 'string' &&
				this.matches.some((expression) => expression.exec(key))
			) {
				filteredData[key] =
					'<secret#' + SecretCleaner.hashCode(filteredData[key]) + '>';
			} else if (typeof filteredData[key] === 'object') {
				filteredData[key] = this.filterSecretValues(filteredData[key]);
			}
		});
		return filteredData;
	}
}