Verdict Engine¶
The verdict engine (ca9.engine) is the core of ca9. It implements a 4-state decision tree that assigns a verdict to each vulnerability.
Decision tree¶
flowchart TD
START([For each vulnerability]) --> VERSION{Version in<br>affected range?}
VERSION -- "No" --> STATIC_V[UNREACHABLE static<br>'version not in affected range']
VERSION -- "Yes / unknown" --> COMPONENT[Extract affected<br>component]
COMPONENT --> IMPORT{Package imported<br>in repo?}
IMPORT -- "No" --> TRANSITIVE{Transitive<br>dependency?}
TRANSITIVE -- "No" --> STATIC_I[UNREACHABLE static<br>'package never imported']
TRANSITIVE -- "Yes" --> STATIC_T[UNREACHABLE static<br>'transitive dep of X,<br>but X not imported']
IMPORT -- "Yes" --> CONFIDENCE{Component<br>confidence?}
CONFIDENCE -- "high / medium" --> SUBMOD_IMPORT{Submodule<br>imported?}
SUBMOD_IMPORT -- "No" --> STATIC_S[UNREACHABLE static<br>'submodule not imported']
SUBMOD_IMPORT -- "Yes" --> COV_SUB{Coverage<br>available?}
COV_SUB -- "No" --> INCONCLUSIVE_1[INCONCLUSIVE]
COV_SUB -- "Yes" --> EXEC_SUB{Submodule<br>executed?}
EXEC_SUB -- "No" --> DYNAMIC_S[UNREACHABLE dynamic<br>'submodule not executed']
EXEC_SUB -- "Yes" --> REACHABLE_S[REACHABLE<br>'submodule executed']
CONFIDENCE -- "low" --> COV_PKG{Coverage<br>available?}
COV_PKG -- "No" --> INCONCLUSIVE_2[INCONCLUSIVE]
COV_PKG -- "Yes" --> EXEC_PKG{Package<br>executed?}
EXEC_PKG -- "No" --> DYNAMIC_P[UNREACHABLE dynamic<br>'package not executed']
EXEC_PKG -- "Yes" --> REACHABLE_P[REACHABLE<br>'package executed']
The four verdicts¶
| Verdict | Meaning | Action |
|---|---|---|
| REACHABLE | Evidence shows the vulnerable package, affected component, or known vulnerable API is reachable | Fix this CVE |
| UNREACHABLE (static) | Vulnerable code is never imported | Safe to ignore |
| UNREACHABLE (dynamic) | Code is imported but never executed in tests | Likely safe; monitor |
| INCONCLUSIVE | Cannot determine without more data | Provide coverage data |
Entry point¶
from ca9.engine import analyze
from ca9.models import Vulnerability, Report
report: Report = analyze(
vulnerabilities=vulnerabilities,
repo_path=Path("."),
coverage_path=Path("coverage.json"), # optional
)
The diagram shows the base package/submodule flow. Curated vulnerable API rules can also mark a finding reachable when first-party code calls a known affected API, and proof standards can downgrade weak suppressions to INCONCLUSIVE.
The analyze() function:
- Collects all imports from the repository (via
ast_scanner) - Resolves transitive dependencies
- Loads coverage data (if provided)
- For each vulnerability, extracts the affected component and walks the decision tree
- Returns a
Reportwith allVerdictResultobjects
Reasoning trace¶
Every VerdictResult includes a reason string explaining why that verdict was assigned. Use --verbose in the CLI to see these traces: