Files
Open_Ultra_SAST/.kiro/specs/propose-adjudicate-prove/research.md
T
Marius CiepluchandClaude Opus 4.8 39a1873e5b docs: plain-English pass over the active specs
Ran the humanizer rules over 79 prose files in the active specs (brief,
design, requirements, research and narrative docs). Removed em dashes,
significance inflation, forced rule-of-three, bold-label list items and
filler. Skipped tasks.md checklists and the retired specs (harnessx-*,
openrouter-sast-harness, unseen-repo-evaluation).

Every number, path, command, hash and identifier was preserved: checked
each file against HEAD for drift in numbers, inline code and code fences,
zero drift across all 79 files.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-10-12 18:16:41 +02:00

7.4 KiB
Raw Blame History

Research & Design Decisions

Summary

  • Feature: propose-adjudicate-prove
  • Discovery Scope: Extension of three-stage-scan (MAP grows an overlay; STATIC and REGRESS stay)
  • Key Findings:
    • Inventory already decides; MAP ranks; REGRESS can run. Nothing adjudicates flow.
    • Labeled SAST slice Youden is about +9%. Leaks are OWASP-false / Juliet goodG2B that still contain the sink. Misses are one-hop assignments and sink aliases.
    • Clearwing already does the split we want: tree-sitter callgraph + pattern taint tables, then ranker (surface/influence/reachability 0.5/0.2/0.3), then mechanism-memory embeddings. OpenUltraSAST already copied the rank formula and has json-local vectors; they are not wired as prove-budget.
  • Tree-sitter CLI/API covers C/C++/Python/JS/Go/Rust (Clearwing grammars); Joern is the CPG sidecar when present. Stdlib ast is last-resort Python only. LLM hunter remains suspicion-only.
    • Real working trees will often fail parse or cross a file boundary. Those cases must stay unadjudicated, not demoted.

Research Log

Labeled vs unlabeled code

  • Context: Operator: known-vuln corpora are "nice"; written code will not be.
  • Sources Consulted: In-tree LANGUAGE_MANIFESTS, benchmarks/pairs/, OWASP Benchmark true/false files, Juliet bad/goodG2B/goodB2G, CWE-Bench-Java catalog.
  • Findings:
    • Cheat-sheets: one CWE, named sink, often same-line source.
    • OWASP: true and false files both often contain exec / eval; the difference is source constancy or infeasible branches.
    • Juliet goodG2B: hardcoded source, same sink.
    • Juliet goodB2G: same source, sanitized sink (printf("%s")).
    • Working trees: syntax errors, generated bundles, wrappers (helpers.db_sqlite.execute), no labels.
  • Implications: Overlay scored on sast slice; unlabeled scans must not pretend demote. Unadjudicated is a first-class disposition.

Engine choice

  • Context: Operator prefers tree-sitter + Joern look, embeddings before prove, still v1-focused.
  • Sources Consulted: clearwing/sourcehunt/callgraph.py, taint.py, ranker.py, mechanism_memory.py; openultrasast/rank.py, vectorstore.py, index.py.
  • Findings:
    • Clearwing tree-sitter is optional: missing grammar disables callgraph, does not crash.
    • Clearwing taint is intra-procedural, pattern tables for sources/sinks, same shape as our fact TOML.
    • Clearwing ranker fills reachability from the callgraph; taint_paths boost surface. Embeddings retrieve mechanisms, they do not decide taint.
    • Our heuristic_rank already uses surface·0.5 + influence·0.2 + reachability·0.3. Vector store bakeoff selected json-local. VALID_NAMESPACES already includes mechanisms but nothing writes Clearwing-style records into it.

Clearwing vector-store reference (real-world bugs)

  • Context: Operator: Clearwing's store is effective on real bugs, not textbook files.
  • Sources Consulted: clearwing/sourcehunt/mechanism_memory.py, data/memory/semantic_memory.py, docs/architecture.md steps 7–8; openultrasast/index.py namespaces.
  • Findings:
    • Store mechanisms, not findings and not 80-line file windows. Example: "length field trusted before alloc; 16-bit value widened to size_t."
    • JSONL is the source of truth (~/.clearwing/sourcehunt/mechanisms.jsonl). Chroma/sqlite embeddings are ephemeral caches rebuilt from JSONL.
    • Write path is verified findings only (after adversarial verify / crash). Suspicion never seeds the store.
    • Clearwing's JSONL + verified-write + metadata filters are the reference. Its TF-IDF default is not: bag-of-words will not match paraphrased real bugs (length field trusted before alloc vs user-controlled size_t in malloc).
    • OpenUltraSAST already has OpenRouterEmbeddingClient and json-local cosine. Use that as the only semantic ranker; CWE/language/tags are hard filters. No TF-IDF, no Chroma, no local MiniLM.
    • Query is language + file tags + optional free text; same-language boost +0.5. Top-N injects hunter context and variant-grep seeds. It does not decide taint.
    • Variant loop then greps the messy tree for structural matches of that mechanism. That is how it leaves Juliet behind.
  • Implications: Copy JSONL + verified-write + metadata filters. Rank with OpenRouter embeddings only. Do not copy TF-IDF. Do not embed 80-line file windows as the prove store. Seed from sandbox-proven mechanisms, not OWASP BenchmarkTest00007.
    • Joern CLI (joern-parse / scripts) is the only practical CPG without a Scala runtime in-process.
  • Implications: Probe tree-sitter like Docker. Joern sidecar cannot demote. Embeddings order prove only.

Decision: Python intra-file first

  • Context: Zero-dep core vs multi-language trees.
  • Selected: Superseded. Tree-sitter is the prio engine; stdlib ast is Python last resort; quick stays zero-dep.
  • Rationale: Operator working trees are mixed-language; Python-only adjudication would leave JS/C/Java as perpetual unadjudicated.
  • Trade-offs: standard needs a grammar or CLI on PATH; missing tools → unadjudicated, not skip the scan.

Evidence ladder

  • Context: Three-stage-scan forbids hunter JSON as corroboration.
  • Sources Consulted: verification.EvidenceLevel, tool_hunter suspicion rule, worth_fixing gate.
  • Findings: Overlay can justify promote at static_corroboration. It cannot skip to crash or exploit. Worth-fixing stays sandbox + reachability.
  • Implications: Disposition is not an evidence rung. Do not add a seventh ladder value.

Architecture Pattern Evaluation

Option Description Strengths Risks Notes
Replace regex with CodeQL External engine is the detector Real dataflow Breaks zero-dep, smoke gate, ledger shadows Rejected
LLM hunter as adjudicator Model reads file and votes Flexible on messy code Unstable Youden; violates suspicion rule Rejected
Overlay on inventory Regex proposes; tree-sitter taint adjudicates; embed ranks prove Fits stages; multi-lang Parser must be probed Selected
Whole-program taint v1 Inter-file Closes wrappers Too large; false demote risk Defer; stay unadjudicated

Design Decisions

Decision: Unadjudicated is not demote

  • Context: Messy files and cross-file flow.
  • Alternatives: Silent drop; treat as safe; force demote.
  • Selected: Explicit unadjudicated with reason.
  • Rationale: Operator writing code must not read silence as "clean."
  • Trade-offs: Reports stay noisy until languages and inter-file land.
  • Follow-up: Count unadjudicated rate on unlabeled trees as an honesty metric.

Decision: Score overlay on sast slice, inventory on smoke gate

  • Context: +9% Youden vs 93% cheat-sheet recall.
  • Selected: Two gates stay separate; overlay Youden cannot unblock smoke failure.
  • Rationale: Same as detection-gate vs score in the improve loop.

Risks & Mitigations

  • False demote on real bugs (infeasible-branch OWASP tricks, wrappers). Never demote when flow is incomplete; only demote when the engine has a dominating constant or sanitizer.
  • Youden chase via extra sinks. Each coverage rule needs a false/good counterpart that must stay demoted or silent.
  • Parse failures on operator WIP, unadjudicated, quick still works.

References

  • .kiro/specs/three-stage-scan/design.md, stage plan, sandbox, hunter suspicion
  • benchmarks/pairs/sast/README.md, OWASP/Juliet slice
  • NIST Juliet goodG2B vs goodB2G templates
  • OWASP Benchmark expectedresults true/false protocol