UNPKG

snowflake-sdk

Version:
138 lines 5.98 kB
"use strict"; /** * The secret detector detects sensitive information. * It masks secrets that might be leaked from two potential avenues * 1. Out of Band Telemetry * 2. Logging * * @param {Object} customPatterns contains 'regex' and 'mask' for customized masking * @param {Object} mock * * @returns {Object} * @constructor */ function SecretDetector(customPatterns, mock) { const CUSTOM_PATTERNS_REGEX = []; const CUSTOM_PATTERNS_MASK = []; let CUSTOM_PATTERNS_LENGTH; if (customPatterns) { // Check that the customPatterns object contains the keys 'regex' and 'mask if (!customPatterns.regex) { throw new Error("The customPatterns object must contain the 'regex' key"); } if (!customPatterns.mask) { throw new Error("The customPatterns object must contain the 'mask' key"); } // Also check that their lengths are equal if (customPatterns.regex.length !== customPatterns.mask.length) { throw new Error("The customPatterns object must have equal length for both 'regex' and 'mask'"); } CUSTOM_PATTERNS_LENGTH = customPatterns.regex.length; // Push the regex and mask elements onto their respective arrays for (let index = 0; index < CUSTOM_PATTERNS_LENGTH; index++) { CUSTOM_PATTERNS_REGEX.push(new RegExp(`${customPatterns.regex[index]}`, 'gi')); CUSTOM_PATTERNS_MASK.push(String.raw `${customPatterns.mask[index]}`); } } function maskCustomPattern(text) { let result; for (let index = 0; index < CUSTOM_PATTERNS_LENGTH; index++) { result = text.replace(CUSTOM_PATTERNS_REGEX[index], CUSTOM_PATTERNS_MASK[index]); // If the text is replaced, return the result if (text !== result) { return result; } } // If text is unchanged, return the original return text; } const AWS_KEY_PATTERN = new RegExp(String.raw `(aws_key_id|aws_secret_key|access_key_id|secret_access_key)\s*=\s*'([^']+)'`, 'gi'); const AWS_TOKEN_PATTERN = new RegExp(String.raw `(accessToken|tempToken|keySecret)\s*:\s*"([a-z0-9/+]{32,}={0,2})"`, 'gi'); const SAS_TOKEN_PATTERN = new RegExp(String.raw `(sig|signature|AWSAccessKeyId|password|passcode)=(\?P<secret>[a-z0-9%/+]{16,})`, 'gi'); const PRIVATE_KEY_PATTERN = new RegExp(String.raw `-----BEGIN PRIVATE KEY-----\\n([a-z0-9/+=\\n]{32,})\\n-----END PRIVATE KEY-----`, // pragma: allowlist secret 'gim'); const PRIVATE_KEY_DATA_PATTERN = new RegExp(String.raw `"privateKeyData": "([a-z0-9/+=\\n]{10,})"`, 'gim'); // Colon in the group ([a-z0-9=/:_%-+]{8,}) was added to detect tokens that contain additional details before the actual token. // Such as version or hint (token=ver:1-hint:1233-realToken...). const CONNECTION_TOKEN_PATTERN = new RegExp(String.raw `(token|assertion content)([\'\"\s:=]+)([a-z0-9=/:_\%\-\+]{8,})`, 'gi'); const PASSWORD_PATTERN = new RegExp(String.raw `(password|pwd)([\'\"\s:=]+)([a-z0-9!\"#\$%&\\\'\(\)\*\+\,-\./:;<=>\?\@\[\]\^_` + '`' + String.raw `\{\|\}~]{8,})`, 'gi'); const PASSCODE_PATTERN = new RegExp(String.raw `(passcode|otp|pin|otac)\s*([:=])\s*([0-9]{4,6})`, 'gi'); const OAUTH_CLIENT_SECRET_PATTERN = new RegExp(String.raw `(oauthClientId|oauthClientSecret|clientSecret)([\'\"\s:=]+)([a-z0-9!\"#\$%&\\\'\(\)\*\+\,-\./:;<=>\?\@\[\]\^_` + '`' + String.raw `\{\|\}~]{8,})`, 'gi'); function maskAwsKeys(text) { return text.replace(AWS_KEY_PATTERN, String.raw `$1$2****`); } function maskAwsToken(text) { return text.replace(AWS_TOKEN_PATTERN, String.raw `$1":"XXXX"`); } function maskSasToken(text) { return text.replace(SAS_TOKEN_PATTERN, String.raw `$1=****`); } function maskPrivateKey(text) { return text.replace(PRIVATE_KEY_PATTERN, String.raw `-----BEGIN PRIVATE KEY-----\\\\nXXXX\\\\n-----END PRIVATE KEY-----`); // pragma: allowlist secret } function maskPrivateKeyData(text) { return text.replace(PRIVATE_KEY_DATA_PATTERN, String.raw `"privateKeyData": "XXXX"`); } function maskConnectionToken(text) { return text.replace(CONNECTION_TOKEN_PATTERN, String.raw `$1$2****`); } function maskPassword(text) { return text.replace(PASSWORD_PATTERN, String.raw `$1$2****`); } function maskPasscode(text) { return text.replace(PASSCODE_PATTERN, String.raw `$1$2****`); } function maskClientSecret(text) { return text.replace(OAUTH_CLIENT_SECRET_PATTERN, String.raw `$1$2****`); } /** * Masks any secrets. * * @param {String} text may contain a secret. * * @returns {Object} the masked string. */ this.maskSecrets = function (text) { let result; if (!text) { result = { masked: false, maskedtxt: text, errstr: null, }; return result; } let masked = false; let maskedtxt = ''; let errstr = null; try { if (mock) { mock.execute(); } maskedtxt = maskClientSecret(maskPasscode(maskConnectionToken(maskPassword(maskPrivateKeyData(maskPrivateKey(maskAwsToken(maskSasToken(maskAwsKeys(text))))))))); if (CUSTOM_PATTERNS_LENGTH > 0) { maskedtxt = maskCustomPattern(maskedtxt); } if (maskedtxt !== text) { masked = true; } } catch (err) { masked = true; maskedtxt = err.toString(); errstr = err.toString(); } result = { masked: masked, maskedtxt: maskedtxt, errstr: errstr, }; return result; }; } module.exports = SecretDetector; //# sourceMappingURL=secret_detector.js.map