mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-11-13 01:48:17 +00:00
feat(access-control-prover): integrate reviewed entry facts
This commit is contained in:
@@ -389,7 +389,7 @@ its final resolution, dominance, fact-acquisition and measurement semantics with
|
|||||||
- _Requirements: 7.2, 7.4, 12.2, 12.3, 13.7_
|
- _Requirements: 7.2, 7.4, 12.2, 12.3, 13.7_
|
||||||
- _Boundary: Downstream Adapter Conformance Handoff_
|
- _Boundary: Downstream Adapter Conformance Handoff_
|
||||||
- _Depends: 14.7_
|
- _Depends: 14.7_
|
||||||
- [ ] 14.9 Migrate the production entry caller and prove zero bypasses
|
- [x] 14.9 Migrate the production entry caller and prove zero bypasses
|
||||||
- Wire the externally bootstrapped authority-bearing adapter into the target-aware `mapping/legacy.py` entry path while keeping `analyze_entry_points` stable for its callers.
|
- Wire the externally bootstrapped authority-bearing adapter into the target-aware `mapping/legacy.py` entry path while keeping `analyze_entry_points` stable for its callers.
|
||||||
- Retain the legacy projection only for compatibility recognition; prohibit it from supplying v2 evidence, readiness or admission.
|
- Retain the legacy projection only for compatibility recognition; prohibit it from supplying v2 evidence, readiness or admission.
|
||||||
- Run import/caller and artifact-writer censuses proving no production path bypasses authority or treats legacy projection output as v2 evidence.
|
- Run import/caller and artifact-writer censuses proving no production path bypasses authority or treats legacy projection output as v2 evidence.
|
||||||
|
|||||||
@@ -0,0 +1,152 @@
|
|||||||
|
"""Authority-bearing integration of reviewed entry facts at the mapping boundary."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import re
|
||||||
|
from collections.abc import Mapping, Sequence
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from openultrasast.core import family
|
||||||
|
from openultrasast.core.factpack import FactPack, StructuralRequirementKind
|
||||||
|
from openultrasast.mapping.entry_fact_matcher import EntryFactMatchResult, match_entry_fact
|
||||||
|
from openultrasast.mapping.entry_fact_structure import candidates, read_source_structure
|
||||||
|
from openultrasast.mapping.entry_packs import load_installed_entry_fact_authority
|
||||||
|
from openultrasast.mapping.fact_authority import EntryFactAuthorityBundle, EntryFactAuthorityContext
|
||||||
|
from openultrasast.mapping.fact_match_store import FactMatchArtifactWriter
|
||||||
|
from openultrasast.mapping.installed_fact_authority import DigestDirectoryResolver, fact_pack_bytes, installed_authority_bytes
|
||||||
|
from openultrasast.preprocess import FileTarget
|
||||||
|
|
||||||
|
_DIGEST = re.compile(r"[0-9a-f]{64}")
|
||||||
|
_LANGUAGES = frozenset(("javascript", "typescript"))
|
||||||
|
|
||||||
|
|
||||||
|
class EntryFactIntegrationError(ValueError):
|
||||||
|
"""The production mapping boundary cannot run reviewed fact matching safely."""
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_contract_and_packs(supplied: EntryFactAuthorityBundle, installed: EntryFactAuthorityBundle) -> None:
|
||||||
|
if type(supplied.contract) is not family.FamilyContract:
|
||||||
|
raise TypeError("authority contract must use the exact FamilyContract type")
|
||||||
|
try:
|
||||||
|
supplied_contract = family.family_contract_digest(supplied.contract)
|
||||||
|
installed_contract = family.family_contract_digest(installed.contract)
|
||||||
|
except (family.FamilyContractError, TypeError, ValueError) as exc:
|
||||||
|
raise EntryFactIntegrationError("authority contract cannot be validated") from exc
|
||||||
|
if supplied_contract != installed_contract:
|
||||||
|
raise EntryFactIntegrationError("authority contract differs from the installed reviewed contract")
|
||||||
|
if type(supplied.packs) is not tuple or len(supplied.packs) != len(installed.packs):
|
||||||
|
raise EntryFactIntegrationError("authority pack tuple differs from the installed reviewed packs")
|
||||||
|
for supplied_pack, installed_pack in zip(supplied.packs, installed.packs, strict=True):
|
||||||
|
if type(supplied_pack) is not FactPack or type(supplied_pack.language) is not str:
|
||||||
|
raise TypeError("authority packs must use exact FactPack and language types")
|
||||||
|
if supplied_pack.language != installed_pack.language or fact_pack_bytes(supplied_pack) != fact_pack_bytes(installed_pack):
|
||||||
|
raise EntryFactIntegrationError("authority pack body differs from the installed reviewed pack")
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_inputs(
|
||||||
|
authority: EntryFactAuthorityContext,
|
||||||
|
writer: FactMatchArtifactWriter,
|
||||||
|
evaluation_population_digest: str,
|
||||||
|
) -> EntryFactAuthorityContext:
|
||||||
|
if type(authority) is not EntryFactAuthorityContext:
|
||||||
|
raise TypeError("authority must be an authenticated EntryFactAuthorityContext")
|
||||||
|
if type(authority.bundle) is not EntryFactAuthorityBundle:
|
||||||
|
raise TypeError("authority bundle must use the exact reviewed bundle type")
|
||||||
|
if type(writer) is not FactMatchArtifactWriter:
|
||||||
|
raise TypeError("writer must be a FactMatchArtifactWriter")
|
||||||
|
if writer.store_identity not in authority.bundle.review_policy.trusted_store_identities:
|
||||||
|
raise EntryFactIntegrationError("fact-match writer does not use a trusted store")
|
||||||
|
if not isinstance(evaluation_population_digest, str) or _DIGEST.fullmatch(evaluation_population_digest) is None:
|
||||||
|
raise EntryFactIntegrationError("evaluation population digest is invalid")
|
||||||
|
installed = load_installed_entry_fact_authority(authority.custody)
|
||||||
|
_validate_contract_and_packs(authority.bundle, installed.bundle)
|
||||||
|
if installed_authority_bytes(authority.bundle) != installed_authority_bytes(installed.bundle):
|
||||||
|
raise EntryFactIntegrationError("authority context does not equal externally authenticated installed authority")
|
||||||
|
supplied_resolver, installed_resolver = authority.resolver, installed.resolver
|
||||||
|
if type(supplied_resolver) is not DigestDirectoryResolver or type(installed_resolver) is not DigestDirectoryResolver:
|
||||||
|
raise EntryFactIntegrationError("authority resolver is not the exact installed resolver type")
|
||||||
|
if (
|
||||||
|
type(supplied_resolver.root) is not type(installed_resolver.root)
|
||||||
|
or supplied_resolver.root.parts != installed_resolver.root.parts
|
||||||
|
or type(supplied_resolver.store_identity) is not str
|
||||||
|
or supplied_resolver.store_identity != installed_resolver.store_identity
|
||||||
|
):
|
||||||
|
raise EntryFactIntegrationError("authority resolver does not equal the installed resolver identity")
|
||||||
|
return installed
|
||||||
|
|
||||||
|
|
||||||
|
def _pack(authority: EntryFactAuthorityContext, language: str) -> FactPack:
|
||||||
|
packs = tuple(item for item in authority.bundle.packs if item.language == language)
|
||||||
|
if len(packs) != 1:
|
||||||
|
raise EntryFactIntegrationError("reviewed language pack is missing or ambiguous")
|
||||||
|
return packs[0]
|
||||||
|
|
||||||
|
|
||||||
|
def _target_for_fact(fact: object) -> str:
|
||||||
|
requirements = getattr(fact, "structural_requirements", ())
|
||||||
|
values = tuple(
|
||||||
|
item.expected for item in requirements if item.kind is StructuralRequirementKind.QUALIFIED_NAME and isinstance(item.expected, str)
|
||||||
|
)
|
||||||
|
if len(values) != 1:
|
||||||
|
raise EntryFactIntegrationError("reviewed fact does not declare one qualified target")
|
||||||
|
return values[0]
|
||||||
|
|
||||||
|
|
||||||
|
def _candidate_fact_ids(repository_root: Path, target: FileTarget, pack: FactPack) -> tuple[str, ...]:
|
||||||
|
source = read_source_structure(repository_root, target.path, target.language)
|
||||||
|
return tuple(fact.fact_id for fact in pack.facts if candidates(source, _target_for_fact(fact)))
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_source_texts(repository_root: Path, targets: Sequence[FileTarget], source_texts: Mapping[str, str] | None) -> None:
|
||||||
|
if source_texts is None:
|
||||||
|
return
|
||||||
|
for target in targets:
|
||||||
|
if target.language not in _LANGUAGES:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
installed = (repository_root / target.path).read_bytes().decode("utf-8")
|
||||||
|
except (OSError, UnicodeDecodeError) as exc:
|
||||||
|
raise EntryFactIntegrationError("reviewed matching requires readable UTF-8 product source") from exc
|
||||||
|
if source_texts.get(target.path) != installed:
|
||||||
|
raise EntryFactIntegrationError("cached source text differs from reviewed matcher input")
|
||||||
|
|
||||||
|
|
||||||
|
def match_reviewed_entry_targets(
|
||||||
|
repository_root: Path,
|
||||||
|
targets: Sequence[FileTarget],
|
||||||
|
authority: EntryFactAuthorityContext,
|
||||||
|
writer: FactMatchArtifactWriter,
|
||||||
|
evaluation_population_digest: str,
|
||||||
|
*,
|
||||||
|
source_texts: Mapping[str, str] | None = None,
|
||||||
|
) -> tuple[EntryFactMatchResult, ...]:
|
||||||
|
"""Persist and validate every structurally present reviewed entry fact.
|
||||||
|
|
||||||
|
Compatibility projection rows are deliberately absent from this interface. They
|
||||||
|
may recognize legacy entries, but only reviewed pack facts with typed structure
|
||||||
|
reach the matcher and its canonical artifact writer.
|
||||||
|
"""
|
||||||
|
authenticated = _validate_inputs(authority, writer, evaluation_population_digest)
|
||||||
|
root = Path(repository_root)
|
||||||
|
_validate_source_texts(root, targets, source_texts)
|
||||||
|
results: list[EntryFactMatchResult] = []
|
||||||
|
for target in targets:
|
||||||
|
if target.language not in _LANGUAGES:
|
||||||
|
continue
|
||||||
|
pack = _pack(authenticated, target.language)
|
||||||
|
for fact_id in _candidate_fact_ids(root, target, pack):
|
||||||
|
results.extend(
|
||||||
|
match_entry_fact(
|
||||||
|
repository_root=root,
|
||||||
|
relative_path=target.path,
|
||||||
|
language=target.language,
|
||||||
|
fact_id=fact_id,
|
||||||
|
authority=authenticated,
|
||||||
|
writer=writer,
|
||||||
|
evaluation_population_digest=evaluation_population_digest,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return tuple(results)
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["EntryFactIntegrationError", "match_reviewed_entry_targets"]
|
||||||
@@ -6,12 +6,17 @@ import re
|
|||||||
from collections.abc import Mapping, Sequence
|
from collections.abc import Mapping, Sequence
|
||||||
from dataclasses import asdict, dataclass, replace
|
from dataclasses import asdict, dataclass, replace
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Literal, cast
|
from typing import TYPE_CHECKING, Any, Literal, cast
|
||||||
|
|
||||||
from ..preprocess import FileTarget
|
from ..preprocess import FileTarget
|
||||||
from .entry_packs import load_entry_pack_patterns
|
from .entry_packs import load_entry_pack_patterns
|
||||||
from .registry import registry_entries
|
from .registry import registry_entries
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .entry_fact_matcher import EntryFactMatchResult
|
||||||
|
from .fact_authority import EntryFactAuthorityContext
|
||||||
|
from .fact_match_store import FactMatchArtifactWriter
|
||||||
|
|
||||||
MappingKind = Literal["semgrep", "codeql", "differential", "sharp_edge"]
|
MappingKind = Literal["semgrep", "codeql", "differential", "sharp_edge"]
|
||||||
AccessLevel = Literal[
|
AccessLevel = Literal[
|
||||||
"public",
|
"public",
|
||||||
@@ -76,6 +81,14 @@ class EntryPointRecord:
|
|||||||
registered_by: str = ""
|
registered_by: str = ""
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class EntryPointFactAnalysis:
|
||||||
|
"""Byte-stable compatibility records plus explicit reviewed v2 matches."""
|
||||||
|
|
||||||
|
entry_points: tuple[EntryPointRecord, ...]
|
||||||
|
fact_matches: tuple[EntryFactMatchResult, ...]
|
||||||
|
|
||||||
|
|
||||||
def ingest_sarif(path: Path) -> list[StaticHint]:
|
def ingest_sarif(path: Path) -> list[StaticHint]:
|
||||||
payload = json.loads(path.read_text())
|
payload = json.loads(path.read_text())
|
||||||
hints: list[StaticHint] = []
|
hints: list[StaticHint] = []
|
||||||
@@ -121,6 +134,31 @@ def analyze_entry_points(
|
|||||||
return sorted(records, key=lambda item: (item.path, item.line is None, item.line or 0, item.name))
|
return sorted(records, key=lambda item: (item.path, item.line is None, item.line or 0, item.name))
|
||||||
|
|
||||||
|
|
||||||
|
def analyze_entry_points_with_facts(
|
||||||
|
root: Path,
|
||||||
|
targets: list[FileTarget],
|
||||||
|
*,
|
||||||
|
authority: EntryFactAuthorityContext,
|
||||||
|
writer: FactMatchArtifactWriter,
|
||||||
|
evaluation_population_digest: str,
|
||||||
|
texts: Mapping[str, str] | None = None,
|
||||||
|
include_manifests: bool = True,
|
||||||
|
) -> EntryPointFactAnalysis:
|
||||||
|
"""Run reviewed v2 matching beside unchanged compatibility recognition."""
|
||||||
|
from .entry_fact_integration import match_reviewed_entry_targets
|
||||||
|
|
||||||
|
entries = analyze_entry_points(root, targets, texts=texts, include_manifests=include_manifests)
|
||||||
|
matches = match_reviewed_entry_targets(
|
||||||
|
root,
|
||||||
|
targets,
|
||||||
|
authority,
|
||||||
|
writer,
|
||||||
|
evaluation_population_digest,
|
||||||
|
source_texts=texts,
|
||||||
|
)
|
||||||
|
return EntryPointFactAnalysis(tuple(entries), matches)
|
||||||
|
|
||||||
|
|
||||||
def php_hook_callbacks(root: Path, targets: list[FileTarget], register_calls: Sequence[str] | None = None) -> dict[str, tuple[str, ...]]:
|
def php_hook_callbacks(root: Path, targets: list[FileTarget], register_calls: Sequence[str] | None = None) -> dict[str, tuple[str, ...]]:
|
||||||
"""WordPress's hook registry, read out of the source text because the graph does not carry it.
|
"""WordPress's hook registry, read out of the source text because the graph does not carry it.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,362 @@
|
|||||||
|
"""Production mapping integration for reviewed entry facts."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import ast
|
||||||
|
import json
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from dataclasses import asdict, replace
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from openultrasast.core import canonical, provenance
|
||||||
|
from openultrasast.core.semantics import RelationStatus
|
||||||
|
from openultrasast.mapping.entry_fact_integration import match_reviewed_entry_targets
|
||||||
|
from openultrasast.mapping.entry_packs import load_installed_entry_fact_authority
|
||||||
|
from openultrasast.mapping.fact_authority import EntryFactAuthorityContext
|
||||||
|
from openultrasast.mapping.fact_match_store import FactMatchArtifactWriter
|
||||||
|
from openultrasast.mapping.legacy import analyze_entry_points, analyze_entry_points_with_facts
|
||||||
|
from openultrasast.preprocess import FileTarget, detect_language
|
||||||
|
|
||||||
|
SRC = Path(__file__).parents[1] / "src" / "openultrasast"
|
||||||
|
|
||||||
|
|
||||||
|
class _UnusedCustody:
|
||||||
|
def current_log(self, population_digest: str) -> provenance.PopulationCustodyLog:
|
||||||
|
raise AssertionError(f"authority load read custody: {population_digest}")
|
||||||
|
|
||||||
|
|
||||||
|
class _Custody:
|
||||||
|
def __init__(self, logs: dict[str, provenance.PopulationCustodyLog]) -> None:
|
||||||
|
self.logs = logs
|
||||||
|
|
||||||
|
def current_log(self, population_digest: str) -> provenance.PopulationCustodyLog:
|
||||||
|
return self.logs[population_digest]
|
||||||
|
|
||||||
|
|
||||||
|
class _EqualitySpoofResolver:
|
||||||
|
def __eq__(self, other: object) -> bool:
|
||||||
|
return True
|
||||||
|
|
||||||
|
def resolve(self, reference: canonical.ContentRef) -> canonical.ResolvedContent:
|
||||||
|
raise AssertionError(f"synthetic resolver used: {reference.artifact_id}")
|
||||||
|
|
||||||
|
|
||||||
|
class _ContextSubclass(EntryFactAuthorityContext):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class _MemoryWriter(FactMatchArtifactWriter):
|
||||||
|
def persist_observation(self, value: object) -> canonical.ContentRef:
|
||||||
|
raise AssertionError(f"memory writer used: {value!r}")
|
||||||
|
|
||||||
|
def persist_match(self, value: object) -> canonical.ContentRef:
|
||||||
|
raise AssertionError(f"memory writer used: {value!r}")
|
||||||
|
|
||||||
|
|
||||||
|
def _authority() -> tuple[EntryFactAuthorityContext, str]:
|
||||||
|
loaded = load_installed_entry_fact_authority(_UnusedCustody())
|
||||||
|
bundle = loaded.bundle
|
||||||
|
custodian = bundle.custody_policy.reviewed_custodian_digests[0]
|
||||||
|
logs: dict[str, provenance.PopulationCustodyLog] = {}
|
||||||
|
for lineage in bundle.lineages:
|
||||||
|
for use in lineage.uses:
|
||||||
|
prior = logs.setdefault(
|
||||||
|
use.source_population_digest,
|
||||||
|
provenance.empty_population_custody_log(use.source_population_digest, custodian),
|
||||||
|
)
|
||||||
|
request = provenance.PopulationUseRequest(
|
||||||
|
use.source_population_digest,
|
||||||
|
lineage.arm_id,
|
||||||
|
use.purpose,
|
||||||
|
use.labels_accessed,
|
||||||
|
use.influenced_artifact_digest,
|
||||||
|
prior.log_digest,
|
||||||
|
)
|
||||||
|
receipt = provenance.record_population_use(request, prior, custodian, bundle.custody_policy)
|
||||||
|
assert receipt.receipt_digest == use.population_use_receipt_digest
|
||||||
|
logs[use.source_population_digest] = provenance.append_population_use(prior, receipt, bundle.custody_policy)
|
||||||
|
evaluation = canonical.content_digest("production-entry-integration-population")
|
||||||
|
logs[evaluation] = provenance.empty_population_custody_log(evaluation, custodian)
|
||||||
|
return replace(loaded, custody=_Custody(logs)), evaluation
|
||||||
|
|
||||||
|
|
||||||
|
def _target(root: Path, name: str, content: str) -> FileTarget:
|
||||||
|
path = root / name
|
||||||
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
path.write_text(content, encoding="utf-8")
|
||||||
|
assert path.stat().st_size > 0
|
||||||
|
return FileTarget(name, str(path), detect_language(path), content.count("\n") + 1, [], False, [], [])
|
||||||
|
|
||||||
|
|
||||||
|
def _writer(root: Path, authority: EntryFactAuthorityContext) -> FactMatchArtifactWriter:
|
||||||
|
return FactMatchArtifactWriter(
|
||||||
|
root / "fact-matches",
|
||||||
|
authority.bundle.review_policy.trusted_store_identities[0],
|
||||||
|
canonical.content_digest("retain-production-entry-facts"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _canonical_records(values: object) -> bytes:
|
||||||
|
return json.dumps([asdict(item) for item in values], sort_keys=True, separators=(",", ":")).encode()
|
||||||
|
|
||||||
|
|
||||||
|
def test_default_entry_mapping_remains_byte_stable_when_v2_is_not_requested(tmp_path: Path) -> None:
|
||||||
|
from openultrasast.mapping import legacy
|
||||||
|
|
||||||
|
source = 'import express from "express";\nconst app = express();\napp.get("/x", handler);\nfunction handler() {}\n'
|
||||||
|
target = _target(tmp_path, "src/routes.js", source)
|
||||||
|
before = analyze_entry_points(tmp_path, [target], include_manifests=False)
|
||||||
|
assert not hasattr(legacy, "match_reviewed_entry_targets")
|
||||||
|
after = analyze_entry_points(tmp_path, [target], include_manifests=False)
|
||||||
|
|
||||||
|
assert _canonical_records(before) == _canonical_records(after)
|
||||||
|
assert json.loads(_canonical_records(after)) == [
|
||||||
|
{
|
||||||
|
"access_declared": False,
|
||||||
|
"access_evidence": ['app.get("/x", handler);'],
|
||||||
|
"access_level": "public",
|
||||||
|
"conditions": [],
|
||||||
|
"defines_handler": True,
|
||||||
|
"end_line": 4,
|
||||||
|
"function_name": "handler",
|
||||||
|
"kind": "route",
|
||||||
|
"line": 4,
|
||||||
|
"name": "http_handler",
|
||||||
|
"path": "src/routes.js",
|
||||||
|
"provenance": "entrypoint:route:public",
|
||||||
|
"rationale": "route surface classified as public at http_request",
|
||||||
|
"registered_by": "",
|
||||||
|
"trust_boundary": "http_request",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_explicit_v2_path_persists_complete_matches_beside_legacy_output(tmp_path: Path) -> None:
|
||||||
|
authority, evaluation = _authority()
|
||||||
|
writer = _writer(tmp_path, authority)
|
||||||
|
target = _target(
|
||||||
|
tmp_path,
|
||||||
|
"src/routes.js",
|
||||||
|
'import express from "express";\n'
|
||||||
|
"const app = express();\n"
|
||||||
|
'app.get("/admin", adminHandler);\n'
|
||||||
|
"function adminHandler(req, res) { res.sendStatus(204); }\n",
|
||||||
|
)
|
||||||
|
|
||||||
|
analysis = analyze_entry_points_with_facts(
|
||||||
|
tmp_path,
|
||||||
|
[target],
|
||||||
|
authority=authority,
|
||||||
|
writer=writer,
|
||||||
|
evaluation_population_digest=evaluation,
|
||||||
|
include_manifests=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert _canonical_records(analysis.entry_points) == _canonical_records(
|
||||||
|
analyze_entry_points(tmp_path, [target], include_manifests=False)
|
||||||
|
)
|
||||||
|
route = next(item for item in analysis.fact_matches if item.match.fact_id == "entry:express:app-route:get")
|
||||||
|
assert route.decision.status is RelationStatus.COMPLETE
|
||||||
|
assert route.relations
|
||||||
|
assert writer.resolver.resolve(route.match_reference).content
|
||||||
|
assert all(writer.resolver.resolve(item.evidence).content for item in route.match.observations)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"name,source,reason,legacy_only",
|
||||||
|
(
|
||||||
|
(
|
||||||
|
"name-only.js",
|
||||||
|
'app.get("/admin", adminHandler);\nfunction adminHandler(req, res) { res.sendStatus(204); }\n',
|
||||||
|
"missing_observation:receiver",
|
||||||
|
False,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"partial.js",
|
||||||
|
'import express from "express";\nconst app = express();\napp.get("/admin");\n',
|
||||||
|
"missing_observation:arguments",
|
||||||
|
False,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"legacy-only.js",
|
||||||
|
'// Legacy recognition text only: app.get("/admin", adminHandler)\nexport const marker = "not structural";\n',
|
||||||
|
None,
|
||||||
|
True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
def test_projection_or_incomplete_structure_cannot_become_v2_evidence(
|
||||||
|
tmp_path: Path, name: str, source: str, reason: str | None, legacy_only: bool
|
||||||
|
) -> None:
|
||||||
|
authority, evaluation = _authority()
|
||||||
|
target = _target(tmp_path, name, source)
|
||||||
|
analysis = analyze_entry_points_with_facts(
|
||||||
|
tmp_path,
|
||||||
|
[target],
|
||||||
|
authority=authority,
|
||||||
|
writer=_writer(tmp_path, authority),
|
||||||
|
evaluation_population_digest=evaluation,
|
||||||
|
include_manifests=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
route_matches = [item for item in analysis.fact_matches if item.match.fact_id == "entry:express:app-route:get"]
|
||||||
|
if legacy_only:
|
||||||
|
assert analysis.entry_points and route_matches == []
|
||||||
|
else:
|
||||||
|
assert len(route_matches) == 1
|
||||||
|
assert route_matches[0].decision.status is not RelationStatus.COMPLETE
|
||||||
|
assert reason in route_matches[0].decision.reasons
|
||||||
|
assert all(relation.status is not RelationStatus.COMPLETE for relation in route_matches[0].relations)
|
||||||
|
|
||||||
|
|
||||||
|
def test_explicit_v2_boundary_rejects_untrusted_writer_or_context(tmp_path: Path) -> None:
|
||||||
|
authority, evaluation = _authority()
|
||||||
|
target = _target(tmp_path, "src/routes.js", 'app.get("/x", handler);\nfunction handler() {}\n')
|
||||||
|
untrusted = FactMatchArtifactWriter(
|
||||||
|
tmp_path / "untrusted",
|
||||||
|
"unreviewed-store",
|
||||||
|
canonical.content_digest("retain-untrusted"),
|
||||||
|
)
|
||||||
|
with pytest.raises(ValueError, match="trusted store"):
|
||||||
|
match_reviewed_entry_targets(tmp_path, [target], authority, untrusted, evaluation)
|
||||||
|
with pytest.raises(TypeError, match="authority"):
|
||||||
|
match_reviewed_entry_targets(tmp_path, [target], object(), _writer(tmp_path, authority), evaluation) # type: ignore[arg-type]
|
||||||
|
substituted = replace(authority, bundle=replace(authority.bundle, bundle_digest="0" * 64))
|
||||||
|
with pytest.raises(ValueError, match="externally authenticated"):
|
||||||
|
match_reviewed_entry_targets(tmp_path, [target], substituted, _writer(tmp_path, authority), evaluation)
|
||||||
|
subclassed = _ContextSubclass(authority.bundle, authority.custody, authority.resolver)
|
||||||
|
with pytest.raises(TypeError, match="authority"):
|
||||||
|
match_reviewed_entry_targets(tmp_path, [target], subclassed, _writer(tmp_path, authority), evaluation)
|
||||||
|
|
||||||
|
|
||||||
|
def test_explicit_v2_rejects_equality_spoofed_resolver_before_matching(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
|
from openultrasast.mapping import entry_fact_integration
|
||||||
|
|
||||||
|
authority, evaluation = _authority()
|
||||||
|
target = _target(tmp_path, "src/routes.js", 'app.get("/x", handler);\nfunction handler() {}\n')
|
||||||
|
spoofed = replace(authority, resolver=_EqualitySpoofResolver())
|
||||||
|
monkeypatch.setattr(entry_fact_integration, "match_entry_fact", lambda **kwargs: pytest.fail("matcher reached"))
|
||||||
|
|
||||||
|
with pytest.raises(ValueError, match="resolver"):
|
||||||
|
match_reviewed_entry_targets(tmp_path, [target], spoofed, _writer(tmp_path, authority), evaluation)
|
||||||
|
|
||||||
|
|
||||||
|
def test_explicit_v2_rejects_writer_subclass_before_persistence(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
|
from openultrasast.mapping import entry_fact_integration
|
||||||
|
|
||||||
|
authority, evaluation = _authority()
|
||||||
|
target = _target(tmp_path, "src/routes.js", 'app.get("/x", handler);\nfunction handler() {}\n')
|
||||||
|
real = _writer(tmp_path, authority)
|
||||||
|
writer = _MemoryWriter(real.root, real.store_identity, real.retention_policy_digest)
|
||||||
|
monkeypatch.setattr(entry_fact_integration, "match_entry_fact", lambda **kwargs: pytest.fail("matcher reached"))
|
||||||
|
|
||||||
|
with pytest.raises(TypeError, match="writer"):
|
||||||
|
match_reviewed_entry_targets(tmp_path, [target], authority, writer, evaluation)
|
||||||
|
|
||||||
|
|
||||||
|
def test_explicit_v2_rejects_same_type_pack_body_substitution_before_matching(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
|
from openultrasast.mapping import entry_fact_integration
|
||||||
|
|
||||||
|
authority, evaluation = _authority()
|
||||||
|
target = _target(tmp_path, "src/routes.js", 'app.get("/x", handler);\nfunction handler() {}\n')
|
||||||
|
pack = replace(authority.bundle.packs[0], normalized_content_digest="0" * 64)
|
||||||
|
bundle = replace(authority.bundle, packs=(pack, *authority.bundle.packs[1:]))
|
||||||
|
substituted = replace(authority, bundle=bundle)
|
||||||
|
monkeypatch.setattr(entry_fact_integration, "match_entry_fact", lambda **kwargs: pytest.fail("matcher reached"))
|
||||||
|
|
||||||
|
with pytest.raises(ValueError, match="pack"):
|
||||||
|
match_reviewed_entry_targets(tmp_path, [target], substituted, _writer(tmp_path, authority), evaluation)
|
||||||
|
|
||||||
|
|
||||||
|
def test_explicit_v2_rejects_same_type_contract_body_substitution_before_matching(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
|
from openultrasast.mapping import entry_fact_integration
|
||||||
|
|
||||||
|
authority, evaluation = _authority()
|
||||||
|
target = _target(tmp_path, "src/routes.js", 'app.get("/x", handler);\nfunction handler() {}\n')
|
||||||
|
contract = replace(authority.bundle.contract, family_version="2.0.1")
|
||||||
|
substituted = replace(authority, bundle=replace(authority.bundle, contract=contract))
|
||||||
|
monkeypatch.setattr(entry_fact_integration, "match_entry_fact", lambda **kwargs: pytest.fail("matcher reached"))
|
||||||
|
|
||||||
|
with pytest.raises(ValueError, match="contract"):
|
||||||
|
match_reviewed_entry_targets(tmp_path, [target], substituted, _writer(tmp_path, authority), evaluation)
|
||||||
|
|
||||||
|
|
||||||
|
def test_default_legacy_import_does_not_eagerly_load_v2_matcher_modules() -> None:
|
||||||
|
script = """
|
||||||
|
import sys
|
||||||
|
import openultrasast.mapping.legacy
|
||||||
|
forbidden = {
|
||||||
|
'openultrasast.mapping.entry_fact_integration',
|
||||||
|
'openultrasast.mapping.entry_fact_matcher',
|
||||||
|
'openultrasast.mapping.entry_fact_structure',
|
||||||
|
}
|
||||||
|
assert forbidden.isdisjoint(sys.modules), sorted(forbidden.intersection(sys.modules))
|
||||||
|
"""
|
||||||
|
completed = subprocess.run([sys.executable, "-I", "-c", script], capture_output=True, text=True, check=False)
|
||||||
|
assert completed.returncode == 0, completed.stderr
|
||||||
|
|
||||||
|
|
||||||
|
def test_explicit_v2_refuses_cached_text_that_differs_from_product_bytes(tmp_path: Path) -> None:
|
||||||
|
authority, evaluation = _authority()
|
||||||
|
source = 'app.get("/real", handler);\nfunction handler() {}\n'
|
||||||
|
target = _target(tmp_path, "src/routes.js", source)
|
||||||
|
with pytest.raises(ValueError, match="cached source text differs"):
|
||||||
|
analyze_entry_points_with_facts(
|
||||||
|
tmp_path,
|
||||||
|
[target],
|
||||||
|
authority=authority,
|
||||||
|
writer=_writer(tmp_path, authority),
|
||||||
|
evaluation_population_digest=evaluation,
|
||||||
|
texts={target.path: source.replace("/real", "/stale")},
|
||||||
|
include_manifests=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_production_censuses_find_no_projection_evidence_or_writer_bypass() -> None:
|
||||||
|
calls: dict[str, list[str]] = {
|
||||||
|
"load_entry_pack_patterns": [],
|
||||||
|
"match_entry_fact": [],
|
||||||
|
"persist_observation": [],
|
||||||
|
"persist_match": [],
|
||||||
|
"StructuralObservation": [],
|
||||||
|
"FactMatchEvidence": [],
|
||||||
|
"FactMatchArtifactWriter": [],
|
||||||
|
}
|
||||||
|
conformance_only = {
|
||||||
|
"mapping/_entry_fact_conformance_fs.py",
|
||||||
|
"mapping/_entry_fact_conformance_negative.py",
|
||||||
|
"mapping/_entry_fact_conformance_replay.py",
|
||||||
|
"mapping/_entry_fact_conformance_wire.py",
|
||||||
|
"mapping/entry_fact_conformance.py",
|
||||||
|
"mapping/entry_fact_conformance_manifest.py",
|
||||||
|
}
|
||||||
|
production = tuple(path for path in SRC.rglob("*.py") if path.relative_to(SRC).as_posix() not in conformance_only)
|
||||||
|
imports: dict[str, list[str]] = {"load_entry_pack_patterns": [], "match_entry_fact": []}
|
||||||
|
for path in production:
|
||||||
|
tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path))
|
||||||
|
for node in ast.walk(tree):
|
||||||
|
if isinstance(node, ast.ImportFrom):
|
||||||
|
for alias in node.names:
|
||||||
|
if alias.name in imports:
|
||||||
|
imports[alias.name].append(path.relative_to(SRC).as_posix())
|
||||||
|
if not isinstance(node, ast.Call):
|
||||||
|
continue
|
||||||
|
name = node.func.id if isinstance(node.func, ast.Name) else node.func.attr if isinstance(node.func, ast.Attribute) else ""
|
||||||
|
if name in calls:
|
||||||
|
calls[name].append(path.relative_to(SRC).as_posix())
|
||||||
|
|
||||||
|
assert imports["load_entry_pack_patterns"] == ["mapping/legacy.py"]
|
||||||
|
assert calls["load_entry_pack_patterns"] == ["mapping/legacy.py"]
|
||||||
|
assert imports["match_entry_fact"] == ["mapping/entry_fact_integration.py"]
|
||||||
|
assert calls["match_entry_fact"] == ["mapping/entry_fact_integration.py"]
|
||||||
|
assert calls["persist_observation"] == ["mapping/entry_fact_matcher.py"]
|
||||||
|
assert calls["persist_match"] == ["mapping/entry_fact_matcher.py"]
|
||||||
|
assert calls["StructuralObservation"] == ["core/factvalidate.py", "mapping/entry_fact_matcher.py"]
|
||||||
|
assert calls["FactMatchEvidence"] == ["core/factvalidate.py", "mapping/entry_fact_matcher.py"]
|
||||||
|
assert calls["FactMatchArtifactWriter"] == []
|
||||||
|
assert "load_entry_pack_patterns" not in (SRC / "mapping" / "entry_fact_integration.py").read_text(encoding="utf-8")
|
||||||
|
legacy_source = (SRC / "mapping" / "legacy.py").read_text(encoding="utf-8")
|
||||||
|
assert "FactMatchEvidence" not in legacy_source and "StructuralObservation" not in legacy_source
|
||||||
Reference in New Issue
Block a user