mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-11-13 17:58:18 +00:00
feat(access-control-prover): 11.1 entry-recognition strategy registry
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
"""Entry-point mapping package: legacy recognisers plus the strategy registry."""
|
||||
|
||||
from .legacy import * # noqa: F401,F403
|
||||
from .legacy import _php_methods as _php_methods # re-exported internal helpers (enforcement adapter)
|
||||
from .legacy import _php_routes as _php_routes
|
||||
@@ -8,7 +8,8 @@ from dataclasses import asdict, dataclass, replace
|
||||
from pathlib import Path
|
||||
from typing import Any, Literal, cast
|
||||
|
||||
from .preprocess import FileTarget
|
||||
from ..preprocess import FileTarget
|
||||
from .registry import registry_entries
|
||||
|
||||
MappingKind = Literal["semgrep", "codeql", "differential", "sharp_edge"]
|
||||
AccessLevel = Literal[
|
||||
@@ -107,6 +108,7 @@ def analyze_entry_points(
|
||||
for target in targets:
|
||||
text = source_texts[target.path]
|
||||
records.extend(_language_entry_points(target, text))
|
||||
records.extend(registry_entries(target, text))
|
||||
if target.language == "php":
|
||||
framework_entries = _php_framework_entries(target, text, source_texts)
|
||||
names = {e.function_name for e in framework_entries}
|
||||
@@ -138,7 +140,7 @@ def php_hook_callbacks(root: Path, targets: list[FileTarget], register_calls: Se
|
||||
plugin system's event bus -- and a second framework is a row in a TOML, not a change to this function.
|
||||
"""
|
||||
if register_calls is None:
|
||||
from .semantic.facts import load_facts
|
||||
from ..semantic.facts import load_facts
|
||||
|
||||
register_calls = tuple(sorted({call for fact in load_facts().for_language("php").dispatches for call in fact.register}))
|
||||
if not register_calls:
|
||||
@@ -222,7 +224,7 @@ class _PhpMethod:
|
||||
|
||||
def _php_methods(text: str) -> list[_PhpMethod]:
|
||||
"""CST class and method boundaries, including the method's own attributes and docblock."""
|
||||
from .semantic.extra import grammar_for
|
||||
from ..semantic.extra import grammar_for
|
||||
|
||||
grammar = grammar_for("php")
|
||||
if grammar is None:
|
||||
@@ -417,7 +419,7 @@ def _tornado_entries(target: FileTarget, text: str) -> list[EntryPointRecord]:
|
||||
def _remix_exports(target: FileTarget, text: str) -> list[EntryPointRecord]:
|
||||
if "app/routes/" not in "/" + target.path:
|
||||
return []
|
||||
from .semantic.extra import grammar_for
|
||||
from ..semantic.extra import grammar_for
|
||||
|
||||
grammar = grammar_for("tsx" if Path(target.path).suffix == ".tsx" else target.language)
|
||||
if grammar is None:
|
||||
@@ -490,7 +492,7 @@ def entry_framework(entry: object) -> str | None:
|
||||
|
||||
def _java_methods(target: FileTarget, text: str) -> list[tuple[object, str, str]]:
|
||||
"""Use the CST to keep class declarations, parameters and method bodies separate."""
|
||||
from .semantic.extra import grammar_for
|
||||
from ..semantic.extra import grammar_for
|
||||
|
||||
grammar = grammar_for("java")
|
||||
if grammar is None:
|
||||
@@ -0,0 +1,52 @@
|
||||
"""Entry-recognition strategy registry (task 11.1, Reqs 1.1, 1.5, 1.7).
|
||||
|
||||
Target knowledge enters entry recognition through registered strategies; the
|
||||
registry stamps every returned record with the strategy identifier unless the
|
||||
strategy recorded its own provenance. The registry itself names no target.
|
||||
"""
|
||||
|
||||
from dataclasses import replace
|
||||
from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .legacy import EntryPointRecord
|
||||
|
||||
__all__ = ["EntryRecogniser", "register", "registry_entries", "registered_strategies"]
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
class EntryRecogniser(Protocol):
|
||||
"""A target-specific entry-recognition strategy registered from data or a strategy module."""
|
||||
|
||||
identifier: str
|
||||
languages: tuple[str, ...]
|
||||
|
||||
def recognize(self, target: Any, text: str) -> list["EntryPointRecord"]:
|
||||
"""Return the entry points this strategy recognises in one file."""
|
||||
...
|
||||
|
||||
|
||||
_STRATEGIES: dict[str, EntryRecogniser] = {}
|
||||
|
||||
|
||||
def register(recogniser: EntryRecogniser) -> None:
|
||||
"""Register a strategy; re-registering an identifier replaces the earlier strategy."""
|
||||
_STRATEGIES[recogniser.identifier] = recogniser
|
||||
|
||||
|
||||
def registered_strategies(language: str) -> tuple[EntryRecogniser, ...]:
|
||||
"""Return the strategies that recognise entries for one language, ordered by identifier."""
|
||||
return tuple(
|
||||
strategy for strategy in sorted(_STRATEGIES.values(), key=lambda strategy: strategy.identifier) if language in strategy.languages
|
||||
)
|
||||
|
||||
|
||||
def registry_entries(target: Any, text: str) -> list["EntryPointRecord"]:
|
||||
"""Run every matching strategy and stamp its identifier onto each returned record."""
|
||||
records: list[EntryPointRecord] = []
|
||||
for strategy in registered_strategies(getattr(target, "language", "")):
|
||||
for record in strategy.recognize(target, text):
|
||||
if record.provenance == "":
|
||||
record = replace(record, provenance=f"strategy:{strategy.identifier}")
|
||||
records.append(record)
|
||||
return records
|
||||
Reference in New Issue
Block a user