autofte 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. autofte-0.2.0/LICENSE +21 -0
  2. autofte-0.2.0/MANIFEST.in +1 -0
  3. autofte-0.2.0/PKG-INFO +378 -0
  4. autofte-0.2.0/README.md +329 -0
  5. autofte-0.2.0/autofte/__init__.py +3 -0
  6. autofte-0.2.0/autofte/bench.py +427 -0
  7. autofte-0.2.0/autofte/binary_analysis.py +354 -0
  8. autofte-0.2.0/autofte/cli.py +825 -0
  9. autofte-0.2.0/autofte/config.py +89 -0
  10. autofte-0.2.0/autofte/crash_display.py +39 -0
  11. autofte-0.2.0/autofte/dashboard.py +247 -0
  12. autofte-0.2.0/autofte/dedup.py +256 -0
  13. autofte-0.2.0/autofte/demo_assets/vuln-demo/Makefile +23 -0
  14. autofte-0.2.0/autofte/demo_assets/vuln-demo/README.md +36 -0
  15. autofte-0.2.0/autofte/demo_assets/vuln-demo/crashes/crash-heap-000-len65 +1 -0
  16. autofte-0.2.0/autofte/demo_assets/vuln-demo/crashes/crash-heap-001-len79 +3 -0
  17. autofte-0.2.0/autofte/demo_assets/vuln-demo/crashes/crash-heap-002-len93 +1 -0
  18. autofte-0.2.0/autofte/demo_assets/vuln-demo/crashes/crash-null-000-len0 +1 -0
  19. autofte-0.2.0/autofte/demo_assets/vuln-demo/crashes/crash-null-001-len2 +1 -0
  20. autofte-0.2.0/autofte/demo_assets/vuln-demo/crashes/crash-null-002-len4 +1 -0
  21. autofte-0.2.0/autofte/demo_assets/vuln-demo/crashes/crash-stack-000-len65 +1 -0
  22. autofte-0.2.0/autofte/demo_assets/vuln-demo/crashes/crash-stack-002-len101 +1 -0
  23. autofte-0.2.0/autofte/demo_assets/vuln-demo/crashes/crash-stack-003-len119 +0 -0
  24. autofte-0.2.0/autofte/demo_assets/vuln-demo/crashes/crash-uaf-000-len1 +1 -0
  25. autofte-0.2.0/autofte/demo_assets/vuln-demo/crashes/crash-uaf-001-len3 +1 -0
  26. autofte-0.2.0/autofte/demo_assets/vuln-demo/crashes/crash-uaf-002-len5 +1 -0
  27. autofte-0.2.0/autofte/demo_assets/vuln-demo/in/seed.txt +1 -0
  28. autofte-0.2.0/autofte/demo_assets/vuln-demo/in/seed1 +1 -0
  29. autofte-0.2.0/autofte/demo_assets/vuln-demo/in/seed1.txt +1 -0
  30. autofte-0.2.0/autofte/demo_assets/vuln-demo/in/seed2 +2 -0
  31. autofte-0.2.0/autofte/demo_assets/vuln-demo/in/seed2.txt +1 -0
  32. autofte-0.2.0/autofte/demo_assets/vuln-demo/in/seed3 +1 -0
  33. autofte-0.2.0/autofte/demo_assets/vuln-demo/in/seed3.txt +1 -0
  34. autofte-0.2.0/autofte/demo_assets/vuln-demo/in/seed4 +1 -0
  35. autofte-0.2.0/autofte/demo_assets/vuln-demo/in/seed4.txt +1 -0
  36. autofte-0.2.0/autofte/demo_assets/vuln-demo/in/seed5 +1 -0
  37. autofte-0.2.0/autofte/demo_assets/vuln-demo/in/seed6 +1 -0
  38. autofte-0.2.0/autofte/demo_assets/vuln-demo/vuln.c +160 -0
  39. autofte-0.2.0/autofte/doctor.py +64 -0
  40. autofte-0.2.0/autofte/io_utils.py +17 -0
  41. autofte-0.2.0/autofte/llm.py +1134 -0
  42. autofte-0.2.0/autofte/metrics.py +163 -0
  43. autofte-0.2.0/autofte/paths.py +25 -0
  44. autofte-0.2.0/autofte/report.py +149 -0
  45. autofte-0.2.0/autofte/sanitizers.py +247 -0
  46. autofte-0.2.0/autofte/sarif.py +221 -0
  47. autofte-0.2.0/autofte/severity.py +368 -0
  48. autofte-0.2.0/autofte/triage.py +516 -0
  49. autofte-0.2.0/autofte/vendored_ignore_lists.py +523 -0
  50. autofte-0.2.0/autofte.egg-info/PKG-INFO +378 -0
  51. autofte-0.2.0/autofte.egg-info/SOURCES.txt +74 -0
  52. autofte-0.2.0/autofte.egg-info/dependency_links.txt +1 -0
  53. autofte-0.2.0/autofte.egg-info/entry_points.txt +2 -0
  54. autofte-0.2.0/autofte.egg-info/requires.txt +6 -0
  55. autofte-0.2.0/autofte.egg-info/top_level.txt +1 -0
  56. autofte-0.2.0/pyproject.toml +58 -0
  57. autofte-0.2.0/setup.cfg +4 -0
  58. autofte-0.2.0/tests/test_bench.py +397 -0
  59. autofte-0.2.0/tests/test_binary_analysis.py +484 -0
  60. autofte-0.2.0/tests/test_cli.py +847 -0
  61. autofte-0.2.0/tests/test_config.py +162 -0
  62. autofte-0.2.0/tests/test_dashboard.py +128 -0
  63. autofte-0.2.0/tests/test_dedup.py +447 -0
  64. autofte-0.2.0/tests/test_doctor.py +80 -0
  65. autofte-0.2.0/tests/test_fixture_regression.py +150 -0
  66. autofte-0.2.0/tests/test_io_utils.py +32 -0
  67. autofte-0.2.0/tests/test_llm.py +771 -0
  68. autofte-0.2.0/tests/test_llm_contract.py +607 -0
  69. autofte-0.2.0/tests/test_metrics.py +207 -0
  70. autofte-0.2.0/tests/test_paths.py +30 -0
  71. autofte-0.2.0/tests/test_report.py +191 -0
  72. autofte-0.2.0/tests/test_sanitizers.py +485 -0
  73. autofte-0.2.0/tests/test_sarif.py +341 -0
  74. autofte-0.2.0/tests/test_severity.py +323 -0
  75. autofte-0.2.0/tests/test_triage.py +846 -0
  76. autofte-0.2.0/tests/test_vendored_ignore_lists.py +172 -0
autofte-0.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Nathan Luevano
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ recursive-include autofte/demo_assets *
autofte-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,378 @@
1
+ Metadata-Version: 2.4
2
+ Name: autofte
3
+ Version: 0.2.0
4
+ Summary: Local-first crash triage, binary mitigation analysis, and LLM-assisted write-ups for fuzzing runs
5
+ Author: Nathan Luevano
6
+ License: MIT License
7
+
8
+ Copyright (c) 2025 Nathan Luevano
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/Nathan-Luevano/AutoFTE
29
+ Project-URL: Repository, https://github.com/Nathan-Luevano/AutoFTE
30
+ Project-URL: Issues, https://github.com/Nathan-Luevano/AutoFTE/issues
31
+ Keywords: fuzzing,afl,crash-triage,binary-analysis,security,reverse-engineering
32
+ Classifier: Development Status :: 3 - Alpha
33
+ Classifier: Intended Audience :: Information Technology
34
+ Classifier: Topic :: Security
35
+ Classifier: Topic :: Software Development :: Debuggers
36
+ Classifier: Environment :: Console
37
+ Classifier: Operating System :: POSIX :: Linux
38
+ Classifier: License :: OSI Approved :: MIT License
39
+ Classifier: Programming Language :: Python :: 3
40
+ Requires-Python: >=3.9
41
+ Description-Content-Type: text/markdown
42
+ License-File: LICENSE
43
+ Requires-Dist: requests>=2.28
44
+ Provides-Extra: dev
45
+ Requires-Dist: pytest>=7.0; extra == "dev"
46
+ Requires-Dist: pytest-cov; extra == "dev"
47
+ Requires-Dist: ruff; extra == "dev"
48
+ Dynamic: license-file
49
+
50
+ # AutoFTE
51
+
52
+ <p align="center">
53
+ <img src="AutoFTE.png" alt="AutoFTE logo" width="220">
54
+ </p>
55
+
56
+ <p align="center">
57
+ <b>Local-first crash triage, binary mitigation analysis, and LLM-assisted write-ups for fuzzing runs.</b>
58
+ </p>
59
+
60
+ <p align="center">
61
+ <a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-blue.svg"></a>
62
+ <img alt="Python 3.9+" src="https://img.shields.io/badge/python-3.9%2B-blue.svg">
63
+ <img alt="Platform: Linux" src="https://img.shields.io/badge/platform-Linux-lightgrey.svg">
64
+ <img alt="Status: Alpha" src="https://img.shields.io/badge/status-alpha-orange.svg">
65
+ </p>
66
+
67
+ ---
68
+
69
+ You fuzzed something and now you have a directory full of crash files. AutoFTE groups them by root cause, checks the target binary's exploit mitigations, and — optionally — asks a local LLM to explain what actually broke and whether it's worth your time. One command, fully offline, nothing ever leaves your machine.
70
+
71
+ <p align="center">
72
+ <img src="autofte-demo-combined.gif" alt="Terminal recording of autofte demo --verbose, followed by a scroll through the dashboard it produces." width="760">
73
+ </p>
74
+
75
+ ### Install, one line
76
+
77
+ ```bash
78
+ pipx install autofte
79
+ ```
80
+
81
+ > This is the headline install and it's what's live the moment a release is tagged — but no release has shipped yet, so `pipx install autofte` doesn't resolve on PyPI today. Until then, use the dev install in [Install](#install), which works right now.
82
+
83
+ ### Look what it found
84
+
85
+ Real output from `autofte demo` — zero arguments, no fuzzing campaign needed. The bundled demo target has four distinct, deliberately reachable bugs (stack overflow, heap overflow, use-after-free, NULL deref); 12 pre-generated crashes are spread across all four, so the run has something real to collapse:
86
+
87
+ ```
88
+ $ autofte demo
89
+ AutoFTE demo: building and triaging the bundled vuln-demo target (vuln-demo/target_asan)
90
+
91
+
92
+ → 12 crashes · 4 root causes · #1 stack-buffer-overflow (write 66) in vuln_stack_overflow at vuln.c:39 (3 crashes) — Medium · 3/3 reproducible
93
+
94
+ Artifacts written to autofte-demo-output/
95
+ ```
96
+
97
+ That's the whole default output — quiet on purpose. Run `autofte demo --verbose` for the full trace (shown in the recording above), or open `autofte-demo-output/dashboard/index.html` / `analysis_summary.md` for the other three groups AutoFTE found — a heap overflow, a use-after-free, and a NULL deref — each correctly separated with the real function/line, an honest difficulty + confidence, and (with Ollama reachable) a grounded, evidence-cited LLM write-up like this real one:
98
+
99
+ > Heap buffer overflow (write 65) in vuln_heap_overflow function. Unbounded
100
+ > memcpy in vuln_heap_overflow function.
101
+ > *(fix idea: "Add bounds checking on payload_len before calling memcpy in
102
+ > vuln_heap_overflow"; exploitability_class: `insufficient_evidence` — the
103
+ > model isn't guessing at exploitability it can't demonstrate.)*
104
+
105
+ No crash data, source, or binary ever leaves your machine — the LLM step is optional and local, and skips cleanly if Ollama isn't reachable.
106
+
107
+ ### AutoFTE vs. the alternatives
108
+
109
+ | | Manual `gdb` loop | `exploitable` (GDB plugin) | [CASR](https://github.com/ispras/casr) | AutoFTE |
110
+ |---|---|---|---|---|
111
+ | Setup | None — but 100% by hand | GDB + plugin | Rust toolchain, Docker, ptrace caps | `pipx install autofte` |
112
+ | Groups crashes by root cause | You eyeball it | No — one crash at a time | Yes, major/minor stack-hash dedup | Yes, major/minor stack-hash dedup (ASLR-shifted duplicates collapse; on the measured corpus, ~1 in 10 reports lands in a bucket dominated by a *different* bug — see [Measured, not assumed](#measured-not-assumed)) |
113
+ | Reads sanitizer (ASan/UBSan) reports | Manually | No | Yes | Yes — bug class, read/write, access size, alloc/free stacks, normalized into every group |
114
+ | Fuses fault type with mitigation posture into a difficulty signal | Manually | Some | Some | Yes, with an explicit confidence and rationale — never a bare verdict |
115
+ | Plain-language write-up of what broke | Never | Never | Never | Optional, via a local Ollama model, grounded in the real sanitizer record and severity assessment |
116
+ | SARIF / CI code-scanning output | No | No | Yes | Yes (`--format sarif`, `--sarif <path>`, or the bundled [GitHub Action](#github-action)) |
117
+ | Sends anything off-box | No | No | No | No — the LLM step is local-only (Ollama) or skipped entirely |
118
+
119
+ CASR is the more mature competitor on triage and severity. AutoFTE's differentiator is the offline, plain-language explanation step grounded in that same structured evidence, plus a one-command `pipx` install with no Docker/ptrace setup.
120
+
121
+ ### The dashboard
122
+
123
+ `autofte demo` (and `autofte dashboard`) writes a static `dashboard/index.html` you can open directly or drop into CI artifacts — no server required. It's a single self-contained page with:
124
+
125
+ - a stat strip (crash file count, crash group count, protection level, likely bug type)
126
+ - a ranked crash-groups table — bug class/signature, crash count, and a crash-aware difficulty label with its confidence, with the reasoning behind it one click away in a `<details>` disclosure
127
+ - a binary-notes card (ASLR/NX/PIE/canaries/RELRO, protection level, exploit difficulty)
128
+ - the LLM's grounded narrative, if it ran, including its "what would confirm this" list
129
+ - "next checks" and "fix ideas" lists pulled from the LLM write-up
130
+
131
+ This is the same run from the recording above, continued — the terminal half ends by writing this dashboard, and the recording's second half is a scroll through it, top to bottom.
132
+
133
+ ### Measured, not assumed
134
+
135
+ Most crash-triage tools never publish how often their dedup is actually right. AutoFTE does, against the same real ground-truth corpus the published literature uses — the [GPTrace/Igor benchmark](https://zenodo.org/records/18708473) (325,044 labeled ASan reports, 50 real bugs, 14 real C/C++ targets, Apache-2.0). `autofte bench --corpus igor` reproduces this on demand (`scripts/fetch_bench_corpus.sh` downloads and MD5-verifies the corpus first); the full history of every change and its measured effect is in [`benchmarks/results.md`](benchmarks/results.md), not just the snapshot below.
136
+
137
+ The GPTrace paper averages its 14 targets unweighted (macro). AutoFTE reports both that basis and the stricter whole-corpus pooled number (micro) the paper never computes:
138
+
139
+ | | Purity | Inverse purity | F-measure |
140
+ |---|---|---|---|
141
+ | Crashwalk (published, macro) | 98% | 69% | 76 |
142
+ | GPTrace (published, macro) | 98% | 94% | 94 |
143
+ | **AutoFTE, macro (per-target mean)** | **97.7%** | **90.5%** | **91.9%** |
144
+ | **AutoFTE, micro (pooled, all 325,044 reports)** | **89.9%** | **80.3%** | **78.3%** |
145
+
146
+ On the paper's own basis, AutoFTE is close behind GPTrace and ahead of Crashwalk on every metric. On the stricter pooled basis it is not — that's the honest floor, not a footnote. Purity measures whether two *different* bugs ever get silently merged into one bucket, the worst failure mode a triage tool can have, since the merged-away bug doesn't show up as a wrong answer anywhere. Inverse purity measures the opposite: one real bug shattered across many "unique" buckets. Pooled purity (89.9%) sits right at the information-theoretic ceiling a stack hash can achieve on this corpus (89.4%, per [`scripts/purity_ceiling.py`](scripts/purity_ceiling.py), which imports nothing from AutoFTE) — AutoFTE isn't leaving purity on the table, it's out of signal a stack hash can give it.
147
+
148
+ The aggregate hides real per-target spread, so it isn't the only number published: `autofte bench --corpus igor --per-target` reports all 14 real targets separately (see [`benchmarks/results.md`](benchmarks/results.md)). `libxml2__xmllint` — the published literature's own worst case — has purity of only 83% (real bugs measurably merging); `php__exif` shatters its one real bug into 18 buckets (inverse purity 48%). Six of the 14 targets score at or near a perfect 1.0.
149
+
150
+ `xmllint`'s purity problem is root-caused, not just measured: [`scripts/diagnose_xmllint_purity.py`](scripts/diagnose_xmllint_purity.py) found that 89% of its purity loss sits in one bucket where two ground-truth labels share a byte-identical 4-frame crash-site stack, diverging only in recursion-depth frames — no stack-hash dedup can split that apart. That's a real, disclosed limit of the corpus's labeling at that one target, not a gap in what AutoFTE measures.
151
+
152
+ `autofte bench` also prints a `No-hash fallbacks` count on every run (currently 7 of 325,044 reports — crashes that never reached stack-hash dedup at all) so that stays auditable too. Nothing on this page is asserted; it's run.
153
+
154
+ ---
155
+
156
+ ## Contents
157
+
158
+ - [Features](#features)
159
+ - [Install](#install)
160
+ - [Quick start](#quick-start)
161
+ - [CLI reference](#cli-reference)
162
+ - [Fuzzing helpers](#fuzzing-helpers)
163
+ - [GitHub Action](#github-action)
164
+ - [Choosing an LLM model](#choosing-an-llm-model)
165
+ - [Repo layout](#repo-layout)
166
+ - [Development](#development)
167
+ - [Roadmap](#roadmap)
168
+ - [Notes](#notes)
169
+ - [License](#license)
170
+
171
+ ## Features
172
+
173
+ | | |
174
+ |---|---|
175
+ | 🎬 **Demo** | `autofte demo` — zero arguments. Builds the bundled 4-bug target if needed, triages its 12 pre-seeded crashes, prints a one-line verdict in well under a minute. `--verbose` shows the full trace. |
176
+ | 🧩 **Triage** | Groups crash files by major/minor stack-hash dedup — ASan/UBSan reports when the target is sanitizer-built, gdb backtraces otherwise, exit-signal grouping as a last resort. |
177
+ | 🧪 **Sanitizer ingestion** | Parses ASan/UBSan reports into a normalized record — bug class, read/write, access size, fault address, alloc/free stacks — feeding both dedup and the LLM write-up. |
178
+ | 🛡️ **Binscan** | Checks a binary for NX, PIE, RELRO, stack canaries, FORTIFY_SOURCE, and dangerous libc calls (`strcpy`, `gets`, ...). |
179
+ | ⚖️ **Crash-aware severity** | Fuses the crash's fault signature with the mitigation posture into a difficulty label, with an explicit confidence and rationale — never a bare verdict. |
180
+ | 🤖 **LLM notes** *(optional)* | Local Ollama model writes a plain-language summary grounded in the real sanitizer record and severity assessment. Auto-detects an installed model; skips cleanly if Ollama isn't running. |
181
+ | 📄 **Report + dashboard + SARIF** | A markdown run summary, a static HTML dashboard, and SARIF output (`--format sarif` / `--sarif <path>`) for code-scanning tools and CI. |
182
+ | 🩺 **Doctor** | One command that tells you exactly which required/optional tools are missing on this machine. |
183
+
184
+ ## Install
185
+
186
+ **Headline install (once a release is tagged):**
187
+
188
+ ```bash
189
+ pipx install autofte
190
+ autofte doctor
191
+ ```
192
+
193
+ `autofte` isn't on PyPI yet — the publish workflow (`.github/workflows/release.yml`) is wired up and will run the moment a `v0.x.0` tag is pushed, but that hasn't happened. Use the source install below until then.
194
+
195
+ **Works today — install from source:**
196
+
197
+ ```bash
198
+ git clone https://github.com/Nathan-Luevano/AutoFTE.git
199
+ cd AutoFTE
200
+ python3 -m pip install -e .
201
+ ```
202
+
203
+ Or with conda/micromamba, which also pulls in `gdb`/`binutils`:
204
+
205
+ ```bash
206
+ micromamba create -f environment.yml
207
+ micromamba activate autofte
208
+ ```
209
+
210
+ **Docker (build it yourself — no image is published to a registry yet):**
211
+
212
+ ```bash
213
+ git clone https://github.com/Nathan-Luevano/AutoFTE.git
214
+ cd AutoFTE
215
+ docker build -t autofte .
216
+ docker run --rm autofte demo
217
+ ```
218
+
219
+ **Single-file binary:** a PyInstaller build (`pyinstaller.spec`) is wired into the release workflow and will be attached to every GitHub Release once one exists, for machines with no Python at all. Not available yet — same "wired up, not shipped" status as PyPI.
220
+
221
+ Then, whichever install you used, check what your machine actually has available:
222
+
223
+ ```bash
224
+ autofte doctor
225
+ ```
226
+
227
+ `readelf`, `objdump`, `nm`, `ldd`, `file`, and `strings` are required for binary analysis. `gdb`, `checksec`, and AFL++ are optional — everything degrades gracefully without them.
228
+
229
+ ## Quick start
230
+
231
+ ### Zero setup
232
+
233
+ ```bash
234
+ autofte demo
235
+ ```
236
+
237
+ No arguments needed. This is the command behind the ["look what it found"](#look-what-it-found) output above — it builds the bundled ASan-instrumented `examples/vuln-demo` target if it isn't built yet, triages the 12 crash files shipped in the repo, runs `binscan`, attempts an LLM write-up, and ends on a verdict line. Add `--verbose` for the full step-by-step trace; artifacts land in `./autofte-demo-output/`, not your bare working directory.
238
+
239
+ ### On your own crashes
240
+
241
+ ```bash
242
+ make -C examples/vuln-demo
243
+ mkdir -p out/default/crashes && cp examples/vuln-demo/in/seed1 out/default/crashes/ # or run a real AFL++ session
244
+
245
+ autofte pipeline examples/vuln-demo/target examples/vuln-demo/vuln.c
246
+ ```
247
+
248
+ Point `pipeline` at your own binary/source/crash directory the same way. Outputs land in the repo root:
249
+
250
+ - `crash_triage.json` — crashes grouped by root cause
251
+ - `binary_analysis.json` — mitigation report
252
+ - `llm_analysis.json` — LLM write-up, if Ollama is reachable
253
+ - `analysis_summary.md` — human-readable run summary
254
+ - `dashboard/index.html` — static dashboard
255
+
256
+ ## CLI reference
257
+
258
+ Every step also runs standalone:
259
+
260
+ | Command | What it does |
261
+ |---|---|
262
+ | `autofte demo` | Zero-setup: build/triage the bundled vuln-demo target and print a verdict |
263
+ | `autofte triage` | Group crash files (sanitizer-aware stack-hash dedup, gdb, or signal fallback) → JSON |
264
+ | `autofte binscan <binary>` | Exploit mitigation report → JSON |
265
+ | `autofte llm` | Local-LLM write-up from the triage + binscan output, grounded in the real crash record |
266
+ | `autofte report [--format markdown\|sarif]` | Markdown summary (default) or SARIF findings from the JSON artifacts |
267
+ | `autofte dashboard` | Static HTML dashboard from the JSON artifacts |
268
+ | `autofte crash-info [file]` | Quick size/type/preview of one crash file |
269
+ | `autofte doctor` | Report which required/optional tools are installed |
270
+ | `autofte bench [--corpus micro\|igor\|<path>]` | Measure dedup accuracy (purity/inverse-purity/F-measure) against a labeled ground-truth corpus |
271
+ | `autofte pipeline [binary] [source] [--sarif <path>]` | Runs triage → binscan → llm → report → dashboard in order, optionally also writing SARIF |
272
+
273
+ Run `autofte <command> --help` for the full flag list on any of them.
274
+
275
+ ## Fuzzing helpers
276
+
277
+ `scripts/fuzz.sh` and `scripts/minimize.sh` are thin wrappers around `afl-fuzz` and `afl-cmin` — AutoFTE doesn't reimplement a fuzzer, it consumes AFL++'s output.
278
+
279
+ ```bash
280
+ scripts/fuzz.sh examples/vuln-demo/target examples/vuln-demo/in out
281
+ scripts/minimize.sh examples/vuln-demo/target out/default/crashes out/default/crashes_min
282
+ ```
283
+
284
+ ## GitHub Action
285
+
286
+ `action.yml` at the repo root is a reusable composite Action that runs the
287
+ same `autofte pipeline` command against a CI crash-artifact directory and
288
+ uploads the result to GitHub code scanning via
289
+ `github/codeql-action/upload-sarif`. Minimal usage in a consumer's
290
+ workflow:
291
+
292
+ ```yaml
293
+ - uses: Nathan-Luevano/AutoFTE@<ref>
294
+ with:
295
+ target-binary: ./target
296
+ crashes-dir: out/default/crashes
297
+ ```
298
+
299
+ > `@<ref>` needs to be a real tag once one exists — same "wired up, not
300
+ > shipped yet" caveat as the `pipx install autofte` line above. Point it at
301
+ > a commit SHA or `main` to use it before a tag exists.
302
+
303
+ The LLM write-up step is opt-in: pass `model`/`host` if a runner can reach
304
+ an Ollama instance, otherwise the action runs with `--skip-llm` by default
305
+ so it never hangs or fails on a runner with no local LLM. See
306
+ [`.github/workflows/example-fuzzing-triage.yml`](.github/workflows/example-fuzzing-triage.yml)
307
+ for a full example workflow to copy into your own project (it's a
308
+ reference file, not something that runs on AutoFTE's own CI — this repo
309
+ has no real fuzzing crash corpus to triage).
310
+
311
+ ## Choosing an LLM model
312
+
313
+ There's no hard-coded default model — different machines have different models pulled. AutoFTE resolves one at runtime, in order:
314
+
315
+ 1. `--model` flag
316
+ 2. `AUTOFTE_LLM_MODEL` environment variable
317
+ 3. auto-detect: pick an installed Ollama model with "coder" in the name, falling back to whatever's installed first
318
+
319
+ ```bash
320
+ autofte llm --model qwen3-coder:30b
321
+ # or
322
+ export AUTOFTE_LLM_MODEL=qwen3-coder:30b
323
+ ```
324
+
325
+ `OLLAMA_HOST` (or `--host`) controls where AutoFTE looks for Ollama; defaults to `http://localhost:11434`.
326
+
327
+ There's no timeout on the model call by default — a cold model load or CPU-only inference can legitimately take a while, and a hard cap just turns "slow" into "silently skipped." Set `AUTOFTE_LLM_TIMEOUT` (or pass `--llm-timeout`, in seconds) if you'd rather it give up after a bound you choose.
328
+
329
+ ## Repo layout
330
+
331
+ ```
332
+ autofte/ installable package: triage, dedup, sanitizers, binary_analysis, severity, llm, sarif,
333
+ report, dashboard, doctor, config, cli, bench, metrics, io_utils, paths,
334
+ vendored_ignore_lists, crash_display
335
+ autofte/demo_assets/ packaged copy of the vuln-demo target so `autofte demo` works from a wheel/pipx/Docker
336
+ install, not just a source checkout — kept in sync with examples/vuln-demo/
337
+ examples/vuln-demo/ intentionally vulnerable demo target + Makefile + seed corpus + pre-generated crashes
338
+ (the source-of-truth dev copy)
339
+ benchmarks/ accuracy regression gate: micro corpus baseline, dedup baseline.json, and sweep
340
+ results consumed by `autofte bench` (see benchmarks/results.md)
341
+ scripts/ AFL++ wrappers (fuzz.sh, minimize.sh), the PyInstaller entry point, the end-to-end
342
+ smoke-test.sh, demo-recording helpers (record-demo.sh, screenshot-dashboard.mjs,
343
+ stitch-demo.sh), and one-off accuracy investigation scripts
344
+ tests/ pytest suite
345
+ Dockerfile container image; build locally with `docker build -t autofte .` (not published)
346
+ pyinstaller.spec single-file binary build spec, used by the release workflow
347
+ action.yml reusable GitHub Action: runs the pipeline on CI crash artifacts, uploads SARIF
348
+ .github/workflows/ CI (tests + lint), release (PyPI publish + binary attach on `v*` tags), and an example
349
+ consumer workflow for action.yml
350
+ ```
351
+
352
+ ## Development
353
+
354
+ ```bash
355
+ python3 -m pip install -e ".[dev]"
356
+ pytest
357
+ ruff check .
358
+ ```
359
+
360
+ 491 tests: mocked subprocess calls for the tool-parsing logic, plus real end-to-end passes against compiled binaries (including real multi-compiler ASan builds), a real local Ollama call exercising the evidence-cited/schema-constrained LLM path, and `autofte bench` runs against a real, independently-downloaded 325,000-report ground-truth corpus (see [`benchmarks/results.md`](benchmarks/results.md) for the measured accuracy numbers). [`scripts/smoke-test.sh`](scripts/smoke-test.sh) is a separate, manually-run end-to-end check against the real CLI — see [CONTRIBUTING.md](CONTRIBUTING.md#before-opening-a-pr) for when to run it. See [CONTRIBUTING.md](CONTRIBUTING.md) for the commit convention and how to add a new binscan check.
361
+
362
+ ## Roadmap
363
+
364
+ - Disassembly around the faulting instruction, fed into the LLM prompt (the prompt already accepts it — `llm.build_prompt`'s `disassembly` param — nothing produces it yet)
365
+ - Support fuzzer backends beyond AFL++ (libFuzzer, honggfuzz)
366
+ - macOS support (depends on gdb/binutils availability there)
367
+ - Distro packaging (BlackArch, Kali) once PyPI + binary releases exist
368
+
369
+ ## Notes
370
+
371
+ - Standard binutils tools (`readelf`, `objdump`, `nm`, `ldd`, `file`, `strings`) are required for binary analysis; run `autofte doctor` to check.
372
+ - `gdb` and AFL++ are optional. If `gdb` is missing, crash grouping falls back to signal-based buckets.
373
+ - Ollama is optional; if it's not running, the rest of the pipeline still finishes.
374
+ - Everything runs locally. AutoFTE makes no network calls except to a local (or explicitly configured) Ollama host — nothing about a crash, a binary, or its source is ever sent anywhere else.
375
+
376
+ ## License
377
+
378
+ [MIT](LICENSE)