diff --git a/backend/app/scanners/entropy.py b/backend/app/scanners/entropy.py index 8cfda41..4c7080c 100644 --- a/backend/app/scanners/entropy.py +++ b/backend/app/scanners/entropy.py @@ -44,6 +44,15 @@ def is_allowlisted(token: str) -> bool: def run_entropy(repo_dir: Path) -> List[Finding]: + """ + Scan the repository directory for files containing high entropy strings. + + Args: + repo_dir: Path to the directory to scan. + + Returns: + A list of Finding objects discovered during the scan. + """ findings: List[Finding] = [] ignored_extensions = { ".png", @@ -62,65 +71,78 @@ def run_entropy(repo_dir: Path) -> List[Finding]: ".eot", ".ttf", } + ignored_dirs = { + "node_modules", + "venv", + ".venv", + "build", + "dist", + "target", + ".next", + ".cache", + "coverage", + "vendor", + "__pycache__", + ".git", + } + + MAX_FILE_SIZE = 1 * 1024 * 1024 for file_path in repo_dir.rglob("*"): - if ( - not file_path.is_file() - or file_path.suffix.lower() in ignored_extensions - or ".git" in file_path.parts - ): + if any(part in ignored_dirs for part in file_path.parts): + continue + + if not file_path.is_file() or file_path.suffix.lower() in ignored_extensions: + continue + + if file_path.stat().st_size > MAX_FILE_SIZE: continue try: - content = file_path.read_text(encoding="utf-8", errors="ignore") + with open(file_path, "r", encoding="utf-8", errors="ignore") as f: + for line_idx, line in enumerate(f, start=1): + for match in STRING_LITERAL_REGEX.finditer(line): + token = match.group(2) + + if is_allowlisted(token): + continue + + entropy_score = calculate_shannon_entropy(token) + if entropy_score > 4.0: + relative_path = str(file_path.relative_to(repo_dir)) + finding_id = f"entropy:high-entropy-string:{relative_path}:{line_idx}" + severity = "HIGH" + + raw_data = { + "id": finding_id, + "severity": severity, + "location": {"path": relative_path}, + "metadata": {"cwe_category": "CWE-798"}, + } + ml_features = extract_features( + raw_data, scanner_name="entropy" + ) + + findings.append( + Finding( + id=finding_id, + category="secret", + severity=severity, + title="High Entropy String Detected", + description=f"Potential hardcoded secret discovered (Entropy: {entropy_score:.2f})", + location=Location( + path=relative_path, + start_line=line_idx, + end_line=line_idx, + ), + metadata={ + "engine": "entropy", + "entropy_score": entropy_score, + "token_preview": token[:10], + }, + features=ml_features, + ) + ) except Exception: continue - - lines = content.splitlines() - for line_idx, line in enumerate(lines, start=1): - for match in STRING_LITERAL_REGEX.finditer(line): - token = match.group(2) - - if is_allowlisted(token): - continue - - entropy_score = calculate_shannon_entropy(token) - if entropy_score > 4.0: - relative_path = str(file_path.relative_to(repo_dir)) - finding_id = ( - f"entropy:high-entropy-string:{relative_path}:{line_idx}" - ) - severity = "HIGH" - - raw_data_for_extractor = { - "id": finding_id, - "severity": severity, - "location": {"path": relative_path}, - "metadata": {"cwe_category": "CWE-798"}, - } - - ml_features = extract_features( - raw_data_for_extractor, scanner_name="entropy" - ) - - findings.append( - Finding( - id=finding_id, - category="secret", - severity=severity, - title="High Entropy String Detected", - description=f"Potential hardcoded secret or token discovered (Entropy: {entropy_score:.2f})", - location=Location( - path=relative_path, - start_line=line_idx, - end_line=line_idx, - ), - metadata={ - "engine": "entropy", - "entropy_score": entropy_score, - "token_preview": token[:10], - }, - features=ml_features, - ) - ) return findings