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]>
11 KiB
Design Document
Overview
Purpose. This feature turns on a real structured parse for overlay adjudication by installing an optional extra. AppSec scans of JavaScript, C/C++, Java, and Python then get FileIR from official tree-sitter grammars instead of language_unsupported.
Users. Default/CI installs keep quick and extra-free pytest. Operators who install openultrasast[semantic] get overlay promote/demote on the in-scope languages.
Impact. parse_file prefers extra grammars when they load. The dead CLI parse-success path is removed. Taint, facts, dispositions, sandbox, and the detection gate do not change.
Goals
- Optional extra with official grammar wheels; core
dependenciesstay empty. - One CST walker into existing
FileIR(Bind,CallSite,FunctionIR). - Fail closed: missing extra, missing grammar, or error tree →
unadjudicated. - Dual test matrix: extra-free always; extra-on JS plus C or Java.
Non-Goals
- Joern queries, inter-file taint, CWE-121 buffer semantics, pair-catalog harvest, LLM parse.
- New overlay dispositions or evidence rungs.
- Requiring the extra for
quickor for importingopenultrasast.
Boundary Commitments
This Spec Owns
- Packaging extra
semanticand lazy grammar load. - CST →
FileIRprojection andparse_filedispatch order. - Probe semantics: extra/grammar vs host CLI.
- pytest
semanticmarker and CI extra-on job.
Out of Boundary
- Overlay dispositions, fact TOML, taint worklist (
propose-adjudicate-prove). - Stage plan, sandbox, hunter suspicion (
three-stage-scan). - Pair catalog slices (
real-world-vfc-slice). - CWE policy, pattern rules, MCP
deep.
Allowed Dependencies
- Existing
FileIRtypes andparse_filecallers (taint.py,overlay.py). harness_extcapability-guard pattern (find_spec, no import at module load).- External: PyPI
tree-sitterand grammar packages listed in Technology Stack (extra only).
Revalidation Triggers
- Changing
FileIR/Bind/CallSitefields. - Importing
tree_sitterat module load ofcliorsemantic/__init__. - Adding core (non-extra) parser dependencies.
- Using host
tree-sitterCLI output as overlay IR.
Architecture
Existing Architecture Analysis
Overlay already calls parse_file → taint → dispositions. Python ast works. parse_tree_sitter_cli always returns None. tree_sitter_available() is true if a CLI binary exists, which does not imply a grammar.
Architecture Pattern and Boundary Map
Selected: hybrid extra guard + CST walker + existing dispatch. Same packaging seam as HarnessX. parse_file remains the only overlay parse entry.
flowchart TD
overlay[overlay adjudicate]
parse[parse_file]
extra[has_semantic_extra]
gram[grammar_for language]
cst[cst walker]
ast[python ast]
fail[FileIR language_unsupported or parse_failed]
taint[taint on FileIR]
overlay --> parse
parse --> extra
extra -->|yes| gram
gram -->|loaded| cst
cst -->|has_error| fail
cst -->|ok| taint
extra -->|no| ast
gram -->|missing| ast
ast -->|python ok| taint
ast -->|not python| fail
Key decisions
- Extra grammars are the product parse. Host CLI is not a success path.
- Taint does not import tree-sitter.
- Dependency direction: extra guard → cst walker →
parse_file→ taint → overlay. No reverse imports.
Technology Stack
| Layer | Choice / Version | Role in Feature | Notes |
|---|---|---|---|
| Packaging | extra semantic |
Optional wheels | Core dependencies stay [] |
| Parser runtime | tree-sitter>=0.23,<0.27 |
Language + Parser |
Constructor API, not build_library |
| Grammars | tree-sitter-python, tree-sitter-javascript, tree-sitter-c, tree-sitter-cpp, tree-sitter-java |
Per-language CST | cpp for .cpp; c for .c |
| Tests | pytest marker semantic |
Skip extra-on tests | Default CI extra-free |
| Types | mypy override tree_sitter* |
Extra absent in typecheck env | Same as harnessx.* |
License of adopted wheels: MIT. Pins are verified at implement time against the selected minor.
File Structure Plan
src/openultrasast/semantic/
extra.py # has_semantic_extra, grammar_for; no tree_sitter import at module load
cst.py # Node walk → FileIR; node-kind map
ir.py # parse_file dispatch; keep python ast; remove CLI success path
engines.py # stop treating host CLI as overlay-ready
pyproject.toml
tests/test_semantic_extra.py
tests/test_semantic_cst.py
.github/workflows/ci.yml
Modified Files
pyproject.toml: extrasemantic; pytest marker; mypy overrides.src/openultrasast/semantic/ir.py: dispatch; deleteparse_tree_sitter_clias success path.src/openultrasast/semantic/engines.py:tree_sitter_availablemust not mean CLI-only; extra/grammar helpers live inextra.py.src/openultrasast/semantic/__init__.py: exporthas_semantic_extraonly if needed; do not import cst at package load.tests/test_semantic_overlay.py: extra-free JS stayslanguage_unsupportedwithout monkeypatching a fake IR as "tree-sitter success"..github/workflows/ci.yml: second job with--extra semanticrunning@pytest.mark.semantic.dagger/ci.py: keep extra-free unless the same extra-on command is cheap to add; default is GHA second job only.
Do not modify taint.py, overlay.py, facts.py, gate.py, pairs.py.
System Flows
flowchart TD
start[parse_file]
g{grammar_for language}
err{root has_error}
py{language python}
start --> g
g -->|yes| err
err -->|yes| failed[parse_failed]
err -->|no| ir[FileIR engine tree-sitter]
g -->|no| py
py -->|yes| ast[python-ast FileIR]
py -->|no| uns[language_unsupported]
Gating: grammar load failure is missing extra/grammar, not a scan abort. has_error is parse_failed.
Requirements Traceability
| Requirement | Summary | Components | Flows |
|---|---|---|---|
| 1.1–1.3 | Extra-free quick and import | extra.py, pyproject | no grammar load on quick |
| 2.1–2.4 | Structured parse; JS promote; C/Java demote; same dispositions | extra.py, cst.py, ir.py, existing taint/overlay | parse_file → taint |
| 3.1–3.5 | Fail closed; Python ast last resort; missing extra does not fail standard | ir.py, extra.py | unsupported / parse_failed |
| 4.1–4.3 | One walker; no JS/C visitors; CLI not success | cst.py, ir.py | grammar_for only |
| 5.1–5.5 | No LLM; Joern not required; corroboration; prove/gate unchanged | none new | existing overlay |
| 6.1–6.3 | Dual tests | markers, CI job | skip vs extra-on job |
Components and Interfaces
| Component | Domain | Intent | Req Coverage | Key Dependencies | Contracts |
|---|---|---|---|---|---|
| SemanticExtra | Packaging | Probe extra and load grammar | 1.1–1.3, 3.1–3.2, 4.3 | PyPI wheels P0 | Service |
| CstWalker | Parse | CST to FileIR | 2.1–2.3, 3.4, 4.1 | SemanticExtra P0, FileIR P0 | Service |
| ParseDispatch | Parse | parse_file order | 2.1, 3.3–3.5, 4.3 | CstWalker P0, python ast P0 | Service |
| ExtraTestMatrix | Validation | skip vs extra-on CI | 6.1–6.3 | pytest P0 | Batch |
Parse layer
SemanticExtra
| Field | Detail |
|---|---|
| Intent | Capability guard and per-language grammar load |
| Requirements | 1.2, 1.3, 3.1, 3.2, 4.3 |
Contracts: Service
def has_semantic_extra() -> bool:
"""True iff tree_sitter and at least one in-scope grammar module are importable. No eager import of wheels at semantic package load."""
def grammar_for(language: str) -> object | None:
"""Return a tree_sitter.Language for python, javascript, c, cpp, java, or None. Lazy importlib.import_module. Never raises into the scan."""
- Preconditions:
languageis a preprocess language id. - Postconditions:
Nonemeans overlay must not claim tree-sitter IR. - Invariants: host CLI path does not make this return non-None.
CstWalker
| Field | Detail |
|---|---|
| Intent | One walk from a tree-sitter tree to FileIR |
| Requirements | 2.1–2.3, 3.4, 4.1 |
Contracts: Service
def file_ir_from_tree(*, path: str, text: str, language: str, tree: object) -> FileIR:
"""Project named nodes into Bind, CallSite, FunctionIR. If root.has_error, parse_ok False reason parse_failed. engine is tree-sitter when parse_ok."""
Node-kind map (names are grammar types; walker is shared):
| Kind | python | javascript | c / cpp | java |
|---|---|---|---|---|
| function | function_definition | function_declaration, method_definition, arrow_function | function_definition | method_declaration, constructor_declaration |
| assign | assignment, augmented_assignment | assignment_expression | assignment_expression | assignment_expression |
| call | call | call_expression | call_expression | method_invocation |
| name | identifier | identifier | identifier | identifier |
| string | string | string | string_literal | string_literal |
Implementation Notes
- Integration: only
parse_filecalls this aftergrammar_for. - Validation: JS
eval(req.query.x)and Cprintf("%s\\n", data)fixtures with extra on. - Risks: node-type aliases (JS
assignment_pattern); table is the extension point, not a new visitor module per language.
ParseDispatch
| Field | Detail |
|---|---|
| Intent | Existing parse_file with extra-first order |
| Requirements | 2.1, 3.3–3.5, 4.2, 4.3 |
Order: grammar_for → cst; else python ast if language == python; else language_unsupported. Do not call host CLI for overlay IR.
Data Models
FileIR, Bind, CallSite, FunctionIR stay as in ir.py. No new disposition. engine values: tree-sitter | python-ast | none.
Error Handling
| Condition | Overlay | Scan |
|---|---|---|
| Extra not installed | language_unsupported for non-Python | standard succeeds |
| Grammar missing for language | language_unsupported or Python ast | standard succeeds |
| root.has_error | parse_failed | standard succeeds |
| Wheel import error | treat as extra absent | do not raise |
Testing Strategy
- Unit extra-free: import
openultrasast.clidoes not importtree_sitter; JSparse_file→language_unsupported; Python ast still promoteseval(request.args);quickscan unchanged (1.1–1.3, 3.1, 3.3, 6.1). - Unit extra-on (
pytest.mark.semantic): JSeval(req.query.x)overlaypromote(2.2); Cprintf("%s\\n", data)overlaydemotewith dominating fact (2.3); syntax-broken JS →parse_failed(3.4);engine == tree-sitteron success (2.1). - Skip: if
not has_semantic_extra(), marked tests skip, extra-free suite green (6.3). - CI: default job extra-free; second job installs extra and runs
-m semantic(6.2). - Do not assert detection-gate Youden (5.5).
Security Considerations
Parser wheels run in-process on operator source. Same trust as reading the tree today. Extra does not execute target code. Overlay evidence stays static_corroboration or below (5.3).
Performance
Parse is per-file MAP, same file budget as rank. No whole-program index.
Migration Strategy
Default install behavior unchanged. Operators who want JS/C/Java overlay run uv sync --extra semantic. No artifact format migration.
Supporting References
Grammar load sketch (not runtime-imported in core):
from tree_sitter import Language, Parser
import tree_sitter_javascript as tsjs
parser = Parser(Language(tsjs.language()))
tree = parser.parse(text.encode())