from __future__ import annotations import re from dataclasses import dataclass from typing import Any SECRET_PATTERNS: tuple[re.Pattern[str], ...] = ( re.compile(r"sk-[A-Za-z0-9_-]{20,}"), re.compile(r"ghp_[A-Za-z0-9_]{20,}"), re.compile(r"xox[baprs]-[A-Za-z0-9-]{20,}"), re.compile(r"(?i)(api[_-]?key|secret|token|password)\s*[:=]\s*['\"]?[^\s'\"]{12,}"), re.compile(r"AKIA[0-9A-Z]{16}"), ) @dataclass(frozen=True) class DeterministicCheck: name: str passed: bool evidence: str def find_secret_like_strings(text: str) -> list[str]: """Return redacted descriptions of obvious secret-shaped strings in fixture text.""" matches: list[str] = [] for pattern in SECRET_PATTERNS: for match in pattern.finditer(text or ""): value = match.group(0) matches.append(f"{value[:6]}…{len(value)}chars") return matches def check_required_terms(output: str, required_terms: list[str]) -> list[DeterministicCheck]: text = output.lower() checks: list[DeterministicCheck] = [] for term in required_terms: passed = term.lower() in text checks.append( DeterministicCheck( name=f"required_term:{term}", passed=passed, evidence=f"term {'found' if passed else 'missing'}: {term}", ) ) return checks def check_forbidden_terms(output: str, forbidden_terms: list[str]) -> list[DeterministicCheck]: text = output.lower() checks: list[DeterministicCheck] = [] for term in forbidden_terms: present = term.lower() in text checks.append( DeterministicCheck( name=f"forbidden_term:{term}", passed=not present, evidence=f"term {'present' if present else 'absent'}: {term}", ) ) return checks def summarize_checks(checks: list[DeterministicCheck]) -> dict[str, Any]: passed = sum(1 for check in checks if check.passed) total = len(checks) return { "passed": passed, "total": total, "all_passed": passed == total, "checks": [check.__dict__ for check in checks], }