mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-16 11:49:28 +00:00
feat(three-stage-scan): run or skip deep regression with visible degradation
This commit is contained in:
@@ -3,7 +3,7 @@ from typing import Any
|
||||
|
||||
import pytest
|
||||
|
||||
from openultrasast.sandbox import FakeSandboxProbe, SandboxProbe
|
||||
from openultrasast.sandbox import FakeSandboxProbe, SandboxProbe, resolve_sandbox_probe
|
||||
from openultrasast.sandbox import probe as probe_mod
|
||||
|
||||
|
||||
@@ -21,6 +21,25 @@ def test_fake_probe_can_be_forced_on() -> None:
|
||||
assert probe.available() is True
|
||||
|
||||
|
||||
def test_resolve_probe_forced_off_via_env(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setenv(probe_mod.PROBE_ENV, "0")
|
||||
probe = resolve_sandbox_probe()
|
||||
assert isinstance(probe, FakeSandboxProbe)
|
||||
assert probe.available() is False
|
||||
|
||||
|
||||
def test_resolve_probe_forced_on_via_env(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setenv(probe_mod.PROBE_ENV, "1")
|
||||
probe = resolve_sandbox_probe()
|
||||
assert isinstance(probe, FakeSandboxProbe)
|
||||
assert probe.available() is True
|
||||
|
||||
|
||||
def test_resolve_probe_defaults_to_real_probe(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.delenv(probe_mod.PROBE_ENV, raising=False)
|
||||
assert isinstance(resolve_sandbox_probe(), SandboxProbe)
|
||||
|
||||
|
||||
def test_real_probe_is_available_when_docker_info_succeeds() -> None:
|
||||
def runner(*_args: object, **_kwargs: object) -> subprocess.CompletedProcess[str]:
|
||||
return _completed(0)
|
||||
|
||||
@@ -8,7 +8,15 @@ from typing import Any
|
||||
|
||||
import pytest
|
||||
|
||||
from openultrasast.sandbox import DockerCliRunner, FakeSandboxRunner, SandboxJob, SandboxResult, SandboxRunner, build_docker_argv
|
||||
from openultrasast.sandbox import (
|
||||
DockerCliRunner,
|
||||
FakeSandboxRunner,
|
||||
SandboxJob,
|
||||
SandboxResult,
|
||||
SandboxRunner,
|
||||
build_docker_argv,
|
||||
resolve_sandbox_runner,
|
||||
)
|
||||
from openultrasast.sandbox import runner as runner_mod
|
||||
|
||||
|
||||
@@ -187,6 +195,16 @@ def test_fake_sandbox_runner_records_jobs_and_returns_programmed_result(tmp_path
|
||||
assert runner.jobs == [job]
|
||||
|
||||
|
||||
def test_resolve_runner_fake_via_env(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setenv(runner_mod.RUNNER_ENV, "fake")
|
||||
assert isinstance(resolve_sandbox_runner(), FakeSandboxRunner)
|
||||
|
||||
|
||||
def test_resolve_runner_defaults_to_docker_cli(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.delenv(runner_mod.RUNNER_ENV, raising=False)
|
||||
assert isinstance(resolve_sandbox_runner(), DockerCliRunner)
|
||||
|
||||
|
||||
def test_fake_sandbox_runner_can_be_reprogrammed(tmp_path: Path) -> None:
|
||||
runner = FakeSandboxRunner()
|
||||
first = _job(tmp_path, command=("echo", "one"))
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
"""Deep mode: skip regression when the sandbox is missing; record verdicts with a fake runner (task 6.3)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from openultrasast.cli import main
|
||||
from openultrasast.sandbox import runner as sandbox_runner
|
||||
|
||||
_VULN = "@app.route('/admin')\ndef admin():\n return eval(request.data)\n"
|
||||
|
||||
|
||||
def _repo(tmp_path: Path) -> Path:
|
||||
repo = tmp_path / "repo"
|
||||
repo.mkdir()
|
||||
(repo / "app.py").write_text(_VULN)
|
||||
return repo
|
||||
|
||||
|
||||
def _fail(message: str): # type: ignore[no-untyped-def]
|
||||
def _inner(*args: object, **kwargs: object) -> object:
|
||||
raise AssertionError(message)
|
||||
|
||||
return _inner
|
||||
|
||||
|
||||
def _guard_docker(monkeypatch: pytest.MonkeyPatch) -> list[list[str]]:
|
||||
docker_argv: list[list[str]] = []
|
||||
original_run = subprocess.run
|
||||
|
||||
def guarded_run(command: object, *args: object, **kwargs: object) -> object:
|
||||
argv = list(command) if isinstance(command, list | tuple) else [command]
|
||||
if argv and Path(str(argv[0])).name == "docker":
|
||||
docker_argv.append([str(item) for item in argv])
|
||||
raise AssertionError(f"deep scan test must not start docker: {argv}")
|
||||
return original_run(command, *args, **kwargs)
|
||||
|
||||
monkeypatch.setattr(subprocess, "run", guarded_run)
|
||||
monkeypatch.setattr(sandbox_runner.DockerCliRunner, "run", _fail("deep scan test must not use DockerCliRunner"))
|
||||
return docker_argv
|
||||
|
||||
|
||||
def _latest_run(repo: Path) -> Path:
|
||||
return sorted((repo / ".runs").iterdir())[-1]
|
||||
|
||||
|
||||
def test_deep_scan_skips_regress_when_sandbox_unavailable(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
repo = _repo(tmp_path)
|
||||
monkeypatch.setenv("OPENULTRASAST_RUNS_DIR", ".runs")
|
||||
monkeypatch.setenv("OPENULTRASAST_SANDBOX_PROBE", "0")
|
||||
docker_argv = _guard_docker(monkeypatch)
|
||||
|
||||
assert main(["scan", str(repo), "--mode", "deep"]) == 0
|
||||
|
||||
run_dir = _latest_run(repo)
|
||||
manifest = json.loads((run_dir / "manifest.json").read_text())
|
||||
stages = manifest["stages"]
|
||||
degradations = manifest.get("degradations", [])
|
||||
|
||||
assert (run_dir / "complexity_map.json").is_file()
|
||||
assert not (run_dir / "verdicts.json").exists()
|
||||
assert docker_argv == []
|
||||
assert stages["requested"] == ["static", "map", "regress"]
|
||||
assert stages["completed"] == ["static", "map"]
|
||||
assert {"stage": "regress", "reason": "sandbox_unavailable"} in stages["skipped"]
|
||||
assert any(entry.get("stage") == "regress" and entry.get("reason") == "sandbox_unavailable" for entry in degradations)
|
||||
|
||||
|
||||
def test_deep_scan_with_fake_runner_writes_verdicts(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
repo = _repo(tmp_path)
|
||||
monkeypatch.setenv("OPENULTRASAST_RUNS_DIR", ".runs")
|
||||
monkeypatch.setenv("OPENULTRASAST_SANDBOX_PROBE", "1")
|
||||
monkeypatch.setenv("OPENULTRASAST_SANDBOX_RUNNER", "fake")
|
||||
docker_argv = _guard_docker(monkeypatch)
|
||||
|
||||
assert main(["scan", str(repo), "--mode", "deep"]) == 0
|
||||
|
||||
run_dir = _latest_run(repo)
|
||||
verdicts_path = run_dir / "verdicts.json"
|
||||
manifest = json.loads((run_dir / "manifest.json").read_text())
|
||||
payload = json.loads(verdicts_path.read_text())
|
||||
stages = manifest["stages"]
|
||||
|
||||
assert docker_argv == []
|
||||
assert verdicts_path.is_file()
|
||||
assert (run_dir / "complexity_map.json").is_file()
|
||||
assert stages["requested"] == ["static", "map", "regress"]
|
||||
assert stages["completed"] == ["static", "map", "regress"]
|
||||
assert not any(entry.get("reason") == "sandbox_unavailable" for entry in stages["skipped"])
|
||||
assert isinstance(payload["verdicts"], list)
|
||||
assert payload["verdicts"]
|
||||
for item in payload["verdicts"]:
|
||||
assert item["verdict"] in {"triggerable", "not_triggerable", "already_covered", "inconclusive"}
|
||||
assert "reason" in item
|
||||
assert "path" in item
|
||||
assert manifest["artifacts"]["verdicts"] == "verdicts.json"
|
||||
assert manifest["artifacts"]["complexity_map"] == "complexity_map.json"
|
||||
Reference in New Issue
Block a user