mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-11-13 17:58:18 +00:00
feat(access-control-prover): 11.6 migrate the stapler recogniser through the registry
This commit is contained in:
@@ -175,7 +175,8 @@ def test_extraction_includes_java_operation_rows() -> None:
|
||||
def test_legacy_strategy_extraction_names_recognisers() -> None:
|
||||
records = extract_legacy_strategies(MAPPING_FILE)
|
||||
names = {record.name for record in records}
|
||||
assert "_stapler_entries" in names
|
||||
# _stapler_entries migrated to mapping/stapler.py (task 11.6); legacy keeps the rest.
|
||||
assert "_stapler_entries" not in names
|
||||
assert "_jetty_entries" in names
|
||||
assert "_python_registered_routes" in names
|
||||
for record in records:
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
import inspect
|
||||
from collections.abc import Generator
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
@@ -18,11 +20,17 @@ from openultrasast.preprocess import FileTarget, detect_language
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _clean_registry() -> None:
|
||||
def _clean_registry() -> Generator[None, None, None]:
|
||||
"""Each test registers its own strategies into the shared module registry."""
|
||||
from openultrasast.mapping import registry
|
||||
|
||||
saved_strategies = dict(registry._STRATEGIES)
|
||||
registry._STRATEGIES.clear()
|
||||
stapler_module = importlib.import_module("openultrasast.mapping.stapler")
|
||||
importlib.reload(stapler_module)
|
||||
yield
|
||||
registry._STRATEGIES.clear()
|
||||
registry._STRATEGIES.update(saved_strategies)
|
||||
|
||||
|
||||
class FixtureRouteStrategy:
|
||||
@@ -93,7 +101,8 @@ JAVA_TEXT = "package p;\nclass H {\n // fixture marker line 3\n void fixtureHa
|
||||
def test_registered_strategy_is_listed_for_its_language() -> None:
|
||||
register(FixtureRouteStrategy())
|
||||
strategies = registered_strategies("java")
|
||||
assert [strategy.identifier for strategy in strategies] == ["test-fixture"]
|
||||
identifiers = [strategy.identifier for strategy in strategies]
|
||||
assert "test-fixture" in identifiers # built-in strategies (e.g. stapler) may coexist
|
||||
assert registered_strategies("php") == ()
|
||||
|
||||
|
||||
@@ -115,7 +124,8 @@ def test_strategy_set_provenance_is_preserved() -> None:
|
||||
def test_re_registration_replaces_the_strategy() -> None:
|
||||
register(FixtureRouteStrategy())
|
||||
register(FixtureRouteStrategy())
|
||||
assert [s.identifier for s in registered_strategies("java")] == ["test-fixture"]
|
||||
identifiers = [s.identifier for s in registered_strategies("java")]
|
||||
assert identifiers.count("test-fixture") == 1 # replaced, never duplicated
|
||||
|
||||
|
||||
def test_registry_entries_are_deterministic() -> None:
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
"""Tests for the migrated Stapler entry-recognition strategy (task 11.6).
|
||||
|
||||
The migration moves the Stapler family out of inline legacy dispatch into a
|
||||
registered strategy. The invariant pinned here (and by the untouched legacy
|
||||
Stapler tests in tests/test_mapping_frameworks.py) is byte-equal entries on
|
||||
the old fixtures: same fields, same provenance, same evidence.
|
||||
"""
|
||||
|
||||
import importlib
|
||||
from collections.abc import Generator
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from openultrasast.mapping import analyze_entry_points, legacy, registry
|
||||
from openultrasast.preprocess import FileTarget
|
||||
|
||||
JAVA_STAPLER = """package hudson.slaves;
|
||||
|
||||
import hudson.model.Descriptor;
|
||||
import org.kohsuke.stapler.StaplerRequest;
|
||||
|
||||
public class FixtureDescriptor extends Descriptor<Thing> {
|
||||
public Object doTest(StaplerRequest req) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private Object helperNoEntry() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
JAVA_UNPROTECTED = """package hudson.model;
|
||||
|
||||
import org.kohsuke.stapler.StaplerRequest;
|
||||
|
||||
public class OpenActions implements UnprotectedRootAction {
|
||||
public Object doExecute(StaplerRequest req) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _fresh_stapler_strategy() -> Generator[None, None, None]:
|
||||
saved_strategies = dict(registry._STRATEGIES)
|
||||
registry._STRATEGIES.clear()
|
||||
stapler_module = importlib.import_module("openultrasast.mapping.stapler")
|
||||
importlib.reload(stapler_module)
|
||||
yield
|
||||
registry._STRATEGIES.clear()
|
||||
registry._STRATEGIES.update(saved_strategies)
|
||||
|
||||
|
||||
def _target(path: str, text: str) -> FileTarget:
|
||||
return FileTarget(
|
||||
path=path,
|
||||
absolute_path=f"/repo/{path}",
|
||||
language="java",
|
||||
loc=len(text.splitlines()),
|
||||
tags=[],
|
||||
has_fuzz_entry_point=False,
|
||||
)
|
||||
|
||||
|
||||
def test_stapler_strategy_is_registered_for_java() -> None:
|
||||
identifiers = tuple(strategy.identifier for strategy in registry.registered_strategies("java"))
|
||||
assert "stapler" in identifiers
|
||||
|
||||
|
||||
def test_migrated_entries_match_the_legacy_shape(tmp_path: Path) -> None:
|
||||
(tmp_path / "Fixture.java").write_text(JAVA_STAPLER, encoding="utf-8")
|
||||
entries = analyze_entry_points(tmp_path, [_target("Fixture.java", JAVA_STAPLER)], include_manifests=False)
|
||||
stapler_entries = [entry for entry in entries if entry.function_name == "doTest"]
|
||||
assert len(stapler_entries) == 1
|
||||
entry = stapler_entries[0]
|
||||
assert entry.kind == "route"
|
||||
assert entry.access_level == "unknown"
|
||||
assert entry.trust_boundary == "http_request"
|
||||
assert entry.provenance == "entrypoint:route:unknown"
|
||||
assert entry.access_declared is False
|
||||
assert any("Stapler public do method" in line for line in entry.access_evidence)
|
||||
|
||||
|
||||
def test_inline_stapler_matching_is_removed_from_legacy() -> None:
|
||||
assert not hasattr(legacy, "_stapler_entries")
|
||||
|
||||
|
||||
def test_jetty_family_stays_inline_in_legacy() -> None:
|
||||
assert hasattr(legacy, "_jetty_entries")
|
||||
|
||||
|
||||
def test_migrated_entries_are_deterministic(tmp_path: Path) -> None:
|
||||
(tmp_path / "Fixture.java").write_text(JAVA_STAPLER, encoding="utf-8")
|
||||
first = analyze_entry_points(tmp_path, [_target("Fixture.java", JAVA_STAPLER)], include_manifests=False)
|
||||
second = analyze_entry_points(tmp_path, [_target("Fixture.java", JAVA_STAPLER)], include_manifests=False)
|
||||
assert first == second
|
||||
|
||||
|
||||
def test_unprotected_root_action_keeps_public_access() -> None:
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
(root / "Open.java").write_text(JAVA_UNPROTECTED, encoding="utf-8")
|
||||
entries = analyze_entry_points(root, [_target("Open.java", JAVA_UNPROTECTED)], include_manifests=False)
|
||||
execute_entries = [entry for entry in entries if entry.function_name == "doExecute"]
|
||||
assert len(execute_entries) == 1
|
||||
entry = execute_entries[0]
|
||||
assert entry.access_level == "public"
|
||||
assert entry.access_declared is True
|
||||
assert entry.provenance == "entrypoint:route:public"
|
||||
Reference in New Issue
Block a user