AST-aware, stdlib-only static checks: swallowed-exception-around-write (C1), undefined-name (C2), hardcoded-secret-literal (C3), fail-open-flag (C4), rotated-secret-captured-once (C5). Tests + CI + examples included. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
102 lines
5.5 KiB
Markdown
102 lines
5.5 KiB
Markdown
# bugclass-detectors
|
|
|
|
**AST-aware static checks for the silent-failure bug classes that AI-generated and fast-moving code keeps reintroducing.**
|
|
|
|
These are not style nits. They are the bugs that pass code review, pass your tests, keep the process *green* — and silently lose data, fail open, or crash on the one unlucky path. They are exactly the classes an AI coding assistant reintroduces because each instance looks locally reasonable.
|
|
|
|
`bugclass-detectors` finds them with a real parser (not grep), in pure-stdlib Python, with zero config.
|
|
|
|
```bash
|
|
pip install bugclass-detectors # or: pipx run bugclass-detectors .
|
|
bugclass-detectors src/ # human report
|
|
bugclass-detectors src/ --lint # exit 1 on any HIGH finding (CI gate)
|
|
bugclass-detectors src/ --json # machine-readable
|
|
```
|
|
|
|
---
|
|
|
|
## The five bug classes
|
|
|
|
| ID | Class | What it catches | Why it bites |
|
|
|----|-------|-----------------|--------------|
|
|
| **C1** | `SWALLOWED_EXCEPTION_AROUND_WRITE` | A bare/catch-all `except` that swallows an exception wrapping a **write** (DB / cache / file) with no log and no re-raise. | The producer stays alive, the heartbeat is green, the data is *gone*, and nothing logs. The single most dangerous silent-failure shape there is. |
|
|
| **C2** | `UNDEFINED_NAME` | Use of a name never bound in its scope and not a builtin/import/param/closure var. | A `NameError` that only fires on the code path you didn't test. (pyflakes F821 class, no pyflakes dependency.) |
|
|
| **C3** | `HARDCODED_SECRET_LITERAL` | A password/token/key assigned to an inline string literal (in code or a systemd `Environment=`). | Diverges silently the moment the real secret rotates → an outage with no obvious cause. Secrets are masked in output. |
|
|
| **C4** | `FAIL_OPEN_FLAG` | A safety / kill / arming / feature gate read from a store repointable by one env var, with no presence assertion. | If the flag is **absent** on the repointed store it reads falsy → the gate fails **open** instead of closed. |
|
|
| **C5** | `ROTATED_SECRET_CAPTURED_ONCE` | A long-lived consumer (`while True`, a daemon) that captures a secret **once** at module scope. | When the secret rotates the process keeps the stale value and silently 401/403s forever. |
|
|
|
|
Every check is **AST/tokenizer-aware** (so it doesn't fire on the word in a comment or a string), **read-only**, and emits a uniform finding:
|
|
|
|
```json
|
|
{ "check": "C1_SWALLOWED_WRITE", "severity": "HIGH",
|
|
"file": "src/recorder.py", "lineno": 88,
|
|
"snippet": "except Exception:",
|
|
"why": "catch-all/bare except SILENTLY SWALLOWS a db/cache write `hset` — ..." }
|
|
```
|
|
|
|
Disable any check with `BUGCLASS_DISABLE=C1,C4` or `--checks C2,C3`.
|
|
|
|
---
|
|
|
|
## Quick start
|
|
|
|
```bash
|
|
git clone https://github.com/<org>/bugclass-detectors
|
|
cd bugclass-detectors
|
|
python -m bugclass_detectors examples/buggy_sample.py # see all five fire
|
|
python tests/test_detectors.py # smoke tests
|
|
```
|
|
|
|
### As a CI gate (GitHub Actions)
|
|
|
|
```yaml
|
|
- name: bug-class lint
|
|
run: pipx run bugclass-detectors . --lint --min-sev HIGH
|
|
```
|
|
|
|
`--lint` returns a non-zero exit on any HIGH finding, so a swallowed write or an undefined name fails the build.
|
|
|
|
### As a library
|
|
|
|
```python
|
|
from bugclass_detectors import run
|
|
for f in run(["./src"], checks=["C1", "C4"], min_sev="HIGH"):
|
|
print(f["file"], f["lineno"], f["why"])
|
|
```
|
|
|
|
---
|
|
|
|
## Why these five (and why a parser, not grep)
|
|
|
|
This pack started as the in-house lint that runs against a continuously-operating multi-service production estate. Each class on this list earned its place by causing a **real** silent outage in production — a recorder whose only output was swallowed for weeks behind a bare `except`, a feature gate that failed open when its flag-store was repointed, a credential captured once that 403'd silently after a rotation. The fix in every case was one line; the cost of *not* catching it was days.
|
|
|
|
Grep can't catch these without drowning you in false positives — `except` and `password` and `kill` appear constantly in comments, strings, and legitimate code. These checks parse the AST and tokenize the source, so they fire on the *shape* of the bug, not the word.
|
|
|
|
---
|
|
|
|
## Limitations (honest)
|
|
|
|
- Python only (today). The classes are language-agnostic; ports welcome.
|
|
- C1/C4/C5 are heuristic — tuned to favor precision over recall (few false positives, will miss exotic phrasings). C2 is conservative (only flags provably-unbound names).
|
|
- This is a *focused* tool, not a replacement for a full linter, type-checker, or SAST suite. Run it alongside them.
|
|
|
|
---
|
|
|
|
## Want this run continuously, across every repo, with triage and auto-fix?
|
|
|
|
This open-source pack is the **engine**. Running it well at scale — across many repos and languages, with severity triage, suppression management, auto-fix PRs, dashboards, and a human in the loop — is an operations problem.
|
|
|
|
- **[Elite Agentic Solutions](https://eliteagenticsolutions.com)** runs this and a larger battery of production-hardened checks as a **continuous code-health / audit-as-a-service** for teams shipping AI-assisted code. We operate the gate so your pipeline doesn't regress.
|
|
- **[CloudHostAI](https://cloudhostai.com)** offers it as a one-click hosted scanner tile for your repos.
|
|
|
|
The library is, and will stay, free under MIT. The hosted operation is the paid product.
|
|
|
|
---
|
|
|
|
## Contributing
|
|
|
|
Issues and PRs welcome — especially new bug classes that have caused you a real silent outage, and ports to other languages. Keep each check AST-aware, read-only, and dependency-free.
|
|
|
|
## License
|
|
|
|
MIT © Elite Agentic Solutions. See [LICENSE](LICENSE).
|