mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-16 11:49:28 +00:00
90 lines
4.0 KiB
Python
90 lines
4.0 KiB
Python
"""Live integration of validated graph and query reuse under one deadline."""
|
|||
|
|
|
||
|
|
import json
|
||
|
|
import time
|
||
|
|
|
||
|
|
from openultrasast.cpg.backend import CpgResult, JoernBackend
|
||
|
|
from openultrasast.model.contracts import ExecutionBudget
|
||
|
|
from openultrasast.push.cache import ArtifactCache, SemanticKeys
|
||
|
|
from openultrasast.push.reuse import ReusingBackend
|
||
|
|
|
||
|
|
|
||
|
|
def test_backend_reuses_graph_and_answers_then_invalidates_source_and_dependencies(tmp_path, monkeypatch):
|
||
|
|
import openultrasast.cpg.backend as module
|
||
|
|
|
||
|
|
monkeypatch.setattr(module, "joern_version", lambda: (4, 0, 625))
|
||
|
|
builds = []
|
||
|
|
queries = []
|
||
|
|
|
||
|
|
class Engine(JoernBackend):
|
||
|
|
def build(self, root, *, language, exclude, execution_budget):
|
||
|
|
builds.append(root)
|
||
|
|
path = tmp_path / f"graph-{len(builds)}.json"
|
||
|
|
paths = [str(p) for p in root.glob("*.js")]
|
||
|
|
path.write_text(json.dumps(paths))
|
||
|
|
return CpgResult(path, lambda kind, params: self.query(path, kind, params), lambda kind, req: self.query_batch(path, kind, req))
|
||
|
|
|
||
|
|
def query(self, path, kind, params):
|
||
|
|
names = json.loads(path.read_text())
|
||
|
|
return {"files": len(names), "methods": 2, "calls": 1, "file_names": names, "overlays": "dataflowOss"}
|
||
|
|
|
||
|
|
def query_batch(self, path, kind, requests):
|
||
|
|
queries.append((kind, requests))
|
||
|
|
return {**{rid: [] for rid in requests}, "__census__": [self.query(path, "census", {})]}
|
||
|
|
|
||
|
|
root = tmp_path / "source"
|
||
|
|
root.mkdir()
|
||
|
|
(root / "a.js").write_text("function f() {}")
|
||
|
|
cache = ArtifactCache(tmp_path / "cache", max_bytes=100000)
|
||
|
|
semantics = SemanticKeys("facts", "queries", "config", "ranking", "admission", "disabled", "empty", "advisory")
|
||
|
|
deadline = ExecutionBudget(time.monotonic() + 10, 0.2)
|
||
|
|
backend = ReusingBackend(Engine(), cache, semantics, declarations="dependencies", exclusions="exclusions")
|
||
|
|
|
||
|
|
def run():
|
||
|
|
result = backend.build(root, language="javascript", exclude=(), execution_budget=deadline)
|
||
|
|
assert result is not None
|
||
|
|
answer = result.run_batch("taint", {"q": {"file": "a.js", "callDepth": 3}})
|
||
|
|
if result.cleanup:
|
||
|
|
result.cleanup()
|
||
|
|
return answer
|
||
|
|
|
||
|
|
expected = run()
|
||
|
|
assert run() == expected
|
||
|
|
assert len(builds) == 1 and len(queries) == 1
|
||
|
|
(root / "a.js").write_text("function g() {}")
|
||
|
|
run()
|
||
|
|
assert len(builds) == 2 and len(queries) == 2
|
||
|
|
backend.declarations = "changed-dependency"
|
||
|
|
run()
|
||
|
|
assert len(builds) == 3 and len(queries) == 3
|
||
|
|
backend.semantics = SemanticKeys("changed-facts", "queries", "config", "ranking", "admission", "disabled", "empty", "advisory")
|
||
|
|
run()
|
||
|
|
assert len(builds) == 3 and len(queries) == 4
|
||
|
|
|
||
|
|
|
||
|
|
def test_fresh_missing_census_cannot_borrow_a_cached_query_census(tmp_path):
|
||
|
|
from openultrasast.cpg.artifact import GraphArtifact, GraphCensus, GraphIdentity
|
||
|
|
|
||
|
|
identity = GraphIdentity(
|
||
|
|
"source", "declarations", "exclude", "repository", "javascript", "engine", "frontend", "version", ("dataflowOss",), ()
|
||
|
|
)
|
||
|
|
artifact = GraphArtifact(identity, "graph-bytes", 10, GraphCensus(1, 1, 1), "complete", (), "/old", ("a.js",))
|
||
|
|
cache = ArtifactCache(tmp_path / "cache", max_bytes=100000)
|
||
|
|
semantics = SemanticKeys("facts", "queries", "config", "ranking", "admission", "disabled", "empty", "advisory")
|
||
|
|
wrapper = ReusingBackend(JoernBackend(), cache, semantics, declarations="d", exclusions="e")
|
||
|
|
census = [{"files": 1, "methods": 1, "file_names": ["a.js"]}]
|
||
|
|
calls = []
|
||
|
|
|
||
|
|
def batch(kind, requests):
|
||
|
|
calls.append(requests)
|
||
|
|
result = {rid: [] for rid in requests}
|
||
|
|
if len(calls) == 1:
|
||
|
|
result["__census__"] = census
|
||
|
|
return result
|
||
|
|
|
||
|
|
graph = CpgResult(tmp_path / "graph", lambda *_: None, batch)
|
||
|
|
result = wrapper._answers(graph, artifact, ExecutionBudget(time.monotonic() + 5, 0.2))
|
||
|
|
assert "q" in result.run_batch("taint", {"q": {"file": "a.js"}})
|
||
|
|
answer = result.run_batch("taint", {"q": {"file": "a.js"}, "fresh": {"file": "a.js", "callDepth": 3}})
|
||
|
|
assert "q" in answer and "fresh" not in answer
|