|
2 | 2 |
|
3 | 3 | All notable changes to OpenAnt are documented in this file. |
4 | 4 |
|
| 5 | +## [2026-05-07] — Incremental scans + scan pipeline rewire |
| 6 | + |
| 7 | +### Changed |
| 8 | + |
| 9 | +- **`openant scan` rewired end-to-end.** The full pipeline is now |
| 10 | + explicit and visible in `--help`: |
| 11 | + `init → parse → app-context → enhance → analyze → verify → |
| 12 | + build-output → dynamic-test → report`. A single run-mode decision |
| 13 | + (full vs incremental) is resolved once — at `init` or at `scan` — |
| 14 | + and recorded in a per-run `meta.json` whose status field |
| 15 | + transitions `running → success | failed | interrupted`. Step verbs |
| 16 | + (`parse`, `enhance`, …) read that file and inherit the mode, so a |
| 17 | + standalone step after `init --incremental` filters correctly |
| 18 | + without re-passing flags. Docker is preflighted before any work |
| 19 | + begins, so a missing daemon is reported up front instead of after |
| 20 | + a full pipeline run. |
| 21 | +- **Dynamic testing is on by default.** The opt-in `--dynamic-test` |
| 22 | + flag has been replaced by an opt-out `--skip-dynamic-test`. `scan` |
| 23 | + now produces dynamic verification of findings out of the box; |
| 24 | + callers without Docker should pass `--skip-dynamic-test`. |
| 25 | + |
| 26 | +### Added |
| 27 | + |
| 28 | +- **Incremental scans.** New `openant diff` subcommand and |
| 29 | + `--diff-base`, `--pr`, and `--diff-scope` flags on `scan` and |
| 30 | + `parse` scope the pipeline to changed files only. Go computes a |
| 31 | + `diff_manifest.json` from the working tree (or a fetched PR head) |
| 32 | + and threads it through every stage — parse → enhance → analyze → |
| 33 | + report — so each phase only processes what changed. The diff range |
| 34 | + surfaces in the CSV export, `_summary.json`, the standalone HTML |
| 35 | + report, the report header, and the live scan banner. |
| 36 | +- **Explicit run-mode flags on `init`, `scan`, and `parse`** — |
| 37 | + `--full`, `--incremental`, `--diff-base <ref>`, `--pr <n>`, and |
| 38 | + `--diff-scope`. With a baseline present, an interactive TTY shows |
| 39 | + a recap prompt (default Enter = full, the safer choice); non-TTY |
| 40 | + callers without a flag get a loud error so CI scripts fail fast |
| 41 | + instead of silently picking a mode. |
| 42 | + |
| 43 | +### Fixed |
| 44 | + |
| 45 | +- **Python call graph no longer drops `self.X()` calls.** The call- |
| 46 | + graph builder fed function bodies straight into `ast.parse`, but |
| 47 | + method bodies are stored with their original class indentation — |
| 48 | + so `ast.parse` raised `IndentationError`, the regex fallback ran |
| 49 | + without `self.X()` resolution, and every method-to-method call in |
| 50 | + every Python codebase silently disappeared from the graph. On |
| 51 | + dbt-core that meant 2,243 of 3,116 functions (72%) marked isolated |
| 52 | + and a real scan returning zero findings. The fix dedents a |
| 53 | + temporary copy before parsing and leaves the stored source byte- |
| 54 | + correct so the disclosure renderer's offsets stay valid. |
| 55 | +- **Disclosure code is byte-faithful to source.** The disclosure |
| 56 | + renderer pulls the actual file slice from the repo instead of |
| 57 | + rerunning an LLM rewrite, so every finding's `Vulnerable Code` |
| 58 | + block matches the real source. |
| 59 | +- **No more silent 401s.** `openant set-api-key` validates the key |
| 60 | + on save and fails loudly on bad input. `openant scan` prints a |
| 61 | + blocking warning and exits non-zero when zero API calls succeed, |
| 62 | + so an all-401 run can no longer masquerade as a clean repo. |
| 63 | +- **CWE tagging is systematic.** `pipeline_output.json` carries |
| 64 | + non-null `cwe`, `cwe_id`, and `vulnerability_type` for every |
| 65 | + finding. The Stage 1 prompt asks for them directly rather than |
| 66 | + relying on the renderer LLM to infer them from prose. |
| 67 | +- **Repo metadata reaches every report envelope.** Repo name, |
| 68 | + commit SHA, and file count are threaded into `parse.report.json` |
| 69 | + and `scan.report.json` instead of being lost between stages, |
| 70 | + eliminating the `[NOT PROVIDED]` placeholders. |
| 71 | +- **`Verified` column reflects the highest evidence tier.** |
| 72 | + `dynamic` > `verified` > `static`, so dynamically reproduced |
| 73 | + findings show as `dynamic` and the disclosure footer reads |
| 74 | + "Confirmed via dynamic test" where applicable. |
| 75 | +- **Call-graph-aware deduplication.** When two findings share a |
| 76 | + sink/vector and the call graph records an edge between them, |
| 77 | + they collapse into a single finding. |
| 78 | +- **Dedup matches on CWE** instead of `attack_vector` text, so |
| 79 | + small wording differences no longer split what's logically the |
| 80 | + same finding. |
| 81 | +- **Dynamic test Docker context is complete on the first try.** |
| 82 | + `openant dynamic-test` pre-stages the vulnerable source file |
| 83 | + into the Docker build context end-to-end through the dynamic- |
| 84 | + test chain — first-try builds no longer fail because the source |
| 85 | + isn't in context. |
| 86 | +- **Concurrency-safe Docker resources.** Docker image and network |
| 87 | + names get a UUID prefix so parallel dynamic-test workers can't |
| 88 | + collide. |
| 89 | +- **Agreement filter checks the final verdict** instead of the |
| 90 | + intermediate `agree` flag, so high-confidence dynamic results |
| 91 | + aren't dropped by a stale agreement signal. |
| 92 | +- **Report prompts respect non-interactive runs.** Prompt output |
| 93 | + goes to stderr (keeping stdout clean for piped JSON) and the |
| 94 | + prompt is skipped entirely when there's no TTY, so CI/scripted |
| 95 | + invocations no longer hang. |
| 96 | + |
5 | 97 | ## [2026-04-29] — Python parser dedent fix |
6 | 98 |
|
7 | 99 | ### Fixed |
|
0 commit comments