mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-16 11:49:28 +00:00
ops/ax/up.sh reads kube_context, registry, pools.default and the images path with `python -m openultrasast.plane.profile --print`, writes the runner digest to ops/k8s/profiles/kind-images.json instead of ~/.cache/.../runner-image, renders ops/ax/workerpool.yaml.tmpl from the profile's pool (POOL_NAME/REPLICAS/CPU/ MEMORY, WORKER_IMAGE), and keeps an existing pool and an ax CLI newer than its checkout instead of rebuilding them (ko and go install would refill several GB of Go cache on this disk). smoke-run.sh takes the runner from `--print images.runner` and runs the checkout's code with OUSAST_PYTHON. repin_templates reads the JSON images file (runner key; the one-line pin still reads). `--print images` returns the file path (the first live rerun fell over this). Templates and the generated validation-46 Tasks re-pinned to the rebuilt image (128 lines, one digest). Live: up.sh reran idempotently on kind in 38 s (every step kept except the runner build); smoke-run.sh done in 24 s on the new image, facts.json 451 B, summary.json 238 B delivered (record: benchmarks/measurements/2026-10-02-k8s-01-profile/). Gates: pytest 2010 passed, 42 skipped, exit 0; ruff check 0; ruff format 0; mypy 0; mkdocs build --strict 0. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
332 lines
19 KiB
Python
332 lines
19 KiB
Python
"""The increment's manifests under ``plane/`` (ai-service-plane task 7) and their generator."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import json
|
|
import re
|
|
import shutil
|
|
import subprocess
|
|
import tomllib
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
import yaml
|
|
from test_plane_reconciler import AX_FIELDS, non_ax_fields
|
|
|
|
from openultrasast.model.endpoint import price_of
|
|
from openultrasast.plane import reconciler
|
|
from openultrasast.plane.budget import prices_from
|
|
from openultrasast.plane.generate import TEMPLATES, fix_ranges, increment, read_cases, render, repin_templates
|
|
from openultrasast.plane.manifests import load_manifests
|
|
from openultrasast.plane.memory import MEMORY_KEY_ANNOTATION, POPULATION_ANNOTATION, SPLIT_ANNOTATION, parse_memory_key
|
|
from openultrasast.plane.tasks.repo_facts import _digest
|
|
|
|
PLANE = Path("plane")
|
|
RUN = PLANE / "runs" / "validation-46.yaml"
|
|
POPULATION = Path("benchmarks/independent/population-v2.toml")
|
|
RESULTS = Path.home() / "ousast-results"
|
|
REPOS = Path.home() / ".cache" / "openultrasast" / "independent"
|
|
PIN = "0123456789abcdef0123456789abcdef01234567"
|
|
|
|
|
|
def _mebibytes(quantity: str) -> float:
|
|
number, unit = re.fullmatch(r"([0-9.]+)(Mi|Gi)?", quantity).groups() # type: ignore[union-attr]
|
|
return float(number) * {"Mi": 1, "Gi": 1024, None: 1 / 2**20}[unit]
|
|
|
|
|
|
def test_every_manifest_under_plane_loads() -> None:
|
|
paths = sorted(p for p in PLANE.rglob("*.yaml") if "experiments" not in p.parts) # experiment manifests are not ax manifests
|
|
assert len(paths) >= 36, paths
|
|
manifests = load_manifests(paths)
|
|
assert set(manifests.models) == {"deepseek-flash", "openrouter-embedding"} # the second: embeddings only (decision engine)
|
|
assert set(TEMPLATES) <= set(manifests.tasks)
|
|
assert len(manifests.workspaces) == 30 and "validation-46" in manifests.runs
|
|
full = {t.name for t in manifests.runs["validation-46"].tasks}
|
|
for name, other in manifests.runs.items(): # check Runs (e.g. check-lmdeploy) are subsets of the measurement
|
|
assert {t.name for t in other.tasks} <= full, name
|
|
|
|
|
|
def test_model_prices_are_the_endpoint_prices() -> None:
|
|
model = load_manifests([PLANE / "models" / "deepseek-flash.yaml"]).models["deepseek-flash"]
|
|
assert model.provider == "deepseek" and model.model == "deepseek-flash"
|
|
assert model.secret_key is not None and model.secret_key.key == "DEEPSEEK_API_KEY"
|
|
assert prices_from(model.parameters) == price_of("deepseek-flash")
|
|
|
|
|
|
def test_validation_run_resolves_and_renders_every_task(tmp_path: Path) -> None:
|
|
run, manifests = reconciler.load_run(RUN)
|
|
# per case: facts, va, vb, agree, then vc, final, then features (learned-decision-engine task 1) and remember
|
|
assert len(run.tasks) == 120
|
|
population = {c["id"]: c for c in tomllib.loads(POPULATION.read_text())["case"]}
|
|
ax_names: set[str] = set()
|
|
for task in manifests.tasks.values():
|
|
assert task.image and "@sha256:" in task.image, task.metadata.name
|
|
limits = task.resources.limits if task.resources else None
|
|
assert limits and limits.cpu == "1" and _mebibytes(limits.memory or "0") <= 1536, task.metadata.name
|
|
for entry in run.tasks:
|
|
task = manifests.tasks[entry.task]
|
|
model = task.metadata.annotations.get(reconciler.MODEL_ANNOTATION)
|
|
assert model is None or model in manifests.models
|
|
for binding in task.workspaces:
|
|
workspace = manifests.workspaces[binding.name]
|
|
for name, sha in workspace.pins.items():
|
|
case_id = binding.name.removesuffix("-vulnerable")
|
|
assert (name, sha) == ("repo", population[case_id]["vulnerable"])
|
|
if entry.name.endswith(("-va", "-vb", "-vc")):
|
|
assert model == "deepseek-flash" and entry.budget and entry.budget.usd and entry.budget.calls
|
|
env = {e.name: e.value for e in task.env}
|
|
assert env["OUSAST_PASS"] == entry.name[-1]
|
|
docs = reconciler.render_task(run, entry, manifests, "http://127.0.0.1:1/")
|
|
for doc in docs:
|
|
assert not non_ax_fields(doc["spec"], AX_FIELDS[doc["kind"]], doc["kind"])
|
|
assert set(doc["metadata"]) <= {"name", "atespace"}
|
|
assert len(doc["metadata"]["name"]) <= 63, doc["metadata"]["name"]
|
|
assert docs[-1]["kind"] == "Task"
|
|
assert docs[-1]["metadata"]["name"] not in ax_names
|
|
ax_names.add(docs[-1]["metadata"]["name"])
|
|
|
|
|
|
# sha256 of the Run's entries as the first measurement ran them (plane-increment-1.json): the text after
|
|
# `` tasks:`` of plane/runs/validation-46.yaml at c5e6ed3. A rerun keeps their ``done`` state only while they match.
|
|
FIRST_RUN_ENTRIES = "412ad6171cf096d5d7f6b8e7358374077e2beabf5f4dff35a2801d37c341b97e"
|
|
|
|
|
|
def test_tiebreak_appends_thirty_tasks_and_keeps_the_first_sixty_byte_identical() -> None:
|
|
text = RUN.read_text().split(" tasks:\n", 1)[1]
|
|
first = text.split(" - name: budibase-mongo-template-nosqli-vc\n", 1)[0]
|
|
assert hashlib.sha256(first.encode()).hexdigest() == FIRST_RUN_ENTRIES
|
|
run, manifests = reconciler.load_run(RUN)
|
|
old, new, appended = run.tasks[:60], run.tasks[60:90], run.tasks[90:]
|
|
assert [e.name.rsplit("-", 1)[1] for e in appended] == ["features", "remember"] * 15
|
|
remembered = appended[1::2]
|
|
assert [e.name.rsplit("-", 1)[1] for e in old] == ["facts", "va", "vb", "agree"] * 15
|
|
assert [e.name.rsplit("-", 1)[1] for e in new] == ["vc", "final"] * 15
|
|
for vc, final in zip(new[::2], new[1::2], strict=True):
|
|
case = vc.name.removesuffix("-vc")
|
|
agree_entry = run.task(f"{case}-agree")
|
|
assert vc.inputs == {"facts": f"{case}-facts/facts.json", "only": f"{case}-agree/disputed.json"}
|
|
assert vc.depends_on == (f"{case}-agree",) and vc.budget == run.task(f"{case}-va").budget
|
|
env = {e.name: e.value for e in manifests.tasks[vc.task].env}
|
|
assert env["OUSAST_PASS"] == "c" and env["OUSAST_INPUT_CANDIDATES"] == "/workspace/inputs/candidates.json"
|
|
assert final.task == agree_entry.task, "the final agree binds the same static inputs as the first"
|
|
assert final.inputs == {**agree_entry.inputs, "pass_c": f"{case}-vc/units.jsonl"}
|
|
assert set(final.producers) == {f"{case}-va", f"{case}-vb", f"{case}-vc"}
|
|
for entry in (vc, final):
|
|
name = reconciler._ax_name(run.metadata.name, entry.name, limit=reconciler.TASK_NAME_LIMIT)
|
|
assert len(name) <= reconciler.TASK_NAME_LIMIT
|
|
for entry in appended[::2]: # learned-decision-engine task 1: model-free, the chain's artifacts in, records out
|
|
case = entry.name.removesuffix("-features")
|
|
assert entry.inputs["agreed"] == f"{case}-final/agreed.json" and entry.outputs == ("features.jsonl", "summary.json")
|
|
assert entry.budget is not None and (entry.budget.usd, entry.budget.calls) == (0, 0)
|
|
assert reconciler.MODEL_ANNOTATION not in manifests.tasks[entry.task].metadata.annotations
|
|
for entry in remembered: # harnessx-removal task 4: appended after the tie-break, model-free
|
|
case = entry.name.removesuffix("-remember")
|
|
assert entry.inputs["agreed"] == f"{case}-final/agreed.json" and entry.inputs["pass_c"] == f"{case}-vc/units.jsonl"
|
|
assert entry.inputs["features"] == f"{case}-features/features.jsonl"
|
|
assert entry.budget is not None and (entry.budget.usd, entry.budget.calls) == (0, 0)
|
|
assert reconciler.MODEL_ANNOTATION not in manifests.tasks[entry.task].metadata.annotations
|
|
assert len(reconciler._ax_name(run.metadata.name, entry.name, limit=reconciler.TASK_NAME_LIMIT)) <= reconciler.TASK_NAME_LIMIT
|
|
|
|
|
|
def test_inputs_carry_the_validation_set() -> None:
|
|
manifests = load_manifests(sorted((PLANE / "workspaces").glob("*-inputs.yaml")))
|
|
assert len(manifests.workspaces) == 15
|
|
candidates = kept = in_set = before = 0
|
|
usd = triage = 0.0
|
|
for workspace in manifests.workspaces.values():
|
|
files = {f.path: json.loads(f.content) for f in workspace.files}
|
|
assert set(files) == {"candidates.json", "functions.json", "case.json", "triage.json"}
|
|
candidates += len(files["triage.json"]["candidates"])
|
|
kept += len(files["candidates.json"]["candidates"])
|
|
assert [[r["path"], r["function"], r["line"]] for r in files["functions.json"]] == files["candidates.json"]["candidates"]
|
|
case = files["case.json"]
|
|
assert case["ranges"] and all(first <= last for spans in case["ranges"].values() for first, last in spans), case["id"]
|
|
assert set(case["sites_in_set"]) <= set(case["sites"])
|
|
in_set += len(case["sites_in_set"])
|
|
before += case["cost"]["candidates_before_triage"]
|
|
usd += case["cost"]["recorded_usd"]
|
|
triage += case["cost"]["recorded_triage_usd"]
|
|
assert 0 < case["cost"]["recorded_triage_usd"] <= case["cost"]["recorded_usd"], case["id"]
|
|
assert (candidates, kept, in_set, before) == (46, 43, 20, 46)
|
|
# the reference's $1.01 for 46 candidates, two passes, triage included; triage was about 9% of it
|
|
assert round(usd / before, 3) == 0.022 and 0.05 < triage / usd < 0.15
|
|
|
|
|
|
def test_templates_are_pinned_to_one_runner_image() -> None:
|
|
images = {task.image for task in load_manifests(sorted((PLANE / "tasks").glob("*.yaml"))).tasks.values()}
|
|
assert len(images) == 1 and re.fullmatch(r"\S+@sha256:[0-9a-f]{64}", images.pop() or "")
|
|
|
|
|
|
def _committed_command() -> str:
|
|
first = RUN.read_text().splitlines()[0]
|
|
found = re.search(r"`([^`]+)`", first)
|
|
assert found, first
|
|
return found.group(1)
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
not (RESULTS / "independent-v2-batched-check" / "scans").is_dir() or not REPOS.is_dir(), reason="recorded results not on this host"
|
|
)
|
|
def test_committed_manifests_match_the_generator() -> None:
|
|
templates = load_manifests([PLANE / "tasks" / f"{name}.yaml" for name in TEMPLATES]).tasks
|
|
cases = read_cases(
|
|
RESULTS / "independent-v2-sets" / "validation.json",
|
|
RESULTS / "independent-v2-modelsinks",
|
|
RESULTS / "independent-v2-batched-check" / "scans",
|
|
POPULATION,
|
|
REPOS,
|
|
)
|
|
rendered = render(cases, templates, "validation-46", _committed_command(), population=POPULATION.stem, split="validation")
|
|
for relative, text in rendered.items():
|
|
assert (PLANE / relative).read_text() == text, relative
|
|
|
|
|
|
# --- the generator on a synthetic set ---------------------------------------------------------------------------
|
|
|
|
|
|
def _fixture(root: Path) -> dict[str, Path]:
|
|
population = root / "population.toml"
|
|
population.write_text(
|
|
"\n".join(
|
|
f'[[case]]\nid = "{cid}"\nrepo = "https://example.com/{cid}"\nfamily = "injection"\n'
|
|
f'vulnerable = "{PIN}"\nsites = ["a.py::run"]\n'
|
|
for cid in ("beta", "alpha")
|
|
)
|
|
)
|
|
candidates, scans = root / "cands", root / "scans"
|
|
candidates.mkdir()
|
|
scans.mkdir()
|
|
rows = [["a.py", "run", 9], ["a.py", "run", 3], ["a.py", "helper", 20], ["b.py", "other", 1]]
|
|
for cid in ("alpha", "beta"):
|
|
(candidates / f"{cid}.json").write_text(json.dumps({"id": cid, "family": "injection", "candidates": rows}))
|
|
asked = [["a.py", "helper", 20], ["a.py", "run", 9]]
|
|
scan = {"path": "a.py", "candidates": asked, "kept": ["run"], "triage": {"helper": "internal"}}
|
|
(scans / f"{cid}--vulnerable_a.jsonl").write_text(json.dumps({**scan, "usd": 0.5}) + "\n")
|
|
validation = root / "set.json"
|
|
validation.write_text(json.dumps({"beta": [["a.py", "run"], ["a.py", "helper"]], "alpha": [["a.py", "helper"], ["a.py", "run"]]}))
|
|
plane = root / "plane"
|
|
(plane / "tasks").mkdir(parents=True)
|
|
for name in TEMPLATES:
|
|
shutil.copy(PLANE / "tasks" / f"{name}.yaml", plane / "tasks")
|
|
shutil.copytree(PLANE / "models", plane / "models")
|
|
return {"population": population, "set": validation, "candidates": candidates, "scans": scans, "plane": plane}
|
|
|
|
|
|
def _generate(paths: dict[str, Path], plane: Path) -> list[Path]:
|
|
return increment(
|
|
paths["population"], paths["set"], paths["candidates"], paths["scans"], plane=plane, run_name="check", command="ousast plane test"
|
|
)
|
|
|
|
|
|
def test_generator_is_deterministic_and_follows_the_reference(tmp_path: Path) -> None:
|
|
paths = _fixture(tmp_path)
|
|
other = tmp_path / "again"
|
|
shutil.copytree(paths["plane"], other)
|
|
first, second = _generate(paths, paths["plane"]), _generate(paths, other)
|
|
assert [p.relative_to(paths["plane"]) for p in first] == [p.relative_to(other) for p in second]
|
|
assert all(a.read_bytes() == b.read_bytes() for a, b in zip(first, second, strict=True))
|
|
assert first[0].read_text().startswith("# generated by `ousast plane test`")
|
|
run, manifests = reconciler.load_run(paths["plane"] / "runs" / "check.yaml")
|
|
assert [t.name for t in run.tasks][:4] == ["alpha-facts", "alpha-va", "alpha-vb", "alpha-agree"]
|
|
inputs = {f.path: json.loads(f.content) for f in manifests.workspaces["alpha-inputs"].files}
|
|
assert inputs["candidates.json"]["candidates"] == [["a.py", "run", 9]] # last line of a repeated pair; triage applied
|
|
assert inputs["triage.json"]["candidates"] == [["a.py", "helper", 20], ["a.py", "run", 9]]
|
|
assert run.task("alpha-va").budget is not None and run.task("alpha-va").budget.usd == 0.38 # 1.5 * 0.5 / 2, cent up
|
|
assert yaml.safe_load(first[0].read_text())["metadata"]["annotations"] == {"openultrasast.io/git-commits": f"repo={PIN}"}
|
|
# memory (harnessx-removal §4): the facts Task names its reuse key, the Run its population and split
|
|
key = parse_memory_key(manifests.tasks["repo-facts-alpha"].metadata.annotations[MEMORY_KEY_ANNOTATION])
|
|
image = manifests.tasks["repo-facts-alpha"].image or ""
|
|
assert key == {"repo": "example.com/alpha", "pin": PIN, "candidates": _digest(["run"]), "image": image.rpartition("@")[2]}
|
|
assert all(MEMORY_KEY_ANNOTATION not in t.metadata.annotations for n, t in manifests.tasks.items() if not n.startswith("repo-facts-"))
|
|
assert dict(run.metadata.annotations) == {POPULATION_ANNOTATION: "population", SPLIT_ANNOTATION: "set"}
|
|
|
|
|
|
def test_generator_refuses_a_set_the_recorded_scans_did_not_ask(tmp_path: Path) -> None:
|
|
paths = _fixture(tmp_path)
|
|
paths["set"].write_text(json.dumps({"alpha": [["a.py", "run"], ["b.py", "other"]]}))
|
|
with pytest.raises(ValueError, match="recorded scans asked"):
|
|
_generate(paths, paths["plane"])
|
|
(paths["scans"] / "alpha--vulnerable_a.jsonl").unlink()
|
|
with pytest.raises(ValueError, match="no recorded triage"):
|
|
_generate(paths, paths["plane"])
|
|
|
|
|
|
def _git(repo: Path, *args: str) -> str:
|
|
done = subprocess.run(
|
|
["git", "-C", str(repo), "-c", "user.name=t", "-c", "user.email=t@t", *args], capture_output=True, text=True, check=True
|
|
)
|
|
return done.stdout.strip()
|
|
|
|
|
|
def test_fix_ranges_are_the_old_side_of_the_fix_diff(tmp_path: Path) -> None:
|
|
repo = tmp_path / "repo"
|
|
repo.mkdir()
|
|
_git(repo, "init", "-q")
|
|
(repo / "a.py").write_text("".join(f"line {i}\n" for i in range(1, 31)))
|
|
_git(repo, "add", "-A")
|
|
_git(repo, "commit", "-qm", "vulnerable")
|
|
vulnerable = _git(repo, "rev-parse", "HEAD")
|
|
(repo / "a.py").write_text("".join(f"line {i}\n" if i not in (3, 4, 20) else "fixed\n" for i in range(1, 31)) + "added\n")
|
|
_git(repo, "commit", "-qam", "fixed")
|
|
assert fix_ranges(repo, vulnerable, _git(repo, "rev-parse", "HEAD")) == {"a.py": [(3, 4), (20, 20), (30, 30)]}
|
|
with pytest.raises(ValueError, match="no repository"):
|
|
fix_ranges(tmp_path / "absent", vulnerable, vulnerable)
|
|
|
|
|
|
def test_case_record_carries_sites_in_set_and_cost_and_the_image_is_repinned(tmp_path: Path) -> None:
|
|
paths = _fixture(tmp_path)
|
|
digest = "localhost:5001/ousast-runner@sha256:" + "ab" * 32
|
|
(tmp_path / "runner-image").write_text(digest + "\n")
|
|
increment(
|
|
paths["population"], paths["set"], paths["candidates"], paths["scans"], plane=paths["plane"], run_name="check",
|
|
command="ousast plane test", runner_image=tmp_path / "runner-image",
|
|
) # fmt: skip
|
|
manifests = load_manifests(sorted(paths["plane"].rglob("*.yaml")))
|
|
assert {t.image for t in manifests.tasks.values()} == {digest}
|
|
case = json.loads(next(f.content for f in manifests.workspaces["alpha-inputs"].files if f.path == "case.json"))
|
|
assert "ranges" not in case and case["sites_in_set"] == ["a.py::run"] # no --repos: agree reports not assessed
|
|
assert case["cost"]["candidates_before_triage"] == 2 and case["cost"]["recorded_triage_usd"] == 0.5 # no hunt usage recorded
|
|
(tmp_path / "runner-image").write_text("localhost:5001/ousast-runner:dev\n")
|
|
with pytest.raises(ValueError, match="digest-pinned"):
|
|
repin_templates(paths["plane"], tmp_path / "runner-image")
|
|
|
|
|
|
def test_repin_reads_the_profiles_images_file(tmp_path: Path) -> None:
|
|
"""plane-on-kubernetes 1.4: ``ops/ax/up.sh`` writes ``kind-images.json`` ({"runner": ...}); repin reads it, the
|
|
older one-line pin still reads, and a file without a runner key or without a digest is refused by name."""
|
|
paths = _fixture(tmp_path)
|
|
digest = "localhost:5001/ousast-runner@sha256:" + "cd" * 32
|
|
images = tmp_path / "kind-images.json"
|
|
images.write_text(json.dumps({"runner": digest}) + "\n")
|
|
assert repin_templates(paths["plane"], images) == digest
|
|
assert {t.image for t in load_manifests(sorted((paths["plane"] / "tasks").glob("*.yaml"))).tasks.values()} == {digest}
|
|
images.write_text(json.dumps({"engine": digest}))
|
|
with pytest.raises(ValueError, match="no runner image"):
|
|
repin_templates(paths["plane"], images)
|
|
images.write_text(json.dumps({"runner": "localhost:5001/ousast-runner:dev"}))
|
|
with pytest.raises(ValueError, match="not digest-pinned"):
|
|
repin_templates(paths["plane"], images)
|
|
|
|
|
|
def test_workspaces_go_to_the_profiles_atespace(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
"""plane-on-kubernetes 1.2: ``generate.ATESPACE`` is a default; ``increment`` takes the profile's atespace."""
|
|
paths = _fixture(tmp_path)
|
|
(tmp_path / "p.toml").write_text('exec = "local"\nkube_context = "c"\nregistry = "r"\nimages = "i.json"\natespace = "team-a"\n')
|
|
monkeypatch.setenv("OUSAST_PLANE_PROFILE", str(tmp_path / "p.toml"))
|
|
increment(paths["population"], paths["set"], paths["candidates"], paths["scans"], plane=paths["plane"], run_name="sp", command="t")
|
|
manifests = load_manifests(sorted((paths["plane"] / "workspaces").glob("*.yaml")))
|
|
assert manifests.workspaces and {w.metadata.atespace for w in manifests.workspaces.values()} == {"team-a"}
|
|
increment(
|
|
paths["population"],
|
|
paths["set"],
|
|
paths["candidates"],
|
|
paths["scans"],
|
|
plane=paths["plane"],
|
|
run_name="sp",
|
|
command="t",
|
|
atespace="z",
|
|
)
|
|
manifests = load_manifests(sorted((paths["plane"] / "workspaces").glob("*.yaml")))
|
|
assert {w.metadata.atespace for w in manifests.workspaces.values()} == {"z"}, "an explicit atespace wins over the profile"
|