mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-11-13 01:48:17 +00:00
feat(access-control-prover): migrate reviewed entry packs
This commit is contained in:
@@ -22,6 +22,9 @@ build/
|
|||||||
.cache/openultrasast/
|
.cache/openultrasast/
|
||||||
**/.cache/openultrasast/
|
**/.cache/openultrasast/
|
||||||
artifacts/
|
artifacts/
|
||||||
|
!src/openultrasast/ruleset/entries/artifacts/
|
||||||
|
!src/openultrasast/ruleset/entries/artifacts/sha256/
|
||||||
|
!src/openultrasast/ruleset/entries/artifacts/sha256/*
|
||||||
reports/
|
reports/
|
||||||
vector-store/
|
vector-store/
|
||||||
*.sarif
|
*.sarif
|
||||||
|
|||||||
@@ -359,7 +359,7 @@ its final resolution, dominance, fact-acquisition and measurement semantics with
|
|||||||
- _Requirements: 13.2, 13.3_
|
- _Requirements: 13.2, 13.3_
|
||||||
- _Boundary: Installed Authority Review Ceremony_
|
- _Boundary: Installed Authority Review Ceremony_
|
||||||
- _Depends: 14.3_
|
- _Depends: 14.3_
|
||||||
- [ ] 14.5 Migrate the installed entry knowledge into reviewed v2 packs
|
- [x] 14.5 Migrate the installed entry knowledge into reviewed v2 packs
|
||||||
- Produce one language-scoped v2 pack per promoted language and a separately authenticated canonical compatibility projection from the existing reviewed entry data.
|
- Produce one language-scoped v2 pack per promoted language and a separately authenticated canonical compatibility projection from the existing reviewed entry data.
|
||||||
- Map every migrated row to declared roles, typed structural requirements and relation effects without adding target vocabulary to engine modules.
|
- Map every migrated row to declared roles, typed structural requirements and relation effects without adding target vocabulary to engine modules.
|
||||||
- Preserve legacy pattern recognition byte-for-byte while preventing the projection from constructing detection-core evidence or readiness.
|
- Preserve legacy pattern recognition byte-for-byte while preventing the projection from constructing detection-core evidence or readiness.
|
||||||
|
|||||||
@@ -1,51 +1,267 @@
|
|||||||
"""Entry-fact packs loaded through the core fact-pack contract.
|
"""Installed entry-fact authority and projection-only legacy recognition."""
|
||||||
|
|
||||||
Packs live in ``ruleset/entries/*.json`` and are admitted only with a self-consistent
|
|
||||||
review attestation (`core.factpack.load_pack_file`). Languages map pack languages to
|
|
||||||
source languages; route patterns become extra recognition regexes.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
from collections import OrderedDict
|
||||||
|
from collections.abc import Mapping
|
||||||
|
from dataclasses import dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from threading import Lock
|
||||||
|
from typing import cast
|
||||||
|
|
||||||
from openultrasast.core.factpack import load_pack_file
|
from openultrasast.core import canonical, provenance
|
||||||
|
from openultrasast.mapping.authority_ceremony import (
|
||||||
|
INSTALLED_ARTIFACT_STORE_IDENTITY,
|
||||||
|
load_installed_authority_ceremony,
|
||||||
|
validate_authority_ceremony_bindings,
|
||||||
|
)
|
||||||
|
from openultrasast.mapping.fact_authority import (
|
||||||
|
EntryFactAuthorityContext,
|
||||||
|
authenticate_entry_fact_authority,
|
||||||
|
)
|
||||||
|
from openultrasast.mapping.installed_fact_authority import (
|
||||||
|
PACK_FILES,
|
||||||
|
DigestDirectoryResolver,
|
||||||
|
InstalledAuthorityWireError,
|
||||||
|
decode_fact_pack_bytes,
|
||||||
|
decode_installed_authority_bytes,
|
||||||
|
)
|
||||||
|
from openultrasast.semantic.obligations.core_contract import production_access_control_contract
|
||||||
|
|
||||||
|
RECOGNITION_PROJECTION_SCHEMA = "openultrasast.entry-recognition-projection.v1"
|
||||||
|
_PROJECTION_FIELDS = {"schema_version", "facts"}
|
||||||
|
_ROW_FIELDS = {"legacy_fact_id", "languages", "defines_handler", "handler_kind", "pattern"}
|
||||||
_LANGUAGE_ALIASES = {"js": "javascript", "ts": "typescript"}
|
_LANGUAGE_ALIASES = {"js": "javascript", "ts": "typescript"}
|
||||||
_cache: dict[tuple[str, tuple[tuple[str, float], ...]], dict[str, list[re.Pattern[str]]]] = {}
|
_STATE_FILES = (
|
||||||
|
"bootstrap.json",
|
||||||
|
"identities/generator.json",
|
||||||
|
"identities/maintainer-reviewer.json",
|
||||||
|
"identities/custodian.json",
|
||||||
|
"authority.json",
|
||||||
|
*PACK_FILES,
|
||||||
|
"express.json",
|
||||||
|
)
|
||||||
|
_CACHE_LIMIT = 8
|
||||||
|
_FileIdentity = tuple[str, int, int, int, int, int, str]
|
||||||
|
_InstalledState = tuple[_FileIdentity, ...]
|
||||||
|
_CachedPatterns = tuple[tuple[str, tuple[re.Pattern[str], ...]], ...]
|
||||||
|
_cache: OrderedDict[str, tuple[_InstalledState, _CachedPatterns]] = OrderedDict()
|
||||||
|
_cache_lock = Lock()
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class _RecognitionRow:
|
||||||
|
legacy_fact_id: str
|
||||||
|
languages: tuple[str, ...]
|
||||||
|
defines_handler: bool
|
||||||
|
handler_kind: str
|
||||||
|
pattern: str
|
||||||
|
|
||||||
|
|
||||||
|
class _ProjectionOnlyCustody:
|
||||||
|
"""Authority authentication port that refuses evaluation-state use."""
|
||||||
|
|
||||||
|
def current_log(self, population_digest: str) -> provenance.PopulationCustodyLog:
|
||||||
|
raise AssertionError(f"projection loading consumed target custody: {population_digest}")
|
||||||
|
|
||||||
|
|
||||||
|
def _package_root(repo_root: Path | None) -> Path:
|
||||||
|
if repo_root is not None:
|
||||||
|
return repo_root
|
||||||
|
import openultrasast
|
||||||
|
|
||||||
|
return Path(openultrasast.__file__).resolve().parent
|
||||||
|
|
||||||
|
|
||||||
|
def _entries_root(repo_root: Path | None) -> Path:
|
||||||
|
return _package_root(repo_root) / "ruleset" / "entries"
|
||||||
|
|
||||||
|
|
||||||
|
def _state_paths(entries: Path) -> tuple[Path, ...]:
|
||||||
|
fixed = tuple(entries / relative for relative in _STATE_FILES)
|
||||||
|
artifact_root = entries / "artifacts" / "sha256"
|
||||||
|
try:
|
||||||
|
artifacts = tuple(sorted(path for path in artifact_root.rglob("*") if path.is_file()))
|
||||||
|
except OSError as exc:
|
||||||
|
raise InstalledAuthorityWireError("installed artifact state is unavailable") from exc
|
||||||
|
return (*fixed, *artifacts)
|
||||||
|
|
||||||
|
|
||||||
|
def _file_identity(entries: Path, path: Path) -> _FileIdentity:
|
||||||
|
try:
|
||||||
|
before = path.lstat()
|
||||||
|
content = path.read_bytes()
|
||||||
|
after = path.lstat()
|
||||||
|
except OSError as exc:
|
||||||
|
raise InstalledAuthorityWireError("installed authority state is unavailable") from exc
|
||||||
|
before_metadata = (before.st_dev, before.st_ino, before.st_mode, before.st_size, before.st_mtime_ns)
|
||||||
|
after_metadata = (after.st_dev, after.st_ino, after.st_mode, after.st_size, after.st_mtime_ns)
|
||||||
|
if before_metadata != after_metadata:
|
||||||
|
raise InstalledAuthorityWireError("installed authority state changed while fingerprinting")
|
||||||
|
return (
|
||||||
|
path.relative_to(entries).as_posix(),
|
||||||
|
before.st_dev,
|
||||||
|
before.st_ino,
|
||||||
|
before.st_mode,
|
||||||
|
before.st_size,
|
||||||
|
before.st_mtime_ns,
|
||||||
|
canonical.bytes_digest(content),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _installed_state(entries: Path) -> _InstalledState:
|
||||||
|
paths = _state_paths(entries)
|
||||||
|
state = tuple(_file_identity(entries, path) for path in paths)
|
||||||
|
if paths != _state_paths(entries):
|
||||||
|
raise InstalledAuthorityWireError("installed artifact set changed while fingerprinting")
|
||||||
|
return state
|
||||||
|
|
||||||
|
|
||||||
|
def _cached_patterns(entries: Path, state: _InstalledState) -> dict[str, list[re.Pattern[str]]] | None:
|
||||||
|
key = str(entries.resolve())
|
||||||
|
with _cache_lock:
|
||||||
|
cached = _cache.get(key)
|
||||||
|
if cached is None or cached[0] != state:
|
||||||
|
return None
|
||||||
|
_cache.move_to_end(key)
|
||||||
|
return {language: list(patterns) for language, patterns in cached[1]}
|
||||||
|
|
||||||
|
|
||||||
|
def _remember_patterns(entries: Path, state: _InstalledState, patterns: dict[str, list[re.Pattern[str]]]) -> None:
|
||||||
|
key = str(entries.resolve())
|
||||||
|
frozen = tuple((language, tuple(values)) for language, values in sorted(patterns.items()))
|
||||||
|
with _cache_lock:
|
||||||
|
_cache[key] = (state, frozen)
|
||||||
|
_cache.move_to_end(key)
|
||||||
|
while len(_cache) > _CACHE_LIMIT:
|
||||||
|
_cache.popitem(last=False)
|
||||||
|
|
||||||
|
|
||||||
|
def load_installed_entry_fact_authority(
|
||||||
|
custody: provenance.PopulationCustodyPort,
|
||||||
|
repo_root: Path | None = None,
|
||||||
|
) -> EntryFactAuthorityContext:
|
||||||
|
"""Decode and externally authenticate the closed installed v2 authority.
|
||||||
|
|
||||||
|
``custody`` remains caller supplied. Authority authentication verifies its port
|
||||||
|
shape but deliberately does not read evaluation population state.
|
||||||
|
"""
|
||||||
|
entries = _entries_root(repo_root)
|
||||||
|
contract = production_access_control_contract()
|
||||||
|
try:
|
||||||
|
packs = tuple(decode_fact_pack_bytes((entries / name).read_bytes()) for name in PACK_FILES)
|
||||||
|
bundle = decode_installed_authority_bytes((entries / "authority.json").read_bytes(), packs, contract)
|
||||||
|
except OSError as exc:
|
||||||
|
raise InstalledAuthorityWireError("installed entry authority is unavailable") from exc
|
||||||
|
ceremony = load_installed_authority_ceremony(entries)
|
||||||
|
validate_authority_ceremony_bindings(ceremony, bundle)
|
||||||
|
resolver = DigestDirectoryResolver(entries, INSTALLED_ARTIFACT_STORE_IDENTITY)
|
||||||
|
return authenticate_entry_fact_authority(bundle, ceremony.bootstrap, custody, resolver)
|
||||||
|
|
||||||
|
|
||||||
|
def _text(value: object, field: str) -> str:
|
||||||
|
if not isinstance(value, str) or not value:
|
||||||
|
raise InstalledAuthorityWireError(f"{field} must be nonempty text")
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def _decode_projection(content: bytes) -> tuple[_RecognitionRow, ...]:
|
||||||
|
try:
|
||||||
|
raw = json.loads(content)
|
||||||
|
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
|
||||||
|
raise InstalledAuthorityWireError("recognition projection is not canonical JSON") from exc
|
||||||
|
if not isinstance(raw, dict) or set(raw) != _PROJECTION_FIELDS:
|
||||||
|
raise InstalledAuthorityWireError("recognition projection has unknown or missing fields")
|
||||||
|
if raw.get("schema_version") != RECOGNITION_PROJECTION_SCHEMA:
|
||||||
|
raise InstalledAuthorityWireError("recognition projection schema is unknown")
|
||||||
|
facts = raw.get("facts")
|
||||||
|
if not isinstance(facts, list) or not facts:
|
||||||
|
raise InstalledAuthorityWireError("recognition projection facts must be a nonempty list")
|
||||||
|
rows: list[_RecognitionRow] = []
|
||||||
|
for item in facts:
|
||||||
|
if not isinstance(item, dict) or set(item) != _ROW_FIELDS:
|
||||||
|
raise InstalledAuthorityWireError("recognition projection row has unknown or missing fields")
|
||||||
|
languages = item["languages"]
|
||||||
|
if (
|
||||||
|
not isinstance(languages, list)
|
||||||
|
or not languages
|
||||||
|
or not all(isinstance(language, str) and language for language in languages)
|
||||||
|
or len(set(languages)) != len(languages)
|
||||||
|
):
|
||||||
|
raise InstalledAuthorityWireError("recognition projection languages are invalid")
|
||||||
|
defines_handler = item["defines_handler"]
|
||||||
|
if not isinstance(defines_handler, bool):
|
||||||
|
raise InstalledAuthorityWireError("recognition projection defines_handler must be boolean")
|
||||||
|
rows.append(
|
||||||
|
_RecognitionRow(
|
||||||
|
_text(item["legacy_fact_id"], "legacy_fact_id"),
|
||||||
|
tuple(languages),
|
||||||
|
defines_handler,
|
||||||
|
_text(item["handler_kind"], "handler_kind"),
|
||||||
|
_text(item["pattern"], "pattern"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
record = cast(
|
||||||
|
Mapping[str, canonical.JsonValue],
|
||||||
|
{
|
||||||
|
"schema_version": RECOGNITION_PROJECTION_SCHEMA,
|
||||||
|
"facts": tuple(
|
||||||
|
{
|
||||||
|
"legacy_fact_id": row.legacy_fact_id,
|
||||||
|
"languages": row.languages,
|
||||||
|
"defines_handler": row.defines_handler,
|
||||||
|
"handler_kind": row.handler_kind,
|
||||||
|
"pattern": row.pattern,
|
||||||
|
}
|
||||||
|
for row in rows
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
expected = canonical.canonical_record_bytes(record, known_schema_versions=(RECOGNITION_PROJECTION_SCHEMA,))
|
||||||
|
if content != expected:
|
||||||
|
raise InstalledAuthorityWireError("recognition projection does not round-trip canonically")
|
||||||
|
return tuple(rows)
|
||||||
|
|
||||||
|
|
||||||
def load_entry_pack_patterns(repo_root: Path | None = None) -> dict[str, list[re.Pattern[str]]]:
|
def load_entry_pack_patterns(repo_root: Path | None = None) -> dict[str, list[re.Pattern[str]]]:
|
||||||
"""Compiled route patterns per source language, from admitted entry packs.
|
"""Return legacy recognition regexes from an authenticated projection only.
|
||||||
|
|
||||||
Packs resolve from the installed package's ``ruleset/entries`` (not the analyzed
|
This compatibility API intentionally returns no ``FactPack``, structural
|
||||||
repository), so recognition is active on external repos; ``repo_root`` is accepted
|
requirement, relation effect, or readiness value. A projection hit therefore
|
||||||
only for tests that stage a fake ruleset.
|
cannot be promoted into detection-core evidence.
|
||||||
"""
|
"""
|
||||||
import openultrasast
|
entries = _entries_root(repo_root)
|
||||||
|
|
||||||
base = repo_root if repo_root is not None else Path(openultrasast.__file__).resolve().parent
|
|
||||||
entries = base / "ruleset" / "entries"
|
|
||||||
if not entries.is_dir():
|
if not entries.is_dir():
|
||||||
return {}
|
return {}
|
||||||
files = sorted(entries.glob("*.json"))
|
state = _installed_state(entries)
|
||||||
stamp = tuple((p.name, p.stat().st_mtime) for p in files)
|
cached = _cached_patterns(entries, state)
|
||||||
key = (str(entries.resolve()), stamp)
|
if cached is not None:
|
||||||
if key in _cache:
|
return cached
|
||||||
return _cache[key]
|
context = load_installed_entry_fact_authority(_ProjectionOnlyCustody(), repo_root)
|
||||||
|
resolved = context.resolver.resolve(context.bundle.recognition_projection).content
|
||||||
|
try:
|
||||||
|
installed = (entries / "express.json").read_bytes()
|
||||||
|
except OSError as exc:
|
||||||
|
raise InstalledAuthorityWireError("installed recognition projection is unavailable") from exc
|
||||||
|
if installed != resolved:
|
||||||
|
raise InstalledAuthorityWireError("compatibility projection differs from authenticated artifact")
|
||||||
patterns: dict[str, list[re.Pattern[str]]] = {}
|
patterns: dict[str, list[re.Pattern[str]]] = {}
|
||||||
for pack_file in files:
|
for row in _decode_projection(installed):
|
||||||
payload = json.loads(pack_file.read_text(encoding="utf-8"))
|
if row.handler_kind != "route" or not row.defines_handler:
|
||||||
pack = load_pack_file(pack_file)
|
continue
|
||||||
for raw_language in payload.get("languages", []):
|
for raw_language in row.languages:
|
||||||
language = _LANGUAGE_ALIASES.get(str(raw_language), str(raw_language))
|
language = _LANGUAGE_ALIASES.get(str(raw_language), str(raw_language))
|
||||||
for fact in pack.facts:
|
patterns.setdefault(language, []).append(re.compile(row.pattern))
|
||||||
if fact.get("handler_kind") == "route" and fact.get("pattern"):
|
if state != _installed_state(entries):
|
||||||
patterns.setdefault(language, []).append(re.compile(str(fact["pattern"])))
|
raise InstalledAuthorityWireError("installed authority state changed during authentication")
|
||||||
_cache[key] = patterns
|
_remember_patterns(entries, state, patterns)
|
||||||
return patterns
|
return patterns
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["load_entry_pack_patterns"]
|
__all__ = [
|
||||||
|
"RECOGNITION_PROJECTION_SCHEMA",
|
||||||
|
"load_entry_pack_patterns",
|
||||||
|
"load_installed_entry_fact_authority",
|
||||||
|
]
|
||||||
|
|||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"facts":[{"defines_handler":true,"handler_kind":"route","languages":["javascript","typescript"],"legacy_fact_id":"entry:express:app-route","pattern":"app.(get|post|put|delete|patch|all|use)\\s*\\("},{"defines_handler":true,"handler_kind":"route","languages":["javascript","typescript"],"legacy_fact_id":"entry:express:router-route","pattern":"router.(get|post|put|delete|patch|all|use)\\s*\\("},{"defines_handler":false,"handler_kind":"app","languages":["javascript","typescript"],"legacy_fact_id":"entry:express:express-app","pattern":"express\\s*\\(\\s*\\)"}],"schema_version":"openultrasast.entry-recognition-projection.v1"}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"conflicts":[],"resolved":true,"schema_version":"openultrasast.access-control.authority-conflict-disclosure.v1"}
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"facts": [
|
||||||
|
{
|
||||||
|
"defines_handler": true,
|
||||||
|
"handler_kind": "route",
|
||||||
|
"id": "entry:express:app-route",
|
||||||
|
"pattern": "app.(get|post|put|delete|patch|all|use)\\s*\\("
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"defines_handler": true,
|
||||||
|
"handler_kind": "route",
|
||||||
|
"id": "entry:express:router-route",
|
||||||
|
"pattern": "router.(get|post|put|delete|patch|all|use)\\s*\\("
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"defines_handler": false,
|
||||||
|
"handler_kind": "app",
|
||||||
|
"id": "entry:express:express-app",
|
||||||
|
"pattern": "express\\s*\\(\\s*\\)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"family": "access_control",
|
||||||
|
"framework": "express",
|
||||||
|
"kind": "entry_facts",
|
||||||
|
"languages": [
|
||||||
|
"javascript",
|
||||||
|
"typescript"
|
||||||
|
],
|
||||||
|
"review": {
|
||||||
|
"digest": "47866e5c15b286d7107b101bd396f78bbbda23788085676c590c3af937085e00",
|
||||||
|
"note": "Reviewed against Express 4/5 routing docs and the pilot pool's manifest-level framework usage (2026-10-30 recognition table). Entry patterns only; no sinks, no labels."
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"authority_review":{"attestation_digest":"aeeb9120c10d8bf83159a9cbd14ad8a070a0ceb99a53b5a1daa616a315c84b55","conflict_disclosure":{"artifact_id":"installed-entry-authority-conflicts-v1","byte_length":113,"content_digest":"a8bf581d321560f2730aae3d5a7c9114c2d8f81f7a702fa3681a87e7be4cb9e1","media_type":"application/json","retention_policy_digest":"09a0fe8a253780b3666ef016d575a212bf0570da392dd5bf1a834dce2edf703f","storage_receipt_digest":"ee92fde020e9b4d316ad285f3f904032c52d902fe1c7c0a94cb0d61d9c0136ce"},"producer_identity":"9635025424be37e1615e597185a425a5443242fa210d6136c38250101088e7b4","reviewer_identity":"f12a7e897cbaf3059619517e1f8a725b90049a0edf3af08efa12fb1ef7fdee43","subject_digest":"0fa8f607297aa5364a0ca6c9b8d47bfcdef28456b7f062ddb251f8f8b9a67da4"},"bundle_digest":"0fa8f607297aa5364a0ca6c9b8d47bfcdef28456b7f062ddb251f8f8b9a67da4","custody_policy":{"policy_digest":"415b1c13adc918c162932a38e79548a7d9867f5331860eb409238c51b1cca1f0","reviewed_custodian_digests":["4fe8ca2923ee759a24fd8e5b91673faa6859a7a0c2df6e7c7ea5e23968708632"]},"lineages":[{"arm_id":"installed-entry-facts:javascript","manifest_digest":"f3b4b192dadd6b7d0d298286008480dadfd8640e9603020b0f24d13dce9bda04","subject_digest":"331e8c2261cb030f4257a8650448560eb94941554df47cb275882e24b8f8923f","uses":[{"influenced_artifact_digest":"331e8c2261cb030f4257a8650448560eb94941554df47cb275882e24b8f8923f","labels_accessed":false,"population_use_receipt_digest":"36ccebd06b60601775ab24a2f7ec9f2b49c2eac6b273816eaf9e867f6c445915","purpose":"semantic_fact","source_population_digest":"0ec4bbe3712a691a76f6525aca59f15d0492adc1e7ec8d7e9bb42e8ee42795ce"}]},{"arm_id":"installed-entry-facts:typescript","manifest_digest":"b7a6a95ddcdd8f6e6dbcd2b90cb707eda9833ec7479cb4d7aae3c91cfe0059ca","subject_digest":"b6d1157e99537bd4928303ec0d887628bc53f0aa9e770427580a32e3948babd9","uses":[{"influenced_artifact_digest":"b6d1157e99537bd4928303ec0d887628bc53f0aa9e770427580a32e3948babd9","labels_accessed":false,"population_use_receipt_digest":"7dc5fe36b1e111ab6cf979137c40bf67b885b2a9deda944aff78e3031cb5a553","purpose":"semantic_fact","source_population_digest":"0ec4bbe3712a691a76f6525aca59f15d0492adc1e7ec8d7e9bb42e8ee42795ce"}]},{"arm_id":"installed-entry-structural-matcher","manifest_digest":"5f7ffd58a773b61d1f8e2ba0ca019e27d778166e13cec770e6f25c16b1f1a994","subject_digest":"0ad18521beeb3708e12f8c9941b3f29b3e1312fe78b9320e5aaf15bc9db8831a","uses":[{"influenced_artifact_digest":"0ad18521beeb3708e12f8c9941b3f29b3e1312fe78b9320e5aaf15bc9db8831a","labels_accessed":false,"population_use_receipt_digest":"a602c8bccc6afc2ff9d857119131a2847509bc693a80192bf8459b9dcba9cbda","purpose":"matching_behavior","source_population_digest":"0ec4bbe3712a691a76f6525aca59f15d0492adc1e7ec8d7e9bb42e8ee42795ce"}]}],"matcher_behavior_digest":"0ad18521beeb3708e12f8c9941b3f29b3e1312fe78b9320e5aaf15bc9db8831a","matcher_lineage_manifest_digest":"5f7ffd58a773b61d1f8e2ba0ca019e27d778166e13cec770e6f25c16b1f1a994","matcher_revision":"entry-structural-matcher-v1","pack_files":["express.javascript.v2.json","express.typescript.v2.json"],"recognition_projection":{"artifact_id":"express-recognition-projection-v1","byte_length":628,"content_digest":"529d18ba2b351a8de985a9cf416261d717ef55a4b7a9d9d65bce53b97d59ab35","media_type":"application/json","retention_policy_digest":"09a0fe8a253780b3666ef016d575a212bf0570da392dd5bf1a834dce2edf703f","storage_receipt_digest":"3a85c0641cddcfc8c926fd01e230360c042606bda78e14a39489de14f88944b4"},"review_policy":{"forbidden_identity_pairs":[],"maximum_evidence_bytes":1048576,"policy_digest":"cb023a4f7e727ec4abfee583256194fe3f11c064a10960f297c1f7bf2e6fecde","reviewed_reviewer_digests":["f12a7e897cbaf3059619517e1f8a725b90049a0edf3af08efa12fb1ef7fdee43"],"trusted_store_identities":["openultrasast-installed-entry-artifacts-v1"]},"schema_version":"openultrasast.access-control.installed-entry-authority.v1"}
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,33 +1 @@
|
|||||||
{
|
{"facts":[{"defines_handler":true,"handler_kind":"route","languages":["javascript","typescript"],"legacy_fact_id":"entry:express:app-route","pattern":"app.(get|post|put|delete|patch|all|use)\\s*\\("},{"defines_handler":true,"handler_kind":"route","languages":["javascript","typescript"],"legacy_fact_id":"entry:express:router-route","pattern":"router.(get|post|put|delete|patch|all|use)\\s*\\("},{"defines_handler":false,"handler_kind":"app","languages":["javascript","typescript"],"legacy_fact_id":"entry:express:express-app","pattern":"express\\s*\\(\\s*\\)"}],"schema_version":"openultrasast.entry-recognition-projection.v1"}
|
||||||
"facts": [
|
|
||||||
{
|
|
||||||
"defines_handler": true,
|
|
||||||
"handler_kind": "route",
|
|
||||||
"id": "entry:express:app-route",
|
|
||||||
"pattern": "app.(get|post|put|delete|patch|all|use)\\s*\\("
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"defines_handler": true,
|
|
||||||
"handler_kind": "route",
|
|
||||||
"id": "entry:express:router-route",
|
|
||||||
"pattern": "router.(get|post|put|delete|patch|all|use)\\s*\\("
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"defines_handler": false,
|
|
||||||
"handler_kind": "app",
|
|
||||||
"id": "entry:express:express-app",
|
|
||||||
"pattern": "express\\s*\\(\\s*\\)"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"family": "access_control",
|
|
||||||
"framework": "express",
|
|
||||||
"kind": "entry_facts",
|
|
||||||
"languages": [
|
|
||||||
"javascript",
|
|
||||||
"typescript"
|
|
||||||
],
|
|
||||||
"review": {
|
|
||||||
"digest": "47866e5c15b286d7107b101bd396f78bbbda23788085676c590c3af937085e00",
|
|
||||||
"note": "Reviewed against Express 4/5 routing docs and the pilot pool's manifest-level framework usage (2026-10-30 recognition table). Entry patterns only; no sinks, no labels."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -5,12 +5,21 @@ import json
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
||||||
|
|
||||||
from openultrasast.core.factpack import FactPackError, admit_pack_file, verify_fact_pack # noqa: E402
|
from openultrasast.core.factpack import FactPackError, admit_pack_file, verify_fact_pack # noqa: E402
|
||||||
|
from openultrasast.mapping.installed_fact_authority import ( # noqa: E402
|
||||||
|
InstalledAuthorityWireError,
|
||||||
|
decode_fact_pack_bytes,
|
||||||
|
)
|
||||||
|
|
||||||
REPO = Path(__file__).resolve().parents[1]
|
REPO = Path(__file__).resolve().parents[1]
|
||||||
PACK = REPO / "src/openultrasast/ruleset/entries/express.json"
|
ENTRIES = REPO / "src/openultrasast/ruleset/entries"
|
||||||
|
LEGACY_DIGEST = "e8e149309abd78f24206c58fde7f73ae2f60364d8b8fee22ecaee8342798d8ce"
|
||||||
|
PACK = ENTRIES / "artifacts" / "sha256" / LEGACY_DIGEST
|
||||||
|
PROJECTION = ENTRIES / "express.json"
|
||||||
|
|
||||||
|
|
||||||
def _review_digest() -> str:
|
def _review_digest() -> str:
|
||||||
@@ -19,6 +28,9 @@ def _review_digest() -> str:
|
|||||||
|
|
||||||
|
|
||||||
def test_express_pack_admitted_with_verifiable_review() -> None:
|
def test_express_pack_admitted_with_verifiable_review() -> None:
|
||||||
|
content = PACK.read_bytes()
|
||||||
|
assert len(content) == 963
|
||||||
|
assert hashlib.sha256(content).hexdigest() == LEGACY_DIGEST
|
||||||
pack = admit_pack_file(PACK, review_digest=_review_digest())
|
pack = admit_pack_file(PACK, review_digest=_review_digest())
|
||||||
assert pack.family == "access_control"
|
assert pack.family == "access_control"
|
||||||
assert len(pack.facts) == 3
|
assert len(pack.facts) == 3
|
||||||
@@ -34,19 +46,20 @@ def test_pack_refused_without_matching_review() -> None:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def test_tampered_pack_file_refused() -> None:
|
def test_tampered_pack_file_refused(tmp_path: Path) -> None:
|
||||||
payload = json.loads(PACK.read_text(encoding="utf-8"))
|
payload = json.loads(PACK.read_text(encoding="utf-8"))
|
||||||
payload["facts"].append({"id": "entry:express:evil", "pattern": "eval("})
|
payload["facts"].append({"id": "entry:express:evil", "pattern": "eval("})
|
||||||
tampered = PACK.parent / "tampered.json"
|
tampered = tmp_path / "tampered.json"
|
||||||
tampered.write_text(json.dumps(payload), encoding="utf-8")
|
tampered.write_text(json.dumps(payload), encoding="utf-8")
|
||||||
try:
|
pack = admit_pack_file(tampered, review_digest=_review_digest())
|
||||||
pack = admit_pack_file(tampered, review_digest=_review_digest())
|
# review attestation still verifies, but content no longer matches the stamped digest
|
||||||
# review attestation still verifies, but content no longer matches the stamped digest
|
original = admit_pack_file(PACK, review_digest=_review_digest())
|
||||||
original = admit_pack_file(PACK, review_digest=_review_digest())
|
with pytest.raises(FactPackError):
|
||||||
try:
|
verify_fact_pack(original, pack.facts)
|
||||||
verify_fact_pack(original, pack.facts)
|
|
||||||
raise AssertionError("tampered content must fail verification")
|
|
||||||
except FactPackError:
|
def test_compatibility_projection_is_neither_a_legacy_nor_v2_fact_pack() -> None:
|
||||||
pass
|
with pytest.raises(FactPackError):
|
||||||
finally:
|
admit_pack_file(PROJECTION, review_digest=_review_digest())
|
||||||
tampered.unlink()
|
with pytest.raises(InstalledAuthorityWireError):
|
||||||
|
decode_fact_pack_bytes(PROJECTION.read_bytes())
|
||||||
|
|||||||
+271
-2
@@ -1,16 +1,39 @@
|
|||||||
"""Entry-pack wiring tests for detection-core 8.2 part 2 (Req 10, 3)."""
|
"""Installed reviewed entry-pack migration and legacy recognition wiring."""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
||||||
|
|
||||||
from openultrasast.mapping.entry_packs import load_entry_pack_patterns # noqa: E402
|
from openultrasast.core import canonical, provenance, semantics # noqa: E402
|
||||||
|
from openultrasast.core.factpack import ( # noqa: E402
|
||||||
|
StructuralRequirementKind,
|
||||||
|
validate_fact_pack,
|
||||||
|
)
|
||||||
|
from openultrasast.mapping import entry_packs as entry_pack_module # noqa: E402
|
||||||
|
from openultrasast.mapping.entry_packs import ( # noqa: E402
|
||||||
|
load_entry_pack_patterns,
|
||||||
|
load_installed_entry_fact_authority,
|
||||||
|
)
|
||||||
|
from openultrasast.mapping.fact_authority import ( # noqa: E402
|
||||||
|
EntryFactAuthorityContext,
|
||||||
|
EntryFactAuthorityError,
|
||||||
|
)
|
||||||
|
from openultrasast.mapping.installed_fact_authority import ( # noqa: E402
|
||||||
|
InstalledAuthorityWireError,
|
||||||
|
decode_fact_pack_bytes,
|
||||||
|
)
|
||||||
from openultrasast.mapping.legacy import _js_entry_points, analyze_entry_points, entry_framework # noqa: E402
|
from openultrasast.mapping.legacy import _js_entry_points, analyze_entry_points, entry_framework # noqa: E402
|
||||||
from openultrasast.preprocess import FileTarget, detect_language # noqa: E402
|
from openultrasast.preprocess import FileTarget, detect_language # noqa: E402
|
||||||
|
|
||||||
REPO = Path(__file__).resolve().parents[1]
|
REPO = Path(__file__).resolve().parents[1]
|
||||||
|
ENTRIES = REPO / "src" / "openultrasast" / "ruleset" / "entries"
|
||||||
|
|
||||||
TS_SOURCE = """import express from "express";
|
TS_SOURCE = """import express from "express";
|
||||||
const app = express();
|
const app = express();
|
||||||
@@ -18,6 +41,61 @@ app.all("/admin", adminHandler);
|
|||||||
function adminHandler(req, res) { res.send("ok"); }
|
function adminHandler(req, res) { res.send("ok"); }
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
_LEGACY_ROWS = (
|
||||||
|
(
|
||||||
|
"entry:express:app-route",
|
||||||
|
True,
|
||||||
|
"route",
|
||||||
|
r"app.(get|post|put|delete|patch|all|use)\s*\(",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"entry:express:router-route",
|
||||||
|
True,
|
||||||
|
"route",
|
||||||
|
r"router.(get|post|put|delete|patch|all|use)\s*\(",
|
||||||
|
),
|
||||||
|
("entry:express:express-app", False, "app", r"express\s*\(\s*\)"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class _UnusedCustody:
|
||||||
|
def current_log(self, population_digest: str) -> provenance.PopulationCustodyLog:
|
||||||
|
raise AssertionError(f"authority authentication read target custody: {population_digest}")
|
||||||
|
|
||||||
|
|
||||||
|
class _InstalledCustody:
|
||||||
|
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]
|
||||||
|
|
||||||
|
|
||||||
|
def _custody_for_installed_lineages(
|
||||||
|
lineages: tuple[provenance.LineageManifest, ...],
|
||||||
|
policy: provenance.PopulationCustodyPolicy,
|
||||||
|
) -> _InstalledCustody:
|
||||||
|
custodian = policy.reviewed_custodian_digests[0]
|
||||||
|
logs: dict[str, provenance.PopulationCustodyLog] = {}
|
||||||
|
for lineage in 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, policy)
|
||||||
|
assert receipt.receipt_digest == use.population_use_receipt_digest
|
||||||
|
logs[use.source_population_digest] = provenance.append_population_use(prior, receipt, policy)
|
||||||
|
return _InstalledCustody(logs)
|
||||||
|
|
||||||
|
|
||||||
def _target(root: Path, name: str, text: str) -> FileTarget:
|
def _target(root: Path, name: str, text: str) -> FileTarget:
|
||||||
path = root / name
|
path = root / name
|
||||||
@@ -26,11 +104,202 @@ def _target(root: Path, name: str, text: str) -> FileTarget:
|
|||||||
return FileTarget(name, str(path), detect_language(path), 5, [], False, [], [])
|
return FileTarget(name, str(path), detect_language(path), 5, [], False, [], [])
|
||||||
|
|
||||||
|
|
||||||
|
def _stage_entries(root: Path) -> Path:
|
||||||
|
staged = root / "ruleset" / "entries"
|
||||||
|
shutil.copytree(ENTRIES, staged)
|
||||||
|
return staged
|
||||||
|
|
||||||
|
|
||||||
def test_pack_patterns_loaded_for_typescript() -> None:
|
def test_pack_patterns_loaded_for_typescript() -> None:
|
||||||
patterns = load_entry_pack_patterns()
|
patterns = load_entry_pack_patterns()
|
||||||
assert patterns.get("typescript") and any(p.search("app.all(") for p in patterns["typescript"])
|
assert patterns.get("typescript") and any(p.search("app.all(") for p in patterns["typescript"])
|
||||||
|
|
||||||
|
|
||||||
|
def test_installed_v2_packs_authenticate_and_validate_from_a_proven_legacy_input() -> None:
|
||||||
|
context = load_installed_entry_fact_authority(_UnusedCustody())
|
||||||
|
bundle = context.bundle
|
||||||
|
legacy_reference = bundle.packs[0].review_evidence[0].content
|
||||||
|
legacy_artifact = REPO / "src" / "openultrasast" / "ruleset" / "entries" / "artifacts" / "sha256" / legacy_reference.content_digest
|
||||||
|
|
||||||
|
legacy_bytes = legacy_artifact.read_bytes()
|
||||||
|
assert legacy_artifact.stat().st_size == 963
|
||||||
|
assert len(legacy_bytes) == 963
|
||||||
|
assert canonical.bytes_digest(legacy_bytes) == legacy_reference.content_digest
|
||||||
|
|
||||||
|
assert tuple(pack.language for pack in bundle.packs) == ("javascript", "typescript")
|
||||||
|
custody = _custody_for_installed_lineages(bundle.lineages, bundle.custody_policy)
|
||||||
|
for pack in bundle.packs:
|
||||||
|
validate_fact_pack(
|
||||||
|
pack,
|
||||||
|
bundle.contract,
|
||||||
|
None,
|
||||||
|
bundle.lineages,
|
||||||
|
custody,
|
||||||
|
bundle.custody_policy,
|
||||||
|
bundle.review_policy,
|
||||||
|
context.resolver,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_reviewed_pack_mapping_is_exact_and_language_scoped() -> None:
|
||||||
|
bundle = load_installed_entry_fact_authority(_UnusedCustody()).bundle
|
||||||
|
methods = ("get", "post", "put", "delete", "patch", "all", "use")
|
||||||
|
|
||||||
|
for pack in bundle.packs:
|
||||||
|
facts = {fact.fact_id: fact for fact in pack.facts}
|
||||||
|
assert set(facts) == {
|
||||||
|
*(f"entry:express:app-route:{method}" for method in methods),
|
||||||
|
*(f"entry:express:router-route:{method}" for method in methods),
|
||||||
|
"entry:express:express-app",
|
||||||
|
}
|
||||||
|
for receiver_name, receiver_type in (("app", "express_application"), ("router", "express_router")):
|
||||||
|
for method in methods:
|
||||||
|
fact = facts[f"entry:express:{receiver_name}-route:{method}"]
|
||||||
|
assert fact.role == "entry_registration"
|
||||||
|
assert tuple((item.requirement_id, item.kind, item.field, item.expected) for item in fact.structural_requirements) == (
|
||||||
|
("qualified_target", StructuralRequirementKind.QUALIFIED_NAME, "qualified_target", f"{receiver_name}.{method}"),
|
||||||
|
("call_shape", StructuralRequirementKind.AST_SHAPE, "call_shape", "member_call"),
|
||||||
|
("receiver", StructuralRequirementKind.RECEIVER_TYPE, "receiver", receiver_type),
|
||||||
|
("arguments", StructuralRequirementKind.ARGUMENT_SHAPE, "arguments", "route_path_and_handler"),
|
||||||
|
)
|
||||||
|
assert tuple(
|
||||||
|
(effect.kind, effect.role, effect.source_role, effect.target_role, effect.conditions)
|
||||||
|
for effect in fact.relation_effects
|
||||||
|
) == ((semantics.RelationKind.REGISTRATION, "entry_registration", "framework_instance", "entry_handler", ()),)
|
||||||
|
app = facts["entry:express:express-app"]
|
||||||
|
assert app.role == "framework_instance"
|
||||||
|
assert tuple((item.requirement_id, item.kind, item.field, item.expected) for item in app.structural_requirements) == (
|
||||||
|
("qualified_target", StructuralRequirementKind.QUALIFIED_NAME, "qualified_target", "express"),
|
||||||
|
("call_shape", StructuralRequirementKind.AST_SHAPE, "call_shape", "factory_call"),
|
||||||
|
("binding", StructuralRequirementKind.ARGUMENT_SHAPE, "binding", "application_instance_binding"),
|
||||||
|
)
|
||||||
|
assert tuple(
|
||||||
|
(effect.kind, effect.role, effect.source_role, effect.target_role, effect.conditions) for effect in app.relation_effects
|
||||||
|
) == ((semantics.RelationKind.REGISTRATION, "framework_instance", "framework_factory", "framework_instance", ()),)
|
||||||
|
|
||||||
|
|
||||||
|
def test_projection_preserves_legacy_rows_but_is_not_a_fact_pack() -> None:
|
||||||
|
projection_path = REPO / "src" / "openultrasast" / "ruleset" / "entries" / "express.json"
|
||||||
|
content = projection_path.read_bytes()
|
||||||
|
raw = json.loads(content)
|
||||||
|
|
||||||
|
assert content == json.dumps(raw, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode()
|
||||||
|
assert raw["schema_version"] == "openultrasast.entry-recognition-projection.v1"
|
||||||
|
assert (
|
||||||
|
tuple(
|
||||||
|
(
|
||||||
|
row["legacy_fact_id"],
|
||||||
|
row["defines_handler"],
|
||||||
|
row["handler_kind"],
|
||||||
|
row["pattern"],
|
||||||
|
)
|
||||||
|
for row in raw["facts"]
|
||||||
|
)
|
||||||
|
== _LEGACY_ROWS
|
||||||
|
)
|
||||||
|
assert all(set(row) == {"legacy_fact_id", "languages", "defines_handler", "handler_kind", "pattern"} for row in raw["facts"])
|
||||||
|
with pytest.raises(InstalledAuthorityWireError):
|
||||||
|
decode_fact_pack_bytes(content)
|
||||||
|
|
||||||
|
|
||||||
|
def test_repeated_pattern_load_reuses_fully_authenticated_state(
|
||||||
|
tmp_path: Path,
|
||||||
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
) -> None:
|
||||||
|
_stage_entries(tmp_path)
|
||||||
|
entry_pack_module._cache.clear()
|
||||||
|
authenticates = 0
|
||||||
|
real_load = entry_pack_module.load_installed_entry_fact_authority
|
||||||
|
|
||||||
|
def counted_load(
|
||||||
|
custody: provenance.PopulationCustodyPort,
|
||||||
|
repo_root: Path | None = None,
|
||||||
|
) -> EntryFactAuthorityContext:
|
||||||
|
nonlocal authenticates
|
||||||
|
authenticates += 1
|
||||||
|
return real_load(custody, repo_root)
|
||||||
|
|
||||||
|
monkeypatch.setattr(entry_pack_module, "load_installed_entry_fact_authority", counted_load)
|
||||||
|
|
||||||
|
first = load_entry_pack_patterns(tmp_path)
|
||||||
|
second = load_entry_pack_patterns(tmp_path)
|
||||||
|
|
||||||
|
assert authenticates == 1
|
||||||
|
assert first is not second
|
||||||
|
assert tuple(pattern.pattern for pattern in first["javascript"]) == tuple(pattern.pattern for pattern in second["javascript"])
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"relative_path",
|
||||||
|
(
|
||||||
|
"bootstrap.json",
|
||||||
|
"identities/generator.json",
|
||||||
|
"authority.json",
|
||||||
|
"express.javascript.v2.json",
|
||||||
|
"express.json",
|
||||||
|
"artifacts/sha256/529d18ba2b351a8de985a9cf416261d717ef55a4b7a9d9d65bce53b97d59ab35",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
def test_pattern_cache_invalidates_for_every_authority_state_class(
|
||||||
|
tmp_path: Path,
|
||||||
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
relative_path: str,
|
||||||
|
) -> None:
|
||||||
|
entries = _stage_entries(tmp_path)
|
||||||
|
entry_pack_module._cache.clear()
|
||||||
|
authenticates = 0
|
||||||
|
real_load = entry_pack_module.load_installed_entry_fact_authority
|
||||||
|
|
||||||
|
def counted_load(
|
||||||
|
custody: provenance.PopulationCustodyPort,
|
||||||
|
repo_root: Path | None = None,
|
||||||
|
) -> EntryFactAuthorityContext:
|
||||||
|
nonlocal authenticates
|
||||||
|
authenticates += 1
|
||||||
|
return real_load(custody, repo_root)
|
||||||
|
|
||||||
|
monkeypatch.setattr(entry_pack_module, "load_installed_entry_fact_authority", counted_load)
|
||||||
|
load_entry_pack_patterns(tmp_path)
|
||||||
|
changed = entries / relative_path
|
||||||
|
stat = changed.stat()
|
||||||
|
os.utime(changed, ns=(stat.st_atime_ns, stat.st_mtime_ns + 1_000_000))
|
||||||
|
|
||||||
|
load_entry_pack_patterns(tmp_path)
|
||||||
|
|
||||||
|
assert authenticates == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_pattern_cache_fingerprints_content_even_when_size_and_mtime_match(
|
||||||
|
tmp_path: Path,
|
||||||
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
) -> None:
|
||||||
|
entries = _stage_entries(tmp_path)
|
||||||
|
entry_pack_module._cache.clear()
|
||||||
|
authenticates = 0
|
||||||
|
real_load = entry_pack_module.load_installed_entry_fact_authority
|
||||||
|
|
||||||
|
def counted_load(
|
||||||
|
custody: provenance.PopulationCustodyPort,
|
||||||
|
repo_root: Path | None = None,
|
||||||
|
) -> EntryFactAuthorityContext:
|
||||||
|
nonlocal authenticates
|
||||||
|
authenticates += 1
|
||||||
|
return real_load(custody, repo_root)
|
||||||
|
|
||||||
|
monkeypatch.setattr(entry_pack_module, "load_installed_entry_fact_authority", counted_load)
|
||||||
|
load_entry_pack_patterns(tmp_path)
|
||||||
|
artifact = entries / "artifacts" / "sha256" / "529d18ba2b351a8de985a9cf416261d717ef55a4b7a9d9d65bce53b97d59ab35"
|
||||||
|
content = artifact.read_bytes()
|
||||||
|
stat = artifact.stat()
|
||||||
|
artifact.write_bytes(bytes((content[0] ^ 1,)) + content[1:])
|
||||||
|
os.utime(artifact, ns=(stat.st_atime_ns, stat.st_mtime_ns))
|
||||||
|
|
||||||
|
with pytest.raises(EntryFactAuthorityError):
|
||||||
|
load_entry_pack_patterns(tmp_path)
|
||||||
|
|
||||||
|
assert authenticates == 2
|
||||||
|
|
||||||
|
|
||||||
def test_pack_patterns_resolve_from_package_not_target_repo() -> None:
|
def test_pack_patterns_resolve_from_package_not_target_repo() -> None:
|
||||||
# default call reads the installed package's ruleset, independent of any analyzed repo
|
# default call reads the installed package's ruleset, independent of any analyzed repo
|
||||||
assert load_entry_pack_patterns().get("typescript")
|
assert load_entry_pack_patterns().get("typescript")
|
||||||
|
|||||||
Reference in New Issue
Block a user