mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-16 19:59:26 +00:00
feat(pre-push-safety-net): key evidence reuse by compatible semantics
This commit is contained in:
@@ -12,10 +12,11 @@ import tempfile
|
||||
import time
|
||||
from collections.abc import Iterator
|
||||
from contextlib import ExitStack, contextmanager
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import asdict, dataclass
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from openultrasast.contracts import Contract
|
||||
from openultrasast.cpg.artifact import digest_value, graph_bytes, read_bytes
|
||||
from openultrasast.cpg.backend import _DeadlineExpired, _remove_owned_tree
|
||||
from openultrasast.model.contracts import ExecutionBudget
|
||||
@@ -123,6 +124,24 @@ class ArtifactCache:
|
||||
self.hits += 1
|
||||
yield entry
|
||||
|
||||
def get_json(self, key: str, *, kind: str, budget: ExecutionBudget) -> Any:
|
||||
with self.lookup(key, budget) as entry:
|
||||
if entry is None or entry.metadata != {"kind": kind} or entry.path.stat().st_size > 32 * 1024**2:
|
||||
return None
|
||||
try:
|
||||
return json.loads(read_bytes(entry.path, budget.deadline_monotonic))
|
||||
except (OSError, ValueError, TimeoutError):
|
||||
return None
|
||||
|
||||
def put_json(self, key: str, payload: object, *, kind: str, budget: ExecutionBudget, complete: bool) -> bool:
|
||||
if not complete or time.monotonic() >= budget.deadline_monotonic:
|
||||
return False
|
||||
try:
|
||||
data = json.dumps(payload, sort_keys=True, allow_nan=False).encode()
|
||||
except (TypeError, ValueError):
|
||||
return False
|
||||
return self.publish(key, data, {"kind": kind}, budget, complete=complete)
|
||||
|
||||
def _remove(self, root: Path, budget: ExecutionBudget) -> None:
|
||||
_remove_owned_tree(root, budget.deadline_monotonic)
|
||||
|
||||
@@ -215,3 +234,37 @@ class ArtifactCache:
|
||||
_remove_owned_tree(pending, budget.deadline_monotonic + budget.cancellation_allowance_seconds)
|
||||
except (OSError, _DeadlineExpired):
|
||||
self.last_reason = "cache_partial_cleanup_incomplete"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SemanticKeys(Contract):
|
||||
facts: str
|
||||
queries: str
|
||||
configuration: str
|
||||
ranking: str
|
||||
admission: str
|
||||
model: str
|
||||
eligibility: str
|
||||
mode: str
|
||||
|
||||
def query(self, *, graph: str, kind: str, request: object, context: object) -> str:
|
||||
return digest_value(
|
||||
{
|
||||
"layer": "query-v1",
|
||||
"graph": graph,
|
||||
"kind": kind,
|
||||
"request": request,
|
||||
"context": context,
|
||||
"facts": self.facts,
|
||||
"queries": self.queries,
|
||||
"configuration": self.configuration,
|
||||
}
|
||||
)
|
||||
|
||||
def comparison(self, *, base: str, head: str, context: object, evidence: object) -> str:
|
||||
return digest_value(
|
||||
{"layer": "comparison-v1", "base": base, "head": head, "context": context, "evidence": evidence, "semantics": asdict(self)}
|
||||
)
|
||||
|
||||
def result(self, *, comparisons: object, scope: object) -> str:
|
||||
return digest_value({"layer": "result-v1", "comparisons": comparisons, "scope": scope, "semantics": asdict(self)})
|
||||
|
||||
@@ -6,7 +6,7 @@ import hashlib
|
||||
import json
|
||||
import time
|
||||
from collections.abc import Sequence
|
||||
from dataclasses import dataclass, replace
|
||||
from dataclasses import asdict, dataclass, replace
|
||||
from pathlib import Path, PurePosixPath
|
||||
from string import Formatter
|
||||
from typing import Any, Literal
|
||||
@@ -20,6 +20,7 @@ from openultrasast.model.pipeline import ModelFinding, _arbitrate_all
|
||||
from openultrasast.model.regions import ScanRegion
|
||||
from openultrasast.model.scan import ModelScanResult, ScanBudget, _prefetched, _spec_for, scan_repository
|
||||
from openultrasast.model.specs import ConfigSpec, DominanceSpec, TaintSpec
|
||||
from openultrasast.push.cache import ArtifactCache, SemanticKeys
|
||||
from openultrasast.push.contracts import ComparisonAnalysis, PushComparison, PushResult
|
||||
|
||||
|
||||
@@ -247,6 +248,92 @@ def compare_evidence(
|
||||
head_semantics: str,
|
||||
base_semantics: str,
|
||||
execution_budget: ExecutionBudget | None = None,
|
||||
cache: ArtifactCache | None = None,
|
||||
cache_semantics: SemanticKeys | None = None,
|
||||
) -> tuple[CandidateDelta, ...]:
|
||||
"""Reuse completed comparison evidence; alert eligibility is always applied later."""
|
||||
|
||||
def complete(scan: ModelScanResult | None) -> bool:
|
||||
return bool(
|
||||
scan
|
||||
and scan.scope
|
||||
and scan.scope.population_complete
|
||||
and not scan.degradations
|
||||
and not scan.scope.unresolved_boundaries
|
||||
and all(item.reason == "tier_zero" for item in scan.scope.deferred)
|
||||
and all(item.status == "completed" for item in scan.question_outcomes)
|
||||
and {item.identity for item in scan.scope.selected} == {item.identity for item in scan.question_outcomes}
|
||||
)
|
||||
|
||||
key = None
|
||||
if (
|
||||
cache is not None
|
||||
and cache_semantics is not None
|
||||
and execution_budget is not None
|
||||
and time.monotonic() < execution_budget.deadline_monotonic
|
||||
and context.base_revision is not None
|
||||
and not context.unresolved_boundaries
|
||||
and head_semantics == base_semantics
|
||||
and complete(head)
|
||||
and complete(base)
|
||||
):
|
||||
from openultrasast.cpg.artifact import read_bytes
|
||||
|
||||
def evidence(scan: ModelScanResult) -> dict[str, object]:
|
||||
return {
|
||||
name: value
|
||||
for name, value in asdict(scan).items()
|
||||
if name not in {"seconds", "build_seconds", "query_seconds", "query_seconds_by_kind"}
|
||||
}
|
||||
|
||||
assert base is not None
|
||||
try:
|
||||
key = cache_semantics.comparison(
|
||||
base=context.base_revision,
|
||||
head=context.head_revision,
|
||||
context=context.to_payload(),
|
||||
evidence={
|
||||
"head": evidence(head),
|
||||
"base": evidence(base),
|
||||
"semantics": head_semantics,
|
||||
"policy": hashlib.sha256(read_bytes(Path(__file__), execution_budget.deadline_monotonic)).hexdigest(),
|
||||
},
|
||||
)
|
||||
raw = cache.get_json(key, kind="comparison", budget=execution_budget)
|
||||
if isinstance(raw, list):
|
||||
decoded = tuple(CandidateDelta.from_payload(item) for item in raw)
|
||||
if time.monotonic() < execution_budget.deadline_monotonic and all(
|
||||
item.novelty != "unknown"
|
||||
and item.base_revision == context.base_revision
|
||||
and item.head_revision == context.head_revision
|
||||
and item.semantics == head_semantics
|
||||
for item in decoded
|
||||
):
|
||||
return decoded
|
||||
except (OSError, ValueError, TimeoutError):
|
||||
key = None
|
||||
result = _compare_evidence(
|
||||
head, base, context=context, head_semantics=head_semantics, base_semantics=base_semantics, execution_budget=execution_budget
|
||||
)
|
||||
if key is not None and cache is not None and execution_budget is not None:
|
||||
cache.put_json(
|
||||
key,
|
||||
[item.to_payload() for item in result],
|
||||
kind="comparison",
|
||||
budget=execution_budget,
|
||||
complete=all(item.novelty != "unknown" for item in result),
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
def _compare_evidence(
|
||||
head: ModelScanResult,
|
||||
base: ModelScanResult | None,
|
||||
*,
|
||||
context: ChangeContext,
|
||||
head_semantics: str,
|
||||
base_semantics: str,
|
||||
execution_budget: ExecutionBudget | None = None,
|
||||
) -> tuple[CandidateDelta, ...]:
|
||||
"""Absence counts only in completed comparable scope, never from scan silence."""
|
||||
head_ops, base_ops = _operations(head, execution_budget), _operations(base, execution_budget) if base else ()
|
||||
@@ -356,6 +443,8 @@ def compare_targeted_base(
|
||||
scan_budget: ScanBudget,
|
||||
head_semantics: str,
|
||||
base_semantics: str,
|
||||
cache: ArtifactCache | None = None,
|
||||
cache_semantics: SemanticKeys | None = None,
|
||||
) -> DeltaComparison:
|
||||
"""Re-run counterparts of the ranker's selected head questions through the existing driver.
|
||||
|
||||
@@ -398,7 +487,14 @@ def compare_targeted_base(
|
||||
except Exception as error: # noqa: BLE001 -- comparison failure is coverage, not a new defect
|
||||
reasons.append(f"base_scan_failed:{type(error).__name__}")
|
||||
candidates = compare_evidence(
|
||||
head, base, context=context, head_semantics=head_semantics, base_semantics=base_semantics, execution_budget=execution_budget
|
||||
head,
|
||||
base,
|
||||
context=context,
|
||||
head_semantics=head_semantics,
|
||||
base_semantics=base_semantics,
|
||||
execution_budget=execution_budget,
|
||||
cache=cache,
|
||||
cache_semantics=cache_semantics,
|
||||
)
|
||||
reasons.extend(c.reason for c in candidates if c.novelty == "unknown")
|
||||
return DeltaComparison(candidates, base, head_semantics, tuple(dict.fromkeys(reasons)))
|
||||
|
||||
Reference in New Issue
Block a user