mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-16 03:49:26 +00:00
fix(cpg): report the line that names an engine failure, not its frames
Every PHP failure logged `cpg batch taint failed: dure1.java:10)`, which is the middle of a stack frame and says nothing. A trace puts its exception on the FIRST line and frames after it, and the backend logged the last 400 characters, so the cause was discarded and the noise kept. Four investigations were spent on that. Failures now report the leading non-frame lines, which is where the exception and its message are, with the frames dropped. A control asserts an OutOfMemoryError survives while a frame does not, and that a timeout and an empty exit stay distinguishable. 1,411 tests passed, nine skipped. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
This commit is contained in:
co-authored by
Claude Opus 5
parent
dfc29238b5
commit
6089c67bf6
@@ -1129,7 +1129,7 @@ class JoernBackend:
|
||||
command += ["--param", f"{name}={value}"]
|
||||
completed = self._run(command, timeout=self.query_timeout, cwd=cpg_path.parent)
|
||||
if completed is None or completed.returncode != 0:
|
||||
detail = (completed.stderr or "")[-400:] if completed is not None else "timeout"
|
||||
detail = _engine_failure(completed)
|
||||
logger.warning("cpg batch %s failed: %s", query, detail)
|
||||
return None
|
||||
body = completed.stdout or ""
|
||||
@@ -1162,7 +1162,7 @@ class JoernBackend:
|
||||
# it can never land in the repository being analysed.
|
||||
completed = self._run(command, timeout=self.query_timeout, cwd=cpg_path.parent)
|
||||
if completed is None or completed.returncode != 0:
|
||||
detail = (completed.stderr or "")[-400:] if completed is not None else "timeout"
|
||||
detail = _engine_failure(completed)
|
||||
logger.warning("cpg query %s failed: %s", query, detail)
|
||||
return None
|
||||
body = completed.stdout or ""
|
||||
@@ -1541,6 +1541,23 @@ def _configured_timeout() -> int:
|
||||
return QUERY_TIMEOUT_SECONDS
|
||||
|
||||
|
||||
def _engine_failure(completed: subprocess.CompletedProcess[str] | None) -> str:
|
||||
"""What the engine said, keeping the line that names the cause.
|
||||
|
||||
A stack trace's exception is on its FIRST line and its frames follow, so logging only the tail
|
||||
reports frames and discards the cause. Four PHP investigations were spent on `cpg batch taint
|
||||
failed: dure1.java:10)`, which is the middle of a frame and says nothing at all.
|
||||
"""
|
||||
if completed is None:
|
||||
return "timeout"
|
||||
text = (completed.stderr or completed.stdout or "").strip()
|
||||
if not text:
|
||||
return f"exit {completed.returncode} with no output"
|
||||
lines = [line for line in text.splitlines() if line.strip() and not line.strip().startswith("at ")]
|
||||
head = " | ".join(lines[:4])[:600]
|
||||
return head or text[:300]
|
||||
|
||||
|
||||
def _render(value: object) -> str:
|
||||
"""CPGQL parameters are strings; a sequence becomes a comma-separated list the script splits."""
|
||||
if isinstance(value, str):
|
||||
|
||||
@@ -1217,3 +1217,16 @@ def test_a_batch_no_part_can_answer_is_still_unavailable(tmp_path, monkeypatch):
|
||||
graph.write_bytes(b"graph")
|
||||
# None means "could not ask", which must never become an empty answer.
|
||||
assert backend.query_batch(graph, "taint", {str(i): {"file": f"f{i}.php"} for i in range(8)}) is None
|
||||
|
||||
|
||||
def test_an_engine_failure_reports_the_line_that_names_the_cause(tmp_path, monkeypatch):
|
||||
"""A stack trace's exception is on its first line; logging the tail reports frames only."""
|
||||
from openultrasast.cpg.backend import JoernBackend, _engine_failure
|
||||
|
||||
trace = "java.lang.OutOfMemoryError: Java heap space\n\tat foo.Bar.baz(Bar.java:10)\n\tat x.Procedure1.java:10)"
|
||||
named = _engine_failure(subprocess.CompletedProcess(["joern"], 1, "", trace))
|
||||
assert "OutOfMemoryError" in named and "Java heap space" in named
|
||||
assert "Bar.java:10" not in named, "frames are noise; the cause is not"
|
||||
assert _engine_failure(None) == "timeout"
|
||||
assert "no output" in _engine_failure(subprocess.CompletedProcess(["joern"], 3, "", ""))
|
||||
assert isinstance(JoernBackend(), JoernBackend)
|
||||
|
||||
Reference in New Issue
Block a user