diff --git a/README.md b/README.md index 8887a33213..c421615446 100644 --- a/README.md +++ b/README.md @@ -194,7 +194,7 @@ Included skills cover the common security-analysis loop: - **Scan and triage:** `build-project`, `run-scan`, `analyze-findings`, `generate-poc` - **Coverage expansion:** `triage-dependencies`, `discover-attack-surface`, `create-test-project`, `create-rule`, `assemble-lib-rules` -- **Dataflow modeling:** `analyze-external-methods`, `create-pass-through-approximation`, `create-dataflow-approximation`, `debug-rule`, `report-analyzer-issue` +- **Dataflow modeling:** `analyze-external-methods`, `create-pass-through-model`, `create-dataflow-model`, `debug-rule`, `report-analyzer-issue` --- diff --git a/docs/README.md b/docs/README.md index 760695b752..e50bf5231e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -134,7 +134,7 @@ opentaint summary --show-findings --verbose-flow --show-code-snippets results.sa | `opentaint summary` | View SARIF results | | `opentaint health` | Show resolved analyzer, autobuilder, rules, and runtime paths | | `opentaint test rule` | Scaffold, test, and debug detection rules | -| `opentaint test approximation` | Scaffold and test dataflow approximations | +| `opentaint test model` | Scaffold and test dataflow models | | `opentaint pull` | Download dependencies | | `opentaint update` | Update to latest version | | `opentaint prune` | Remove stale artifacts and cached models | diff --git a/docs/faq.md b/docs/faq.md index ccf4c844a0..1a7a6aa5c7 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -22,7 +22,7 @@ Java and Kotlin today. The engine works on bytecode, which gives it precise reso **Why is OpenTaint the most thorough taint analyzer for Spring apps?** -It does inter-procedural data-flow analysis, following tainted data across method boundaries and through async constructs — Reactor, Spring WebFlux, and Kotlin coroutines are all modeled via data-flow approximations. Out of the box, it also models JPA persistence layers, so it catches stored injections where untrusted input arrives at one endpoint, gets saved to the database, and reappears in a completely different request later. Most engines treat the persistence layer as an opaque boundary; OpenTaint models it as part of the flow, linking writes in one request to reads in another. +It does inter-procedural data-flow analysis, following tainted data across method boundaries and through async constructs — Reactor, Spring WebFlux, and Kotlin coroutines are all modeled via data-flow models. Out of the box, it also models JPA persistence layers, so it catches stored injections where untrusted input arrives at one endpoint, gets saved to the database, and reappears in a completely different request later. Most engines treat the persistence layer as an opaque boundary; OpenTaint models it as part of the flow, linking writes in one request to reads in another. **How does OpenTaint compare to Semgrep?** diff --git a/docs/translations/README.ar.md b/docs/translations/README.ar.md index 6f098d8f92..875d5948ee 100644 --- a/docs/translations/README.ar.md +++ b/docs/translations/README.ar.md @@ -173,7 +173,7 @@ npx skills add https://github.com/seqra/opentaint - **الفحص والفرز:** `build-project`, `run-scan`, `analyze-findings`, `generate-poc` - **توسيع التغطية:** `triage-dependencies`, `discover-attack-surface`, `create-test-project`, `create-rule`, `assemble-lib-rules` -- **نمذجة تدفق البيانات:** `analyze-external-methods`, `create-pass-through-approximation`, `create-dataflow-approximation`, `debug-rule`, `report-analyzer-issue` +- **نمذجة تدفق البيانات:** `analyze-external-methods`, `create-pass-through-model`, `create-dataflow-model`, `debug-rule`, `report-analyzer-issue` --- diff --git a/docs/translations/README.bn.md b/docs/translations/README.bn.md index 40f67d0bbe..c6a59a13f1 100644 --- a/docs/translations/README.bn.md +++ b/docs/translations/README.bn.md @@ -173,7 +173,7 @@ npx skills add https://github.com/seqra/opentaint - **স্ক্যান ও ট্রায়াজ:** `build-project`, `run-scan`, `analyze-findings`, `generate-poc` - **কভারেজ সম্প্রসারণ:** `triage-dependencies`, `discover-attack-surface`, `create-test-project`, `create-rule`, `assemble-lib-rules` -- **ডেটাফ্লো মডেলিং:** `analyze-external-methods`, `create-pass-through-approximation`, `create-dataflow-approximation`, `debug-rule`, `report-analyzer-issue` +- **ডেটাফ্লো মডেলিং:** `analyze-external-methods`, `create-pass-through-model`, `create-dataflow-model`, `debug-rule`, `report-analyzer-issue` --- diff --git a/docs/translations/README.br.md b/docs/translations/README.br.md index 95e6b068f4..4ac8dabd9e 100644 --- a/docs/translations/README.br.md +++ b/docs/translations/README.br.md @@ -173,7 +173,7 @@ As skills incluídas cobrem o ciclo comum de análise de segurança: - **Varredura e triagem:** `build-project`, `run-scan`, `analyze-findings`, `generate-poc` - **Expansão de cobertura:** `triage-dependencies`, `discover-attack-surface`, `create-test-project`, `create-rule`, `assemble-lib-rules` -- **Modelagem de fluxo de dados:** `analyze-external-methods`, `create-pass-through-approximation`, `create-dataflow-approximation`, `debug-rule`, `report-analyzer-issue` +- **Modelagem de fluxo de dados:** `analyze-external-methods`, `create-pass-through-model`, `create-dataflow-model`, `debug-rule`, `report-analyzer-issue` --- diff --git a/docs/translations/README.bs.md b/docs/translations/README.bs.md index 5a39cf690f..e98f9ad248 100644 --- a/docs/translations/README.bs.md +++ b/docs/translations/README.bs.md @@ -173,7 +173,7 @@ Uključene vještine pokrivaju uobičajenu petlju sigurnosne analize: - **Skeniranje i trijaža:** `build-project`, `run-scan`, `analyze-findings`, `generate-poc` - **Proširenje pokrivenosti:** `triage-dependencies`, `discover-attack-surface`, `create-test-project`, `create-rule`, `assemble-lib-rules` -- **Modeliranje toka podataka:** `analyze-external-methods`, `create-pass-through-approximation`, `create-dataflow-approximation`, `debug-rule`, `report-analyzer-issue` +- **Modeliranje toka podataka:** `analyze-external-methods`, `create-pass-through-model`, `create-dataflow-model`, `debug-rule`, `report-analyzer-issue` --- diff --git a/docs/translations/README.da.md b/docs/translations/README.da.md index 4a86a9b32a..13d3d265ed 100644 --- a/docs/translations/README.da.md +++ b/docs/translations/README.da.md @@ -173,7 +173,7 @@ De inkluderede skills dækker den almindelige sikkerhedsanalyse-løkke: - **Scan og triage:** `build-project`, `run-scan`, `analyze-findings`, `generate-poc` - **Udvidelse af dækning:** `triage-dependencies`, `discover-attack-surface`, `create-test-project`, `create-rule`, `assemble-lib-rules` -- **Dataflow-modellering:** `analyze-external-methods`, `create-pass-through-approximation`, `create-dataflow-approximation`, `debug-rule`, `report-analyzer-issue` +- **Dataflow-modellering:** `analyze-external-methods`, `create-pass-through-model`, `create-dataflow-model`, `debug-rule`, `report-analyzer-issue` --- diff --git a/docs/translations/README.de.md b/docs/translations/README.de.md index a05303bf31..9c16ae2b11 100644 --- a/docs/translations/README.de.md +++ b/docs/translations/README.de.md @@ -173,7 +173,7 @@ Die enthaltenen Skills decken den gangigen Sicherheitsanalyse-Zyklus ab: - **Scannen und Triage:** `build-project`, `run-scan`, `analyze-findings`, `generate-poc` - **Erweiterung der Abdeckung:** `triage-dependencies`, `discover-attack-surface`, `create-test-project`, `create-rule`, `assemble-lib-rules` -- **Datenfluss-Modellierung:** `analyze-external-methods`, `create-pass-through-approximation`, `create-dataflow-approximation`, `debug-rule`, `report-analyzer-issue` +- **Datenfluss-Modellierung:** `analyze-external-methods`, `create-pass-through-model`, `create-dataflow-model`, `debug-rule`, `report-analyzer-issue` --- diff --git a/docs/translations/README.es.md b/docs/translations/README.es.md index 7aa9d70108..bdfd0040b0 100644 --- a/docs/translations/README.es.md +++ b/docs/translations/README.es.md @@ -173,7 +173,7 @@ Las habilidades incluidas cubren el ciclo habitual de analisis de seguridad: - **Escaneo y triaje:** `build-project`, `run-scan`, `analyze-findings`, `generate-poc` - **Ampliacion de cobertura:** `triage-dependencies`, `discover-attack-surface`, `create-test-project`, `create-rule`, `assemble-lib-rules` -- **Modelado de flujo de datos:** `analyze-external-methods`, `create-pass-through-approximation`, `create-dataflow-approximation`, `debug-rule`, `report-analyzer-issue` +- **Modelado de flujo de datos:** `analyze-external-methods`, `create-pass-through-model`, `create-dataflow-model`, `debug-rule`, `report-analyzer-issue` --- diff --git a/docs/translations/README.fr.md b/docs/translations/README.fr.md index 641a330019..abafbd6c41 100644 --- a/docs/translations/README.fr.md +++ b/docs/translations/README.fr.md @@ -173,7 +173,7 @@ Les skills inclus couvrent le cycle habituel d'analyse de sécurité : - **Analyse et triage :** `build-project`, `run-scan`, `analyze-findings`, `generate-poc` - **Extension de la couverture :** `triage-dependencies`, `discover-attack-surface`, `create-test-project`, `create-rule`, `assemble-lib-rules` -- **Modélisation des flux de données :** `analyze-external-methods`, `create-pass-through-approximation`, `create-dataflow-approximation`, `debug-rule`, `report-analyzer-issue` +- **Modélisation des flux de données :** `analyze-external-methods`, `create-pass-through-model`, `create-dataflow-model`, `debug-rule`, `report-analyzer-issue` --- diff --git a/docs/translations/README.gr.md b/docs/translations/README.gr.md index 55c08b4235..053adf4501 100644 --- a/docs/translations/README.gr.md +++ b/docs/translations/README.gr.md @@ -173,7 +173,7 @@ npx skills add https://github.com/seqra/opentaint - **Σάρωση και διαλογή:** `build-project`, `run-scan`, `analyze-findings`, `generate-poc` - **Επέκταση κάλυψης:** `triage-dependencies`, `discover-attack-surface`, `create-test-project`, `create-rule`, `assemble-lib-rules` -- **Μοντελοποίηση ροής δεδομένων:** `analyze-external-methods`, `create-pass-through-approximation`, `create-dataflow-approximation`, `debug-rule`, `report-analyzer-issue` +- **Μοντελοποίηση ροής δεδομένων:** `analyze-external-methods`, `create-pass-through-model`, `create-dataflow-model`, `debug-rule`, `report-analyzer-issue` --- diff --git a/docs/translations/README.hi.md b/docs/translations/README.hi.md index 049dd3891b..e947e4a127 100644 --- a/docs/translations/README.hi.md +++ b/docs/translations/README.hi.md @@ -173,7 +173,7 @@ npx skills add https://github.com/seqra/opentaint - **स्कैन और triage:** `build-project`, `run-scan`, `analyze-findings`, `generate-poc` - **कवरेज विस्तार:** `triage-dependencies`, `discover-attack-surface`, `create-test-project`, `create-rule`, `assemble-lib-rules` -- **डेटा फ़्लो मॉडलिंग:** `analyze-external-methods`, `create-pass-through-approximation`, `create-dataflow-approximation`, `debug-rule`, `report-analyzer-issue` +- **डेटा फ़्लो मॉडलिंग:** `analyze-external-methods`, `create-pass-through-model`, `create-dataflow-model`, `debug-rule`, `report-analyzer-issue` --- diff --git a/docs/translations/README.id.md b/docs/translations/README.id.md index c7b5171958..075b780c09 100644 --- a/docs/translations/README.id.md +++ b/docs/translations/README.id.md @@ -173,7 +173,7 @@ Keterampilan yang disertakan mencakup siklus analisis keamanan umum: - **Pemindaian dan triage:** `build-project`, `run-scan`, `analyze-findings`, `generate-poc` - **Perluasan cakupan:** `triage-dependencies`, `discover-attack-surface`, `create-test-project`, `create-rule`, `assemble-lib-rules` -- **Pemodelan aliran data:** `analyze-external-methods`, `create-pass-through-approximation`, `create-dataflow-approximation`, `debug-rule`, `report-analyzer-issue` +- **Pemodelan aliran data:** `analyze-external-methods`, `create-pass-through-model`, `create-dataflow-model`, `debug-rule`, `report-analyzer-issue` --- diff --git a/docs/translations/README.it.md b/docs/translations/README.it.md index d5139bfe17..9f9d7c8146 100644 --- a/docs/translations/README.it.md +++ b/docs/translations/README.it.md @@ -173,7 +173,7 @@ Le skill incluse coprono il ciclo comune di analisi della sicurezza: - **Scansione e triage:** `build-project`, `run-scan`, `analyze-findings`, `generate-poc` - **Espansione della copertura:** `triage-dependencies`, `discover-attack-surface`, `create-test-project`, `create-rule`, `assemble-lib-rules` -- **Modellazione del dataflow:** `analyze-external-methods`, `create-pass-through-approximation`, `create-dataflow-approximation`, `debug-rule`, `report-analyzer-issue` +- **Modellazione del dataflow:** `analyze-external-methods`, `create-pass-through-model`, `create-dataflow-model`, `debug-rule`, `report-analyzer-issue` --- diff --git a/docs/translations/README.ja.md b/docs/translations/README.ja.md index 4a490749d4..4bf577958e 100644 --- a/docs/translations/README.ja.md +++ b/docs/translations/README.ja.md @@ -173,7 +173,7 @@ npx skills add https://github.com/seqra/opentaint - **スキャンとトリアージ:** `build-project`、`run-scan`、`analyze-findings`、`generate-poc` - **カバレッジ拡張:** `triage-dependencies`、`discover-attack-surface`、`create-test-project`、`create-rule`、`assemble-lib-rules` -- **データフローモデリング:** `analyze-external-methods`、`create-pass-through-approximation`、`create-dataflow-approximation`、`debug-rule`、`report-analyzer-issue` +- **データフローモデリング:** `analyze-external-methods`、`create-pass-through-model`、`create-dataflow-model`、`debug-rule`、`report-analyzer-issue` --- diff --git a/docs/translations/README.ko.md b/docs/translations/README.ko.md index dbc5776739..fa8ed716da 100644 --- a/docs/translations/README.ko.md +++ b/docs/translations/README.ko.md @@ -173,7 +173,7 @@ npx skills add https://github.com/seqra/opentaint - **스캔 및 분류:** `build-project`, `run-scan`, `analyze-findings`, `generate-poc` - **커버리지 확장:** `triage-dependencies`, `discover-attack-surface`, `create-test-project`, `create-rule`, `assemble-lib-rules` -- **데이터플로우 모델링:** `analyze-external-methods`, `create-pass-through-approximation`, `create-dataflow-approximation`, `debug-rule`, `report-analyzer-issue` +- **데이터플로우 모델링:** `analyze-external-methods`, `create-pass-through-model`, `create-dataflow-model`, `debug-rule`, `report-analyzer-issue` --- diff --git a/docs/translations/README.no.md b/docs/translations/README.no.md index 0c4e2838b2..d830ab0acf 100644 --- a/docs/translations/README.no.md +++ b/docs/translations/README.no.md @@ -173,7 +173,7 @@ De inkluderte ferdighetene dekker den vanlige sikkerhetsanalysesyklusen: - **Skanning og triagering:** `build-project`, `run-scan`, `analyze-findings`, `generate-poc` - **Utvidet dekning:** `triage-dependencies`, `discover-attack-surface`, `create-test-project`, `create-rule`, `assemble-lib-rules` -- **Dataflytmodellering:** `analyze-external-methods`, `create-pass-through-approximation`, `create-dataflow-approximation`, `debug-rule`, `report-analyzer-issue` +- **Dataflytmodellering:** `analyze-external-methods`, `create-pass-through-model`, `create-dataflow-model`, `debug-rule`, `report-analyzer-issue` --- diff --git a/docs/translations/README.pl.md b/docs/translations/README.pl.md index 7d84e43134..8d2fbee1c7 100644 --- a/docs/translations/README.pl.md +++ b/docs/translations/README.pl.md @@ -173,7 +173,7 @@ Dołączone umiejętności obejmują typowy cykl analizy bezpieczeństwa: - **Skanowanie i selekcja:** `build-project`, `run-scan`, `analyze-findings`, `generate-poc` - **Rozszerzanie pokrycia:** `triage-dependencies`, `discover-attack-surface`, `create-test-project`, `create-rule`, `assemble-lib-rules` -- **Modelowanie przepływu danych:** `analyze-external-methods`, `create-pass-through-approximation`, `create-dataflow-approximation`, `debug-rule`, `report-analyzer-issue` +- **Modelowanie przepływu danych:** `analyze-external-methods`, `create-pass-through-model`, `create-dataflow-model`, `debug-rule`, `report-analyzer-issue` --- diff --git a/docs/translations/README.ru.md b/docs/translations/README.ru.md index 79015845ef..64d37fbb62 100644 --- a/docs/translations/README.ru.md +++ b/docs/translations/README.ru.md @@ -173,7 +173,7 @@ npx skills add https://github.com/seqra/opentaint - **Сканирование и триаж:** `build-project`, `run-scan`, `analyze-findings`, `generate-poc` - **Расширение покрытия:** `triage-dependencies`, `discover-attack-surface`, `create-test-project`, `create-rule`, `assemble-lib-rules` -- **Моделирование потоков данных:** `analyze-external-methods`, `create-pass-through-approximation`, `create-dataflow-approximation`, `debug-rule`, `report-analyzer-issue` +- **Моделирование потоков данных:** `analyze-external-methods`, `create-pass-through-model`, `create-dataflow-model`, `debug-rule`, `report-analyzer-issue` --- diff --git a/docs/translations/README.sv.md b/docs/translations/README.sv.md index 6bd6b85eed..af15ec5ea6 100644 --- a/docs/translations/README.sv.md +++ b/docs/translations/README.sv.md @@ -175,7 +175,7 @@ Inkluderade färdigheter täcker den vanliga säkerhetsanalysloopen: - **Skanning och triage:** `build-project`, `run-scan`, `analyze-findings`, `generate-poc` - **Utökad täckning:** `triage-dependencies`, `discover-attack-surface`, `create-test-project`, `create-rule`, `assemble-lib-rules` -- **Dataflödesmodellering:** `analyze-external-methods`, `create-pass-through-approximation`, `create-dataflow-approximation`, `debug-rule`, `report-analyzer-issue` +- **Dataflödesmodellering:** `analyze-external-methods`, `create-pass-through-model`, `create-dataflow-model`, `debug-rule`, `report-analyzer-issue` --- diff --git a/docs/translations/README.th.md b/docs/translations/README.th.md index 4f762a2124..5e5dad725d 100644 --- a/docs/translations/README.th.md +++ b/docs/translations/README.th.md @@ -173,7 +173,7 @@ npx skills add https://github.com/seqra/opentaint - **สแกนและคัดแยก:** `build-project`, `run-scan`, `analyze-findings`, `generate-poc` - **ขยายการครอบคลุม:** `triage-dependencies`, `discover-attack-surface`, `create-test-project`, `create-rule`, `assemble-lib-rules` -- **การสร้างแบบจำลอง dataflow:** `analyze-external-methods`, `create-pass-through-approximation`, `create-dataflow-approximation`, `debug-rule`, `report-analyzer-issue` +- **การสร้างแบบจำลอง dataflow:** `analyze-external-methods`, `create-pass-through-model`, `create-dataflow-model`, `debug-rule`, `report-analyzer-issue` --- diff --git a/docs/translations/README.tr.md b/docs/translations/README.tr.md index 754278dedf..0666ba14b2 100644 --- a/docs/translations/README.tr.md +++ b/docs/translations/README.tr.md @@ -173,7 +173,7 @@ npx skills add https://github.com/seqra/opentaint - **Tarama ve önceliklendirme:** `build-project`, `run-scan`, `analyze-findings`, `generate-poc` - **Kapsam genişletme:** `triage-dependencies`, `discover-attack-surface`, `create-test-project`, `create-rule`, `assemble-lib-rules` -- **Veri akışı modelleme:** `analyze-external-methods`, `create-pass-through-approximation`, `create-dataflow-approximation`, `debug-rule`, `report-analyzer-issue` +- **Veri akışı modelleme:** `analyze-external-methods`, `create-pass-through-model`, `create-dataflow-model`, `debug-rule`, `report-analyzer-issue` --- diff --git a/docs/translations/README.ua.md b/docs/translations/README.ua.md index b65c547a89..848fd97e26 100644 --- a/docs/translations/README.ua.md +++ b/docs/translations/README.ua.md @@ -173,7 +173,7 @@ npx skills add https://github.com/seqra/opentaint - **Сканування та тріаж:** `build-project`, `run-scan`, `analyze-findings`, `generate-poc` - **Розширення покриття:** `triage-dependencies`, `discover-attack-surface`, `create-test-project`, `create-rule`, `assemble-lib-rules` -- **Моделювання потоків даних:** `analyze-external-methods`, `create-pass-through-approximation`, `create-dataflow-approximation`, `debug-rule`, `report-analyzer-issue` +- **Моделювання потоків даних:** `analyze-external-methods`, `create-pass-through-model`, `create-dataflow-model`, `debug-rule`, `report-analyzer-issue` --- diff --git a/docs/translations/README.vi.md b/docs/translations/README.vi.md index 115665a8c3..7d7613ee5a 100644 --- a/docs/translations/README.vi.md +++ b/docs/translations/README.vi.md @@ -173,7 +173,7 @@ Các kỹ năng đi kèm bao phủ vòng lặp phân tích bảo mật thông d - **Quét và phân loại:** `build-project`, `run-scan`, `analyze-findings`, `generate-poc` - **Mở rộng phạm vi bao phủ:** `triage-dependencies`, `discover-attack-surface`, `create-test-project`, `create-rule`, `assemble-lib-rules` -- **Mô hình hóa luồng dữ liệu:** `analyze-external-methods`, `create-pass-through-approximation`, `create-dataflow-approximation`, `debug-rule`, `report-analyzer-issue` +- **Mô hình hóa luồng dữ liệu:** `analyze-external-methods`, `create-pass-through-model`, `create-dataflow-model`, `debug-rule`, `report-analyzer-issue` --- diff --git a/docs/translations/README.zh.md b/docs/translations/README.zh.md index 637a017452..ec54843db6 100644 --- a/docs/translations/README.zh.md +++ b/docs/translations/README.zh.md @@ -173,7 +173,7 @@ npx skills add https://github.com/seqra/opentaint - **扫描与分诊:** `build-project`、`run-scan`、`analyze-findings`、`generate-poc` - **覆盖范围扩展:** `triage-dependencies`、`discover-attack-surface`、`create-test-project`、`create-rule`、`assemble-lib-rules` -- **数据流建模:** `analyze-external-methods`、`create-pass-through-approximation`、`create-dataflow-approximation`、`debug-rule`、`report-analyzer-issue` +- **数据流建模:** `analyze-external-methods`、`create-pass-through-model`、`create-dataflow-model`、`debug-rule`、`report-analyzer-issue` --- diff --git a/docs/translations/README.zht.md b/docs/translations/README.zht.md index a7abf11b05..a8e9df1438 100644 --- a/docs/translations/README.zht.md +++ b/docs/translations/README.zht.md @@ -173,7 +173,7 @@ npx skills add https://github.com/seqra/opentaint - **掃描與分級:** `build-project`、`run-scan`、`analyze-findings`、`generate-poc` - **覆蓋範圍擴展:** `triage-dependencies`、`discover-attack-surface`、`create-test-project`、`create-rule`、`assemble-lib-rules` -- **資料流建模:** `analyze-external-methods`、`create-pass-through-approximation`、`create-dataflow-approximation`、`debug-rule`、`report-analyzer-issue` +- **資料流建模:** `analyze-external-methods`、`create-pass-through-model`、`create-dataflow-model`、`debug-rule`、`report-analyzer-issue` --- diff --git a/docs/usage.md b/docs/usage.md index eb2d776d2b..c12340e862 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -84,8 +84,8 @@ Use [CodeChecker](https://github.com/Ericsson/codechecker) for advanced result m | `opentaint summary` | View SARIF analysis results | | `opentaint health` | Show resolved paths for the analyzer, autobuilder, rules, and Java runtime | | `opentaint test rule` | Create, run, and debug detection-rule tests | -| `opentaint test approximation` | Create and run dataflow-approximation tests | -| `opentaint approximation` | Create dataflow approximation projects | +| `opentaint test model` | Create and run dataflow-model tests | +| `opentaint model` | Create dataflow model projects | | `opentaint pull` | Download analyzer dependencies | | `opentaint update` | Update to latest version | | `opentaint prune` | Remove stale downloaded artifacts and cached models | @@ -110,13 +110,13 @@ On the first run, the compiled project model is cached in `~/.opentaint/cache/`. #### Rule-authoring flags -These flags are to work with custom approximations: +These flags are to work with custom models: | Flag | Description | |------|-------------| | `--track-external-methods` | Write external-method coverage files next to the SARIF report | -| `--passthrough-approximations` | Apply pass-through approximation YAML files or directories (repeatable) | -| `--dataflow-approximations` | Apply dataflow approximation projects, build outputs, or class directories (repeatable) | +| `--passthrough-models` | Apply pass-through model YAML files or directories (repeatable) | +| `--dataflow-models` | Apply dataflow model projects, build outputs, or class directories (repeatable) | Use external-method tracking when a scan may miss flows through library methods. The dropped-methods file shows where taint was killed because no model was available; the approximated-methods file shows methods already covered by built-in or custom models. @@ -141,7 +141,7 @@ With no flags, `health` shows the autobuilder, analyzer, built-in rules, and Jav ### opentaint test -The `test` command group is tooling for rule and approximation development. +The `test` command group is tooling for rule and model development. #### Rule tests @@ -158,22 +158,22 @@ opentaint test rule reachability java/security/my-rule.yaml:my-rule --project-mo | `opentaint test rule run ` | Run detection-rule tests on a compiled project model | | `opentaint test rule reachability [source-path]` | Trace why a rule can or cannot reach its facts | -#### Approximation tests +#### Model tests ```bash -opentaint test approximation init .opentaint/test-projects/my-approximation \ +opentaint test model init .opentaint/test-projects/my-model \ --dependency "io.projectreactor:reactor-core:3.8.5" -opentaint compile .opentaint/test-projects/my-approximation -o .opentaint/test-compiled/my-approximation -opentaint test approximation run .opentaint/test-compiled/my-approximation \ - --dataflow-approximations .opentaint/dataflow/my-approximation +opentaint compile .opentaint/test-projects/my-model -o .opentaint/test-compiled/my-model +opentaint test model run .opentaint/test-compiled/my-model \ + --dataflow-models .opentaint/dataflow/my-model ``` | Command | Description | |---------|-------------| -| `opentaint test approximation init ` | Create a test project with a fixed `Taint.source()` to `Taint.sink(...)` harness | -| `opentaint test approximation run ` | Run dataflow approximation tests on a compiled project model | +| `opentaint test model init ` | Create a test project with a fixed `Taint.source()` to `Taint.sink(...)` harness | +| `opentaint test model run ` | Run dataflow model tests on a compiled project model | -Rule and approximation test runs write `test-result.json` and `test-results.sarif` to the selected output directory. +Rule and model test runs write `test-result.json` and `test-results.sarif` to the selected output directory. ### opentaint compile @@ -190,32 +190,32 @@ opentaint scan --project-model ./my-project-model | `--dry-run` | Validate inputs and show what would run without compiling | | `--log-file` | Path to the log file (default: `/logs/.log`) | -### opentaint approximation +### opentaint model -A dataflow approximation models how taint moves through a method the analyzer cannot see into. Because a model references the library type it models, it needs that library to compile — so the models live in their own project, which pins those versions itself: +A dataflow model describes how taint moves through a method the analyzer cannot see into. Because a model references the library type it models, it needs that library to compile — so the models live in their own project, which pins those versions itself: ```bash -opentaint approximation init .opentaint/dataflow/my-batch \ +opentaint model init .opentaint/dataflow/my-batch \ --dependency "io.projectreactor:reactor-core:3.8.5" # write the @Approximate classes under src/main/java/, then apply them opentaint scan --project-model ./my-project-model \ - --dataflow-approximations .opentaint/dataflow/my-batch + --dataflow-models .opentaint/dataflow/my-batch ``` Pin the versions the target application uses. Those pins are the models' compile environment: they, and nothing about the project under analysis, decide what the models compile against, so a model compiles identically wherever it is applied. -`--dataflow-approximations` accepts an approximation project, a build output, or a directory of compiled classes — and a directory holding any of those, so a tree with one project per batch can be passed as a single flag. A directory counts as compiled classes only when nothing below it still needs building; one that holds compiled classes of its own alongside a project is reported rather than guessed at. Paths may overlap, and the same models may be reached twice, but two of them may not offer the same class with different content — which copy an analysis would read is not defined. Projects are built on demand and rebuilt when their sources, their dependency pins, or the compiler change. +`--dataflow-models` accepts a model project, a build output, or a directory of compiled classes — and a directory holding any of those, so a tree with one project per batch can be passed as a single flag. A directory counts as compiled classes only when nothing below it still needs building; one that holds compiled classes of its own alongside a project is reported rather than guessed at. Paths may overlap, and the same models may be reached twice, but two of them may not offer the same class with different content — which copy an analysis would read is not defined. Projects are built on demand and rebuilt when their sources, their dependency pins, or the compiler change. | Command | Description | |---------|-------------| -| `opentaint approximation init ` | Create a dataflow approximation project | -| `opentaint compile approximations ` | Compile a dataflow approximation project | +| `opentaint model init ` | Create a dataflow model project | +| `opentaint compile models ` | Compile a dataflow model project | | Flag | Description | |------|-------------| | `--dependency` | Compile-only Maven dependency coordinates the models are written against (repeatable, `init` only) | -| `--output`, `-o` | Path to the compiled models (default: `/.opentaint/build`, `compile approximations` only) | +| `--output`, `-o` | Path to the compiled models (default: `/.opentaint/build`, `compile models` only) | Compiling ahead of time is optional — scanning and testing build the project themselves. Run it to see compilation errors on their own. diff --git a/model/java/dataflow/README.md b/model/java/dataflow/README.md index 5eeeba3396..dde2b9cfa4 100644 --- a/model/java/dataflow/README.md +++ b/model/java/dataflow/README.md @@ -1,4 +1,4 @@ -# Builtin dataflow approximations +# Builtin dataflow models Code-based models of how taint moves through methods the analyzer cannot see into. Each directory here is an independent Gradle module compiled against **its own pinned @@ -38,7 +38,7 @@ inside the analyzer jar, which `DataFlowApproximationLoader` unpacks at analysis 2. Put the models under `src/main/java/org/opentaint/jvm/dataflow/approximations//`. The per-module - package keeps approximation class names globally unique: an approximation FQCN maps to + package keeps model class names globally unique: a model FQCN maps to exactly one target class, and a collision between two modules fails the build during `processResources`. @@ -47,10 +47,10 @@ discovers every directory here that has a `build.gradle.kts`. ## Writing a model -Same form as a custom model, so the reference in `skills/create-dataflow-approximation` +Same form as a custom model, so the reference in `skills/create-dataflow-model` applies here too. In short: `@Approximate(TargetClass.class)` on one class per target, reach the real receiver with `(TargetClass) (Object) this`, put functional-interface parameters behind `@ArgumentTypeContext`, branch with `OpentaintNdUtil.nextBool()`, and -never leave a body empty. Approximation bodies are analyzed, never executed, and must not +never leave a body empty. Model bodies are analyzed, never executed, and must not introduce synthetic classes (lambdas, anonymous subclasses) — their constructors surface as dropped methods and carry no taint. diff --git a/skills-templates/analyze-external-methods/scripts/check-coverage.py b/skills-templates/analyze-external-methods/scripts/check-coverage.py index 5c4daf5224..c36a9bb5c8 100644 --- a/skills-templates/analyze-external-methods/scripts/check-coverage.py +++ b/skills-templates/analyze-external-methods/scripts/check-coverage.py @@ -16,7 +16,7 @@ import yaml DROPPED = Path(".opentaint/results/dropped-external-methods.yaml") -APPROX_DIR = Path(".opentaint/tracking/approximations") +MODELS_DIR = Path(".opentaint/tracking/models") # classification buckets + skipped.yaml lists; build.done is read separately (nested) CLASSIFIED_KEYS = {"passthrough", "dataflow", "skipped", "methods", "engine_issues"} @@ -39,7 +39,7 @@ def dropped_methods(): def classified_methods(): # every method+signature in a classification bucket, build.done, or skipped.yaml out = set() - for p in sorted(glob.glob(str(APPROX_DIR / "*.yaml"))): + for p in sorted(glob.glob(str(MODELS_DIR / "*.yaml"))): doc = yaml.safe_load(Path(p).read_text(encoding="utf-8")) or {} for key in CLASSIFIED_KEYS: for item in doc.get(key, []) or []: @@ -55,7 +55,7 @@ def batch_methods(batch_arg): # the members a batch plan assigned (scopes: {class: [{method, signature}]}); id or path p = Path(batch_arg) if not p.is_file(): - p = APPROX_DIR / "plans" / f"{batch_arg}.yaml" + p = MODELS_DIR / "plans" / f"{batch_arg}.yaml" if not p.is_file(): return None doc = yaml.safe_load(p.read_text(encoding="utf-8")) or {} diff --git a/skills-templates/analyze-external-methods/sections/input.md b/skills-templates/analyze-external-methods/sections/input.md index ceb3963073..311679e1ed 100644 --- a/skills-templates/analyze-external-methods/sections/input.md +++ b/skills-templates/analyze-external-methods/sections/input.md @@ -2,5 +2,5 @@ {% include "shared/inputs/project-root.md" %} {% include "shared/inputs/language.md" %} -- `plan` (required) — path to this agent's batch plan `.opentaint/tracking/approximations/plans/.yaml`: the dropped methods to classify +- `plan` (required) — path to this agent's batch plan `.opentaint/tracking/models/plans/.yaml`: the dropped methods to classify - `sinks` (optional) — a flag whether to classify sinks per step 2 or not diff --git a/skills-templates/analyze-external-methods/sections/output.md b/skills-templates/analyze-external-methods/sections/output.md index 3f7ce0f867..af66ebe77c 100644 --- a/skills-templates/analyze-external-methods/sections/output.md +++ b/skills-templates/analyze-external-methods/sections/output.md @@ -2,7 +2,7 @@ Short and concise report of what was done ### Artifacts: -- `.opentaint/tracking/approximations/.yaml` — the batch classification (per Tracking) +- `.opentaint/tracking/models/.yaml` — the batch classification (per Tracking) - `.opentaint/tracking/rules/sinks/.yaml` — the per-package sink units, when `sinks` was set (per Tracking) ### Summary: diff --git a/skills-templates/analyze-external-methods/sections/preamble.md b/skills-templates/analyze-external-methods/sections/preamble.md index eb483d4929..86c58648f6 100644 --- a/skills-templates/analyze-external-methods/sections/preamble.md +++ b/skills-templates/analyze-external-methods/sections/preamble.md @@ -1,7 +1,7 @@ # Skill: Analyze External Methods -OpenTaint is a dataflow taint analyzer: it starts from the data a source introduces and follows it call by call until the flow stops. A flow stops for one of two reasons — the data reached a callable that retains none of it (call `size()` on a tainted collection and its whole contents collapse into one number, so the taint is gone), or it reached a callable whose body the analyzer can't see, typically in an external dependency. That opaque callable may itself be taint-killing, or it may in fact carry the data onward — and then it needs an approximation telling the engine exactly how the data moves through the call, or every trace through it is silently cut. +OpenTaint is a dataflow taint analyzer: it starts from the data a source introduces and follows it call by call until the flow stops. A flow stops for one of two reasons — the data reached a callable that retains none of it (call `size()` on a tainted collection and its whole contents collapse into one number, so the taint is gone), or it reached a callable whose body the analyzer can't see, typically in an external dependency. That opaque callable may itself be taint-killing, or it may in fact carry the data onward — and then it needs a model telling the engine exactly how the data moves through the call, or every trace through it is silently cut. -You are handed the list of those dropped methods. Decide which ones actually carry data and which don't, and for each carrier determine the kind of approximation it needs, so the build stage can restore the flow. +You are handed the list of those dropped methods. Decide which ones actually carry data and which don't, and for each carrier determine the kind of model it needs, so the build stage can restore the flow. On a deep run these same methods carry a second, independent question — whether the call is itself a dangerous operation (a sink); that pass is step 2. diff --git a/skills-templates/analyze-external-methods/sections/tracking.md b/skills-templates/analyze-external-methods/sections/tracking.md index fec1e01866..6160cb78b5 100644 --- a/skills-templates/analyze-external-methods/sections/tracking.md +++ b/skills-templates/analyze-external-methods/sections/tracking.md @@ -1,8 +1,8 @@ ### Batch classification -{% include "shared/tracking/approximations-batch.md" %} +{% include "shared/tracking/models-batch.md" %} -This skill writes `passthrough`/`dataflow`/`skipped` and `dependencies`; leave the `build` block and `engine_issues` alone — they are filled later in the approximation stage. +This skill writes `passthrough`/`dataflow`/`skipped` and `dependencies`; leave the `build` block and `engine_issues` alone — they are filled later in the model stage. ### Sink units (only when `sinks` is set) diff --git a/skills-templates/analyze-external-methods/sections/workflow.md b/skills-templates/analyze-external-methods/sections/workflow.md index 203aa697f7..7ca72fdf81 100644 --- a/skills-templates/analyze-external-methods/sections/workflow.md +++ b/skills-templates/analyze-external-methods/sections/workflow.md @@ -1,6 +1,6 @@ ### 1. Classify propagation -Take the members from the plan's `scopes`, judge each one, and write the verdict to the batch file `.opentaint/tracking/approximations/.yaml` — `` is your plan's filename stem, the batch id, reused for the coverage check below. +Take the members from the plan's `scopes`, judge each one, and write the verdict to the batch file `.opentaint/tracking/models/.yaml` — `` is your plan's filename stem, the batch id, reused for the coverage check below. Always classify from the method's real code, never from its name. Start by reading the method's source — `/references/.md` describes how to get it. Then answer, for each method: where does its input data go? Data that arrives on the receiver or an argument — does it come back out, through the return value, an argument the method writes into, the receiver, or an object or field it stores the data in? That answer picks the bucket: diff --git a/skills-templates/appsec-agent/sections/dispatching.md b/skills-templates/appsec-agent/sections/dispatching.md index 6fb78f77d9..4e9012c39c 100644 --- a/skills-templates/appsec-agent/sections/dispatching.md +++ b/skills-templates/appsec-agent/sections/dispatching.md @@ -3,15 +3,15 @@ Dispatch exactly one stage-orchestrator subagent for each stage invocation: ``` Invoke the Skill orchestrate-stage first, then follow its instructions precisely Inputs: - stage: + stage: ``` -For a `deep` approximation round, also pass `sinks: true`. A subagent inherits the project-root working directory, so omit `project-root`. +For a `deep` model round, also pass `sinks: true`. A subagent inherits the project-root working directory, so omit `project-root`. Stage context: - `sources` — discover dependency sources and author tag-grouped source rules -- `approx-round` — classify and build one dropped-method frontier; use a fresh agent for each new frontier +- `model-round` — classify and build one dropped-method frontier; use a fresh agent for each new frontier - `sinks` — author classified sink rules and wire the joins - `triage` — classify the latest findings and refresh the vulnerability report - `poc` — reproduce confirmed findings and add the outcomes to the report diff --git a/skills-templates/appsec-agent/sections/preamble.md b/skills-templates/appsec-agent/sections/preamble.md index 81065ad8a1..f4d5bdd3c6 100644 --- a/skills-templates/appsec-agent/sections/preamble.md +++ b/skills-templates/appsec-agent/sections/preamble.md @@ -1,5 +1,5 @@ # AppSec Agent -Orchestrate an end-to-end OpenTaint security analysis. Keep the long project build and every full-project scan in this main session; delegate each bounded source, approximation, sink, triage, and PoC stage to an `orchestrate-stage` subagent, which owns its leaf fan-out and joins. +Orchestrate an end-to-end OpenTaint security analysis. Keep the long project build and every full-project scan in this main session; delegate each bounded source, model, sink, triage, and PoC stage to an `orchestrate-stage` subagent, which owns its leaf fan-out and joins. -OpenTaint is a whole-program, interprocedural, field-sensitive alias analysis SAST. The run produces confirmed vulnerabilities plus reusable project-specific rules and approximations under one self-contained `.opentaint/` directory at the project root. +OpenTaint is a whole-program, interprocedural, field-sensitive alias analysis SAST. The run produces confirmed vulnerabilities plus reusable project-specific rules and models under one self-contained `.opentaint/` directory at the project root. diff --git a/skills-templates/appsec-agent/sections/setup.md b/skills-templates/appsec-agent/sections/setup.md index b884117c5b..d90eb83b37 100644 --- a/skills-templates/appsec-agent/sections/setup.md +++ b/skills-templates/appsec-agent/sections/setup.md @@ -21,8 +21,8 @@ Ask the user for both levels together: 1. Scan level — `lite` · `normal` · `deep` - lite — build + scan (expected, when there are already existing artifacts) - - normal — build + scan + custom approximations - - deep — build + scan + custom approximations + custom rules + - normal — build + scan + custom models + - deep — build + scan + custom models + custom rules - recommend by what's on disk: a cold start (no `.opentaint` artifacts) → deep; a prior run's artifacts already present → lite 2. Triage level — `static` · `dynamic` - static — classify findings from the model, no running app diff --git a/skills-templates/appsec-agent/sections/state-resumption.md b/skills-templates/appsec-agent/sections/state-resumption.md index 282c81273a..c36094318e 100644 --- a/skills-templates/appsec-agent/sections/state-resumption.md +++ b/skills-templates/appsec-agent/sections/state-resumption.md @@ -5,15 +5,15 @@ Use this ownership map to route work and scan errors: project/ MAIN build results/ MAIN scan rules/ sources or sinks stage - pass-through/ approximation stage - dataflow/ approximation stage + pass-through/ model stage + dataflow/ model stage tracking/state.yaml MAIN run knobs tracking/ stage agents, leaves, and join scripts otherwise vulnerabilities.md triage / PoC stage issues/ escalation stage ``` -The tree is long-lived. On resume, reuse `DONE` artifacts; `get_status.py` derives the next phase from disk. Existing rules and approximations apply to every scan. +The tree is long-lived. On resume, reuse `DONE` artifacts; `get_status.py` derives the next phase from disk. Existing rules and models apply to every scan. `state.yaml` shape: diff --git a/skills-templates/appsec-agent/sections/workflow.md b/skills-templates/appsec-agent/sections/workflow.md index f23ae1ed82..5c1feb988e 100644 --- a/skills-templates/appsec-agent/sections/workflow.md +++ b/skills-templates/appsec-agent/sections/workflow.md @@ -4,7 +4,7 @@ The run is one fixed pipeline; the selected levels determine which phases are in build → MAIN: build discover / source_rules → stage subagent: sources scan → MAIN: scan -approximations → stage subagent: approx-round, then MAIN: rescan; repeat +models → stage subagent: model-round, then MAIN: rescan; repeat sink_rules → stage subagent: sinks, then MAIN: rescan triage → stage subagent: triage poc → stage subagent: poc @@ -20,8 +20,8 @@ After a successful build, write the language-specific build fields named by the When status reports `scan`, or a stage returns with a rescan pending, load and follow the `run-scan` skill in this main session. Start the scan with the harness's main-session background-command facility, keep the engine's self-timeout, add a 1200-second outer backstop, and wait for the process completion event. -A valid `.opentaint/results/report.sarif` means the scan completed, including exit 254 after an engine timeout. Record `max_memory: 16G` when the scan had to bump memory and reuse it on later scans. If no SARIF exists after the allowed retry/backstop, follow the repair path below for a malformed rule/approximation; otherwise dispatch `orchestrate-stage` with `stage: escalation` and the scan `setup` to write the scan-wide resource issue, then stop. +A valid `.opentaint/results/report.sarif` means the scan completed, including exit 254 after an engine timeout. Record `max_memory: 16G` when the scan had to bump memory and reuse it on later scans. If no SARIF exists after the allowed retry/backstop, follow the repair path below for a malformed rule/model; otherwise dispatch `orchestrate-stage` with `stage: escalation` and the scan `setup` to write the scan-wide resource issue, then stop. -When a scan or later stage reports a malformed approximation, unloadable created rule, ineffective join, or a created rule's false positive/negative, route the exact diagnosis and artifact path/id to the responsible stage agent per Dispatching, then scan again in MAIN. +When a scan or later stage reports a malformed model, unloadable created rule, ineffective join, or a created rule's false positive/negative, route the exact diagnosis and artifact path/id to the responsible stage agent per Dispatching, then scan again in MAIN. After every build, scan, or stage return, run `uv run /scripts/get_status.py` once to choose the next action. Use `--full` at run start, on resume, or when the brief output does not settle the question. diff --git a/skills-templates/create-dataflow-approximation/sections/preamble.md b/skills-templates/create-dataflow-approximation/sections/preamble.md deleted file mode 100644 index 260e72b710..0000000000 --- a/skills-templates/create-dataflow-approximation/sections/preamble.md +++ /dev/null @@ -1,3 +0,0 @@ -# Skill: Create Dataflow Approximation - -A dataflow approximation is code that expresses how data moves through a method the analyzer can't trace through — an opaque call where the engine loses taint because it can't see the body. You write a small stand-in that reproduces the method's real propagation from its inputs to its outputs, so the analyzer can follow taint through it. Run it against the prepared test project and refine until the sample passes. diff --git a/skills-templates/create-dataflow-approximation/sections/workflow.md b/skills-templates/create-dataflow-approximation/sections/workflow.md deleted file mode 100644 index 1b642a03e3..0000000000 --- a/skills-templates/create-dataflow-approximation/sections/workflow.md +++ /dev/null @@ -1,15 +0,0 @@ -### 1. Understand the propagation - -Find and read each dataflow method's real source: take methods not yet in `build.done`, or the specific `methods` handed for repair even when already built. Leave built methods outside that explicit subset and their approximation source unchanged. An app-internal method sits in the project's own sources, a library method's source comes from its dependency (the language reference has how to get it). Read it to see how data moves from the method's inputs (receiver, arguments) to its outputs (return value, arguments it writes into, state it stores), gathering the full context needed to understand the function's behavior. - -### 2. Write the approximation - -Reproduce that propagation in the language-specific code form in the batch's approximation project at `.opentaint/dataflow/` — scaffold it if it doesn't exist yet — following the language reference's artifact layout. Cover every assigned callable variant, repair an explicitly handed callable in the existing source, and add new ones there rather than rewriting the file. The engine is field-sensitive — taint is tracked per field — so route data field-to-field exactly as the source does rather than tainting the whole object. The test project's negative samples (if present) verify this by storing taint in one field and reading another, so an over-broad model makes them fire. The concrete constructs and patterns are in the language reference. - -### 3. Test against the test project - -Run the approximation test directly as a foreground, blocking command and wait for exit — never background it or use Monitor. Apply this batch's sources and iterate until the samples pass. Feedback loop: a failing sample might be caused by the model's target type/member or signature not matching what the analyzer sees, or by the body not routing taint from the real source to the modeled output — diagnose the mismatch, fix, and re-run, don't rationalize a non-result. When the cause isn't obvious, localize where taint dies with a fact-reachability trace before guessing further per `references/debugging.md`. On a pass, append the method only if it is not already present in `build.done` (per Tracking); a repaired method remains recorded there. - -### 4. Escalate - -When the sample won't converge after ~3 fixes — whether the trace shows a faithful model still can't propagate (taint dying at a plain instruction the engine should carry through, an engine limitation) or the cause stays unclear — don't add a new method to `build.done` or alter an existing repaired method's tracking entry. Report it with the brief cause you found (per Output), for the orchestrator to escalate. Don't retry further. diff --git a/skills-templates/create-dataflow-approximation/main.md.j2 b/skills-templates/create-dataflow-model/main.md.j2 similarity index 70% rename from skills-templates/create-dataflow-approximation/main.md.j2 rename to skills-templates/create-dataflow-model/main.md.j2 index 1e8a7a049a..236fb23778 100644 --- a/skills-templates/create-dataflow-approximation/main.md.j2 +++ b/skills-templates/create-dataflow-model/main.md.j2 @@ -1,6 +1,6 @@ --- -name: create-dataflow-approximation -description: Model a method's taint propagation as code-based dataflow approximation and refine it against a test project until the sample passes. Use for a dropped method that requires code-based approximation +name: create-dataflow-model +description: Model a method's taint propagation as code-based dataflow model and refine it against a test project until the sample passes. Use for a dropped method that requires code-based model license: Apache-2.0 metadata: author: opentaint diff --git a/skills-templates/create-dataflow-approximation/references/debugging.md.j2 b/skills-templates/create-dataflow-model/references/debugging.md.j2 similarity index 100% rename from skills-templates/create-dataflow-approximation/references/debugging.md.j2 rename to skills-templates/create-dataflow-model/references/debugging.md.j2 diff --git a/skills-templates/create-dataflow-approximation/references/java.md.j2 b/skills-templates/create-dataflow-model/references/java.md.j2 similarity index 62% rename from skills-templates/create-dataflow-approximation/references/java.md.j2 rename to skills-templates/create-dataflow-model/references/java.md.j2 index 125fabdef3..3176036b17 100644 --- a/skills-templates/create-dataflow-approximation/references/java.md.j2 +++ b/skills-templates/create-dataflow-model/references/java.md.j2 @@ -1,4 +1,4 @@ -# create-dataflow-approximation — Java / JVM +# create-dataflow-model — Java / JVM The JVM procedure, keyed to the body's steps. @@ -8,12 +8,12 @@ The JVM procedure, keyed to the body's steps. An app-internal method sits in the project's own sources under ``. For a library method, the resolved jars are in `.opentaint/project/dependencies` — locate the class with `unzip -l | grep ` (dotted class name with `.` → `/`) and decompile it for readable source, or disassemble it with `javap -c -p -classpath ` when no decompiler is available. -### 2. Write the approximation source +### 2. Write the model source -Scaffold the approximation project with the same `--dependency` pins as the batch's test project (`.opentaint/test-projects//build.gradle.kts`): +Scaffold the model project with the same `--dependency` pins as the batch's test project (`.opentaint/test-projects//build.gradle.kts`): ```bash -opentaint approximation init .opentaint/dataflow/ \ +opentaint model init .opentaint/dataflow/ \ --dependency "io.projectreactor:reactor-core:3.8.5" ``` @@ -22,7 +22,7 @@ When a model needs a library the project doesn't pin yet, add it to the project' Create Java files under `.opentaint/dataflow//src/main/java` — one `@Approximate` class per target class. `@Approximate(TargetClass.class)` binds a model to exactly that class, so target the EXACT class the analyzer dropped — the dropped FQN reflects how the call resolved: an interface-typed receiver (`Map m = ...; m.computeIfAbsent(...)`) drops `java.util.Map#computeIfAbsent`, a concrete one (`new HashMap<>()`) drops `java.util.HashMap#computeIfAbsent`. An interface is a valid target — write `@Approximate` on it exactly as on a concrete class, modelling whichever FQN the analyzer dropped. Reach the real object with `(TargetClass) (Object) this`, put functional-interface parameters behind `@ArgumentTypeContext`, and branch with `OpentaintNdUtil.nextBool()` so the analyzer walks both paths. Never leave a body empty. ```java -package com.example.approximations.batchpkg; // per-batch package (e.g. ...approximations.cn_hutool_001) — see the globally-unique rule below +package com.example.models.batchpkg; // per-batch package (e.g. ...models.cn_hutool_001) — see the globally-unique rule below import org.opentaint.ir.approximation.annotation.Approximate; import org.opentaint.jvm.dataflow.approximations.ArgumentTypeContext; @@ -69,15 +69,15 @@ public class Mono { ### 3. Test against the test project -Run `test approximation run` over the compiled test project applying this batch's sources, iterate the sources until the samples pass: +Run `test model run` over the compiled test project applying this batch's sources, iterate the sources until the samples pass: ```bash -opentaint test approximation run .opentaint/test-compiled/ \ +opentaint test model run .opentaint/test-compiled/ \ -o .opentaint/test-results/ \ - --dataflow-approximations .opentaint/dataflow/ + --dataflow-models .opentaint/dataflow/ ``` -`test approximation run` applies its own bundled fixed source→sink rule automatically — you don't author or pass one. The CLI builds the approximation project first. if compilation fails it reports the errors and aborts before the tests. A positive sample is a `falseNegative` until the model propagates taint. Read the result with the bundled script — it prints the pass/fail counts and names each failing sample, so you don't parse the JSON by hand: +`test model run` applies its own bundled fixed source→sink rule automatically — you don't author or pass one. The CLI builds the model project first. if compilation fails it reports the errors and aborts before the tests. A positive sample is a `falseNegative` until the model propagates taint. Read the result with the bundled script — it prints the pass/fail counts and names each failing sample, so you don't parse the JSON by hand: ```bash uv run /scripts/check-test-result.py @@ -92,7 +92,7 @@ Fix by the verdict it reports: | Pattern | Usage | |---|---| -| `@Approximate(TargetClass.class)` | Link the approximation to its target class. Must be on the compile classpath (a pinned dependency or a JDK type) | +| `@Approximate(TargetClass.class)` | Link the model to its target class. Must be on the compile classpath (a pinned dependency or a JDK type) | | `(TargetClass) (Object) this` | Cast to reach the real object's methods | | `@ArgumentTypeContext` | On lambda / functional-interface parameters | | `OpentaintNdUtil.nextBool()` | Non-deterministic branch — the analyzer considers both paths | @@ -100,11 +100,11 @@ Fix by the verdict it reports: ## Constraints - Java 8 source compatibility -- Put the `@Approximate` classes in a neutral package (e.g. `com.example.approximations`) — never the target library's own package. Inside the library's package every bare FQN resolves to your approximation's non-generic class instead of the real type, breaking compilation wholesale -- Namespace the package PER BATCH (`com.example.approximations.`, e.g. `...approximations.cn_hutool_001`). Every batch is loaded together into one shared index, and approximation class FQCNs must be GLOBALLY unique across all batches — two batches that both model a `…$Nested` (or any recurring simple name) in the bare `com.example.approximations` package collide on `com.example.approximations.Nested` and crash the whole scan at config-load. A per-batch package guarantees uniqueness. -- One approximation class per target class — a strict global bijection enforced at load: each approximation FQCN maps to exactly one target and vice-versa; duplicates or a reused FQCN across batches throw `IllegalArgumentException` +- Put the `@Approximate` classes in a neutral package (e.g. `com.example.models`) — never the target library's own package. Inside the library's package every bare FQN resolves to your model's non-generic class instead of the real type, breaking compilation wholesale +- Namespace the package PER BATCH (`com.example.models.`, e.g. `...models.cn_hutool_001`). Every batch is loaded together into one shared index, and model class FQCNs must be GLOBALLY unique across all batches — two batches that both model a `…$Nested` (or any recurring simple name) in the bare `com.example.models` package collide on `com.example.models.Nested` and crash the whole scan at config-load. A per-batch package guarantees uniqueness. +- One model class per target class — a strict global bijection enforced at load: each model FQCN maps to exactly one target and vice-versa; duplicates or a reused FQCN across batches throw `IllegalArgumentException` - Method signatures must match the target class methods exactly -- Built-in dataflow approximations are first-priority and presumed correct — you cannot override them locally -- A custom dataflow approximation overrides a passThrough for the same method — use dataflow as the fallback when a faithful passThrough cannot express or restore the propagation -- Built-in passThrough approximations, by contrast, a user config on the same method overrides. So a coarse built-in on a *related* method — a driver that invokes the callback you modelled, a wrapper the flow passes through — can silently suppress an otherwise-correct model. When a faithful model doesn't fire, suspect a built-in on a neighbouring method and model/override that method instead; debug-rule shows which model actually applied +- Built-in dataflow models are first-priority and presumed correct — you cannot override them locally +- A custom dataflow model overrides a passThrough for the same method — use dataflow as the fallback when a faithful passThrough cannot express or restore the propagation +- Built-in passThrough models, by contrast, a user config on the same method overrides. So a coarse built-in on a *related* method — a driver that invokes the callback you modelled, a wrapper the flow passes through — can silently suppress an otherwise-correct model. When a faithful model doesn't fire, suspect a built-in on a neighbouring method and model/override that method instead; debug-rule shows which model actually applied - Don't unpack or grep the analyzer JAR for built-in models or signatures — its internals aren't a stable API; go through the CLI diff --git a/skills/create-dataflow-approximation/scripts/check-test-result.py b/skills-templates/create-dataflow-model/scripts/check-test-result.py similarity index 93% rename from skills/create-dataflow-approximation/scripts/check-test-result.py rename to skills-templates/create-dataflow-model/scripts/check-test-result.py index 2ff4c0237e..213feb6f0b 100644 --- a/skills/create-dataflow-approximation/scripts/check-test-result.py +++ b/skills-templates/create-dataflow-model/scripts/check-test-result.py @@ -5,7 +5,7 @@ Summarize an OpenTaint test-result.json so the agent never parses it by hand. Prints pass/fail counts and names every failing sample by verdict. Exit 0 when all samples pass, 1 when any fail, 2 on a bad/missing file. -Works for both `test rule run` and `test approximation run` outputs (same shape). +Works for both `test rule run` and `test model run` outputs (same shape). """ import json import sys @@ -33,7 +33,7 @@ def names(v): def resolve(arg): # accept either a full path to a test-result.json or a shorthand id under - # .opentaint/test-results/: a rule `/` or an approximation `` + # .opentaint/test-results/: a rule `/` or a model `` if arg.endswith("test-result.json"): return Path(arg) return Path(".opentaint/test-results") / arg / "test-result.json" diff --git a/skills-templates/create-dataflow-approximation/sections/constraints.md b/skills-templates/create-dataflow-model/sections/constraints.md similarity index 83% rename from skills-templates/create-dataflow-approximation/sections/constraints.md rename to skills-templates/create-dataflow-model/sections/constraints.md index 663b788607..c9dd1cf318 100644 --- a/skills-templates/create-dataflow-approximation/sections/constraints.md +++ b/skills-templates/create-dataflow-model/sections/constraints.md @@ -1,5 +1,5 @@ {% include "shared/engine/facts.md" %} -- Verify only with the approximation test on the test project +- Verify only with the model test on the test project - The test project's sample sources are a fixed input — never edit or recompile them to force a pass; if a faithful model can't pass, leave the method out and report it (per Output) - Model every dataflow method and overload the batch lists, not only the ones you have a sample for diff --git a/skills-templates/create-dataflow-approximation/sections/input.md b/skills-templates/create-dataflow-model/sections/input.md similarity index 62% rename from skills-templates/create-dataflow-approximation/sections/input.md rename to skills-templates/create-dataflow-model/sections/input.md index 5694c37e80..32e304c89e 100644 --- a/skills-templates/create-dataflow-approximation/sections/input.md +++ b/skills-templates/create-dataflow-model/sections/input.md @@ -2,5 +2,5 @@ {% include "shared/inputs/project-root.md" %} {% include "shared/inputs/language.md" %} -- `batch` (required) — the batch whose `.opentaint/tracking/approximations/.yaml` provides the `dataflow` methods to model and holds tracking state +- `batch` (required) — the batch whose `.opentaint/tracking/models/.yaml` provides the `dataflow` methods to model and holds tracking state - `methods` (optional) — a specific subset of the batch's dataflow methods to (re)model; default all not yet in `build.done` diff --git a/skills-templates/create-dataflow-approximation/sections/output.md b/skills-templates/create-dataflow-model/sections/output.md similarity index 54% rename from skills-templates/create-dataflow-approximation/sections/output.md rename to skills-templates/create-dataflow-model/sections/output.md index eec392b57c..25beee99b7 100644 --- a/skills-templates/create-dataflow-approximation/sections/output.md +++ b/skills-templates/create-dataflow-model/sections/output.md @@ -1,6 +1,6 @@ ### Artifacts -- `.opentaint/dataflow/` — the batch's approximation project with the language-specific code approximation artifacts that the scan consumes; report the path and the exact test command used +- `.opentaint/dataflow/` — the batch's model project with the language-specific code model artifacts that the scan consumes; report the path and the exact test command used - the passing methods present in the batch file's `build.done` (new methods appended; repaired methods already recorded, per Tracking) ### Summary diff --git a/skills-templates/create-dataflow-model/sections/preamble.md b/skills-templates/create-dataflow-model/sections/preamble.md new file mode 100644 index 0000000000..d261f43671 --- /dev/null +++ b/skills-templates/create-dataflow-model/sections/preamble.md @@ -0,0 +1,3 @@ +# Skill: Create Dataflow Model + +A dataflow model is code that expresses how data moves through a method the analyzer can't trace through — an opaque call where the engine loses taint because it can't see the body. You write a small stand-in that reproduces the method's real propagation from its inputs to its outputs, so the analyzer can follow taint through it. Run it against the prepared test project and refine until the sample passes. diff --git a/skills-templates/create-dataflow-approximation/sections/tracking.md b/skills-templates/create-dataflow-model/sections/tracking.md similarity index 88% rename from skills-templates/create-dataflow-approximation/sections/tracking.md rename to skills-templates/create-dataflow-model/sections/tracking.md index 93e53c1601..26b8928b1e 100644 --- a/skills-templates/create-dataflow-approximation/sections/tracking.md +++ b/skills-templates/create-dataflow-model/sections/tracking.md @@ -1,3 +1,3 @@ -{% include "shared/tracking/approximations-batch.md" %} +{% include "shared/tracking/models-batch.md" %} This skill appends each method whose sample passes to `build.done` as `{ method, signature }` when absent. A newly assigned method that still fails, or one the engine provably can't propagate, stays out and is reported (per Output); an explicitly repaired method leaves its existing entry unchanged. Don't touch the classification buckets (`passthrough`/`dataflow`/`skipped`/`engine_issues`) or edit an entry already in `build.done`. diff --git a/skills-templates/create-dataflow-model/sections/workflow.md b/skills-templates/create-dataflow-model/sections/workflow.md new file mode 100644 index 0000000000..eb43891c58 --- /dev/null +++ b/skills-templates/create-dataflow-model/sections/workflow.md @@ -0,0 +1,15 @@ +### 1. Understand the propagation + +Find and read each dataflow method's real source: take methods not yet in `build.done`, or the specific `methods` handed for repair even when already built. Leave built methods outside that explicit subset and their model source unchanged. An app-internal method sits in the project's own sources, a library method's source comes from its dependency (the language reference has how to get it). Read it to see how data moves from the method's inputs (receiver, arguments) to its outputs (return value, arguments it writes into, state it stores), gathering the full context needed to understand the function's behavior. + +### 2. Write the model + +Reproduce that propagation in the language-specific code form in the batch's model project at `.opentaint/dataflow/` — scaffold it if it doesn't exist yet — following the language reference's artifact layout. Cover every assigned callable variant, repair an explicitly handed callable in the existing source, and add new ones there rather than rewriting the file. The engine is field-sensitive — taint is tracked per field — so route data field-to-field exactly as the source does rather than tainting the whole object. The test project's negative samples (if present) verify this by storing taint in one field and reading another, so an over-broad model makes them fire. The concrete constructs and patterns are in the language reference. + +### 3. Test against the test project + +Run the model test directly as a foreground, blocking command and wait for exit — never background it or use Monitor. Apply this batch's sources and iterate until the samples pass. Feedback loop: a failing sample might be caused by the model's target type/member or signature not matching what the analyzer sees, or by the body not routing taint from the real source to the modeled output — diagnose the mismatch, fix, and re-run, don't rationalize a non-result. When the cause isn't obvious, localize where taint dies with a fact-reachability trace before guessing further per `references/debugging.md`. On a pass, append the method only if it is not already present in `build.done` (per Tracking); a repaired method remains recorded there. + +### 4. Escalate + +When the sample won't converge after ~3 fixes — whether the trace shows a faithful model still can't propagate (taint dying at a plain instruction the engine should carry through, an engine limitation) or the cause stays unclear — don't add a new method to `build.done` or alter an existing repaired method's tracking entry. Report it with the brief cause you found (per Output), for the orchestrator to escalate. Don't retry further. diff --git a/skills-templates/create-pass-through-approximation/sections/preamble.md b/skills-templates/create-pass-through-approximation/sections/preamble.md deleted file mode 100644 index d59db479fa..0000000000 --- a/skills-templates/create-pass-through-approximation/sections/preamble.md +++ /dev/null @@ -1,3 +0,0 @@ -# Skill: Create PassThrough Approximation - -Model a dropped method's taint propagation as a passThrough approximation — a config that tells the engine how data moves from a method's inputs to its outputs, so a flow the analyzer lost through the opaque call is restored. diff --git a/skills-templates/create-pass-through-approximation/main.md.j2 b/skills-templates/create-pass-through-model/main.md.j2 similarity index 78% rename from skills-templates/create-pass-through-approximation/main.md.j2 rename to skills-templates/create-pass-through-model/main.md.j2 index 95209092d5..a42b4cf830 100644 --- a/skills-templates/create-pass-through-approximation/main.md.j2 +++ b/skills-templates/create-pass-through-model/main.md.j2 @@ -1,6 +1,6 @@ --- -name: create-pass-through-approximation -description: Model a method's taint propagation as a passThrough approximation. Use for a dropped method whose propagation is simple copying +name: create-pass-through-model +description: Model a method's taint propagation as a passThrough model. Use for a dropped method whose propagation is simple copying license: Apache-2.0 metadata: author: opentaint diff --git a/skills-templates/create-pass-through-approximation/references/java.md.j2 b/skills-templates/create-pass-through-model/references/java.md.j2 similarity index 99% rename from skills-templates/create-pass-through-approximation/references/java.md.j2 rename to skills-templates/create-pass-through-model/references/java.md.j2 index 238e762c50..093d952956 100644 --- a/skills-templates/create-pass-through-approximation/references/java.md.j2 +++ b/skills-templates/create-pass-through-model/references/java.md.j2 @@ -1,4 +1,4 @@ -# create-pass-through-approximation — Java / JVM +# create-pass-through-model — Java / JVM ## Workflow diff --git a/skills-templates/create-pass-through-approximation/sections/constraints.md b/skills-templates/create-pass-through-model/sections/constraints.md similarity index 100% rename from skills-templates/create-pass-through-approximation/sections/constraints.md rename to skills-templates/create-pass-through-model/sections/constraints.md diff --git a/skills-templates/create-pass-through-approximation/sections/input.md b/skills-templates/create-pass-through-model/sections/input.md similarity index 74% rename from skills-templates/create-pass-through-approximation/sections/input.md rename to skills-templates/create-pass-through-model/sections/input.md index c3ea17c165..c2ecc051ef 100644 --- a/skills-templates/create-pass-through-approximation/sections/input.md +++ b/skills-templates/create-pass-through-model/sections/input.md @@ -2,5 +2,5 @@ {% include "shared/inputs/project-root.md" %} {% include "shared/inputs/language.md" %} -- `batch` (required) — the batch id; its `passthrough` entries live in `.opentaint/tracking/approximations/.yaml`, and you append the ones you build to that file's `build.done` +- `batch` (required) — the batch id; its `passthrough` entries live in `.opentaint/tracking/models/.yaml`, and you append the ones you build to that file's `build.done` - `methods` (optional) — a specific `{ method, signature }` subset to (re)work instead of the whole passthrough bucket, when the caller needs only those diff --git a/skills-templates/create-pass-through-approximation/sections/output.md b/skills-templates/create-pass-through-model/sections/output.md similarity index 100% rename from skills-templates/create-pass-through-approximation/sections/output.md rename to skills-templates/create-pass-through-model/sections/output.md diff --git a/skills-templates/create-pass-through-model/sections/preamble.md b/skills-templates/create-pass-through-model/sections/preamble.md new file mode 100644 index 0000000000..d2bfe29e96 --- /dev/null +++ b/skills-templates/create-pass-through-model/sections/preamble.md @@ -0,0 +1,3 @@ +# Skill: Create PassThrough Model + +Model a dropped method's taint propagation as a passThrough model — a config that tells the engine how data moves from a method's inputs to its outputs, so a flow the analyzer lost through the opaque call is restored. diff --git a/skills-templates/create-pass-through-approximation/sections/tracking.md b/skills-templates/create-pass-through-model/sections/tracking.md similarity index 86% rename from skills-templates/create-pass-through-approximation/sections/tracking.md rename to skills-templates/create-pass-through-model/sections/tracking.md index ed07eaca21..b0b66eda43 100644 --- a/skills-templates/create-pass-through-approximation/sections/tracking.md +++ b/skills-templates/create-pass-through-model/sections/tracking.md @@ -1,3 +1,3 @@ -{% include "shared/tracking/approximations-batch.md" %} +{% include "shared/tracking/models-batch.md" %} This skill only appends each cleanly-built method to `build.done` as `{ method, signature }` when absent. A newly assigned method that you failed to write stays out, so the loop comes back to it; an explicitly repaired method leaves its existing entry unchanged. Never touch the classification buckets or edit an entry already in `build.done`. diff --git a/skills-templates/create-pass-through-approximation/sections/workflow.md b/skills-templates/create-pass-through-model/sections/workflow.md similarity index 100% rename from skills-templates/create-pass-through-approximation/sections/workflow.md rename to skills-templates/create-pass-through-model/sections/workflow.md diff --git a/skills-templates/create-rule/references/debugging.md.j2 b/skills-templates/create-rule/references/debugging.md.j2 index dd8fce4a05..bd27742644 100644 --- a/skills-templates/create-rule/references/debugging.md.j2 +++ b/skills-templates/create-rule/references/debugging.md.j2 @@ -8,7 +8,7 @@ When a positive won't pass and the suspicion is a library method on its flow dro opentaint scan --project-model .opentaint/test-compiled// \ -o .opentaint/test-results///diag.sarif \ --ruleset builtin --ruleset .opentaint/rules --ruleset .opentaint/test-projects///test-rules \ - --passthrough-approximations .opentaint/pass-through \ + --passthrough-models .opentaint/pass-through \ --track-external-methods ``` diff --git a/skills-templates/create-rule/scripts/check-test-result.py b/skills-templates/create-rule/scripts/check-test-result.py index 4fd13e62f7..00f78b70d5 100644 --- a/skills-templates/create-rule/scripts/check-test-result.py +++ b/skills-templates/create-rule/scripts/check-test-result.py @@ -5,7 +5,7 @@ Summarize an OpenTaint test-result.json so the agent never parses it by hand. Prints pass/fail counts and names every failing sample by verdict. Exit 0 when all samples pass, 1 when any fail, 2 on a bad/missing file. -Works for both `test rule run` and `test approximation run` outputs (same shape). +Works for both `test rule run` and `test model run` outputs (same shape). """ import json import sys @@ -33,7 +33,7 @@ def names(v): def resolve(arg): # accept either a full path to a test-result.json or a shorthand id under - # .opentaint/test-results/: a rule `/` or an approximation `` + # .opentaint/test-results/: a rule `/` or a model `` if arg.endswith("test-result.json"): return Path(arg) return Path(".opentaint/test-results") / arg / "test-result.json" diff --git a/skills-templates/create-rule/sections/gotchas.md b/skills-templates/create-rule/sections/gotchas.md index b289103578..cef6cd9e27 100644 --- a/skills-templates/create-rule/sections/gotchas.md +++ b/skills-templates/create-rule/sections/gotchas.md @@ -1,3 +1,3 @@ - If you think a test project is wrong — hand it back upstream -- A positive that won't pass because a library method drops taint is not a rule bug — surface it for approximation (per step 5), don't broaden the rule to force it +- A positive that won't pass because a library method drops taint is not a rule bug — surface it for model (per step 5), don't broaden the rule to force it - Keep produced rule files comment-free diff --git a/skills-templates/create-rule/sections/output.md b/skills-templates/create-rule/sections/output.md index 3220390d5d..de3cb2e7c1 100644 --- a/skills-templates/create-rule/sections/output.md +++ b/skills-templates/create-rule/sections/output.md @@ -7,5 +7,5 @@ - the implementing lib rule ids, identifying which were built-in and which were created, a one-line test summary for the side, and the exact `test rule run` command used - if blocked at step 5: `stages.tests_passing` left pending, and the cause — - - a dropped library method on the failing sample's flow → the methods that need a model, to be approximated before a re-dispatch + - a dropped library method on the failing sample's flow → the methods that need a model, to be modeled before a re-dispatch - nothing dropped and no clear rule cause → non-convergence, for the orchestrator to intervene diff --git a/skills-templates/create-rule/sections/workflow.md b/skills-templates/create-rule/sections/workflow.md index 4c4a933ff9..e495566b6e 100644 --- a/skills-templates/create-rule/sections/workflow.md +++ b/skills-templates/create-rule/sections/workflow.md @@ -28,10 +28,10 @@ Run the rule tests directly as a foreground, blocking command and wait for exit opentaint test rule run .opentaint/test-compiled// \ -o .opentaint/test-results// \ --ruleset .opentaint/rules --ruleset .opentaint/test-projects///test-rules \ - --passthrough-approximations .opentaint/pass-through + --passthrough-models .opentaint/pass-through ``` -`test rule run` auto-loads the built-in rules, so pass only your custom rulesets. Apply the passthrough approximations as-is, an empty one is harmless. Read the result with the bundled script — it prints the pass/fail counts and names the failing samples, so you never parse the JSON by hand: +`test rule run` auto-loads the built-in rules, so pass only your custom rulesets. Apply the passthrough models as-is, an empty one is harmless. Read the result with the bundled script — it prints the pass/fail counts and names the failing samples, so you never parse the JSON by hand: ```bash uv run /scripts/check-test-result.py / diff --git a/skills-templates/create-test-project/main.md.j2 b/skills-templates/create-test-project/main.md.j2 index cccf7acfd3..10b5ee4f90 100644 --- a/skills-templates/create-test-project/main.md.j2 +++ b/skills-templates/create-test-project/main.md.j2 @@ -1,6 +1,6 @@ --- name: create-test-project -description: Create an OpenTaint test project with positive/negative samples for verifying a rule or approximation. Use when a rule or approximation needs a test project to check against +description: Create an OpenTaint test project with positive/negative samples for verifying a rule or model. Use when a rule or model needs a test project to check against license: Apache-2.0 metadata: author: opentaint diff --git a/skills-templates/create-test-project/references/java-approximation.md.j2 b/skills-templates/create-test-project/references/java-approximation.md.j2 deleted file mode 100644 index 8cee6ff26b..0000000000 --- a/skills-templates/create-test-project/references/java-approximation.md.j2 +++ /dev/null @@ -1,69 +0,0 @@ -# create-test-project — Java approximation test project - -Language reference for a `dataflow` type, keyed to the body's steps. - -## Workflow - -### 1. Scaffold the project - -`opentaint test approximation init ` scaffolds the Gradle build plus `Taint.java` and the fixed `approximation-rule.yaml` the harness applies. Pass each lib as a `--dependency` at its exact version from the app's dependency management (`.opentaint/project/sources/**/pom.xml`) verbatim — including timestamped snapshots — not a guess. The approximation project scaffolded later takes the same pins, so the samples and the models they exercise compile against one version. - -```bash -opentaint test approximation init .opentaint/test-projects/ \ - --dependency "io.projectreactor:reactor-core:3.8.5" -``` - -### 2. Write the samples - -Write one sample per method in the batch's `dataflow` bucket, across all its classes. Each `dataflow` entry records the method's `signature` (JVM descriptor) — reuse it so your sample matches. Look at how the method is actually called in the project to shape the sample and pick the receiver type. Each sample is a plain public method under `src/main/java/test/` — you don't author a rule; the scaffold's fixed `approximation-rule.yaml` is what the harness applies. The sample just routes `Taint.source()` through the approximated method into `Taint.sink(...)`. Its verdict is recorded in `rule-test.yaml` (below), not on the method. - -Mark each method's test-project status (per Tracking): `done` once its sample is in the project. If no sample can be written for a method, exclude it, mark it `failed`, and note it in the summary — don't let one unmodelable method block the rest. - -Positive sample — routes `Taint.source()` through the approximated method into `Taint.sink(...)`, one per method being approximated. Start every sample with `String tainted = Taint.source();`. When the approximated method takes a non-String input, cast the tainted string to that type so the taint fact reaches the method as the right type — the analyzer tracks the fact, not the runtime value. - -```java -package test; - -import java.util.HashMap; -import java.util.Map; - -public class ApproximationSamples { - - public void taintReachesSink() { - String tainted = Taint.source(); - Map cache = new HashMap<>(); - String routed = cache.computeIfAbsent(tainted, k -> k); // the approximated method - Taint.sink(routed); - } -} -``` - -Negative sample — only when the approximated method stores its input in the object. The analyzer is field-sensitive: taint set on one field doesn't surface from another. Write a negative sample that stores tainted data into one field and reads a different one — a field-precise model keeps that read clean, so the sample must not fire. This is what forces a precise approximation instead of one that taints the whole object. For plain propagation the positive alone proves the model — skip the negative. - -```java - public void taintStaysInItsField() { - UserBean bean = new UserBean(); - bean.setName(Taint.source()); // taint stored in the `name` field - Taint.sink(bean.getEmail()); // a different field — must stay clean - } -``` - -Register the verdicts — write `rule-test.yaml` at the test-project root (next to `build.gradle.kts`), one entry under the fixed rule, listing each sample method by `test.#`. `positive` samples must flag, `negative` must not: - -```yaml -tests: - - rule-id: approximation-rule.yaml#approximation-rule - positive: - - test.ApproximationSamples#taintReachesSink - negative: - - test.ApproximationSamples#taintStaysInItsField -``` - -Note — a sample's receiver type fixes the dropped method's fully-qualified name, and the later approximation targets that exact class, so mirror the real call's receiver type. The same call off an interface-typed receiver (`Map m`, e.g. a parameter) resolves to `java.util.Map#computeIfAbsent`, off a concrete one (`new HashMap<>()`) to `java.util.HashMap#computeIfAbsent` — match whichever the real flow uses. - -When the approximated method hands the data to a callback instead of returning it, put the sink inside the callback body: `obj.method(c -> Taint.sink(tainted))`. Don't stash the value in a captured local or array and sink it after the call. - -## Constraints - -- The scaffold defaults to Java 8. A sample using a library that needs Java 17/21 must bump `source/targetCompatibility` and set `release` on the running JDK -- Diagnose a compile failure with `gradle compileJava --console=plain` in the sub-project — no gradlew is generated diff --git a/skills-templates/create-test-project/references/java-model.md.j2 b/skills-templates/create-test-project/references/java-model.md.j2 new file mode 100644 index 0000000000..c3d62b94c8 --- /dev/null +++ b/skills-templates/create-test-project/references/java-model.md.j2 @@ -0,0 +1,69 @@ +# create-test-project — Java model test project + +Language reference for a `dataflow` type, keyed to the body's steps. + +## Workflow + +### 1. Scaffold the project + +`opentaint test model init ` scaffolds the Gradle build plus `Taint.java` and the fixed `model-rule.yaml` the harness applies. Pass each lib as a `--dependency` at its exact version from the app's dependency management (`.opentaint/project/sources/**/pom.xml`) verbatim — including timestamped snapshots — not a guess. The model project scaffolded later takes the same pins, so the samples and the models they exercise compile against one version. + +```bash +opentaint test model init .opentaint/test-projects/ \ + --dependency "io.projectreactor:reactor-core:3.8.5" +``` + +### 2. Write the samples + +Write one sample per method in the batch's `dataflow` bucket, across all its classes. Each `dataflow` entry records the method's `signature` (JVM descriptor) — reuse it so your sample matches. Look at how the method is actually called in the project to shape the sample and pick the receiver type. Each sample is a plain public method under `src/main/java/test/` — you don't author a rule; the scaffold's fixed `model-rule.yaml` is what the harness applies. The sample just routes `Taint.source()` through the modeled method into `Taint.sink(...)`. Its verdict is recorded in `rule-test.yaml` (below), not on the method. + +Mark each method's test-project status (per Tracking): `done` once its sample is in the project. If no sample can be written for a method, exclude it, mark it `failed`, and note it in the summary — don't let one unmodelable method block the rest. + +Positive sample — routes `Taint.source()` through the modeled method into `Taint.sink(...)`, one per method being modeled. Start every sample with `String tainted = Taint.source();`. When the modeled method takes a non-String input, cast the tainted string to that type so the taint fact reaches the method as the right type — the analyzer tracks the fact, not the runtime value. + +```java +package test; + +import java.util.HashMap; +import java.util.Map; + +public class ModelSamples { + + public void taintReachesSink() { + String tainted = Taint.source(); + Map cache = new HashMap<>(); + String routed = cache.computeIfAbsent(tainted, k -> k); // the modeled method + Taint.sink(routed); + } +} +``` + +Negative sample — only when the modeled method stores its input in the object. The analyzer is field-sensitive: taint set on one field doesn't surface from another. Write a negative sample that stores tainted data into one field and reads a different one — a field-precise model keeps that read clean, so the sample must not fire. This is what forces a precise model instead of one that taints the whole object. For plain propagation the positive alone proves the model — skip the negative. + +```java + public void taintStaysInItsField() { + UserBean bean = new UserBean(); + bean.setName(Taint.source()); // taint stored in the `name` field + Taint.sink(bean.getEmail()); // a different field — must stay clean + } +``` + +Register the verdicts — write `rule-test.yaml` at the test-project root (next to `build.gradle.kts`), one entry under the fixed rule, listing each sample method by `test.#`. `positive` samples must flag, `negative` must not: + +```yaml +tests: + - rule-id: model-rule.yaml#model-rule + positive: + - test.ModelSamples#taintReachesSink + negative: + - test.ModelSamples#taintStaysInItsField +``` + +Note — a sample's receiver type fixes the dropped method's fully-qualified name, and the later model targets that exact class, so mirror the real call's receiver type. The same call off an interface-typed receiver (`Map m`, e.g. a parameter) resolves to `java.util.Map#computeIfAbsent`, off a concrete one (`new HashMap<>()`) to `java.util.HashMap#computeIfAbsent` — match whichever the real flow uses. + +When the modeled method hands the data to a callback instead of returning it, put the sink inside the callback body: `obj.method(c -> Taint.sink(tainted))`. Don't stash the value in a captured local or array and sink it after the call. + +## Constraints + +- The scaffold defaults to Java 8. A sample using a library that needs Java 17/21 must bump `source/targetCompatibility` and set `release` on the running JDK +- Diagnose a compile failure with `gradle compileJava --console=plain` in the sub-project — no gradlew is generated diff --git a/skills-templates/create-test-project/sections/input.md b/skills-templates/create-test-project/sections/input.md index c8b18f02c2..0aba69be7f 100644 --- a/skills-templates/create-test-project/sections/input.md +++ b/skills-templates/create-test-project/sections/input.md @@ -4,6 +4,6 @@ {% include "shared/inputs/language.md" %} - `type` (required) — what this project verifies, selecting the sample style and the identifying inputs below: `rule-source`, `rule-sink`, or `dataflow` - for `rule-source` / `rule-sink` — `unit`: the unit id; its members and `dependencies` come from `.opentaint/tracking/rules/sources|sinks/.yaml` -- for `dataflow` — `batch`: the batch whose `.opentaint/tracking/approximations/.yaml` provides the dataflow methods to exercise and their `dependencies` +- for `dataflow` — `batch`: the batch whose `.opentaint/tracking/models/.yaml` provides the dataflow methods to exercise and their `dependencies` -The project folder `` is that identifier — the `unit` for a rule side, the `batch` for a dataflow approximation. +The project folder `` is that identifier — the `unit` for a rule side, the `batch` for a dataflow model. diff --git a/skills-templates/create-test-project/sections/preamble.md b/skills-templates/create-test-project/sections/preamble.md index 3c553e0b8b..6b4b8d089b 100644 --- a/skills-templates/create-test-project/sections/preamble.md +++ b/skills-templates/create-test-project/sections/preamble.md @@ -1,3 +1,3 @@ # Skill: Create Test Project -Build a minimal compiled test project whose samples reproduce the flow a rule or approximation is verified against. A sample routes data between a real library method and the generic taint marker the scaffold provides, with the one verdict it must produce — a positive that must flag, a negative that must not. The compiled model is the deliverable; its sample sources sit alongside it. +Build a minimal compiled test project whose samples reproduce the flow a rule or model is verified against. A sample routes data between a real library method and the generic taint marker the scaffold provides, with the one verdict it must produce — a positive that must flag, a negative that must not. The compiled model is the deliverable; its sample sources sit alongside it. diff --git a/skills-templates/create-test-project/sections/tracking.md b/skills-templates/create-test-project/sections/tracking.md index 31cb3220b2..7b88f5de3b 100644 --- a/skills-templates/create-test-project/sections/tracking.md +++ b/skills-templates/create-test-project/sections/tracking.md @@ -1,10 +1,10 @@ This skill writes only the test-project stage back: - a rule side → `stages.test_project: done` in the source or sink unit -- a dataflow approximation → one `build.test_project` entry per method in the batch file, `status: done` for a method whose sample made it into the project, `status: failed` for one no sample could be written for (excluded) +- a dataflow model → one `build.test_project` entry per method in the batch file, `status: done` for a method whose sample made it into the project, `status: failed` for one no sample could be written for (excluded) {% include "shared/tracking/source-unit.md" %} {% include "shared/tracking/sink-unit.md" %} -{% include "shared/tracking/approximations-batch.md" %} +{% include "shared/tracking/models-batch.md" %} diff --git a/skills-templates/create-test-project/sections/workflow.md b/skills-templates/create-test-project/sections/workflow.md index 888111d35f..a5bb9b512c 100644 --- a/skills-templates/create-test-project/sections/workflow.md +++ b/skills-templates/create-test-project/sections/workflow.md @@ -1,6 +1,6 @@ ### 1. Scaffold the project -The scaffold command and sample form are language-specific — read the reference for your `type`: `references/-rule.md` for `rule-source` / `rule-sink`, or `references/-approximation.md` for `dataflow` (one self-contained reference, no need to read the other). Scaffold the project for the `type`, passing each of the unit's `dependencies` at its pinned version. The scaffold provides the generic taint marker and the fixed test rule the samples run against, so you author only the samples. If the `` project already exists — re-invoked because its surface grew or a dependency moved — extend it instead: add the missing samples and recompile rather than scaffolding fresh. The init command is in that reference. +The scaffold command and sample form are language-specific — read the reference for your `type`: `references/-rule.md` for `rule-source` / `rule-sink`, or `references/-model.md` for `dataflow` (one self-contained reference, no need to read the other). Scaffold the project for the `type`, passing each of the unit's `dependencies` at its pinned version. The scaffold provides the generic taint marker and the fixed test rule the samples run against, so you author only the samples. If the `` project already exists — re-invoked because its surface grew or a dependency moved — extend it instead: add the missing samples and recompile rather than scaffolding fresh. The init command is in that reference. ### 2. Write the samples diff --git a/skills-templates/debug-rule/main.md.j2 b/skills-templates/debug-rule/main.md.j2 index 39361354d4..f1485108b4 100644 --- a/skills-templates/debug-rule/main.md.j2 +++ b/skills-templates/debug-rule/main.md.j2 @@ -1,6 +1,6 @@ --- name: debug-rule -description: Debug a rule or approximation that behaves unexpectedly by tracing where taint is dropped. Use when its samples won't pass after repeated attempts, or it passes tests but is wrong on a real scan +description: Debug a rule or model that behaves unexpectedly by tracing where taint is dropped. Use when its samples won't pass after repeated attempts, or it passes tests but is wrong on a real scan license: Apache-2.0 metadata: author: opentaint diff --git a/skills-templates/debug-rule/sections/input.md b/skills-templates/debug-rule/sections/input.md index 8077ed95ad..5ab14137ff 100644 --- a/skills-templates/debug-rule/sections/input.md +++ b/skills-templates/debug-rule/sections/input.md @@ -1,5 +1,5 @@ {% include "shared/inputs-preamble.md" %} {% include "shared/inputs/project-root.md" %} -- `rule` (required) — the one rule whose sample or flow routes taint through the code under test, as `.yaml:`. For an approximation, the rule whose sample routes taint through the approximated method +- `rule` (required) — the one rule whose sample or flow routes taint through the code under test, as `.yaml:`. For a model, the rule whose sample routes taint through the modeled method - `model` (required) — the project model where the behavior shows up diff --git a/skills-templates/debug-rule/sections/preamble.md b/skills-templates/debug-rule/sections/preamble.md index 63b8e9a841..491b60b74d 100644 --- a/skills-templates/debug-rule/sections/preamble.md +++ b/skills-templates/debug-rule/sections/preamble.md @@ -1,3 +1,3 @@ # Skill: Debug Rule -Diagnose why a rule or approximation behaves unexpectedly on a model by tracing where taint is dropped, and decide who owns the fix: the rule, a missing library model, or the engine. +Diagnose why a rule or model behaves unexpectedly on a model by tracing where taint is dropped, and decide who owns the fix: the rule, a missing library model, or the engine. diff --git a/skills-templates/debug-rule/sections/workflow.md b/skills-templates/debug-rule/sections/workflow.md index 3a490974f6..46123fc010 100644 --- a/skills-templates/debug-rule/sections/workflow.md +++ b/skills-templates/debug-rule/sections/workflow.md @@ -1,14 +1,14 @@ ### 1. Reproduce and localize the kill -Reproduce the exact run that showed the problem — same `model`, rulesets, and applied approximation dirs — and trace where taint dies with a fact-reachability run. Run it directly as a foreground, blocking command and wait for exit — never background it or use Monitor: +Reproduce the exact run that showed the problem — same `model`, rulesets, and applied model dirs — and trace where taint dies with a fact-reachability run. Run it directly as a foreground, blocking command and wait for exit — never background it or use Monitor: ```bash opentaint test rule reachability \ --project-model \ -o /report.sarif \ --ruleset builtin --ruleset .opentaint/rules \ - --passthrough-approximations .opentaint/pass-through \ - --dataflow-approximations .opentaint/dataflow + --passthrough-models .opentaint/pass-through \ + --dataflow-models .opentaint/dataflow ``` `` is `.opentaint/test-results/` for a test model, `.opentaint/results` for the main scan. The per-instruction facts are in the sibling `/debug-ifds-fact-reachability.sarif`, not the `-o` file — the `-o` SARIF only shows whether the rule fired. Read that sibling to find the kill: @@ -16,16 +16,16 @@ opentaint test rule reachability \ - a missed detection (a positive that won't pass, or a flow absent from a scan) — confirm a fact exists at the source; if none, the gap is in `pattern-sources`, not the flow. Otherwise walk the facts to the last instruction still carrying it and the first where it's gone — that gap is the kill - a spurious detection (a negative that fires) — the reverse: find where a fact appears with no tainted input reaching it -Trace the exact run that misbehaved — a different `model` or ruleset traces something else; taint dying at an approximated call means that approximation isn't propagating. When the flow is missed and the entry method may never be analyzed, rerun with `--entry-points ""`: a finding that appears only then is an entry-point-discovery problem, not dataflow. On Spring the flag is additive — auto-discovered endpoints stay and your method is added, so use it to force-include an endpoint the analyzer never starts from, not to narrow to one method. +Trace the exact run that misbehaved — a different `model` or ruleset traces something else; taint dying at a modeled call means that model isn't propagating. When the flow is missed and the entry method may never be analyzed, rerun with `--entry-points ""`: a finding that appears only then is an entry-point-discovery problem, not dataflow. On Spring the flag is additive — auto-discovered endpoints stay and your method is added, so use it to force-include an endpoint the analyzer never starts from, not to narrow to one method. ### 2. Classify the cause The killing instruction decides who owns the fix. An engine bug is by far the least likely — assume it last, only once the other two are ruled out; nearly every kill is a missing or wrong library model or a rule defect, both tedious to exclude but far more probable, and the tedium is no reason to jump to "engine". Three outcomes: -- the kill is at an external library method → a model issue. Cross-check `dropped-external-methods.yaml` from that run (a `--track-external-methods` scan regenerates it if absent): listed there means the method is unmodeled — the missing model is the cause, for the approximation stage to model. Not listed but a built-in claims to model it, yet taint dies here → that model is wrong for this case: a passThrough override applies at the rule level, so prefer one for the method; a dataflow override conflicts with built-ins at load, so fall back to a passThrough, or call it an engine issue when only a dataflow shape can express the propagation +- the kill is at an external library method → a model issue. Cross-check `dropped-external-methods.yaml` from that run (a `--track-external-methods` scan regenerates it if absent): listed there means the method is unmodeled — the missing model is the cause, for the model stage to model. Not listed but a built-in claims to model it, yet taint dies here → that model is wrong for this case: a passThrough override applies at the rule level, so prefer one for the method; a dataflow override conflicts with built-ins at load, so fall back to a passThrough, or call it an engine issue when only a dataflow shape can express the propagation - the kill is where the rule should have matched — a sanitizer misfires, a sink or source variant went unmatched → a rule defect, for rule authoring to fix - the kill is a plain instruction the engine must propagate through (assignment, cast, field read, an already-modeled call), with the rule correct and the model complete → an engine issue ### 3. Report the diagnosis -This skill diagnoses and routes the fix — it doesn't author the rule or approximation, or re-run the pipeline. Report the diagnosis per Output. +This skill diagnoses and routes the fix — it doesn't author the rule or model, or re-run the pipeline. Report the diagnosis per Output. diff --git a/skills-templates/orchestrate-stage/references/escalation.md b/skills-templates/orchestrate-stage/references/escalation.md index 037fdf64aa..3dd67882cc 100644 --- a/skills-templates/orchestrate-stage/references/escalation.md +++ b/skills-templates/orchestrate-stage/references/escalation.md @@ -1,17 +1,17 @@ # Escalation -Use when a leaf reports a rule or approximation it could not make work after its retries, when the latest scan still drops an already-built approximation, or when MAIN hands off a full-project scan that finished its allowed retry/backstop without producing SARIF. +Use when a leaf reports a rule or model it could not make work after its retries, when the latest scan still drops an already-built model, or when MAIN hands off a full-project scan that finished its allowed retry/backstop without producing SARIF. ## Full-scan resource issue When the task supplies the failed scan `setup`, dispatch report-analyzer-issue. Inputs: -- `setup` — the exact scan command, project model, rulesets, approximation dirs, final memory bound, current commit, and scan log path +- `setup` — the exact scan command, project model, rulesets, model dirs, final memory bound, current commit, and scan log path -Expect `.opentaint/issues/.md`; return it to MAIN and stop this stage. There is no rule/approximation terminal mark for a scan-wide resource failure. +Expect `.opentaint/issues/.md`; return it to MAIN and stop this stage. There is no rule/model terminal mark for a scan-wide resource failure. -## Rule or approximation issue +## Rule or model issue Handle the reported item here instead of re-dispatching it blindly, and don't run debug-rule. @@ -22,7 +22,7 @@ A scan-flagged created rule that fails to load, false-positives/negatives, or be Dispatch report-analyzer-issue before marking the item terminal. Inputs: -- `artifact` — the rule's full id and ruleset, or the approximation target method(s) +- `artifact` — the rule's full id and ruleset, or the model target method(s) - `diagnosis` — the leaf's one-line cause - `name` (optional) — the known test-project unit/batch used for the failed check @@ -33,12 +33,12 @@ Expect back — `.opentaint/issues/.md` written. The stage orchestrator writes the terminal mark after the reporting leaf returns; the leaf leaves the item pending and only reports its diagnosis. So the stage stops returning to it: - source/sink rule unit → add one top-level `blocker` string to the known `.opentaint/tracking/rules//.yaml` -- approximation method → append it to the known owning batch's `engine_issues`, quoting method and signature: +- model method → append it to the known owning batch's `engine_issues`, quoting method and signature: ```yaml - { method: "cn.hutool.core.lang.Assert#notNull", signature: "(Ljava/lang/Object;Ljava/util/function/Supplier;)Ljava/lang/Object;", reason: "one-line engine limitation" } ``` -After an approximation mark, run `uv run /scripts/generate.py merge-skipped` so `approximations/skipped.yaml` reflects the terminal item. +After a model mark, run `uv run /scripts/generate.py merge-skipped` so `models/skipped.yaml` reflects the terminal item. -A malformed approximation is not immediately an engine issue: re-dispatch its authoring skill with `language` + `batch` and, when the scan identifies the affected methods, that `methods` subset. The leaf repairs the existing package config or dataflow source even when those methods are already in `build.done`; unrelated existing carriers stay unchanged. Finish with the project rescan as the next step. If the repaired carrier is still dropped, use the terminal flow above. +A malformed model is not immediately an engine issue: re-dispatch its authoring skill with `language` + `batch` and, when the scan identifies the affected methods, that `methods` subset. The leaf repairs the existing package config or dataflow source even when those methods are already in `build.done`; unrelated existing carriers stay unchanged. Finish with the project rescan as the next step. If the repaired carrier is still dropped, use the terminal flow above. diff --git a/skills/orchestrate-stage/references/approx-round.md b/skills-templates/orchestrate-stage/references/model-round.md similarity index 79% rename from skills/orchestrate-stage/references/approx-round.md rename to skills-templates/orchestrate-stage/references/model-round.md index 95c1b0375a..9624c6b6ee 100644 --- a/skills/orchestrate-stage/references/approx-round.md +++ b/skills-templates/orchestrate-stage/references/model-round.md @@ -1,6 +1,6 @@ -# Approximation round — classify + build +# Model round — classify + build -Model one frontier of library methods dropped by the latest project scan: classify each method into a batch bucket, then build its passthrough/dataflow carrier or settle it terminal. This is one round of the pipeline's approximation loop. Finish before the rescan; a later invocation handles the frontier exposed by that rescan. +Model one frontier of library methods dropped by the latest project scan: classify each method into a batch bucket, then build its passthrough/dataflow carrier or settle it terminal. This is one round of the pipeline's model loop. Finish before the rescan; a later invocation handles the frontier exposed by that rescan. ## Built-but-still-dropped methods @@ -27,13 +27,13 @@ Expect back — every method classified into the batch's `passthrough` / `datafl uv run /scripts/generate.py merge-skipped ``` -It merges every batch's `skipped` / `engine_issues` into `approximations/skipped.yaml` and prunes the consumed plans. +It merges every batch's `skipped` / `engine_issues` into `models/skipped.yaml` and prunes the consumed plans. ## Build the unbuilt batches `get_status.py` lists the batches with an unbuilt carrier by kind. Independent batches fan out in parallel. -- passthrough → dispatch create-pass-through-approximation +- passthrough → dispatch create-pass-through-model - `language` - `batch` - `methods` (optional) — only an explicitly assigned subset to redo @@ -41,7 +41,7 @@ It merges every batch's `skipped` / `engine_issues` into `approximations/skipped - `language` - `type: dataflow` - `batch` -- dataflow step 2, after its test project completes → dispatch create-dataflow-approximation +- dataflow step 2, after its test project completes → dispatch create-dataflow-model - `language` - `batch` - `methods` (optional) — only an explicitly assigned subset to redo @@ -50,4 +50,4 @@ Expect back — the built carriers appended to each batch's `build.done`; `get_s ## Stage gate -`get_status.py` names unclassified methods, plans, unbuilt batches, or built-but-still-dropped methods. Delete this round's dataflow `test-compiled/` models and finish when its methods are classified and built or terminal. If status reports `approximations built after the last scan`, report the pending rescan and stop. +`get_status.py` names unclassified methods, plans, unbuilt batches, or built-but-still-dropped methods. Delete this round's dataflow `test-compiled/` models and finish when its methods are classified and built or terminal. If status reports `models built after the last scan`, report the pending rescan and stop. diff --git a/skills-templates/orchestrate-stage/references/sinks.md b/skills-templates/orchestrate-stage/references/sinks.md index 265ca7d20f..6ed69de8f9 100644 --- a/skills-templates/orchestrate-stage/references/sinks.md +++ b/skills-templates/orchestrate-stage/references/sinks.md @@ -1,6 +1,6 @@ # Author sinks + assemble -Author the sink rules flagged into per-package units during approximation classification and join them to every relevant source. The final project rescan follows to surface findings. +Author the sink rules flagged into per-package units during model classification and join them to every relevant source. The final project rescan follows to surface findings. ## Sink lib rules diff --git a/skills-templates/orchestrate-stage/sections/dispatching.md b/skills-templates/orchestrate-stage/sections/dispatching.md index 5295eaebca..4d7bfb8d63 100644 --- a/skills-templates/orchestrate-stage/sections/dispatching.md +++ b/skills-templates/orchestrate-stage/sections/dispatching.md @@ -13,10 +13,10 @@ Universal rules: - trust the returned summary; open an artifact only when the summary does not resolve the result - don't read a leaf skill's contents unless genuinely needed - never bundle multiple steps into one dispatch -- when a rule or approximation does not converge after its leaf's retries, load `/references/escalation.md`, settle that item, and continue. Only a blocker shared by every remaining item stops the stage +- when a rule or model does not converge after its leaf's retries, load `/references/escalation.md`, settle that item, and continue. Only a blocker shared by every remaining item stops the stage Fan-out and caps: -- `get_status.py --full` prints `global` and `heavy` caps; the heavy leaves in these stages are `create-rule` and `create-dataflow-approximation`. Never exceed either cap; reduce `global` by 1 for the rest of the stage when a leaf is rate-limited +- `get_status.py --full` prints `global` and `heavy` caps; the heavy leaves in these stages are `create-rule` and `create-dataflow-model`. Never exceed either cap; reduce `global` by 1 for the rest of the stage when a leaf is rate-limited - fan out independent units and dispatch the next queued unit as each slot frees - block on native agent completion. Never use Monitor, a background command, or resume-polling to wait inside this agent diff --git a/skills-templates/orchestrate-stage/sections/workflow.md b/skills-templates/orchestrate-stage/sections/workflow.md index a2ca715e84..751ea41da6 100644 --- a/skills-templates/orchestrate-stage/sections/workflow.md +++ b/skills-templates/orchestrate-stage/sections/workflow.md @@ -2,7 +2,7 @@ Match the stage keyword to its reference and read it fully: ``` sources → /references/sources.md -approx-round → /references/approx-round.md +model-round → /references/model-round.md sinks → /references/sinks.md triage → /references/triage.md poc → /references/poc.md diff --git a/skills-templates/report-analyzer-issue/sections/input.md b/skills-templates/report-analyzer-issue/sections/input.md index 7f0d99a639..38968a1a0f 100644 --- a/skills-templates/report-analyzer-issue/sections/input.md +++ b/skills-templates/report-analyzer-issue/sections/input.md @@ -2,6 +2,6 @@ {% include "shared/inputs/project-root.md" %} - `diagnosis` (required for `analysis`) — the caller's brief engine-level cause: roughly where taint appears to die and why. A short hand-off, not a proven trace -- `artifact` (required for `analysis`) — the rule or approximation the issue concerns: a rule's full id and ruleset, or the approximation's target method(s) +- `artifact` (required for `analysis`) — the rule or model the issue concerns: a rule's full id and ruleset, or the model's target method(s) - `name` (optional, `analysis`) — the test-project name the artifact was traced on; its tree is `.opentaint/test-projects/` and model `.opentaint/test-compiled/`, cited so the engine team can reproduce -- `setup` (required for `resource`) — what was running when the scan failed without SARIF: the ruleset(s), approximation dirs, project model, final memory bound, timeout/backstop outcome, scan log, and commit hash (`git rev-parse HEAD`) +- `setup` (required for `resource`) — what was running when the scan failed without SARIF: the ruleset(s), model dirs, project model, final memory bound, timeout/backstop outcome, scan log, and commit hash (`git rev-parse HEAD`) diff --git a/skills-templates/report-analyzer-issue/sections/workflow.md b/skills-templates/report-analyzer-issue/sections/workflow.md index 5a813cdf36..86fc84e93b 100644 --- a/skills-templates/report-analyzer-issue/sections/workflow.md +++ b/skills-templates/report-analyzer-issue/sections/workflow.md @@ -13,14 +13,14 @@ Write `.opentaint/issues/.md` — the self-contained deliverable, `` Header (both kinds): - Type — `analysis` or `resource` -- Setup — the exact command that reproduces it: the `opentaint test rule …` / `test approximation run` for `analysis`, or the failed `opentaint scan` for `resource`, each with its model, rulesets, and approximation dirs (plus final memory and timeout/backstop outcome for `resource`) +- Setup — the exact command that reproduces it: the `opentaint test rule …` / `test model run` for `analysis`, or the failed `opentaint scan` for `resource`, each with its model, rulesets, and model dirs (plus final memory and timeout/backstop outcome for `resource`) - Run logs — the run's log files, by path: for `analysis`, any debug log or fact-reachability SARIF the caller cited, if present; for `resource`, the scan's output log -- Minimal repro — the smallest set of files/folders that demonstrates it: for `analysis`, the test project `.opentaint/test-projects/` if one was named; for `resource`, the project model, rulesets, and approximation dirs in play (with their rough size) +- Minimal repro — the smallest set of files/folders that demonstrates it: for `analysis`, the test project `.opentaint/test-projects/` if one was named; for `resource`, the project model, rulesets, and model dirs in play (with their rough size) - TL;DR — 2–3 sentences: the symptom, and for `analysis` where taint appears to die and what the engine should do instead Below the header, free-form — whatever makes the case, at least: -- `analysis` — the `artifact` under test (rule id + ruleset, or approximation target method(s)); the caller's brief cause; observed vs expected and any fact-reachability trace, if the caller supplied them; a 1–3 sentence hypothesis of what the engine does wrong (a hypothesis, not a fix) +- `analysis` — the `artifact` under test (rule id + ruleset, or model target method(s)); the caller's brief cause; observed vs expected and any fact-reachability trace, if the caller supplied them; a 1–3 sentence hypothesis of what the engine does wrong (a hypothesis, not a fix) - `resource` — the model's rough size (classes/modules if known) and the commit hash (`git rev-parse HEAD`) it was built from Keep it tight: the header plus about one screen of body. diff --git a/skills-templates/run-scan/sections/output.md b/skills-templates/run-scan/sections/output.md index 2d8dea72f6..84dd1a8854 100644 --- a/skills-templates/run-scan/sections/output.md +++ b/skills-templates/run-scan/sections/output.md @@ -10,7 +10,7 @@ All three sit next to the report under `.opentaint/results/`: ### Summary: -- finding count, and dropped-vs-approximated external-method counts +- finding count, and dropped-vs-modeled external-method counts - whether memory was bumped to 16G -- if the scan failed at config-load on a malformed approximation: the engine error and the offending file under `.opentaint/pass-through` or `.opentaint/dataflow` (located from the error), so it can be fixed and rescanned — this is not an out-of-memory failure and 16G won't help -- if no SARIF came out even at 16G: that the scan is left failed, with the setup used (full scan command with ruleset, approximation dirs, model) +- if the scan failed at config-load on a malformed model: the engine error and the offending file under `.opentaint/pass-through` or `.opentaint/dataflow` (located from the error), so it can be fixed and rescanned — this is not an out-of-memory failure and 16G won't help +- if no SARIF came out even at 16G: that the scan is left failed, with the setup used (full scan command with ruleset, model dirs, model) diff --git a/skills-templates/run-scan/sections/workflow.md b/skills-templates/run-scan/sections/workflow.md index d351480681..80c538a4c0 100644 --- a/skills-templates/run-scan/sections/workflow.md +++ b/skills-templates/run-scan/sections/workflow.md @@ -10,10 +10,10 @@ opentaint scan --project-model .opentaint/project \ ``` - `--rule-id ` — restrict to specific executable rules (repeatable, one per input rule ID); referenced library rules remain available without listing their IDs separately. Omit to run all loaded rules -- `--passthrough-approximations .opentaint/pass-through` — add when that directory exists: passThrough configs override built-ins at the rule level, a provided rule overriding a built-in only when it matches one -- `--dataflow-approximations .opentaint/dataflow` — add when that directory exists: code-based approximations, one project per batch +- `--passthrough-models .opentaint/pass-through` — add when that directory exists: passThrough configs override built-ins at the rule level, a provided rule overriding a built-in only when it matches one +- `--dataflow-models .opentaint/dataflow` — add when that directory exists: code-based models, one project per batch -Both approximation-dir flags walk their trees recursively; pass each parent directory once, not every package or batch separately. +Both model-dir flags walk their trees recursively; pass each parent directory once, not every package or batch separately. The scan is long — run it in the background and wait for it to finish. Leave `--timeout` at the engine default (900s); the CLI ends the analysis itself and writes whatever SARIF it has. @@ -23,4 +23,4 @@ Start at the 8G default. An out-of-memory failure at 8G — even when the engine ### 3. Collect the report, or escalate -A SARIF is complete enough to use even alongside the two normal scan errors — a timeout (the CLI ended the analysis and wrote what it had), or an out-of-memory at 16G (after §2's bump), take it as-is. The other two outcomes are not results to accept. A config-load failure on a malformed approximation is fixed, not bumped: report the engine error and the offending file under `.opentaint/pass-through`/`.opentaint/dataflow` per Output so it can be repaired and rescanned. No SARIF at all, even at 16G, is a plain failure: report it per Output with the scan setup. +A SARIF is complete enough to use even alongside the two normal scan errors — a timeout (the CLI ended the analysis and wrote what it had), or an out-of-memory at 16G (after §2's bump), take it as-is. The other two outcomes are not results to accept. A config-load failure on a malformed model is fixed, not bumped: report the engine error and the offending file under `.opentaint/pass-through`/`.opentaint/dataflow` per Output so it can be repaired and rescanned. No SARIF at all, even at 16G, is a plain failure: report it per Output with the scan setup. diff --git a/skills-templates/shared/debugging.md b/skills-templates/shared/debugging.md index 8b7ce31714..02cea7b142 100644 --- a/skills-templates/shared/debugging.md +++ b/skills-templates/shared/debugging.md @@ -15,8 +15,8 @@ opentaint test rule reachability \ - `` — the one rule whose sample routes taint through the code under test (`.yaml:`). One rule per run — across many rules the trace is unusably huge. Its library `refs` are collected automatically - read the sibling `/debug-ifds-fact-reachability.sarif`, not the `-o` file. The `-o` SARIF only shows default scan output. The sibling holds the per-instruction facts that show where taint dies -- apply the approximations the failing run used so the trace matches it: `--passthrough-approximations ` and/or `--dataflow-approximations `. Taint dying at an approximated call then means that approximation isn't propagating -- debug the exact run that showed the problem — same model, rulesets, approximation dirs — or you're debugging something else +- apply the models the failing run used so the trace matches it: `--passthrough-models ` and/or `--dataflow-models `. Taint dying at a modeled call then means that model isn't propagating +- debug the exact run that showed the problem — same model, rulesets, model dirs — or you're debugging something else ## Reading the trace diff --git a/skills-templates/shared/engine/facts.md b/skills-templates/shared/engine/facts.md index bdced44f46..1d1419bc26 100644 --- a/skills-templates/shared/engine/facts.md +++ b/skills-templates/shared/engine/facts.md @@ -1 +1 @@ -OpenTaint is a whole-program, interprocedural, field-sensitive alias analysis engine. It already propagates through visible application code, calls, aliases, and individual fields; custom rules and approximations model only the assigned source, sink, or opaque-method boundary. Compile-time constants and literals carry no taint, so a source or carrier whose output is only a constant introduces nothing. +OpenTaint is a whole-program, interprocedural, field-sensitive alias analysis engine. It already propagates through visible application code, calls, aliases, and individual fields; custom rules and models cover only the assigned source, sink, or opaque-method boundary. Compile-time constants and literals carry no taint, so a source or carrier whose output is only a constant introduces nothing. diff --git a/skills-templates/shared/scripts/_common.py b/skills-templates/shared/scripts/_common.py index 5d4864533f..048424f8ff 100644 --- a/skills-templates/shared/scripts/_common.py +++ b/skills-templates/shared/scripts/_common.py @@ -15,7 +15,7 @@ ROOT = Path(".opentaint") MODEL = ROOT / "project" TRACKING = ROOT / "tracking" -APPROX = TRACKING / "approximations" +MODELS_TR = TRACKING / "models" RULES_TR = TRACKING / "rules" SOURCES_TR = RULES_TR / "sources" SINKS_TR = RULES_TR / "sinks" @@ -166,7 +166,7 @@ def git_head(): return None -# ---- approximation batch readers (shared by coverage + partition) ---- +# ---- model batch readers (shared by coverage + partition) ---- # a method counts classified once it sits in any batch classification bucket or in build.done CLASSIFIED_BUCKETS = ("passthrough", "dataflow", "skipped", "engine_issues") @@ -174,8 +174,8 @@ def git_head(): def batch_files(): - """Every approximation batch file (skipped.yaml is the merged view, not a batch).""" - return [Path(p) for p in sorted(glob.glob(str(APPROX / "*.yaml"))) + """Every model batch file (skipped.yaml is the merged view, not a batch).""" + return [Path(p) for p in sorted(glob.glob(str(MODELS_TR / "*.yaml"))) if Path(p).name != "skipped.yaml"] @@ -211,7 +211,7 @@ def classified_keys(): def skipped_keys(): """method+signature keys classified terminal — the `skipped` and `engine_issues` buckets of every batch. Terminal means the method will never build a working carrier, so it must not hold - the approximations phase pending even if it is still modeled and still dropped by the scan. + the models phase pending even if it is still modeled and still dropped by the scan. (skipped.yaml is the merged view of these same buckets, so reading the batches alone suffices.)""" out = set() for p in batch_files(): @@ -236,7 +236,7 @@ def modeled_entries(): def build_done_keys(): - """Overload-precise keys of every built approximation across batches.""" + """Overload-precise keys of every built model across batches.""" keys = set() for p in batch_files(): doc = load_yaml(p, {}) or {} diff --git a/skills-templates/shared/scripts/generate.py b/skills-templates/shared/scripts/generate.py index 53e6b4488d..fff5d7fc63 100644 --- a/skills-templates/shared/scripts/generate.py +++ b/skills-templates/shared/scripts/generate.py @@ -9,10 +9,10 @@ init bootstrap the .opentaint tree + state.yaml from the workflow flags tags refresh tracking/rules/tags.yaml from the active lib rules - partition analyze dropped external methods -> per-root batch plans (approximations) + partition analyze dropped external methods -> per-root batch plans (models) partition discover coverage.yaml's used members -> balanced discover plans mark-safe discover plans' verdicts -> classification.yaml ledger (+prune plans) - merge-skipped batch skipped/engine_issues -> approximations/skipped.yaml (+prune plans) + merge-skipped batch skipped/engine_issues -> models/skipped.yaml (+prune plans) findings results/report.sarif -> per-rule finding tracking files (idempotent) """ import argparse @@ -27,7 +27,7 @@ import yaml -from _common import (APPROX, DATAFLOW, DROPPED, +from _common import (MODELS_TR, DATAFLOW, DROPPED, FINDINGS_TR, MODEL, PASS_THROUGH, RESULTS, RULES, RULES_TR, SARIF, SINKS_TR, SOURCES_TR, TAGS, TRACKING, builtin_rules_root, class_of, classified_keys, @@ -35,20 +35,20 @@ git_head, ledger_verdicted_keys, load_yaml, member_key, package_of, strip_quotes) -ANALYZE_BUDGET = 20 # methods per approximation batch +ANALYZE_BUDGET = 20 # methods per model batch ANALYZE_MISC = 6 # roots with <= this many methods pool into one misc batch ROOT_DEPTH = 2 # library root = first 2 dotted segments DISCOVER_TARGET, DISCOVER_BAND = 50, 15 # project-used members per discover plan (~50, loose) DISCOVER_PLANS = RULES_TR / "plans" -APPROX_PLANS = APPROX / "plans" +MODEL_PLANS = MODELS_TR / "plans" # ---- init: bootstrap the working tree + state.yaml ---- # the durable directories a run writes into; the leaves/scripts mkdir on write, but seeding # them up front gives every stage a place to land and makes the empty tree self-describing. -INIT_DIRS = [TRACKING, APPROX, SOURCES_TR, SINKS_TR, FINDINGS_TR, +INIT_DIRS = [TRACKING, MODELS_TR, SOURCES_TR, SINKS_TR, FINDINGS_TR, RESULTS, RULES, PASS_THROUGH, DATAFLOW] STATE_DERIVED = ("model_commit", "build_jdk", "max_memory") # build/scan fill these, init preserves @@ -202,7 +202,7 @@ def _root_next_index(prefix): # owns, or the new plan's analyze agent would overwrite that batch. Continue past the highest # index any existing batch OR leftover plan already claims for this root. mx = 0 - for d in (APPROX, APPROX_PLANS): + for d in (MODELS_TR, MODEL_PLANS): for p in glob.glob(str(d / f"{prefix}-*.yaml")): m = re.match(rf"^{re.escape(prefix)}-(\d+)\.yaml$", Path(p).name) if m: @@ -211,7 +211,7 @@ def _root_next_index(prefix): def cmd_analyze(args): - regen_plans(APPROX_PLANS) + regen_plans(MODEL_PLANS) classified = classified_keys() rows = [r for r in dropped_entries() if member_key(r) not in classified] if not rows: @@ -243,7 +243,7 @@ def cmd_analyze(args): norm = {cls: sorted(v, key=lambda x: (x["method"], x.get("signature", ""))) for cls, v in sorted(scopes.items())} pid = f"{prefix}-{start + i:03d}" - path = APPROX_PLANS / f"{pid}.yaml" + path = MODEL_PLANS / f"{pid}.yaml" path.write_text(dump_yaml({"scopes": norm}), encoding="utf-8") paths.append(str(path)) for p in paths: @@ -448,17 +448,17 @@ def cmd_merge_skipped(args): # collects the `skipped` and `engine_issues` buckets of every batch into skipped.yaml, keeping # them as two separate groups — regular skips under `methods`, engine issues under `engine_issues`. docs = [load_yaml(p, {}) or {} for p in - (Path(x) for x in sorted(glob.glob(str(APPROX / "*.yaml")))) + (Path(x) for x in sorted(glob.glob(str(MODELS_TR / "*.yaml")))) if p.name != "skipped.yaml"] out = {"methods": _collect(docs, "skipped"), "engine_issues": _collect(docs, "engine_issues")} - (APPROX / "skipped.yaml").write_text(dump_yaml(out), encoding="utf-8") + (MODELS_TR / "skipped.yaml").write_text(dump_yaml(out), encoding="utf-8") print(f"skipped.yaml: {len(out['methods'])} methods, {len(out['engine_issues'])} engine_issues") - if not args.keep and APPROX_PLANS.is_dir(): - pruned = [p for p in glob.glob(str(APPROX_PLANS / "*.yaml"))] + if not args.keep and MODEL_PLANS.is_dir(): + pruned = [p for p in glob.glob(str(MODEL_PLANS / "*.yaml"))] for p in pruned: Path(p).unlink() if pruned: - print(f"pruned {len(pruned)} consumed approximation plan(s)") + print(f"pruned {len(pruned)} consumed model plan(s)") return 0 @@ -630,8 +630,8 @@ def main(): m.add_argument("--keep", action="store_true", help="keep the reconciled discover plans") m.set_defaults(func=cmd_mark_safe) - s = sub.add_parser("merge-skipped", help="rebuild approximations/skipped.yaml from batches") - s.add_argument("--keep", action="store_true", help="keep the consumed approximation plans") + s = sub.add_parser("merge-skipped", help="rebuild models/skipped.yaml from batches") + s.add_argument("--keep", action="store_true", help="keep the consumed model plans") s.set_defaults(func=cmd_merge_skipped) f = sub.add_parser("findings", help="seed per-rule finding files from results/report.sarif") diff --git a/skills-templates/shared/scripts/get_status.py b/skills-templates/shared/scripts/get_status.py index 3eaa1969c5..8099f4b25c 100644 --- a/skills-templates/shared/scripts/get_status.py +++ b/skills-templates/shared/scripts/get_status.py @@ -21,7 +21,7 @@ import sys from pathlib import Path -from _common import (APPROX, DATAFLOW, +from _common import (MODELS_TR, DATAFLOW, FINDINGS_TR, MODEL, PASS_THROUGH, ROOT, RULES, RULES_TR, SARIF, SINKS_TR, SOURCES_TR, TAGS, TRACKING, active_lib_rules, build_done_keys, builtin_rules_root, @@ -34,7 +34,7 @@ TRIAGE_LEVEL = STATE.get("triage_level") DISCOVER_PLANS = RULES_TR / "plans" -APPROX_PLANS = APPROX / "plans" +MODEL_PLANS = MODELS_TR / "plans" VULN = ROOT / "vulnerabilities.md" GLOBAL_CAP = 10 @@ -73,8 +73,8 @@ def newest_mtime(paths): return m -def approx_dirty(): - # scan older than the newest applied approximation artifact -> a rescan is pending +def models_dirty(): + # scan older than the newest applied model artifact -> a rescan is pending if not SARIF.is_file(): return False arts = list(PASS_THROUGH.rglob("*")) + list(DATAFLOW.rglob("*")) @@ -315,13 +315,13 @@ def ph_scan(): return True, [], None -def ph_approximations(): +def ph_models(): if not SARIF.is_file(): return False, ["dispatch run-scan"], None classified = classified_keys() uncovered = [e for e in dropped_entries() if member_key(e) not in classified] if uncovered: - plans = sorted(glob.glob(str(APPROX_PLANS / "*.yaml"))) + plans = sorted(glob.glob(str(MODEL_PLANS / "*.yaml"))) if plans: tasks = [f"dispatch analyze-external-methods, one per plan (cap {GLOBAL_CAP}):"] tasks += [f" {p}" for p in plans] @@ -340,14 +340,14 @@ def ph_approximations(): by_kind.setdefault(kind, set()).add(p.stem) tasks = ["build unbuilt batches:"] if by_kind.get("passthrough"): - tasks.append(" passthrough create-pass-through-approximation: " + tasks.append(" passthrough create-pass-through-model: " + ", ".join(sorted(by_kind["passthrough"]))) if by_kind.get("dataflow"): tasks.append(" dataflow create-test-project type dataflow, then " - "create-dataflow-approximation: " + ", ".join(sorted(by_kind["dataflow"]))) + "create-dataflow-model: " + ", ".join(sorted(by_kind["dataflow"]))) return False, tasks, None - if approx_dirty(): - return False, ["approximations built after the last scan", "dispatch run-scan"], None + if models_dirty(): + return False, ["models built after the last scan", "dispatch run-scan"], None stuck = sorted({e["method"] for e in dropped_entries() if member_key(e) in done and member_key(e) not in terminal}) if stuck: @@ -411,7 +411,7 @@ def ph_poc(): ("discover", ph_discover, lambda: SCAN_LEVEL == "deep"), ("source_rules", ph_source_rules, lambda: SCAN_LEVEL == "deep"), ("scan", ph_scan, lambda: True), - ("approximations", ph_approximations, lambda: SCAN_LEVEL in ("normal", "deep")), + ("models", ph_models, lambda: SCAN_LEVEL in ("normal", "deep")), ("sink_rules", ph_sink_rules, lambda: SCAN_LEVEL == "deep"), ("triage", ph_triage, lambda: True), ("poc", ph_poc, lambda: TRIAGE_LEVEL == "dynamic"), diff --git a/skills-templates/shared/tracking/approximations-batch.md b/skills-templates/shared/tracking/models-batch.md similarity index 82% rename from skills-templates/shared/tracking/approximations-batch.md rename to skills-templates/shared/tracking/models-batch.md index 5fa27bdffa..d86e0a2a05 100644 --- a/skills-templates/shared/tracking/approximations-batch.md +++ b/skills-templates/shared/tracking/models-batch.md @@ -1,4 +1,4 @@ -`.opentaint/tracking/approximations/.yaml` — one batch's callable classification, `` the plan's filename stem. Every callable sits in exactly one verdict bucket, keyed with the exact language-specific `method` and `signature` from the plan so distinct variants stay separate: +`.opentaint/tracking/models/.yaml` — one batch's callable classification, `` the plan's filename stem. Every callable sits in exactly one verdict bucket, keyed with the exact language-specific `method` and `signature` from the plan so distinct variants stay separate: - `passthrough`, `dataflow` — modeled carriers; each entry `{ method, signature }` - `skipped` — terminal non-carriers; each `{ method, signature, reason }` - `engine_issues` — a separate bucket for carriers the engine provably can't propagate (built but still dropped); each `{ method, signature, reason }`. Terminal and treated just like `skipped` — the only difference is the reason. `merge-skipped` carries it into `skipped.yaml` as its own `engine_issues` group alongside the regular skipped `methods`. diff --git a/skills/analyze-external-methods/SKILL.md b/skills/analyze-external-methods/SKILL.md index 2a6de26042..ffb6d85c21 100644 --- a/skills/analyze-external-methods/SKILL.md +++ b/skills/analyze-external-methods/SKILL.md @@ -9,9 +9,9 @@ metadata: # Skill: Analyze External Methods -OpenTaint is a dataflow taint analyzer: it starts from the data a source introduces and follows it call by call until the flow stops. A flow stops for one of two reasons — the data reached a callable that retains none of it (call `size()` on a tainted collection and its whole contents collapse into one number, so the taint is gone), or it reached a callable whose body the analyzer can't see, typically in an external dependency. That opaque callable may itself be taint-killing, or it may in fact carry the data onward — and then it needs an approximation telling the engine exactly how the data moves through the call, or every trace through it is silently cut. +OpenTaint is a dataflow taint analyzer: it starts from the data a source introduces and follows it call by call until the flow stops. A flow stops for one of two reasons — the data reached a callable that retains none of it (call `size()` on a tainted collection and its whole contents collapse into one number, so the taint is gone), or it reached a callable whose body the analyzer can't see, typically in an external dependency. That opaque callable may itself be taint-killing, or it may in fact carry the data onward — and then it needs a model telling the engine exactly how the data moves through the call, or every trace through it is silently cut. -You are handed the list of those dropped methods. Decide which ones actually carry data and which don't, and for each carrier determine the kind of approximation it needs, so the build stage can restore the flow. +You are handed the list of those dropped methods. Decide which ones actually carry data and which don't, and for each carrier determine the kind of model it needs, so the build stage can restore the flow. On a deep run these same methods carry a second, independent question — whether the call is itself a dangerous operation (a sink); that pass is step 2. @@ -21,14 +21,14 @@ Provided by the caller, fall back to the default value when omitted. Ask back on - `project-root` (optional) — root of the target project. Opentaint keeps all analysis artifacts under the fixed `/.opentaint/` directory, so every `.opentaint/...` path below resolves there. Default: current directory - `language` (required) — target language for this project and language-specific instructions -- `plan` (required) — path to this agent's batch plan `.opentaint/tracking/approximations/plans/.yaml`: the dropped methods to classify +- `plan` (required) — path to this agent's batch plan `.opentaint/tracking/models/plans/.yaml`: the dropped methods to classify - `sinks` (optional) — a flag whether to classify sinks per step 2 or not ## Workflow ### 1. Classify propagation -Take the members from the plan's `scopes`, judge each one, and write the verdict to the batch file `.opentaint/tracking/approximations/.yaml` — `` is your plan's filename stem, the batch id, reused for the coverage check below. +Take the members from the plan's `scopes`, judge each one, and write the verdict to the batch file `.opentaint/tracking/models/.yaml` — `` is your plan's filename stem, the batch id, reused for the coverage check below. Always classify from the method's real code, never from its name. Start by reading the method's source — `/references/.md` describes how to get it. Then answer, for each method: where does its input data go? Data that arrives on the receiver or an argument — does it come back out, through the return value, an argument the method writes into, the receiver, or an object or field it stores the data in? That answer picks the bucket: @@ -74,7 +74,7 @@ Short and concise report of what was done ### Artifacts: -- `.opentaint/tracking/approximations/.yaml` — the batch classification (per Tracking) +- `.opentaint/tracking/models/.yaml` — the batch classification (per Tracking) - `.opentaint/tracking/rules/sinks/.yaml` — the per-package sink units, when `sinks` was set (per Tracking) ### Summary: @@ -86,7 +86,7 @@ Short and concise report of what was done ### Batch classification -`.opentaint/tracking/approximations/.yaml` — one batch's callable classification, `` the plan's filename stem. Every callable sits in exactly one verdict bucket, keyed with the exact language-specific `method` and `signature` from the plan so distinct variants stay separate: +`.opentaint/tracking/models/.yaml` — one batch's callable classification, `` the plan's filename stem. Every callable sits in exactly one verdict bucket, keyed with the exact language-specific `method` and `signature` from the plan so distinct variants stay separate: - `passthrough`, `dataflow` — modeled carriers; each entry `{ method, signature }` - `skipped` — terminal non-carriers; each `{ method, signature, reason }` - `engine_issues` — a separate bucket for carriers the engine provably can't propagate (built but still dropped); each `{ method, signature, reason }`. Terminal and treated just like `skipped` — the only difference is the reason. `merge-skipped` carries it into `skipped.yaml` as its own `engine_issues` group alongside the regular skipped `methods`. @@ -108,7 +108,7 @@ build: done: [] ``` -This skill writes `passthrough`/`dataflow`/`skipped` and `dependencies`; leave the `build` block and `engine_issues` alone — they are filled later in the approximation stage. +This skill writes `passthrough`/`dataflow`/`skipped` and `dependencies`; leave the `build` block and `engine_issues` alone — they are filled later in the model stage. ### Sink units (only when `sinks` is set) @@ -130,7 +130,7 @@ The partition keeps a whole package in one batch, so populate only the units you ## Constraints -OpenTaint is a whole-program, interprocedural, field-sensitive alias analysis engine. It already propagates through visible application code, calls, aliases, and individual fields; custom rules and approximations model only the assigned source, sink, or opaque-method boundary. Compile-time constants and literals carry no taint, so a source or carrier whose output is only a constant introduces nothing. +OpenTaint is a whole-program, interprocedural, field-sensitive alias analysis engine. It already propagates through visible application code, calls, aliases, and individual fields; custom rules and models cover only the assigned source, sink, or opaque-method boundary. Compile-time constants and literals carry no taint, so a source or carrier whose output is only a constant introduces nothing. - Judge intrinsic propagation only, never per-project flow. Don't skip a carrier because its data doesn't seem to reach a sink here, and don't gate a sink on where its data goes — whether a flow reaches a sink is the analyzer's job, not yours - Classify every method the plan assigns and only those — each is a real place data was lost, don't invent methods outside the plan. `check-coverage.py --batch` must report `0 UNCOVERED` before you return diff --git a/skills/analyze-external-methods/scripts/check-coverage.py b/skills/analyze-external-methods/scripts/check-coverage.py index 5c4daf5224..c36a9bb5c8 100644 --- a/skills/analyze-external-methods/scripts/check-coverage.py +++ b/skills/analyze-external-methods/scripts/check-coverage.py @@ -16,7 +16,7 @@ import yaml DROPPED = Path(".opentaint/results/dropped-external-methods.yaml") -APPROX_DIR = Path(".opentaint/tracking/approximations") +MODELS_DIR = Path(".opentaint/tracking/models") # classification buckets + skipped.yaml lists; build.done is read separately (nested) CLASSIFIED_KEYS = {"passthrough", "dataflow", "skipped", "methods", "engine_issues"} @@ -39,7 +39,7 @@ def dropped_methods(): def classified_methods(): # every method+signature in a classification bucket, build.done, or skipped.yaml out = set() - for p in sorted(glob.glob(str(APPROX_DIR / "*.yaml"))): + for p in sorted(glob.glob(str(MODELS_DIR / "*.yaml"))): doc = yaml.safe_load(Path(p).read_text(encoding="utf-8")) or {} for key in CLASSIFIED_KEYS: for item in doc.get(key, []) or []: @@ -55,7 +55,7 @@ def batch_methods(batch_arg): # the members a batch plan assigned (scopes: {class: [{method, signature}]}); id or path p = Path(batch_arg) if not p.is_file(): - p = APPROX_DIR / "plans" / f"{batch_arg}.yaml" + p = MODELS_DIR / "plans" / f"{batch_arg}.yaml" if not p.is_file(): return None doc = yaml.safe_load(p.read_text(encoding="utf-8")) or {} diff --git a/skills/appsec-agent/SKILL.md b/skills/appsec-agent/SKILL.md index 35129654d4..53ce5544b5 100644 --- a/skills/appsec-agent/SKILL.md +++ b/skills/appsec-agent/SKILL.md @@ -9,9 +9,9 @@ metadata: # AppSec Agent -Orchestrate an end-to-end OpenTaint security analysis. Keep the long project build and every full-project scan in this main session; delegate each bounded source, approximation, sink, triage, and PoC stage to an `orchestrate-stage` subagent, which owns its leaf fan-out and joins. +Orchestrate an end-to-end OpenTaint security analysis. Keep the long project build and every full-project scan in this main session; delegate each bounded source, model, sink, triage, and PoC stage to an `orchestrate-stage` subagent, which owns its leaf fan-out and joins. -OpenTaint is a whole-program, interprocedural, field-sensitive alias analysis SAST. The run produces confirmed vulnerabilities plus reusable project-specific rules and approximations under one self-contained `.opentaint/` directory at the project root. +OpenTaint is a whole-program, interprocedural, field-sensitive alias analysis SAST. The run produces confirmed vulnerabilities plus reusable project-specific rules and models under one self-contained `.opentaint/` directory at the project root. ## Setup @@ -38,8 +38,8 @@ Ask the user for both levels together: 1. Scan level — `lite` · `normal` · `deep` - lite — build + scan (expected, when there are already existing artifacts) - - normal — build + scan + custom approximations - - deep — build + scan + custom approximations + custom rules + - normal — build + scan + custom models + - deep — build + scan + custom models + custom rules - recommend by what's on disk: a cold start (no `.opentaint` artifacts) → deep; a prior run's artifacts already present → lite 2. Triage level — `static` · `dynamic` - static — classify findings from the model, no running app @@ -63,7 +63,7 @@ The run is one fixed pipeline; the selected levels determine which phases are in build → MAIN: build discover / source_rules → stage subagent: sources scan → MAIN: scan -approximations → stage subagent: approx-round, then MAIN: rescan; repeat +models → stage subagent: model-round, then MAIN: rescan; repeat sink_rules → stage subagent: sinks, then MAIN: rescan triage → stage subagent: triage poc → stage subagent: poc @@ -79,9 +79,9 @@ After a successful build, write the language-specific build fields named by the When status reports `scan`, or a stage returns with a rescan pending, load and follow the `run-scan` skill in this main session. Start the scan with the harness's main-session background-command facility, keep the engine's self-timeout, add a 1200-second outer backstop, and wait for the process completion event. -A valid `.opentaint/results/report.sarif` means the scan completed, including exit 254 after an engine timeout. Record `max_memory: 16G` when the scan had to bump memory and reuse it on later scans. If no SARIF exists after the allowed retry/backstop, follow the repair path below for a malformed rule/approximation; otherwise dispatch `orchestrate-stage` with `stage: escalation` and the scan `setup` to write the scan-wide resource issue, then stop. +A valid `.opentaint/results/report.sarif` means the scan completed, including exit 254 after an engine timeout. Record `max_memory: 16G` when the scan had to bump memory and reuse it on later scans. If no SARIF exists after the allowed retry/backstop, follow the repair path below for a malformed rule/model; otherwise dispatch `orchestrate-stage` with `stage: escalation` and the scan `setup` to write the scan-wide resource issue, then stop. -When a scan or later stage reports a malformed approximation, unloadable created rule, ineffective join, or a created rule's false positive/negative, route the exact diagnosis and artifact path/id to the responsible stage agent per Dispatching, then scan again in MAIN. +When a scan or later stage reports a malformed model, unloadable created rule, ineffective join, or a created rule's false positive/negative, route the exact diagnosis and artifact path/id to the responsible stage agent per Dispatching, then scan again in MAIN. After every build, scan, or stage return, run `uv run /scripts/get_status.py` once to choose the next action. Use `--full` at run start, on resume, or when the brief output does not settle the question. @@ -92,15 +92,15 @@ Dispatch exactly one stage-orchestrator subagent for each stage invocation: ``` Invoke the Skill orchestrate-stage first, then follow its instructions precisely Inputs: - stage: + stage: ``` -For a `deep` approximation round, also pass `sinks: true`. A subagent inherits the project-root working directory, so omit `project-root`. +For a `deep` model round, also pass `sinks: true`. A subagent inherits the project-root working directory, so omit `project-root`. Stage context: - `sources` — discover dependency sources and author tag-grouped source rules -- `approx-round` — classify and build one dropped-method frontier; use a fresh agent for each new frontier +- `model-round` — classify and build one dropped-method frontier; use a fresh agent for each new frontier - `sinks` — author classified sink rules and wire the joins - `triage` — classify the latest findings and refresh the vulnerability report - `poc` — reproduce confirmed findings and add the outcomes to the report @@ -119,15 +119,15 @@ Use this ownership map to route work and scan errors: project/ MAIN build results/ MAIN scan rules/ sources or sinks stage - pass-through/ approximation stage - dataflow/ approximation stage + pass-through/ model stage + dataflow/ model stage tracking/state.yaml MAIN run knobs tracking/ stage agents, leaves, and join scripts otherwise vulnerabilities.md triage / PoC stage issues/ escalation stage ``` -The tree is long-lived. On resume, reuse `DONE` artifacts; `get_status.py` derives the next phase from disk. Existing rules and approximations apply to every scan. +The tree is long-lived. On resume, reuse `DONE` artifacts; `get_status.py` derives the next phase from disk. Existing rules and models apply to every scan. `state.yaml` shape: diff --git a/skills/appsec-agent/scripts/_common.py b/skills/appsec-agent/scripts/_common.py index 5d4864533f..048424f8ff 100644 --- a/skills/appsec-agent/scripts/_common.py +++ b/skills/appsec-agent/scripts/_common.py @@ -15,7 +15,7 @@ ROOT = Path(".opentaint") MODEL = ROOT / "project" TRACKING = ROOT / "tracking" -APPROX = TRACKING / "approximations" +MODELS_TR = TRACKING / "models" RULES_TR = TRACKING / "rules" SOURCES_TR = RULES_TR / "sources" SINKS_TR = RULES_TR / "sinks" @@ -166,7 +166,7 @@ def git_head(): return None -# ---- approximation batch readers (shared by coverage + partition) ---- +# ---- model batch readers (shared by coverage + partition) ---- # a method counts classified once it sits in any batch classification bucket or in build.done CLASSIFIED_BUCKETS = ("passthrough", "dataflow", "skipped", "engine_issues") @@ -174,8 +174,8 @@ def git_head(): def batch_files(): - """Every approximation batch file (skipped.yaml is the merged view, not a batch).""" - return [Path(p) for p in sorted(glob.glob(str(APPROX / "*.yaml"))) + """Every model batch file (skipped.yaml is the merged view, not a batch).""" + return [Path(p) for p in sorted(glob.glob(str(MODELS_TR / "*.yaml"))) if Path(p).name != "skipped.yaml"] @@ -211,7 +211,7 @@ def classified_keys(): def skipped_keys(): """method+signature keys classified terminal — the `skipped` and `engine_issues` buckets of every batch. Terminal means the method will never build a working carrier, so it must not hold - the approximations phase pending even if it is still modeled and still dropped by the scan. + the models phase pending even if it is still modeled and still dropped by the scan. (skipped.yaml is the merged view of these same buckets, so reading the batches alone suffices.)""" out = set() for p in batch_files(): @@ -236,7 +236,7 @@ def modeled_entries(): def build_done_keys(): - """Overload-precise keys of every built approximation across batches.""" + """Overload-precise keys of every built model across batches.""" keys = set() for p in batch_files(): doc = load_yaml(p, {}) or {} diff --git a/skills/appsec-agent/scripts/generate.py b/skills/appsec-agent/scripts/generate.py index 53e6b4488d..fff5d7fc63 100644 --- a/skills/appsec-agent/scripts/generate.py +++ b/skills/appsec-agent/scripts/generate.py @@ -9,10 +9,10 @@ init bootstrap the .opentaint tree + state.yaml from the workflow flags tags refresh tracking/rules/tags.yaml from the active lib rules - partition analyze dropped external methods -> per-root batch plans (approximations) + partition analyze dropped external methods -> per-root batch plans (models) partition discover coverage.yaml's used members -> balanced discover plans mark-safe discover plans' verdicts -> classification.yaml ledger (+prune plans) - merge-skipped batch skipped/engine_issues -> approximations/skipped.yaml (+prune plans) + merge-skipped batch skipped/engine_issues -> models/skipped.yaml (+prune plans) findings results/report.sarif -> per-rule finding tracking files (idempotent) """ import argparse @@ -27,7 +27,7 @@ import yaml -from _common import (APPROX, DATAFLOW, DROPPED, +from _common import (MODELS_TR, DATAFLOW, DROPPED, FINDINGS_TR, MODEL, PASS_THROUGH, RESULTS, RULES, RULES_TR, SARIF, SINKS_TR, SOURCES_TR, TAGS, TRACKING, builtin_rules_root, class_of, classified_keys, @@ -35,20 +35,20 @@ git_head, ledger_verdicted_keys, load_yaml, member_key, package_of, strip_quotes) -ANALYZE_BUDGET = 20 # methods per approximation batch +ANALYZE_BUDGET = 20 # methods per model batch ANALYZE_MISC = 6 # roots with <= this many methods pool into one misc batch ROOT_DEPTH = 2 # library root = first 2 dotted segments DISCOVER_TARGET, DISCOVER_BAND = 50, 15 # project-used members per discover plan (~50, loose) DISCOVER_PLANS = RULES_TR / "plans" -APPROX_PLANS = APPROX / "plans" +MODEL_PLANS = MODELS_TR / "plans" # ---- init: bootstrap the working tree + state.yaml ---- # the durable directories a run writes into; the leaves/scripts mkdir on write, but seeding # them up front gives every stage a place to land and makes the empty tree self-describing. -INIT_DIRS = [TRACKING, APPROX, SOURCES_TR, SINKS_TR, FINDINGS_TR, +INIT_DIRS = [TRACKING, MODELS_TR, SOURCES_TR, SINKS_TR, FINDINGS_TR, RESULTS, RULES, PASS_THROUGH, DATAFLOW] STATE_DERIVED = ("model_commit", "build_jdk", "max_memory") # build/scan fill these, init preserves @@ -202,7 +202,7 @@ def _root_next_index(prefix): # owns, or the new plan's analyze agent would overwrite that batch. Continue past the highest # index any existing batch OR leftover plan already claims for this root. mx = 0 - for d in (APPROX, APPROX_PLANS): + for d in (MODELS_TR, MODEL_PLANS): for p in glob.glob(str(d / f"{prefix}-*.yaml")): m = re.match(rf"^{re.escape(prefix)}-(\d+)\.yaml$", Path(p).name) if m: @@ -211,7 +211,7 @@ def _root_next_index(prefix): def cmd_analyze(args): - regen_plans(APPROX_PLANS) + regen_plans(MODEL_PLANS) classified = classified_keys() rows = [r for r in dropped_entries() if member_key(r) not in classified] if not rows: @@ -243,7 +243,7 @@ def cmd_analyze(args): norm = {cls: sorted(v, key=lambda x: (x["method"], x.get("signature", ""))) for cls, v in sorted(scopes.items())} pid = f"{prefix}-{start + i:03d}" - path = APPROX_PLANS / f"{pid}.yaml" + path = MODEL_PLANS / f"{pid}.yaml" path.write_text(dump_yaml({"scopes": norm}), encoding="utf-8") paths.append(str(path)) for p in paths: @@ -448,17 +448,17 @@ def cmd_merge_skipped(args): # collects the `skipped` and `engine_issues` buckets of every batch into skipped.yaml, keeping # them as two separate groups — regular skips under `methods`, engine issues under `engine_issues`. docs = [load_yaml(p, {}) or {} for p in - (Path(x) for x in sorted(glob.glob(str(APPROX / "*.yaml")))) + (Path(x) for x in sorted(glob.glob(str(MODELS_TR / "*.yaml")))) if p.name != "skipped.yaml"] out = {"methods": _collect(docs, "skipped"), "engine_issues": _collect(docs, "engine_issues")} - (APPROX / "skipped.yaml").write_text(dump_yaml(out), encoding="utf-8") + (MODELS_TR / "skipped.yaml").write_text(dump_yaml(out), encoding="utf-8") print(f"skipped.yaml: {len(out['methods'])} methods, {len(out['engine_issues'])} engine_issues") - if not args.keep and APPROX_PLANS.is_dir(): - pruned = [p for p in glob.glob(str(APPROX_PLANS / "*.yaml"))] + if not args.keep and MODEL_PLANS.is_dir(): + pruned = [p for p in glob.glob(str(MODEL_PLANS / "*.yaml"))] for p in pruned: Path(p).unlink() if pruned: - print(f"pruned {len(pruned)} consumed approximation plan(s)") + print(f"pruned {len(pruned)} consumed model plan(s)") return 0 @@ -630,8 +630,8 @@ def main(): m.add_argument("--keep", action="store_true", help="keep the reconciled discover plans") m.set_defaults(func=cmd_mark_safe) - s = sub.add_parser("merge-skipped", help="rebuild approximations/skipped.yaml from batches") - s.add_argument("--keep", action="store_true", help="keep the consumed approximation plans") + s = sub.add_parser("merge-skipped", help="rebuild models/skipped.yaml from batches") + s.add_argument("--keep", action="store_true", help="keep the consumed model plans") s.set_defaults(func=cmd_merge_skipped) f = sub.add_parser("findings", help="seed per-rule finding files from results/report.sarif") diff --git a/skills/appsec-agent/scripts/get_status.py b/skills/appsec-agent/scripts/get_status.py index 3eaa1969c5..8099f4b25c 100644 --- a/skills/appsec-agent/scripts/get_status.py +++ b/skills/appsec-agent/scripts/get_status.py @@ -21,7 +21,7 @@ import sys from pathlib import Path -from _common import (APPROX, DATAFLOW, +from _common import (MODELS_TR, DATAFLOW, FINDINGS_TR, MODEL, PASS_THROUGH, ROOT, RULES, RULES_TR, SARIF, SINKS_TR, SOURCES_TR, TAGS, TRACKING, active_lib_rules, build_done_keys, builtin_rules_root, @@ -34,7 +34,7 @@ TRIAGE_LEVEL = STATE.get("triage_level") DISCOVER_PLANS = RULES_TR / "plans" -APPROX_PLANS = APPROX / "plans" +MODEL_PLANS = MODELS_TR / "plans" VULN = ROOT / "vulnerabilities.md" GLOBAL_CAP = 10 @@ -73,8 +73,8 @@ def newest_mtime(paths): return m -def approx_dirty(): - # scan older than the newest applied approximation artifact -> a rescan is pending +def models_dirty(): + # scan older than the newest applied model artifact -> a rescan is pending if not SARIF.is_file(): return False arts = list(PASS_THROUGH.rglob("*")) + list(DATAFLOW.rglob("*")) @@ -315,13 +315,13 @@ def ph_scan(): return True, [], None -def ph_approximations(): +def ph_models(): if not SARIF.is_file(): return False, ["dispatch run-scan"], None classified = classified_keys() uncovered = [e for e in dropped_entries() if member_key(e) not in classified] if uncovered: - plans = sorted(glob.glob(str(APPROX_PLANS / "*.yaml"))) + plans = sorted(glob.glob(str(MODEL_PLANS / "*.yaml"))) if plans: tasks = [f"dispatch analyze-external-methods, one per plan (cap {GLOBAL_CAP}):"] tasks += [f" {p}" for p in plans] @@ -340,14 +340,14 @@ def ph_approximations(): by_kind.setdefault(kind, set()).add(p.stem) tasks = ["build unbuilt batches:"] if by_kind.get("passthrough"): - tasks.append(" passthrough create-pass-through-approximation: " + tasks.append(" passthrough create-pass-through-model: " + ", ".join(sorted(by_kind["passthrough"]))) if by_kind.get("dataflow"): tasks.append(" dataflow create-test-project type dataflow, then " - "create-dataflow-approximation: " + ", ".join(sorted(by_kind["dataflow"]))) + "create-dataflow-model: " + ", ".join(sorted(by_kind["dataflow"]))) return False, tasks, None - if approx_dirty(): - return False, ["approximations built after the last scan", "dispatch run-scan"], None + if models_dirty(): + return False, ["models built after the last scan", "dispatch run-scan"], None stuck = sorted({e["method"] for e in dropped_entries() if member_key(e) in done and member_key(e) not in terminal}) if stuck: @@ -411,7 +411,7 @@ def ph_poc(): ("discover", ph_discover, lambda: SCAN_LEVEL == "deep"), ("source_rules", ph_source_rules, lambda: SCAN_LEVEL == "deep"), ("scan", ph_scan, lambda: True), - ("approximations", ph_approximations, lambda: SCAN_LEVEL in ("normal", "deep")), + ("models", ph_models, lambda: SCAN_LEVEL in ("normal", "deep")), ("sink_rules", ph_sink_rules, lambda: SCAN_LEVEL == "deep"), ("triage", ph_triage, lambda: True), ("poc", ph_poc, lambda: TRIAGE_LEVEL == "dynamic"), diff --git a/skills/create-dataflow-approximation/SKILL.md b/skills/create-dataflow-model/SKILL.md similarity index 54% rename from skills/create-dataflow-approximation/SKILL.md rename to skills/create-dataflow-model/SKILL.md index c09015791d..2196d87f6c 100644 --- a/skills/create-dataflow-approximation/SKILL.md +++ b/skills/create-dataflow-model/SKILL.md @@ -1,15 +1,15 @@ --- -name: create-dataflow-approximation -description: Model a method's taint propagation as code-based dataflow approximation and refine it against a test project until the sample passes. Use for a dropped method that requires code-based approximation +name: create-dataflow-model +description: Model a method's taint propagation as code-based dataflow model and refine it against a test project until the sample passes. Use for a dropped method that requires code-based model license: Apache-2.0 metadata: author: opentaint version: "0.3.0" --- -# Skill: Create Dataflow Approximation +# Skill: Create Dataflow Model -A dataflow approximation is code that expresses how data moves through a method the analyzer can't trace through — an opaque call where the engine loses taint because it can't see the body. You write a small stand-in that reproduces the method's real propagation from its inputs to its outputs, so the analyzer can follow taint through it. Run it against the prepared test project and refine until the sample passes. +A dataflow model is code that expresses how data moves through a method the analyzer can't trace through — an opaque call where the engine loses taint because it can't see the body. You write a small stand-in that reproduces the method's real propagation from its inputs to its outputs, so the analyzer can follow taint through it. Run it against the prepared test project and refine until the sample passes. ## Inputs @@ -17,22 +17,22 @@ Provided by the caller, fall back to the default value when omitted. Ask back on - `project-root` (optional) — root of the target project. Opentaint keeps all analysis artifacts under the fixed `/.opentaint/` directory, so every `.opentaint/...` path below resolves there. Default: current directory - `language` (required) — target language for this project and language-specific instructions -- `batch` (required) — the batch whose `.opentaint/tracking/approximations/.yaml` provides the `dataflow` methods to model and holds tracking state +- `batch` (required) — the batch whose `.opentaint/tracking/models/.yaml` provides the `dataflow` methods to model and holds tracking state - `methods` (optional) — a specific subset of the batch's dataflow methods to (re)model; default all not yet in `build.done` ## Workflow ### 1. Understand the propagation -Find and read each dataflow method's real source: take methods not yet in `build.done`, or the specific `methods` handed for repair even when already built. Leave built methods outside that explicit subset and their approximation source unchanged. An app-internal method sits in the project's own sources, a library method's source comes from its dependency (the language reference has how to get it). Read it to see how data moves from the method's inputs (receiver, arguments) to its outputs (return value, arguments it writes into, state it stores), gathering the full context needed to understand the function's behavior. +Find and read each dataflow method's real source: take methods not yet in `build.done`, or the specific `methods` handed for repair even when already built. Leave built methods outside that explicit subset and their model source unchanged. An app-internal method sits in the project's own sources, a library method's source comes from its dependency (the language reference has how to get it). Read it to see how data moves from the method's inputs (receiver, arguments) to its outputs (return value, arguments it writes into, state it stores), gathering the full context needed to understand the function's behavior. -### 2. Write the approximation +### 2. Write the model -Reproduce that propagation in the language-specific code form in the batch's approximation project at `.opentaint/dataflow/` — scaffold it if it doesn't exist yet — following the language reference's artifact layout. Cover every assigned callable variant, repair an explicitly handed callable in the existing source, and add new ones there rather than rewriting the file. The engine is field-sensitive — taint is tracked per field — so route data field-to-field exactly as the source does rather than tainting the whole object. The test project's negative samples (if present) verify this by storing taint in one field and reading another, so an over-broad model makes them fire. The concrete constructs and patterns are in the language reference. +Reproduce that propagation in the language-specific code form in the batch's model project at `.opentaint/dataflow/` — scaffold it if it doesn't exist yet — following the language reference's artifact layout. Cover every assigned callable variant, repair an explicitly handed callable in the existing source, and add new ones there rather than rewriting the file. The engine is field-sensitive — taint is tracked per field — so route data field-to-field exactly as the source does rather than tainting the whole object. The test project's negative samples (if present) verify this by storing taint in one field and reading another, so an over-broad model makes them fire. The concrete constructs and patterns are in the language reference. ### 3. Test against the test project -Run the approximation test directly as a foreground, blocking command and wait for exit — never background it or use Monitor. Apply this batch's sources and iterate until the samples pass. Feedback loop: a failing sample might be caused by the model's target type/member or signature not matching what the analyzer sees, or by the body not routing taint from the real source to the modeled output — diagnose the mismatch, fix, and re-run, don't rationalize a non-result. When the cause isn't obvious, localize where taint dies with a fact-reachability trace before guessing further per `references/debugging.md`. On a pass, append the method only if it is not already present in `build.done` (per Tracking); a repaired method remains recorded there. +Run the model test directly as a foreground, blocking command and wait for exit — never background it or use Monitor. Apply this batch's sources and iterate until the samples pass. Feedback loop: a failing sample might be caused by the model's target type/member or signature not matching what the analyzer sees, or by the body not routing taint from the real source to the modeled output — diagnose the mismatch, fix, and re-run, don't rationalize a non-result. When the cause isn't obvious, localize where taint dies with a fact-reachability trace before guessing further per `references/debugging.md`. On a pass, append the method only if it is not already present in `build.done` (per Tracking); a repaired method remains recorded there. ### 4. Escalate @@ -42,7 +42,7 @@ When the sample won't converge after ~3 fixes — whether the trace shows a fait ### Artifacts -- `.opentaint/dataflow/` — the batch's approximation project with the language-specific code approximation artifacts that the scan consumes; report the path and the exact test command used +- `.opentaint/dataflow/` — the batch's model project with the language-specific code model artifacts that the scan consumes; report the path and the exact test command used - the passing methods present in the batch file's `build.done` (new methods appended; repaired methods already recorded, per Tracking) ### Summary @@ -51,7 +51,7 @@ When the sample won't converge after ~3 fixes — whether the trace shows a fait ## Tracking -`.opentaint/tracking/approximations/.yaml` — one batch's callable classification, `` the plan's filename stem. Every callable sits in exactly one verdict bucket, keyed with the exact language-specific `method` and `signature` from the plan so distinct variants stay separate: +`.opentaint/tracking/models/.yaml` — one batch's callable classification, `` the plan's filename stem. Every callable sits in exactly one verdict bucket, keyed with the exact language-specific `method` and `signature` from the plan so distinct variants stay separate: - `passthrough`, `dataflow` — modeled carriers; each entry `{ method, signature }` - `skipped` — terminal non-carriers; each `{ method, signature, reason }` - `engine_issues` — a separate bucket for carriers the engine provably can't propagate (built but still dropped); each `{ method, signature, reason }`. Terminal and treated just like `skipped` — the only difference is the reason. `merge-skipped` carries it into `skipped.yaml` as its own `engine_issues` group alongside the regular skipped `methods`. @@ -77,8 +77,8 @@ This skill appends each method whose sample passes to `build.done` as `{ method, ## Constraints -OpenTaint is a whole-program, interprocedural, field-sensitive alias analysis engine. It already propagates through visible application code, calls, aliases, and individual fields; custom rules and approximations model only the assigned source, sink, or opaque-method boundary. Compile-time constants and literals carry no taint, so a source or carrier whose output is only a constant introduces nothing. +OpenTaint is a whole-program, interprocedural, field-sensitive alias analysis engine. It already propagates through visible application code, calls, aliases, and individual fields; custom rules and models cover only the assigned source, sink, or opaque-method boundary. Compile-time constants and literals carry no taint, so a source or carrier whose output is only a constant introduces nothing. -- Verify only with the approximation test on the test project +- Verify only with the model test on the test project - The test project's sample sources are a fixed input — never edit or recompile them to force a pass; if a faithful model can't pass, leave the method out and report it (per Output) - Model every dataflow method and overload the batch lists, not only the ones you have a sample for diff --git a/skills/create-dataflow-approximation/references/debugging.md b/skills/create-dataflow-model/references/debugging.md similarity index 85% rename from skills/create-dataflow-approximation/references/debugging.md rename to skills/create-dataflow-model/references/debugging.md index 8b7ce31714..02cea7b142 100644 --- a/skills/create-dataflow-approximation/references/debugging.md +++ b/skills/create-dataflow-model/references/debugging.md @@ -15,8 +15,8 @@ opentaint test rule reachability \ - `` — the one rule whose sample routes taint through the code under test (`.yaml:`). One rule per run — across many rules the trace is unusably huge. Its library `refs` are collected automatically - read the sibling `/debug-ifds-fact-reachability.sarif`, not the `-o` file. The `-o` SARIF only shows default scan output. The sibling holds the per-instruction facts that show where taint dies -- apply the approximations the failing run used so the trace matches it: `--passthrough-approximations ` and/or `--dataflow-approximations `. Taint dying at an approximated call then means that approximation isn't propagating -- debug the exact run that showed the problem — same model, rulesets, approximation dirs — or you're debugging something else +- apply the models the failing run used so the trace matches it: `--passthrough-models ` and/or `--dataflow-models `. Taint dying at a modeled call then means that model isn't propagating +- debug the exact run that showed the problem — same model, rulesets, model dirs — or you're debugging something else ## Reading the trace diff --git a/skills/create-dataflow-approximation/references/java.md b/skills/create-dataflow-model/references/java.md similarity index 62% rename from skills/create-dataflow-approximation/references/java.md rename to skills/create-dataflow-model/references/java.md index 125fabdef3..3176036b17 100644 --- a/skills/create-dataflow-approximation/references/java.md +++ b/skills/create-dataflow-model/references/java.md @@ -1,4 +1,4 @@ -# create-dataflow-approximation — Java / JVM +# create-dataflow-model — Java / JVM The JVM procedure, keyed to the body's steps. @@ -8,12 +8,12 @@ The JVM procedure, keyed to the body's steps. An app-internal method sits in the project's own sources under ``. For a library method, the resolved jars are in `.opentaint/project/dependencies` — locate the class with `unzip -l | grep ` (dotted class name with `.` → `/`) and decompile it for readable source, or disassemble it with `javap -c -p -classpath ` when no decompiler is available. -### 2. Write the approximation source +### 2. Write the model source -Scaffold the approximation project with the same `--dependency` pins as the batch's test project (`.opentaint/test-projects//build.gradle.kts`): +Scaffold the model project with the same `--dependency` pins as the batch's test project (`.opentaint/test-projects//build.gradle.kts`): ```bash -opentaint approximation init .opentaint/dataflow/ \ +opentaint model init .opentaint/dataflow/ \ --dependency "io.projectreactor:reactor-core:3.8.5" ``` @@ -22,7 +22,7 @@ When a model needs a library the project doesn't pin yet, add it to the project' Create Java files under `.opentaint/dataflow//src/main/java` — one `@Approximate` class per target class. `@Approximate(TargetClass.class)` binds a model to exactly that class, so target the EXACT class the analyzer dropped — the dropped FQN reflects how the call resolved: an interface-typed receiver (`Map m = ...; m.computeIfAbsent(...)`) drops `java.util.Map#computeIfAbsent`, a concrete one (`new HashMap<>()`) drops `java.util.HashMap#computeIfAbsent`. An interface is a valid target — write `@Approximate` on it exactly as on a concrete class, modelling whichever FQN the analyzer dropped. Reach the real object with `(TargetClass) (Object) this`, put functional-interface parameters behind `@ArgumentTypeContext`, and branch with `OpentaintNdUtil.nextBool()` so the analyzer walks both paths. Never leave a body empty. ```java -package com.example.approximations.batchpkg; // per-batch package (e.g. ...approximations.cn_hutool_001) — see the globally-unique rule below +package com.example.models.batchpkg; // per-batch package (e.g. ...models.cn_hutool_001) — see the globally-unique rule below import org.opentaint.ir.approximation.annotation.Approximate; import org.opentaint.jvm.dataflow.approximations.ArgumentTypeContext; @@ -69,15 +69,15 @@ public class Mono { ### 3. Test against the test project -Run `test approximation run` over the compiled test project applying this batch's sources, iterate the sources until the samples pass: +Run `test model run` over the compiled test project applying this batch's sources, iterate the sources until the samples pass: ```bash -opentaint test approximation run .opentaint/test-compiled/ \ +opentaint test model run .opentaint/test-compiled/ \ -o .opentaint/test-results/ \ - --dataflow-approximations .opentaint/dataflow/ + --dataflow-models .opentaint/dataflow/ ``` -`test approximation run` applies its own bundled fixed source→sink rule automatically — you don't author or pass one. The CLI builds the approximation project first. if compilation fails it reports the errors and aborts before the tests. A positive sample is a `falseNegative` until the model propagates taint. Read the result with the bundled script — it prints the pass/fail counts and names each failing sample, so you don't parse the JSON by hand: +`test model run` applies its own bundled fixed source→sink rule automatically — you don't author or pass one. The CLI builds the model project first. if compilation fails it reports the errors and aborts before the tests. A positive sample is a `falseNegative` until the model propagates taint. Read the result with the bundled script — it prints the pass/fail counts and names each failing sample, so you don't parse the JSON by hand: ```bash uv run /scripts/check-test-result.py @@ -92,7 +92,7 @@ Fix by the verdict it reports: | Pattern | Usage | |---|---| -| `@Approximate(TargetClass.class)` | Link the approximation to its target class. Must be on the compile classpath (a pinned dependency or a JDK type) | +| `@Approximate(TargetClass.class)` | Link the model to its target class. Must be on the compile classpath (a pinned dependency or a JDK type) | | `(TargetClass) (Object) this` | Cast to reach the real object's methods | | `@ArgumentTypeContext` | On lambda / functional-interface parameters | | `OpentaintNdUtil.nextBool()` | Non-deterministic branch — the analyzer considers both paths | @@ -100,11 +100,11 @@ Fix by the verdict it reports: ## Constraints - Java 8 source compatibility -- Put the `@Approximate` classes in a neutral package (e.g. `com.example.approximations`) — never the target library's own package. Inside the library's package every bare FQN resolves to your approximation's non-generic class instead of the real type, breaking compilation wholesale -- Namespace the package PER BATCH (`com.example.approximations.`, e.g. `...approximations.cn_hutool_001`). Every batch is loaded together into one shared index, and approximation class FQCNs must be GLOBALLY unique across all batches — two batches that both model a `…$Nested` (or any recurring simple name) in the bare `com.example.approximations` package collide on `com.example.approximations.Nested` and crash the whole scan at config-load. A per-batch package guarantees uniqueness. -- One approximation class per target class — a strict global bijection enforced at load: each approximation FQCN maps to exactly one target and vice-versa; duplicates or a reused FQCN across batches throw `IllegalArgumentException` +- Put the `@Approximate` classes in a neutral package (e.g. `com.example.models`) — never the target library's own package. Inside the library's package every bare FQN resolves to your model's non-generic class instead of the real type, breaking compilation wholesale +- Namespace the package PER BATCH (`com.example.models.`, e.g. `...models.cn_hutool_001`). Every batch is loaded together into one shared index, and model class FQCNs must be GLOBALLY unique across all batches — two batches that both model a `…$Nested` (or any recurring simple name) in the bare `com.example.models` package collide on `com.example.models.Nested` and crash the whole scan at config-load. A per-batch package guarantees uniqueness. +- One model class per target class — a strict global bijection enforced at load: each model FQCN maps to exactly one target and vice-versa; duplicates or a reused FQCN across batches throw `IllegalArgumentException` - Method signatures must match the target class methods exactly -- Built-in dataflow approximations are first-priority and presumed correct — you cannot override them locally -- A custom dataflow approximation overrides a passThrough for the same method — use dataflow as the fallback when a faithful passThrough cannot express or restore the propagation -- Built-in passThrough approximations, by contrast, a user config on the same method overrides. So a coarse built-in on a *related* method — a driver that invokes the callback you modelled, a wrapper the flow passes through — can silently suppress an otherwise-correct model. When a faithful model doesn't fire, suspect a built-in on a neighbouring method and model/override that method instead; debug-rule shows which model actually applied +- Built-in dataflow models are first-priority and presumed correct — you cannot override them locally +- A custom dataflow model overrides a passThrough for the same method — use dataflow as the fallback when a faithful passThrough cannot express or restore the propagation +- Built-in passThrough models, by contrast, a user config on the same method overrides. So a coarse built-in on a *related* method — a driver that invokes the callback you modelled, a wrapper the flow passes through — can silently suppress an otherwise-correct model. When a faithful model doesn't fire, suspect a built-in on a neighbouring method and model/override that method instead; debug-rule shows which model actually applied - Don't unpack or grep the analyzer JAR for built-in models or signatures — its internals aren't a stable API; go through the CLI diff --git a/skills-templates/create-dataflow-approximation/scripts/check-test-result.py b/skills/create-dataflow-model/scripts/check-test-result.py similarity index 93% rename from skills-templates/create-dataflow-approximation/scripts/check-test-result.py rename to skills/create-dataflow-model/scripts/check-test-result.py index 2ff4c0237e..213feb6f0b 100644 --- a/skills-templates/create-dataflow-approximation/scripts/check-test-result.py +++ b/skills/create-dataflow-model/scripts/check-test-result.py @@ -5,7 +5,7 @@ Summarize an OpenTaint test-result.json so the agent never parses it by hand. Prints pass/fail counts and names every failing sample by verdict. Exit 0 when all samples pass, 1 when any fail, 2 on a bad/missing file. -Works for both `test rule run` and `test approximation run` outputs (same shape). +Works for both `test rule run` and `test model run` outputs (same shape). """ import json import sys @@ -33,7 +33,7 @@ def names(v): def resolve(arg): # accept either a full path to a test-result.json or a shorthand id under - # .opentaint/test-results/: a rule `/` or an approximation `` + # .opentaint/test-results/: a rule `/` or a model `` if arg.endswith("test-result.json"): return Path(arg) return Path(".opentaint/test-results") / arg / "test-result.json" diff --git a/skills/create-pass-through-approximation/SKILL.md b/skills/create-pass-through-model/SKILL.md similarity index 84% rename from skills/create-pass-through-approximation/SKILL.md rename to skills/create-pass-through-model/SKILL.md index 826c70cebd..77a2cd1fa0 100644 --- a/skills/create-pass-through-approximation/SKILL.md +++ b/skills/create-pass-through-model/SKILL.md @@ -1,15 +1,15 @@ --- -name: create-pass-through-approximation -description: Model a method's taint propagation as a passThrough approximation. Use for a dropped method whose propagation is simple copying +name: create-pass-through-model +description: Model a method's taint propagation as a passThrough model. Use for a dropped method whose propagation is simple copying license: Apache-2.0 metadata: author: opentaint version: "0.3.0" --- -# Skill: Create PassThrough Approximation +# Skill: Create PassThrough Model -Model a dropped method's taint propagation as a passThrough approximation — a config that tells the engine how data moves from a method's inputs to its outputs, so a flow the analyzer lost through the opaque call is restored. +Model a dropped method's taint propagation as a passThrough model — a config that tells the engine how data moves from a method's inputs to its outputs, so a flow the analyzer lost through the opaque call is restored. ## Inputs @@ -17,7 +17,7 @@ Provided by the caller, fall back to the default value when omitted. Ask back on - `project-root` (optional) — root of the target project. Opentaint keeps all analysis artifacts under the fixed `/.opentaint/` directory, so every `.opentaint/...` path below resolves there. Default: current directory - `language` (required) — target language for this project and language-specific instructions -- `batch` (required) — the batch id; its `passthrough` entries live in `.opentaint/tracking/approximations/.yaml`, and you append the ones you build to that file's `build.done` +- `batch` (required) — the batch id; its `passthrough` entries live in `.opentaint/tracking/models/.yaml`, and you append the ones you build to that file's `build.done` - `methods` (optional) — a specific `{ method, signature }` subset to (re)work instead of the whole passthrough bucket, when the caller needs only those ## Workflow @@ -52,7 +52,7 @@ Short and concise report of what was done ## Tracking -`.opentaint/tracking/approximations/.yaml` — one batch's callable classification, `` the plan's filename stem. Every callable sits in exactly one verdict bucket, keyed with the exact language-specific `method` and `signature` from the plan so distinct variants stay separate: +`.opentaint/tracking/models/.yaml` — one batch's callable classification, `` the plan's filename stem. Every callable sits in exactly one verdict bucket, keyed with the exact language-specific `method` and `signature` from the plan so distinct variants stay separate: - `passthrough`, `dataflow` — modeled carriers; each entry `{ method, signature }` - `skipped` — terminal non-carriers; each `{ method, signature, reason }` - `engine_issues` — a separate bucket for carriers the engine provably can't propagate (built but still dropped); each `{ method, signature, reason }`. Terminal and treated just like `skipped` — the only difference is the reason. `merge-skipped` carries it into `skipped.yaml` as its own `engine_issues` group alongside the regular skipped `methods`. @@ -78,7 +78,7 @@ This skill only appends each cleanly-built method to `build.done` as `{ method, ## Constraints -OpenTaint is a whole-program, interprocedural, field-sensitive alias analysis engine. It already propagates through visible application code, calls, aliases, and individual fields; custom rules and approximations model only the assigned source, sink, or opaque-method boundary. Compile-time constants and literals carry no taint, so a source or carrier whose output is only a constant introduces nothing. +OpenTaint is a whole-program, interprocedural, field-sensitive alias analysis engine. It already propagates through visible application code, calls, aliases, and individual fields; custom rules and models cover only the assigned source, sink, or opaque-method boundary. Compile-time constants and literals carry no taint, so a source or carrier whose output is only a constant introduces nothing. - Model one function per rule — never a regex/wildcard matcher or an all-arguments position to cover many at once; over-modeling copies taint through methods you never vetted and manufactures false positives - Keep produced configs comment-free diff --git a/skills/create-pass-through-approximation/references/java.md b/skills/create-pass-through-model/references/java.md similarity index 99% rename from skills/create-pass-through-approximation/references/java.md rename to skills/create-pass-through-model/references/java.md index 238e762c50..093d952956 100644 --- a/skills/create-pass-through-approximation/references/java.md +++ b/skills/create-pass-through-model/references/java.md @@ -1,4 +1,4 @@ -# create-pass-through-approximation — Java / JVM +# create-pass-through-model — Java / JVM ## Workflow diff --git a/skills/create-rule/SKILL.md b/skills/create-rule/SKILL.md index 9f8ae12700..22bf79a8c3 100644 --- a/skills/create-rule/SKILL.md +++ b/skills/create-rule/SKILL.md @@ -9,7 +9,7 @@ metadata: # Skill: Create Rule -OpenTaint is a whole-program, interprocedural, field-sensitive alias analysis engine. It already propagates through visible application code, calls, aliases, and individual fields; custom rules and approximations model only the assigned source, sink, or opaque-method boundary. Compile-time constants and literals carry no taint, so a source or carrier whose output is only a constant introduces nothing. +OpenTaint is a whole-program, interprocedural, field-sensitive alias analysis engine. It already propagates through visible application code, calls, aliases, and individual fields; custom rules and models cover only the assigned source, sink, or opaque-method boundary. Compile-time constants and literals carry no taint, so a source or carrier whose output is only a constant introduces nothing. A unit names the source or sink members to detect. Identify an existing built-in or project library rule when it already implements the boundary, otherwise author a custom rule, then verify the unit against its test project. Every selected or created source rule must carry the unit's `untrusted-data-source` tag, and every selected or created sink rule must carry its unit group's `*-sink` tag. Production joins use those tags and are assembled later only when a new sink tag needs one. @@ -55,10 +55,10 @@ Run the rule tests directly as a foreground, blocking command and wait for exit opentaint test rule run .opentaint/test-compiled// \ -o .opentaint/test-results// \ --ruleset .opentaint/rules --ruleset .opentaint/test-projects///test-rules \ - --passthrough-approximations .opentaint/pass-through + --passthrough-models .opentaint/pass-through ``` -`test rule run` auto-loads the built-in rules, so pass only your custom rulesets. Apply the passthrough approximations as-is, an empty one is harmless. Read the result with the bundled script — it prints the pass/fail counts and names the failing samples, so you never parse the JSON by hand: +`test rule run` auto-loads the built-in rules, so pass only your custom rulesets. Apply the passthrough models as-is, an empty one is harmless. Read the result with the bundled script — it prints the pass/fail counts and names the failing samples, so you never parse the JSON by hand: ```bash uv run /scripts/check-test-result.py / @@ -87,7 +87,7 @@ A positive that won't pass after ~3 rule fixes may have a cause no rule edit can - the implementing lib rule ids, identifying which were built-in and which were created, a one-line test summary for the side, and the exact `test rule run` command used - if blocked at step 5: `stages.tests_passing` left pending, and the cause — - - a dropped library method on the failing sample's flow → the methods that need a model, to be approximated before a re-dispatch + - a dropped library method on the failing sample's flow → the methods that need a model, to be modeled before a re-dispatch - nothing dropped and no clear rule cause → non-convergence, for the orchestrator to intervene ## Tracking @@ -136,5 +136,5 @@ stages: ## Gotchas - If you think a test project is wrong — hand it back upstream -- A positive that won't pass because a library method drops taint is not a rule bug — surface it for approximation (per step 5), don't broaden the rule to force it +- A positive that won't pass because a library method drops taint is not a rule bug — surface it for model (per step 5), don't broaden the rule to force it - Keep produced rule files comment-free diff --git a/skills/create-rule/references/debugging.md b/skills/create-rule/references/debugging.md index e6d8f8ad17..386cba6953 100644 --- a/skills/create-rule/references/debugging.md +++ b/skills/create-rule/references/debugging.md @@ -15,8 +15,8 @@ opentaint test rule reachability \ - `` — the one rule whose sample routes taint through the code under test (`.yaml:`). One rule per run — across many rules the trace is unusably huge. Its library `refs` are collected automatically - read the sibling `/debug-ifds-fact-reachability.sarif`, not the `-o` file. The `-o` SARIF only shows default scan output. The sibling holds the per-instruction facts that show where taint dies -- apply the approximations the failing run used so the trace matches it: `--passthrough-approximations ` and/or `--dataflow-approximations `. Taint dying at an approximated call then means that approximation isn't propagating -- debug the exact run that showed the problem — same model, rulesets, approximation dirs — or you're debugging something else +- apply the models the failing run used so the trace matches it: `--passthrough-models ` and/or `--dataflow-models `. Taint dying at a modeled call then means that model isn't propagating +- debug the exact run that showed the problem — same model, rulesets, model dirs — or you're debugging something else ## Reading the trace @@ -35,7 +35,7 @@ When a positive won't pass and the suspicion is a library method on its flow dro opentaint scan --project-model .opentaint/test-compiled// \ -o .opentaint/test-results///diag.sarif \ --ruleset builtin --ruleset .opentaint/rules --ruleset .opentaint/test-projects///test-rules \ - --passthrough-approximations .opentaint/pass-through \ + --passthrough-models .opentaint/pass-through \ --track-external-methods ``` diff --git a/skills/create-rule/scripts/check-test-result.py b/skills/create-rule/scripts/check-test-result.py index 4fd13e62f7..00f78b70d5 100644 --- a/skills/create-rule/scripts/check-test-result.py +++ b/skills/create-rule/scripts/check-test-result.py @@ -5,7 +5,7 @@ Summarize an OpenTaint test-result.json so the agent never parses it by hand. Prints pass/fail counts and names every failing sample by verdict. Exit 0 when all samples pass, 1 when any fail, 2 on a bad/missing file. -Works for both `test rule run` and `test approximation run` outputs (same shape). +Works for both `test rule run` and `test model run` outputs (same shape). """ import json import sys @@ -33,7 +33,7 @@ def names(v): def resolve(arg): # accept either a full path to a test-result.json or a shorthand id under - # .opentaint/test-results/: a rule `/` or an approximation `` + # .opentaint/test-results/: a rule `/` or a model `` if arg.endswith("test-result.json"): return Path(arg) return Path(".opentaint/test-results") / arg / "test-result.json" diff --git a/skills/create-test-project/SKILL.md b/skills/create-test-project/SKILL.md index 7c85f6d82c..d7120bcf25 100644 --- a/skills/create-test-project/SKILL.md +++ b/skills/create-test-project/SKILL.md @@ -1,6 +1,6 @@ --- name: create-test-project -description: Create an OpenTaint test project with positive/negative samples for verifying a rule or approximation. Use when a rule or approximation needs a test project to check against +description: Create an OpenTaint test project with positive/negative samples for verifying a rule or model. Use when a rule or model needs a test project to check against license: Apache-2.0 metadata: author: opentaint @@ -9,7 +9,7 @@ metadata: # Skill: Create Test Project -Build a minimal compiled test project whose samples reproduce the flow a rule or approximation is verified against. A sample routes data between a real library method and the generic taint marker the scaffold provides, with the one verdict it must produce — a positive that must flag, a negative that must not. The compiled model is the deliverable; its sample sources sit alongside it. +Build a minimal compiled test project whose samples reproduce the flow a rule or model is verified against. A sample routes data between a real library method and the generic taint marker the scaffold provides, with the one verdict it must produce — a positive that must flag, a negative that must not. The compiled model is the deliverable; its sample sources sit alongside it. ## Inputs @@ -19,15 +19,15 @@ Provided by the caller, fall back to the default value when omitted. Ask back on - `language` (required) — target language for this project and language-specific instructions - `type` (required) — what this project verifies, selecting the sample style and the identifying inputs below: `rule-source`, `rule-sink`, or `dataflow` - for `rule-source` / `rule-sink` — `unit`: the unit id; its members and `dependencies` come from `.opentaint/tracking/rules/sources|sinks/.yaml` -- for `dataflow` — `batch`: the batch whose `.opentaint/tracking/approximations/.yaml` provides the dataflow methods to exercise and their `dependencies` +- for `dataflow` — `batch`: the batch whose `.opentaint/tracking/models/.yaml` provides the dataflow methods to exercise and their `dependencies` -The project folder `` is that identifier — the `unit` for a rule side, the `batch` for a dataflow approximation. +The project folder `` is that identifier — the `unit` for a rule side, the `batch` for a dataflow model. ## Workflow ### 1. Scaffold the project -The scaffold command and sample form are language-specific — read the reference for your `type`: `references/-rule.md` for `rule-source` / `rule-sink`, or `references/-approximation.md` for `dataflow` (one self-contained reference, no need to read the other). Scaffold the project for the `type`, passing each of the unit's `dependencies` at its pinned version. The scaffold provides the generic taint marker and the fixed test rule the samples run against, so you author only the samples. If the `` project already exists — re-invoked because its surface grew or a dependency moved — extend it instead: add the missing samples and recompile rather than scaffolding fresh. The init command is in that reference. +The scaffold command and sample form are language-specific — read the reference for your `type`: `references/-rule.md` for `rule-source` / `rule-sink`, or `references/-model.md` for `dataflow` (one self-contained reference, no need to read the other). Scaffold the project for the `type`, passing each of the unit's `dependencies` at its pinned version. The scaffold provides the generic taint marker and the fixed test rule the samples run against, so you author only the samples. If the `` project already exists — re-invoked because its surface grew or a dependency moved — extend it instead: add the missing samples and recompile rather than scaffolding fresh. The init command is in that reference. ### 2. Write the samples @@ -77,7 +77,7 @@ Short and concise report of what was done This skill writes only the test-project stage back: - a rule side → `stages.test_project: done` in the source or sink unit -- a dataflow approximation → one `build.test_project` entry per method in the batch file, `status: done` for a method whose sample made it into the project, `status: failed` for one no sample could be written for (excluded) +- a dataflow model → one `build.test_project` entry per method in the batch file, `status: done` for a method whose sample made it into the project, `status: failed` for one no sample could be written for (excluded) `.opentaint/tracking/rules/sources/.yaml` — one source unit per package (a dependency can span several packages), the file named for that package with `.` → `-`. `tag` is always the reusable `untrusted-data-source` group. `dependencies` names the dependency the package comes from, `sources` each an entry point `{ method, signature, note, rule_id }` (`method` and `signature` use the exact language-specific identifiers from the plan), `stages` tracks the unit through rule authoring, and a `blocker` string is added under it when the unit can't be made to pass. Keep it clear from comments @@ -106,7 +106,7 @@ stages: tests_passing: pending ``` -`.opentaint/tracking/approximations/.yaml` — one batch's callable classification, `` the plan's filename stem. Every callable sits in exactly one verdict bucket, keyed with the exact language-specific `method` and `signature` from the plan so distinct variants stay separate: +`.opentaint/tracking/models/.yaml` — one batch's callable classification, `` the plan's filename stem. Every callable sits in exactly one verdict bucket, keyed with the exact language-specific `method` and `signature` from the plan so distinct variants stay separate: - `passthrough`, `dataflow` — modeled carriers; each entry `{ method, signature }` - `skipped` — terminal non-carriers; each `{ method, signature, reason }` - `engine_issues` — a separate bucket for carriers the engine provably can't propagate (built but still dropped); each `{ method, signature, reason }`. Terminal and treated just like `skipped` — the only difference is the reason. `merge-skipped` carries it into `skipped.yaml` as its own `engine_issues` group alongside the regular skipped `methods`. @@ -130,6 +130,6 @@ build: ## Constraints -OpenTaint is a whole-program, interprocedural, field-sensitive alias analysis engine. It already propagates through visible application code, calls, aliases, and individual fields; custom rules and approximations model only the assigned source, sink, or opaque-method boundary. Compile-time constants and literals carry no taint, so a source or carrier whose output is only a constant introduces nothing. +OpenTaint is a whole-program, interprocedural, field-sensitive alias analysis engine. It already propagates through visible application code, calls, aliases, and individual fields; custom rules and models cover only the assigned source, sink, or opaque-method boundary. Compile-time constants and literals carry no taint, so a source or carrier whose output is only a constant introduces nothing. - One `` folder per unit — never write into another unit's test project, so concurrent agents don't race diff --git a/skills/create-test-project/references/java-approximation.md b/skills/create-test-project/references/java-approximation.md deleted file mode 100644 index 8cee6ff26b..0000000000 --- a/skills/create-test-project/references/java-approximation.md +++ /dev/null @@ -1,69 +0,0 @@ -# create-test-project — Java approximation test project - -Language reference for a `dataflow` type, keyed to the body's steps. - -## Workflow - -### 1. Scaffold the project - -`opentaint test approximation init ` scaffolds the Gradle build plus `Taint.java` and the fixed `approximation-rule.yaml` the harness applies. Pass each lib as a `--dependency` at its exact version from the app's dependency management (`.opentaint/project/sources/**/pom.xml`) verbatim — including timestamped snapshots — not a guess. The approximation project scaffolded later takes the same pins, so the samples and the models they exercise compile against one version. - -```bash -opentaint test approximation init .opentaint/test-projects/ \ - --dependency "io.projectreactor:reactor-core:3.8.5" -``` - -### 2. Write the samples - -Write one sample per method in the batch's `dataflow` bucket, across all its classes. Each `dataflow` entry records the method's `signature` (JVM descriptor) — reuse it so your sample matches. Look at how the method is actually called in the project to shape the sample and pick the receiver type. Each sample is a plain public method under `src/main/java/test/` — you don't author a rule; the scaffold's fixed `approximation-rule.yaml` is what the harness applies. The sample just routes `Taint.source()` through the approximated method into `Taint.sink(...)`. Its verdict is recorded in `rule-test.yaml` (below), not on the method. - -Mark each method's test-project status (per Tracking): `done` once its sample is in the project. If no sample can be written for a method, exclude it, mark it `failed`, and note it in the summary — don't let one unmodelable method block the rest. - -Positive sample — routes `Taint.source()` through the approximated method into `Taint.sink(...)`, one per method being approximated. Start every sample with `String tainted = Taint.source();`. When the approximated method takes a non-String input, cast the tainted string to that type so the taint fact reaches the method as the right type — the analyzer tracks the fact, not the runtime value. - -```java -package test; - -import java.util.HashMap; -import java.util.Map; - -public class ApproximationSamples { - - public void taintReachesSink() { - String tainted = Taint.source(); - Map cache = new HashMap<>(); - String routed = cache.computeIfAbsent(tainted, k -> k); // the approximated method - Taint.sink(routed); - } -} -``` - -Negative sample — only when the approximated method stores its input in the object. The analyzer is field-sensitive: taint set on one field doesn't surface from another. Write a negative sample that stores tainted data into one field and reads a different one — a field-precise model keeps that read clean, so the sample must not fire. This is what forces a precise approximation instead of one that taints the whole object. For plain propagation the positive alone proves the model — skip the negative. - -```java - public void taintStaysInItsField() { - UserBean bean = new UserBean(); - bean.setName(Taint.source()); // taint stored in the `name` field - Taint.sink(bean.getEmail()); // a different field — must stay clean - } -``` - -Register the verdicts — write `rule-test.yaml` at the test-project root (next to `build.gradle.kts`), one entry under the fixed rule, listing each sample method by `test.#`. `positive` samples must flag, `negative` must not: - -```yaml -tests: - - rule-id: approximation-rule.yaml#approximation-rule - positive: - - test.ApproximationSamples#taintReachesSink - negative: - - test.ApproximationSamples#taintStaysInItsField -``` - -Note — a sample's receiver type fixes the dropped method's fully-qualified name, and the later approximation targets that exact class, so mirror the real call's receiver type. The same call off an interface-typed receiver (`Map m`, e.g. a parameter) resolves to `java.util.Map#computeIfAbsent`, off a concrete one (`new HashMap<>()`) to `java.util.HashMap#computeIfAbsent` — match whichever the real flow uses. - -When the approximated method hands the data to a callback instead of returning it, put the sink inside the callback body: `obj.method(c -> Taint.sink(tainted))`. Don't stash the value in a captured local or array and sink it after the call. - -## Constraints - -- The scaffold defaults to Java 8. A sample using a library that needs Java 17/21 must bump `source/targetCompatibility` and set `release` on the running JDK -- Diagnose a compile failure with `gradle compileJava --console=plain` in the sub-project — no gradlew is generated diff --git a/skills/create-test-project/references/java-model.md b/skills/create-test-project/references/java-model.md new file mode 100644 index 0000000000..c3d62b94c8 --- /dev/null +++ b/skills/create-test-project/references/java-model.md @@ -0,0 +1,69 @@ +# create-test-project — Java model test project + +Language reference for a `dataflow` type, keyed to the body's steps. + +## Workflow + +### 1. Scaffold the project + +`opentaint test model init ` scaffolds the Gradle build plus `Taint.java` and the fixed `model-rule.yaml` the harness applies. Pass each lib as a `--dependency` at its exact version from the app's dependency management (`.opentaint/project/sources/**/pom.xml`) verbatim — including timestamped snapshots — not a guess. The model project scaffolded later takes the same pins, so the samples and the models they exercise compile against one version. + +```bash +opentaint test model init .opentaint/test-projects/ \ + --dependency "io.projectreactor:reactor-core:3.8.5" +``` + +### 2. Write the samples + +Write one sample per method in the batch's `dataflow` bucket, across all its classes. Each `dataflow` entry records the method's `signature` (JVM descriptor) — reuse it so your sample matches. Look at how the method is actually called in the project to shape the sample and pick the receiver type. Each sample is a plain public method under `src/main/java/test/` — you don't author a rule; the scaffold's fixed `model-rule.yaml` is what the harness applies. The sample just routes `Taint.source()` through the modeled method into `Taint.sink(...)`. Its verdict is recorded in `rule-test.yaml` (below), not on the method. + +Mark each method's test-project status (per Tracking): `done` once its sample is in the project. If no sample can be written for a method, exclude it, mark it `failed`, and note it in the summary — don't let one unmodelable method block the rest. + +Positive sample — routes `Taint.source()` through the modeled method into `Taint.sink(...)`, one per method being modeled. Start every sample with `String tainted = Taint.source();`. When the modeled method takes a non-String input, cast the tainted string to that type so the taint fact reaches the method as the right type — the analyzer tracks the fact, not the runtime value. + +```java +package test; + +import java.util.HashMap; +import java.util.Map; + +public class ModelSamples { + + public void taintReachesSink() { + String tainted = Taint.source(); + Map cache = new HashMap<>(); + String routed = cache.computeIfAbsent(tainted, k -> k); // the modeled method + Taint.sink(routed); + } +} +``` + +Negative sample — only when the modeled method stores its input in the object. The analyzer is field-sensitive: taint set on one field doesn't surface from another. Write a negative sample that stores tainted data into one field and reads a different one — a field-precise model keeps that read clean, so the sample must not fire. This is what forces a precise model instead of one that taints the whole object. For plain propagation the positive alone proves the model — skip the negative. + +```java + public void taintStaysInItsField() { + UserBean bean = new UserBean(); + bean.setName(Taint.source()); // taint stored in the `name` field + Taint.sink(bean.getEmail()); // a different field — must stay clean + } +``` + +Register the verdicts — write `rule-test.yaml` at the test-project root (next to `build.gradle.kts`), one entry under the fixed rule, listing each sample method by `test.#`. `positive` samples must flag, `negative` must not: + +```yaml +tests: + - rule-id: model-rule.yaml#model-rule + positive: + - test.ModelSamples#taintReachesSink + negative: + - test.ModelSamples#taintStaysInItsField +``` + +Note — a sample's receiver type fixes the dropped method's fully-qualified name, and the later model targets that exact class, so mirror the real call's receiver type. The same call off an interface-typed receiver (`Map m`, e.g. a parameter) resolves to `java.util.Map#computeIfAbsent`, off a concrete one (`new HashMap<>()`) to `java.util.HashMap#computeIfAbsent` — match whichever the real flow uses. + +When the modeled method hands the data to a callback instead of returning it, put the sink inside the callback body: `obj.method(c -> Taint.sink(tainted))`. Don't stash the value in a captured local or array and sink it after the call. + +## Constraints + +- The scaffold defaults to Java 8. A sample using a library that needs Java 17/21 must bump `source/targetCompatibility` and set `release` on the running JDK +- Diagnose a compile failure with `gradle compileJava --console=plain` in the sub-project — no gradlew is generated diff --git a/skills/debug-rule/SKILL.md b/skills/debug-rule/SKILL.md index 0907d496d4..2b28778e7a 100644 --- a/skills/debug-rule/SKILL.md +++ b/skills/debug-rule/SKILL.md @@ -1,6 +1,6 @@ --- name: debug-rule -description: Debug a rule or approximation that behaves unexpectedly by tracing where taint is dropped. Use when its samples won't pass after repeated attempts, or it passes tests but is wrong on a real scan +description: Debug a rule or model that behaves unexpectedly by tracing where taint is dropped. Use when its samples won't pass after repeated attempts, or it passes tests but is wrong on a real scan license: Apache-2.0 metadata: author: opentaint @@ -9,29 +9,29 @@ metadata: # Skill: Debug Rule -Diagnose why a rule or approximation behaves unexpectedly on a model by tracing where taint is dropped, and decide who owns the fix: the rule, a missing library model, or the engine. +Diagnose why a rule or model behaves unexpectedly on a model by tracing where taint is dropped, and decide who owns the fix: the rule, a missing library model, or the engine. ## Inputs Provided by the caller, fall back to the default value when omitted. Ask back only when a required input is missing and has no sensible default - `project-root` (optional) — root of the target project. Opentaint keeps all analysis artifacts under the fixed `/.opentaint/` directory, so every `.opentaint/...` path below resolves there. Default: current directory -- `rule` (required) — the one rule whose sample or flow routes taint through the code under test, as `.yaml:`. For an approximation, the rule whose sample routes taint through the approximated method +- `rule` (required) — the one rule whose sample or flow routes taint through the code under test, as `.yaml:`. For a model, the rule whose sample routes taint through the modeled method - `model` (required) — the project model where the behavior shows up ## Workflow ### 1. Reproduce and localize the kill -Reproduce the exact run that showed the problem — same `model`, rulesets, and applied approximation dirs — and trace where taint dies with a fact-reachability run. Run it directly as a foreground, blocking command and wait for exit — never background it or use Monitor: +Reproduce the exact run that showed the problem — same `model`, rulesets, and applied model dirs — and trace where taint dies with a fact-reachability run. Run it directly as a foreground, blocking command and wait for exit — never background it or use Monitor: ```bash opentaint test rule reachability \ --project-model \ -o /report.sarif \ --ruleset builtin --ruleset .opentaint/rules \ - --passthrough-approximations .opentaint/pass-through \ - --dataflow-approximations .opentaint/dataflow + --passthrough-models .opentaint/pass-through \ + --dataflow-models .opentaint/dataflow ``` `` is `.opentaint/test-results/` for a test model, `.opentaint/results` for the main scan. The per-instruction facts are in the sibling `/debug-ifds-fact-reachability.sarif`, not the `-o` file — the `-o` SARIF only shows whether the rule fired. Read that sibling to find the kill: @@ -39,19 +39,19 @@ opentaint test rule reachability \ - a missed detection (a positive that won't pass, or a flow absent from a scan) — confirm a fact exists at the source; if none, the gap is in `pattern-sources`, not the flow. Otherwise walk the facts to the last instruction still carrying it and the first where it's gone — that gap is the kill - a spurious detection (a negative that fires) — the reverse: find where a fact appears with no tainted input reaching it -Trace the exact run that misbehaved — a different `model` or ruleset traces something else; taint dying at an approximated call means that approximation isn't propagating. When the flow is missed and the entry method may never be analyzed, rerun with `--entry-points ""`: a finding that appears only then is an entry-point-discovery problem, not dataflow. On Spring the flag is additive — auto-discovered endpoints stay and your method is added, so use it to force-include an endpoint the analyzer never starts from, not to narrow to one method. +Trace the exact run that misbehaved — a different `model` or ruleset traces something else; taint dying at a modeled call means that model isn't propagating. When the flow is missed and the entry method may never be analyzed, rerun with `--entry-points ""`: a finding that appears only then is an entry-point-discovery problem, not dataflow. On Spring the flag is additive — auto-discovered endpoints stay and your method is added, so use it to force-include an endpoint the analyzer never starts from, not to narrow to one method. ### 2. Classify the cause The killing instruction decides who owns the fix. An engine bug is by far the least likely — assume it last, only once the other two are ruled out; nearly every kill is a missing or wrong library model or a rule defect, both tedious to exclude but far more probable, and the tedium is no reason to jump to "engine". Three outcomes: -- the kill is at an external library method → a model issue. Cross-check `dropped-external-methods.yaml` from that run (a `--track-external-methods` scan regenerates it if absent): listed there means the method is unmodeled — the missing model is the cause, for the approximation stage to model. Not listed but a built-in claims to model it, yet taint dies here → that model is wrong for this case: a passThrough override applies at the rule level, so prefer one for the method; a dataflow override conflicts with built-ins at load, so fall back to a passThrough, or call it an engine issue when only a dataflow shape can express the propagation +- the kill is at an external library method → a model issue. Cross-check `dropped-external-methods.yaml` from that run (a `--track-external-methods` scan regenerates it if absent): listed there means the method is unmodeled — the missing model is the cause, for the model stage to model. Not listed but a built-in claims to model it, yet taint dies here → that model is wrong for this case: a passThrough override applies at the rule level, so prefer one for the method; a dataflow override conflicts with built-ins at load, so fall back to a passThrough, or call it an engine issue when only a dataflow shape can express the propagation - the kill is where the rule should have matched — a sanitizer misfires, a sink or source variant went unmatched → a rule defect, for rule authoring to fix - the kill is a plain instruction the engine must propagate through (assignment, cast, field read, an already-modeled call), with the rule correct and the model complete → an engine issue ### 3. Report the diagnosis -This skill diagnoses and routes the fix — it doesn't author the rule or approximation, or re-run the pipeline. Report the diagnosis per Output. +This skill diagnoses and routes the fix — it doesn't author the rule or model, or re-run the pipeline. Report the diagnosis per Output. ## Output diff --git a/skills/discover-attack-surface/SKILL.md b/skills/discover-attack-surface/SKILL.md index ed2337e0a3..46162e1a42 100644 --- a/skills/discover-attack-surface/SKILL.md +++ b/skills/discover-attack-surface/SKILL.md @@ -88,7 +88,7 @@ source: ## Constraints -OpenTaint is a whole-program, interprocedural, field-sensitive alias analysis engine. It already propagates through visible application code, calls, aliases, and individual fields; custom rules and approximations model only the assigned source, sink, or opaque-method boundary. Compile-time constants and literals carry no taint, so a source or carrier whose output is only a constant introduces nothing. +OpenTaint is a whole-program, interprocedural, field-sensitive alias analysis engine. It already propagates through visible application code, calls, aliases, and individual fields; custom rules and models cover only the assigned source, sink, or opaque-method boundary. Compile-time constants and literals carry no taint, so a source or carrier whose output is only a constant introduces nothing. - This stage finds only sources — the boundaries where untrusted data enters, sinks are found later from the taint frontier - Work only your own plan and the source units it maps to — never another agent's plan or unit, shared coverage/classification state, or `tags.yaml`. Plans partition their write ownership according to the language reference diff --git a/skills/orchestrate-stage/SKILL.md b/skills/orchestrate-stage/SKILL.md index e46ab21f91..91c13da7db 100644 --- a/skills/orchestrate-stage/SKILL.md +++ b/skills/orchestrate-stage/SKILL.md @@ -17,7 +17,7 @@ Match the stage keyword to its reference and read it fully: ``` sources → /references/sources.md -approx-round → /references/approx-round.md +model-round → /references/model-round.md sinks → /references/sinks.md triage → /references/triage.md poc → /references/poc.md @@ -51,11 +51,11 @@ Universal rules: - trust the returned summary; open an artifact only when the summary does not resolve the result - don't read a leaf skill's contents unless genuinely needed - never bundle multiple steps into one dispatch -- when a rule or approximation does not converge after its leaf's retries, load `/references/escalation.md`, settle that item, and continue. Only a blocker shared by every remaining item stops the stage +- when a rule or model does not converge after its leaf's retries, load `/references/escalation.md`, settle that item, and continue. Only a blocker shared by every remaining item stops the stage Fan-out and caps: -- `get_status.py --full` prints `global` and `heavy` caps; the heavy leaves in these stages are `create-rule` and `create-dataflow-approximation`. Never exceed either cap; reduce `global` by 1 for the rest of the stage when a leaf is rate-limited +- `get_status.py --full` prints `global` and `heavy` caps; the heavy leaves in these stages are `create-rule` and `create-dataflow-model`. Never exceed either cap; reduce `global` by 1 for the rest of the stage when a leaf is rate-limited - fan out independent units and dispatch the next queued unit as each slot frees - block on native agent completion. Never use Monitor, a background command, or resume-polling to wait inside this agent diff --git a/skills/orchestrate-stage/references/escalation.md b/skills/orchestrate-stage/references/escalation.md index 037fdf64aa..3dd67882cc 100644 --- a/skills/orchestrate-stage/references/escalation.md +++ b/skills/orchestrate-stage/references/escalation.md @@ -1,17 +1,17 @@ # Escalation -Use when a leaf reports a rule or approximation it could not make work after its retries, when the latest scan still drops an already-built approximation, or when MAIN hands off a full-project scan that finished its allowed retry/backstop without producing SARIF. +Use when a leaf reports a rule or model it could not make work after its retries, when the latest scan still drops an already-built model, or when MAIN hands off a full-project scan that finished its allowed retry/backstop without producing SARIF. ## Full-scan resource issue When the task supplies the failed scan `setup`, dispatch report-analyzer-issue. Inputs: -- `setup` — the exact scan command, project model, rulesets, approximation dirs, final memory bound, current commit, and scan log path +- `setup` — the exact scan command, project model, rulesets, model dirs, final memory bound, current commit, and scan log path -Expect `.opentaint/issues/.md`; return it to MAIN and stop this stage. There is no rule/approximation terminal mark for a scan-wide resource failure. +Expect `.opentaint/issues/.md`; return it to MAIN and stop this stage. There is no rule/model terminal mark for a scan-wide resource failure. -## Rule or approximation issue +## Rule or model issue Handle the reported item here instead of re-dispatching it blindly, and don't run debug-rule. @@ -22,7 +22,7 @@ A scan-flagged created rule that fails to load, false-positives/negatives, or be Dispatch report-analyzer-issue before marking the item terminal. Inputs: -- `artifact` — the rule's full id and ruleset, or the approximation target method(s) +- `artifact` — the rule's full id and ruleset, or the model target method(s) - `diagnosis` — the leaf's one-line cause - `name` (optional) — the known test-project unit/batch used for the failed check @@ -33,12 +33,12 @@ Expect back — `.opentaint/issues/.md` written. The stage orchestrator writes the terminal mark after the reporting leaf returns; the leaf leaves the item pending and only reports its diagnosis. So the stage stops returning to it: - source/sink rule unit → add one top-level `blocker` string to the known `.opentaint/tracking/rules//.yaml` -- approximation method → append it to the known owning batch's `engine_issues`, quoting method and signature: +- model method → append it to the known owning batch's `engine_issues`, quoting method and signature: ```yaml - { method: "cn.hutool.core.lang.Assert#notNull", signature: "(Ljava/lang/Object;Ljava/util/function/Supplier;)Ljava/lang/Object;", reason: "one-line engine limitation" } ``` -After an approximation mark, run `uv run /scripts/generate.py merge-skipped` so `approximations/skipped.yaml` reflects the terminal item. +After a model mark, run `uv run /scripts/generate.py merge-skipped` so `models/skipped.yaml` reflects the terminal item. -A malformed approximation is not immediately an engine issue: re-dispatch its authoring skill with `language` + `batch` and, when the scan identifies the affected methods, that `methods` subset. The leaf repairs the existing package config or dataflow source even when those methods are already in `build.done`; unrelated existing carriers stay unchanged. Finish with the project rescan as the next step. If the repaired carrier is still dropped, use the terminal flow above. +A malformed model is not immediately an engine issue: re-dispatch its authoring skill with `language` + `batch` and, when the scan identifies the affected methods, that `methods` subset. The leaf repairs the existing package config or dataflow source even when those methods are already in `build.done`; unrelated existing carriers stay unchanged. Finish with the project rescan as the next step. If the repaired carrier is still dropped, use the terminal flow above. diff --git a/skills-templates/orchestrate-stage/references/approx-round.md b/skills/orchestrate-stage/references/model-round.md similarity index 79% rename from skills-templates/orchestrate-stage/references/approx-round.md rename to skills/orchestrate-stage/references/model-round.md index 95c1b0375a..9624c6b6ee 100644 --- a/skills-templates/orchestrate-stage/references/approx-round.md +++ b/skills/orchestrate-stage/references/model-round.md @@ -1,6 +1,6 @@ -# Approximation round — classify + build +# Model round — classify + build -Model one frontier of library methods dropped by the latest project scan: classify each method into a batch bucket, then build its passthrough/dataflow carrier or settle it terminal. This is one round of the pipeline's approximation loop. Finish before the rescan; a later invocation handles the frontier exposed by that rescan. +Model one frontier of library methods dropped by the latest project scan: classify each method into a batch bucket, then build its passthrough/dataflow carrier or settle it terminal. This is one round of the pipeline's model loop. Finish before the rescan; a later invocation handles the frontier exposed by that rescan. ## Built-but-still-dropped methods @@ -27,13 +27,13 @@ Expect back — every method classified into the batch's `passthrough` / `datafl uv run /scripts/generate.py merge-skipped ``` -It merges every batch's `skipped` / `engine_issues` into `approximations/skipped.yaml` and prunes the consumed plans. +It merges every batch's `skipped` / `engine_issues` into `models/skipped.yaml` and prunes the consumed plans. ## Build the unbuilt batches `get_status.py` lists the batches with an unbuilt carrier by kind. Independent batches fan out in parallel. -- passthrough → dispatch create-pass-through-approximation +- passthrough → dispatch create-pass-through-model - `language` - `batch` - `methods` (optional) — only an explicitly assigned subset to redo @@ -41,7 +41,7 @@ It merges every batch's `skipped` / `engine_issues` into `approximations/skipped - `language` - `type: dataflow` - `batch` -- dataflow step 2, after its test project completes → dispatch create-dataflow-approximation +- dataflow step 2, after its test project completes → dispatch create-dataflow-model - `language` - `batch` - `methods` (optional) — only an explicitly assigned subset to redo @@ -50,4 +50,4 @@ Expect back — the built carriers appended to each batch's `build.done`; `get_s ## Stage gate -`get_status.py` names unclassified methods, plans, unbuilt batches, or built-but-still-dropped methods. Delete this round's dataflow `test-compiled/` models and finish when its methods are classified and built or terminal. If status reports `approximations built after the last scan`, report the pending rescan and stop. +`get_status.py` names unclassified methods, plans, unbuilt batches, or built-but-still-dropped methods. Delete this round's dataflow `test-compiled/` models and finish when its methods are classified and built or terminal. If status reports `models built after the last scan`, report the pending rescan and stop. diff --git a/skills/orchestrate-stage/references/sinks.md b/skills/orchestrate-stage/references/sinks.md index 265ca7d20f..6ed69de8f9 100644 --- a/skills/orchestrate-stage/references/sinks.md +++ b/skills/orchestrate-stage/references/sinks.md @@ -1,6 +1,6 @@ # Author sinks + assemble -Author the sink rules flagged into per-package units during approximation classification and join them to every relevant source. The final project rescan follows to surface findings. +Author the sink rules flagged into per-package units during model classification and join them to every relevant source. The final project rescan follows to surface findings. ## Sink lib rules diff --git a/skills/orchestrate-stage/scripts/_common.py b/skills/orchestrate-stage/scripts/_common.py index 5d4864533f..048424f8ff 100644 --- a/skills/orchestrate-stage/scripts/_common.py +++ b/skills/orchestrate-stage/scripts/_common.py @@ -15,7 +15,7 @@ ROOT = Path(".opentaint") MODEL = ROOT / "project" TRACKING = ROOT / "tracking" -APPROX = TRACKING / "approximations" +MODELS_TR = TRACKING / "models" RULES_TR = TRACKING / "rules" SOURCES_TR = RULES_TR / "sources" SINKS_TR = RULES_TR / "sinks" @@ -166,7 +166,7 @@ def git_head(): return None -# ---- approximation batch readers (shared by coverage + partition) ---- +# ---- model batch readers (shared by coverage + partition) ---- # a method counts classified once it sits in any batch classification bucket or in build.done CLASSIFIED_BUCKETS = ("passthrough", "dataflow", "skipped", "engine_issues") @@ -174,8 +174,8 @@ def git_head(): def batch_files(): - """Every approximation batch file (skipped.yaml is the merged view, not a batch).""" - return [Path(p) for p in sorted(glob.glob(str(APPROX / "*.yaml"))) + """Every model batch file (skipped.yaml is the merged view, not a batch).""" + return [Path(p) for p in sorted(glob.glob(str(MODELS_TR / "*.yaml"))) if Path(p).name != "skipped.yaml"] @@ -211,7 +211,7 @@ def classified_keys(): def skipped_keys(): """method+signature keys classified terminal — the `skipped` and `engine_issues` buckets of every batch. Terminal means the method will never build a working carrier, so it must not hold - the approximations phase pending even if it is still modeled and still dropped by the scan. + the models phase pending even if it is still modeled and still dropped by the scan. (skipped.yaml is the merged view of these same buckets, so reading the batches alone suffices.)""" out = set() for p in batch_files(): @@ -236,7 +236,7 @@ def modeled_entries(): def build_done_keys(): - """Overload-precise keys of every built approximation across batches.""" + """Overload-precise keys of every built model across batches.""" keys = set() for p in batch_files(): doc = load_yaml(p, {}) or {} diff --git a/skills/orchestrate-stage/scripts/generate.py b/skills/orchestrate-stage/scripts/generate.py index 53e6b4488d..fff5d7fc63 100644 --- a/skills/orchestrate-stage/scripts/generate.py +++ b/skills/orchestrate-stage/scripts/generate.py @@ -9,10 +9,10 @@ init bootstrap the .opentaint tree + state.yaml from the workflow flags tags refresh tracking/rules/tags.yaml from the active lib rules - partition analyze dropped external methods -> per-root batch plans (approximations) + partition analyze dropped external methods -> per-root batch plans (models) partition discover coverage.yaml's used members -> balanced discover plans mark-safe discover plans' verdicts -> classification.yaml ledger (+prune plans) - merge-skipped batch skipped/engine_issues -> approximations/skipped.yaml (+prune plans) + merge-skipped batch skipped/engine_issues -> models/skipped.yaml (+prune plans) findings results/report.sarif -> per-rule finding tracking files (idempotent) """ import argparse @@ -27,7 +27,7 @@ import yaml -from _common import (APPROX, DATAFLOW, DROPPED, +from _common import (MODELS_TR, DATAFLOW, DROPPED, FINDINGS_TR, MODEL, PASS_THROUGH, RESULTS, RULES, RULES_TR, SARIF, SINKS_TR, SOURCES_TR, TAGS, TRACKING, builtin_rules_root, class_of, classified_keys, @@ -35,20 +35,20 @@ git_head, ledger_verdicted_keys, load_yaml, member_key, package_of, strip_quotes) -ANALYZE_BUDGET = 20 # methods per approximation batch +ANALYZE_BUDGET = 20 # methods per model batch ANALYZE_MISC = 6 # roots with <= this many methods pool into one misc batch ROOT_DEPTH = 2 # library root = first 2 dotted segments DISCOVER_TARGET, DISCOVER_BAND = 50, 15 # project-used members per discover plan (~50, loose) DISCOVER_PLANS = RULES_TR / "plans" -APPROX_PLANS = APPROX / "plans" +MODEL_PLANS = MODELS_TR / "plans" # ---- init: bootstrap the working tree + state.yaml ---- # the durable directories a run writes into; the leaves/scripts mkdir on write, but seeding # them up front gives every stage a place to land and makes the empty tree self-describing. -INIT_DIRS = [TRACKING, APPROX, SOURCES_TR, SINKS_TR, FINDINGS_TR, +INIT_DIRS = [TRACKING, MODELS_TR, SOURCES_TR, SINKS_TR, FINDINGS_TR, RESULTS, RULES, PASS_THROUGH, DATAFLOW] STATE_DERIVED = ("model_commit", "build_jdk", "max_memory") # build/scan fill these, init preserves @@ -202,7 +202,7 @@ def _root_next_index(prefix): # owns, or the new plan's analyze agent would overwrite that batch. Continue past the highest # index any existing batch OR leftover plan already claims for this root. mx = 0 - for d in (APPROX, APPROX_PLANS): + for d in (MODELS_TR, MODEL_PLANS): for p in glob.glob(str(d / f"{prefix}-*.yaml")): m = re.match(rf"^{re.escape(prefix)}-(\d+)\.yaml$", Path(p).name) if m: @@ -211,7 +211,7 @@ def _root_next_index(prefix): def cmd_analyze(args): - regen_plans(APPROX_PLANS) + regen_plans(MODEL_PLANS) classified = classified_keys() rows = [r for r in dropped_entries() if member_key(r) not in classified] if not rows: @@ -243,7 +243,7 @@ def cmd_analyze(args): norm = {cls: sorted(v, key=lambda x: (x["method"], x.get("signature", ""))) for cls, v in sorted(scopes.items())} pid = f"{prefix}-{start + i:03d}" - path = APPROX_PLANS / f"{pid}.yaml" + path = MODEL_PLANS / f"{pid}.yaml" path.write_text(dump_yaml({"scopes": norm}), encoding="utf-8") paths.append(str(path)) for p in paths: @@ -448,17 +448,17 @@ def cmd_merge_skipped(args): # collects the `skipped` and `engine_issues` buckets of every batch into skipped.yaml, keeping # them as two separate groups — regular skips under `methods`, engine issues under `engine_issues`. docs = [load_yaml(p, {}) or {} for p in - (Path(x) for x in sorted(glob.glob(str(APPROX / "*.yaml")))) + (Path(x) for x in sorted(glob.glob(str(MODELS_TR / "*.yaml")))) if p.name != "skipped.yaml"] out = {"methods": _collect(docs, "skipped"), "engine_issues": _collect(docs, "engine_issues")} - (APPROX / "skipped.yaml").write_text(dump_yaml(out), encoding="utf-8") + (MODELS_TR / "skipped.yaml").write_text(dump_yaml(out), encoding="utf-8") print(f"skipped.yaml: {len(out['methods'])} methods, {len(out['engine_issues'])} engine_issues") - if not args.keep and APPROX_PLANS.is_dir(): - pruned = [p for p in glob.glob(str(APPROX_PLANS / "*.yaml"))] + if not args.keep and MODEL_PLANS.is_dir(): + pruned = [p for p in glob.glob(str(MODEL_PLANS / "*.yaml"))] for p in pruned: Path(p).unlink() if pruned: - print(f"pruned {len(pruned)} consumed approximation plan(s)") + print(f"pruned {len(pruned)} consumed model plan(s)") return 0 @@ -630,8 +630,8 @@ def main(): m.add_argument("--keep", action="store_true", help="keep the reconciled discover plans") m.set_defaults(func=cmd_mark_safe) - s = sub.add_parser("merge-skipped", help="rebuild approximations/skipped.yaml from batches") - s.add_argument("--keep", action="store_true", help="keep the consumed approximation plans") + s = sub.add_parser("merge-skipped", help="rebuild models/skipped.yaml from batches") + s.add_argument("--keep", action="store_true", help="keep the consumed model plans") s.set_defaults(func=cmd_merge_skipped) f = sub.add_parser("findings", help="seed per-rule finding files from results/report.sarif") diff --git a/skills/orchestrate-stage/scripts/get_status.py b/skills/orchestrate-stage/scripts/get_status.py index 3eaa1969c5..8099f4b25c 100644 --- a/skills/orchestrate-stage/scripts/get_status.py +++ b/skills/orchestrate-stage/scripts/get_status.py @@ -21,7 +21,7 @@ import sys from pathlib import Path -from _common import (APPROX, DATAFLOW, +from _common import (MODELS_TR, DATAFLOW, FINDINGS_TR, MODEL, PASS_THROUGH, ROOT, RULES, RULES_TR, SARIF, SINKS_TR, SOURCES_TR, TAGS, TRACKING, active_lib_rules, build_done_keys, builtin_rules_root, @@ -34,7 +34,7 @@ TRIAGE_LEVEL = STATE.get("triage_level") DISCOVER_PLANS = RULES_TR / "plans" -APPROX_PLANS = APPROX / "plans" +MODEL_PLANS = MODELS_TR / "plans" VULN = ROOT / "vulnerabilities.md" GLOBAL_CAP = 10 @@ -73,8 +73,8 @@ def newest_mtime(paths): return m -def approx_dirty(): - # scan older than the newest applied approximation artifact -> a rescan is pending +def models_dirty(): + # scan older than the newest applied model artifact -> a rescan is pending if not SARIF.is_file(): return False arts = list(PASS_THROUGH.rglob("*")) + list(DATAFLOW.rglob("*")) @@ -315,13 +315,13 @@ def ph_scan(): return True, [], None -def ph_approximations(): +def ph_models(): if not SARIF.is_file(): return False, ["dispatch run-scan"], None classified = classified_keys() uncovered = [e for e in dropped_entries() if member_key(e) not in classified] if uncovered: - plans = sorted(glob.glob(str(APPROX_PLANS / "*.yaml"))) + plans = sorted(glob.glob(str(MODEL_PLANS / "*.yaml"))) if plans: tasks = [f"dispatch analyze-external-methods, one per plan (cap {GLOBAL_CAP}):"] tasks += [f" {p}" for p in plans] @@ -340,14 +340,14 @@ def ph_approximations(): by_kind.setdefault(kind, set()).add(p.stem) tasks = ["build unbuilt batches:"] if by_kind.get("passthrough"): - tasks.append(" passthrough create-pass-through-approximation: " + tasks.append(" passthrough create-pass-through-model: " + ", ".join(sorted(by_kind["passthrough"]))) if by_kind.get("dataflow"): tasks.append(" dataflow create-test-project type dataflow, then " - "create-dataflow-approximation: " + ", ".join(sorted(by_kind["dataflow"]))) + "create-dataflow-model: " + ", ".join(sorted(by_kind["dataflow"]))) return False, tasks, None - if approx_dirty(): - return False, ["approximations built after the last scan", "dispatch run-scan"], None + if models_dirty(): + return False, ["models built after the last scan", "dispatch run-scan"], None stuck = sorted({e["method"] for e in dropped_entries() if member_key(e) in done and member_key(e) not in terminal}) if stuck: @@ -411,7 +411,7 @@ def ph_poc(): ("discover", ph_discover, lambda: SCAN_LEVEL == "deep"), ("source_rules", ph_source_rules, lambda: SCAN_LEVEL == "deep"), ("scan", ph_scan, lambda: True), - ("approximations", ph_approximations, lambda: SCAN_LEVEL in ("normal", "deep")), + ("models", ph_models, lambda: SCAN_LEVEL in ("normal", "deep")), ("sink_rules", ph_sink_rules, lambda: SCAN_LEVEL == "deep"), ("triage", ph_triage, lambda: True), ("poc", ph_poc, lambda: TRIAGE_LEVEL == "dynamic"), diff --git a/skills/report-analyzer-issue/SKILL.md b/skills/report-analyzer-issue/SKILL.md index 862a9c05a5..de0b9064cb 100644 --- a/skills/report-analyzer-issue/SKILL.md +++ b/skills/report-analyzer-issue/SKILL.md @@ -20,9 +20,9 @@ Provided by the caller, fall back to the default value when omitted. Ask back on - `project-root` (optional) — root of the target project. Opentaint keeps all analysis artifacts under the fixed `/.opentaint/` directory, so every `.opentaint/...` path below resolves there. Default: current directory - `diagnosis` (required for `analysis`) — the caller's brief engine-level cause: roughly where taint appears to die and why. A short hand-off, not a proven trace -- `artifact` (required for `analysis`) — the rule or approximation the issue concerns: a rule's full id and ruleset, or the approximation's target method(s) +- `artifact` (required for `analysis`) — the rule or model the issue concerns: a rule's full id and ruleset, or the model's target method(s) - `name` (optional, `analysis`) — the test-project name the artifact was traced on; its tree is `.opentaint/test-projects/` and model `.opentaint/test-compiled/`, cited so the engine team can reproduce -- `setup` (required for `resource`) — what was running when the scan failed without SARIF: the ruleset(s), approximation dirs, project model, final memory bound, timeout/backstop outcome, scan log, and commit hash (`git rev-parse HEAD`) +- `setup` (required for `resource`) — what was running when the scan failed without SARIF: the ruleset(s), model dirs, project model, final memory bound, timeout/backstop outcome, scan log, and commit hash (`git rev-parse HEAD`) ## Workflow @@ -41,14 +41,14 @@ Write `.opentaint/issues/.md` — the self-contained deliverable, `` Header (both kinds): - Type — `analysis` or `resource` -- Setup — the exact command that reproduces it: the `opentaint test rule …` / `test approximation run` for `analysis`, or the failed `opentaint scan` for `resource`, each with its model, rulesets, and approximation dirs (plus final memory and timeout/backstop outcome for `resource`) +- Setup — the exact command that reproduces it: the `opentaint test rule …` / `test model run` for `analysis`, or the failed `opentaint scan` for `resource`, each with its model, rulesets, and model dirs (plus final memory and timeout/backstop outcome for `resource`) - Run logs — the run's log files, by path: for `analysis`, any debug log or fact-reachability SARIF the caller cited, if present; for `resource`, the scan's output log -- Minimal repro — the smallest set of files/folders that demonstrates it: for `analysis`, the test project `.opentaint/test-projects/` if one was named; for `resource`, the project model, rulesets, and approximation dirs in play (with their rough size) +- Minimal repro — the smallest set of files/folders that demonstrates it: for `analysis`, the test project `.opentaint/test-projects/` if one was named; for `resource`, the project model, rulesets, and model dirs in play (with their rough size) - TL;DR — 2–3 sentences: the symptom, and for `analysis` where taint appears to die and what the engine should do instead Below the header, free-form — whatever makes the case, at least: -- `analysis` — the `artifact` under test (rule id + ruleset, or approximation target method(s)); the caller's brief cause; observed vs expected and any fact-reachability trace, if the caller supplied them; a 1–3 sentence hypothesis of what the engine does wrong (a hypothesis, not a fix) +- `analysis` — the `artifact` under test (rule id + ruleset, or model target method(s)); the caller's brief cause; observed vs expected and any fact-reachability trace, if the caller supplied them; a 1–3 sentence hypothesis of what the engine does wrong (a hypothesis, not a fix) - `resource` — the model's rough size (classes/modules if known) and the commit hash (`git rev-parse HEAD`) it was built from Keep it tight: the header plus about one screen of body. diff --git a/skills/run-scan/SKILL.md b/skills/run-scan/SKILL.md index 221f4a67df..29b478302d 100644 --- a/skills/run-scan/SKILL.md +++ b/skills/run-scan/SKILL.md @@ -33,10 +33,10 @@ opentaint scan --project-model .opentaint/project \ ``` - `--rule-id ` — restrict to specific executable rules (repeatable, one per input rule ID); referenced library rules remain available without listing their IDs separately. Omit to run all loaded rules -- `--passthrough-approximations .opentaint/pass-through` — add when that directory exists: passThrough configs override built-ins at the rule level, a provided rule overriding a built-in only when it matches one -- `--dataflow-approximations .opentaint/dataflow` — add when that directory exists: code-based approximations, one project per batch +- `--passthrough-models .opentaint/pass-through` — add when that directory exists: passThrough configs override built-ins at the rule level, a provided rule overriding a built-in only when it matches one +- `--dataflow-models .opentaint/dataflow` — add when that directory exists: code-based models, one project per batch -Both approximation-dir flags walk their trees recursively; pass each parent directory once, not every package or batch separately. +Both model-dir flags walk their trees recursively; pass each parent directory once, not every package or batch separately. The scan is long — run it in the background and wait for it to finish. Leave `--timeout` at the engine default (900s); the CLI ends the analysis itself and writes whatever SARIF it has. @@ -46,7 +46,7 @@ Start at the 8G default. An out-of-memory failure at 8G — even when the engine ### 3. Collect the report, or escalate -A SARIF is complete enough to use even alongside the two normal scan errors — a timeout (the CLI ended the analysis and wrote what it had), or an out-of-memory at 16G (after §2's bump), take it as-is. The other two outcomes are not results to accept. A config-load failure on a malformed approximation is fixed, not bumped: report the engine error and the offending file under `.opentaint/pass-through`/`.opentaint/dataflow` per Output so it can be repaired and rescanned. No SARIF at all, even at 16G, is a plain failure: report it per Output with the scan setup. +A SARIF is complete enough to use even alongside the two normal scan errors — a timeout (the CLI ended the analysis and wrote what it had), or an out-of-memory at 16G (after §2's bump), take it as-is. The other two outcomes are not results to accept. A config-load failure on a malformed model is fixed, not bumped: report the engine error and the offending file under `.opentaint/pass-through`/`.opentaint/dataflow` per Output so it can be repaired and rescanned. No SARIF at all, even at 16G, is a plain failure: report it per Output with the scan setup. ## Output @@ -62,10 +62,10 @@ All three sit next to the report under `.opentaint/results/`: ### Summary: -- finding count, and dropped-vs-approximated external-method counts +- finding count, and dropped-vs-modeled external-method counts - whether memory was bumped to 16G -- if the scan failed at config-load on a malformed approximation: the engine error and the offending file under `.opentaint/pass-through` or `.opentaint/dataflow` (located from the error), so it can be fixed and rescanned — this is not an out-of-memory failure and 16G won't help -- if no SARIF came out even at 16G: that the scan is left failed, with the setup used (full scan command with ruleset, approximation dirs, model) +- if the scan failed at config-load on a malformed model: the engine error and the offending file under `.opentaint/pass-through` or `.opentaint/dataflow` (located from the error), so it can be fixed and rescanned — this is not an out-of-memory failure and 16G won't help +- if no SARIF came out even at 16G: that the scan is left failed, with the setup used (full scan command with ruleset, model dirs, model) ## Constraints