measure(contributor-scan): first ontology coverage run, and it found a family we do not have

NodeGoat models 18 of 175 distinct call names, covering 12 percent of 1,185 calls. That share is not
a score: most of what a program calls is not security-relevant, and a high number would mean the
tables were too loose. It is a baseline to move deliberately.

The top product candidate is res.render, 23 calls with five in a method that reads untrusted input.
Task 14.3 reasoned its way to that same gap by hand and declined to model it as a flow sink; the
instrument found it in one run, which is the validation that matters. It also surfaced Math.random in
the user data-access module, and weak randomness is a weakness class this ontology has no family for
at all -- so no family census could have found it, because the family list is itself the blind spot.
That is the kind of gap only a coverage instrument finds.

It had to be taught what is not an API. The first run ranked _tmp_3.push and __ecma.Array.factory
first, which is how the frontend lowers a destructuring and an array literal, and a Cypress suite and
a Gruntfile held four of the next five. Both are separated now, because noise at the top of a
worklist is how a worklist stops being read.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
This commit is contained in:
Marius Ciepluch
2026-09-21 17:19:59 +02:00
co-authored by Claude Opus 5
parent 0beae46ba9
commit b0500ecfa0
4 changed files with 72 additions and 2 deletions
+16 -1
View File
@@ -21,9 +21,14 @@ from typing import Any
from openultrasast.cpg.backend import JoernBackend
from openultrasast.model.contracts import ExecutionBudget
from openultrasast.model.layout import is_test_path
from openultrasast.model.specs import config_specs, dominance_specs, taint_specs
from openultrasast.preprocess import enumerate_source_files
# Build tooling a repository runs but does not ship. Named here rather than guessed from a path, because a
# file called Gruntfile is a build script wherever it sits.
_BUILD_SCRIPTS = frozenset({"Gruntfile.js", "gulpfile.js", "webpack.config.js", "rollup.config.js", "karma.conf.js"})
def modelled_tokens(language: str) -> dict[str, tuple[str, ...]]:
"""Every token any fact table names for this language, by the role it plays.
@@ -117,7 +122,17 @@ def main() -> int:
}
record["modelled_seen"] = [{k: r[k] for k in ("name", "count") if k in r} for r in known[: args.top]]
wanted = ("name", "spelling", "count", "nearSource", "file", "line")
record["candidates"] = [{k: r[k] for k in wanted if k in r} for r in unknown[: args.top]]
# A test helper and a build script are not product API. The layout facts already know which paths those
# are, and on the first measured subject a Gruntfile and a Cypress suite held four of the five most
# frequent candidates -- ranked first, they would have buried the two real leads beneath them.
def product(row: dict[str, Any]) -> bool:
where = str(row.get("file") or "")
return bool(where) and not is_test_path(where) and Path(where).name not in _BUILD_SCRIPTS
ranked = [{k: r[k] for k in wanted if k in r} for r in unknown]
record["candidates"] = [r for r in ranked if product(r)][: args.top]
record["candidates_in_test_or_build"] = [r for r in ranked if not product(r)][:20]
record["candidates_near_a_source"] = [r for r in record["candidates"] if int(r.get("nearSource", 0)) > 0][:20]
save()
cleanup = getattr(graph, "cleanup", None)