from __future__ import annotations import re from pathlib import Path EMAIL_RE = re.compile(r"\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b", re.IGNORECASE) TOKEN_PATTERNS = [ re.compile(r"\b(?:sk|hf|ghp|xox[baprs])_[A-Za-z0-9_-]{16,}\b"), re.compile(r"\bAKIA[0-9A-Z]{16}\b"), re.compile(r"(?i)\b(?:api[_ -]?key|access[_ -]?token|secret[_ -]?key)\s*[:=]\s*['\"]?[A-Za-z0-9_\-/.+=]{12,}"), ] PHONE_RE = re.compile(r"(? list[str]: findings: list[str] = [] if EMAIL_RE.search(text): findings.append("possible_email") if PHONE_RE.search(text): findings.append("possible_phone") if any(pattern.search(text) for pattern in TOKEN_PATTERNS): findings.append("possible_secret_or_token") return findings def scan_file(path: str | Path) -> list[str]: file_path = Path(path) findings = scan_text(file_path.name) if file_path.suffix.lower() in TEXT_EXTENSIONS and file_path.stat().st_size <= MAX_SCAN_BYTES: try: findings.extend(scan_text(file_path.read_text(encoding="utf-8", errors="ignore"))) except OSError: findings.append("scan_error") return sorted(set(findings))