UNPKG

token-guardian

Version:

A comprehensive solution for protecting and managing API tokens and secrets

91 lines 3.34 kB
import { createHash } from 'crypto'; import { Logger } from '../utils/Logger'; /** * Scanner that detects potential tokens and secrets in strings */ export class PatternScanner { patterns; logger; constructor(patterns = [], logger) { this.patterns = patterns; this.logger = logger || new Logger('info'); } /** * Scans a string for potential tokens or secrets * @param content The string to scan * @param filepath The file path of the scanned content * @returns Results of the scan */ scan(content, filepath) { const results = []; for (const pattern of this.patterns) { // Ensure regex is global const regex = pattern.regex instanceof RegExp ? new RegExp(pattern.regex.source, pattern.regex.flags + (pattern.regex.flags.includes('g') ? '' : 'g')) : new RegExp(pattern.regex, 'g'); const matches = content.matchAll(regex); for (const match of matches) { const token = match[1] || match[0]; const entropy = this.calculateEntropy(token); // Skip if entropy is too low if (entropy < pattern.entropyThreshold) { continue; } // Validate token if pattern has a validator if (pattern.validate && !pattern.validate(token)) { continue; } // Calculate token fingerprint const fingerprint = createHash('sha256') .update(token) .digest('hex') .substring(0, 16); results.push({ type: pattern.name.toLowerCase().replace(/\s+/g, '_'), value: token, description: pattern.description, fingerprint, entropy, location: { file: filepath, line: this.getLineNumber(content, match.index || 0), column: match.index || 0 } }); } } return results; } /** * Calculates Shannon entropy of a string * @param str The string to calculate entropy for * @returns Entropy value */ calculateEntropy(str) { const len = str.length; const frequencies = new Map(); // Calculate character frequencies for (const char of str) { frequencies.set(char, (frequencies.get(char) || 0) + 1); } // Calculate entropy using Shannon's formula return Array.from(frequencies.values()).reduce((entropy, freq) => { const probability = freq / len; return entropy - probability * Math.log2(probability); }, 0); } /** * Gets the line number for a position in text * @param text The text to search in * @param position The position to find the line number for * @returns The line number (1-based) */ getLineNumber(text, position) { const lines = text.slice(0, position).split('\n'); return lines.length; } addPattern(pattern) { this.patterns.push(pattern); } } //# sourceMappingURL=PatternScanner.js.map