| 248 | ] |
| 249 | |
| 250 | def check_secret_patterns(self) -> list[Finding]: |
| 251 | findings: list[Finding] = [] |
| 252 | for path in self.git_files(): |
| 253 | full_path = self.repo_root / path |
| 254 | if not full_path.is_file() or is_intentional_secret_fixture(path): |
| 255 | continue |
| 256 | if full_path.suffix.lower() not in TEXT_EXTENSIONS and "." in full_path.name: |
| 257 | continue |
| 258 | try: |
| 259 | if full_path.stat().st_size > MAX_SECRET_SCAN_BYTES: |
| 260 | continue |
| 261 | data = full_path.read_bytes() |
| 262 | except OSError as exc: |
| 263 | findings.append(Finding("secret-pattern", path, f"could not read file: {exc}")) |
| 264 | continue |
| 265 | if b"\0" in data: |
| 266 | continue |
| 267 | text = data.decode("utf-8", errors="ignore") |
| 268 | for label, pattern in SECRET_PATTERNS: |
| 269 | match = pattern.search(text) |
| 270 | if match: |
| 271 | if label == "generic secret assignment" and _looks_like_placeholder_or_reference(match): |
| 272 | continue |
| 273 | findings.append( |
| 274 | Finding( |
| 275 | check="secret-pattern", |
| 276 | path=path, |
| 277 | line=_line_number(text, match.start()), |
| 278 | message=f"possible {label}; replace with a documented placeholder", |
| 279 | ), |
| 280 | ) |
| 281 | break |
| 282 | return findings |
| 283 | |
| 284 | def check_syntax(self) -> list[Finding]: |
| 285 | findings: list[Finding] = [] |