Files
Open_Ultra_SAST/tests/test_semantic_extra.py
Marius CiepluchandClaude Fable 5.1 4063fd1562 feat(semantic): the hunter's semantic producer covers Java and PHP
Java parsed already, but the producer failed every Java row because no obligation facts existed for the
language and that failure took roles and def_use down with it. PHP had no grammar. Now: tree-sitter-php in the
semantic extra with PHP node kinds in the CST reader; obligation facts for Jenkins/Stapler, Spring Security and
Servlet code (java.toml) and for Laravel, WordPress and Symfony (php.toml), each header naming the training
clones and references the names were checked against; a language with an IR but no obligation facts keeps
roles and def_use and records obligations as unchecked (None), which the engineered features treat as missing,
never as zero. Proven on the committed pool: 6 of 6 Java rows and 33 of 33 PHP rows of one fix change each now
carry semantic features. Implemented by Codex; gated here with ruff, mypy and the full suite.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-10-14 09:35:16 +02:00

73 lines
3.0 KiB
Python

"""Semantic extra packaging and extra-free overlay parse."""
from __future__ import annotations
import json
import tomllib
from pathlib import Path
import pytest
from openultrasast.cli import main
from openultrasast.semantic.extra import grammar_for, has_semantic_extra
from openultrasast.semantic.ir import parse_file
def test_core_dependencies_are_pyyaml_only_and_semantic_is_optional() -> None:
payload = tomllib.loads(Path("pyproject.toml").read_text())
assert payload["project"]["dependencies"] == ["pyyaml>=6.0"] # ai-service-plane task 1: manifests are YAML
extra = payload["project"]["optional-dependencies"]["semantic"]
joined = " ".join(extra)
assert "tree-sitter" in joined
assert "tree-sitter-javascript" in joined
assert "tree-sitter-c" in joined
assert "tree-sitter-java" in joined
assert "tree-sitter-php>=0.23" in extra
assert "semantic:" in " ".join(payload["tool"]["pytest"]["ini_options"]["markers"])
def test_cli_import_does_not_load_tree_sitter(assert_cold_import) -> None: # type: ignore[no-untyped-def]
proc_code = (
"import sys\n"
"import openultrasast.cli\n"
"leaked = sorted(m for m in sys.modules if m == 'tree_sitter' or m.startswith('tree_sitter_'))\n"
"assert not leaked, leaked\n"
)
assert_cold_import(proc_code)
def test_probe_off_yields_no_javascript_grammar(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("OPENULTRASAST_TREE_SITTER_PROBE", "0")
assert has_semantic_extra() is False
assert grammar_for("javascript") is None
ir = parse_file("app.js", "eval(req.query.x)\n", "javascript")
assert ir.parse_ok is False
assert ir.reason == "language_unsupported"
def test_standard_javascript_is_unadjudicated_without_extra(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("OPENULTRASAST_TREE_SITTER_PROBE", "0")
monkeypatch.setenv("OPENULTRASAST_RUNS_DIR", ".runs")
repo = tmp_path / "repo"
repo.mkdir()
(repo / "app.js").write_text("eval(req.query.x)\n")
assert main(["scan", str(repo), "--mode", "standard"]) == 0
run_dir = sorted((repo / ".runs").iterdir())[-1]
overlay = json.loads((run_dir / "overlay.json").read_text())["records"]
assert overlay
assert all(item["disposition"] == "unadjudicated" for item in overlay)
assert all(item["reason"] in {"language_unsupported", "parse_failed"} for item in overlay)
def test_quick_scan_without_extra_still_inventories(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("OPENULTRASAST_TREE_SITTER_PROBE", "0")
monkeypatch.setenv("OPENULTRASAST_RUNS_DIR", ".runs")
repo = tmp_path / "repo"
repo.mkdir()
(repo / "app.py").write_text("def run():\n return eval(request.args)\n")
assert main(["scan", str(repo), "--mode", "quick"]) == 0
run_dir = sorted((repo / ".runs").iterdir())[-1]
findings = json.loads((run_dir / "findings.json").read_text())["findings"]
assert findings
assert not (run_dir / "overlay.json").exists()