mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-11-13 01:48:17 +00:00
223 lines
8.8 KiB
Python
223 lines
8.8 KiB
Python
"""access-control-prover task 7.2: resource abstract values and operation-fact sufficiency fields (offline).
|
|
|
|
Exact resources, finite alias sets, request-global scope and unknown resources per the design's "Abstract
|
|
evidence domain" section (Req 2.1, 2.5, 2.6, 2.7, 11.1), plus the additive operation-fact field that declares
|
|
a request-global witness kind sufficient at request scope (Req 2.6).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import itertools
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
|
|
def _resource_fixtures():
|
|
from openultrasast.semantic.obligations.abstract_domain import (
|
|
REQUEST_GLOBAL_RESOURCE,
|
|
UNKNOWN_RESOURCE,
|
|
ResourceRef,
|
|
)
|
|
|
|
return (
|
|
ResourceRef(kind="exact", values=("book",)),
|
|
ResourceRef(kind="exact", values=("volume",)),
|
|
ResourceRef(kind="aliases", values=("book", "volume")),
|
|
ResourceRef(kind="aliases", values=("volume", "shelf")),
|
|
REQUEST_GLOBAL_RESOURCE,
|
|
UNKNOWN_RESOURCE,
|
|
)
|
|
|
|
|
|
_OPERATION_TOML = """version = "1"
|
|
language = "python"
|
|
|
|
[[operation]]
|
|
id = "orm-read"
|
|
kind = "protected_read"
|
|
calls = ["filter_by"]
|
|
requires = ["identity_constraint"]
|
|
sensitivity = "high"
|
|
request_global_kinds = ["path_guard"]
|
|
"""
|
|
|
|
|
|
def test_parse_resource_single_token_is_exact() -> None:
|
|
from openultrasast.semantic.obligations.abstract_domain import ResourceRef, parse_resource
|
|
|
|
binding = parse_resource("book")
|
|
assert binding == ResourceRef(kind="exact", values=("book",))
|
|
assert binding.kind == "exact"
|
|
assert binding.values == ("book",)
|
|
|
|
|
|
def test_parse_resource_token_collection_is_a_finite_alias_set() -> None:
|
|
from openultrasast.semantic.obligations.abstract_domain import ResourceRef, parse_resource
|
|
|
|
binding = parse_resource(["volume", "book", "volume"])
|
|
assert binding == ResourceRef(kind="aliases", values=("book", "volume"))
|
|
|
|
|
|
def test_parse_resource_without_a_binding_is_unknown() -> None:
|
|
from openultrasast.semantic.obligations.abstract_domain import UNKNOWN_RESOURCE, parse_resource
|
|
|
|
binding = parse_resource(None)
|
|
assert binding == UNKNOWN_RESOURCE
|
|
assert binding.kind == "unknown"
|
|
assert binding.values == ()
|
|
|
|
|
|
def test_request_global_resource_binds_the_whole_request() -> None:
|
|
from openultrasast.semantic.obligations.abstract_domain import REQUEST_GLOBAL_RESOURCE
|
|
|
|
assert REQUEST_GLOBAL_RESOURCE.kind == "request_global"
|
|
assert REQUEST_GLOBAL_RESOURCE.values == ()
|
|
|
|
|
|
def test_resource_kinds_distinguish_exact_alias_global_and_unknown() -> None:
|
|
# Req 2.7, 11.1: the four grades stay distinct; an unknown binding never collapses onto a concrete one.
|
|
from openultrasast.semantic.obligations.abstract_domain import (
|
|
REQUEST_GLOBAL_RESOURCE,
|
|
UNKNOWN_RESOURCE,
|
|
parse_resource,
|
|
)
|
|
|
|
grades = {parse_resource("book"), parse_resource(["book"]), REQUEST_GLOBAL_RESOURCE, UNKNOWN_RESOURCE}
|
|
assert {resource.kind for resource in grades} == {"exact", "aliases", "request_global", "unknown"}
|
|
|
|
|
|
def test_alias_values_are_normalized_deterministically() -> None:
|
|
from openultrasast.semantic.obligations.abstract_domain import ResourceRef
|
|
|
|
forward = ResourceRef(kind="aliases", values=("volume", "book", "volume"))
|
|
backward = ResourceRef(kind="aliases", values=("book", "volume"))
|
|
assert forward == backward
|
|
assert hash(forward) == hash(backward)
|
|
assert len({forward, backward}) == 1
|
|
|
|
|
|
def test_resource_ref_fails_loud_on_misshapen_kinds_and_arities() -> None:
|
|
from openultrasast.semantic.obligations.abstract_domain import ResourceRef
|
|
|
|
with pytest.raises(ValueError, match="sometimes"):
|
|
ResourceRef(kind="sometimes", values=("book",))
|
|
with pytest.raises(ValueError, match="exact"):
|
|
ResourceRef(kind="exact")
|
|
with pytest.raises(ValueError, match="exact"):
|
|
ResourceRef(kind="exact", values=("book", "volume"))
|
|
with pytest.raises(ValueError, match="alias"):
|
|
ResourceRef(kind="aliases", values=())
|
|
with pytest.raises(ValueError, match="request_global"):
|
|
ResourceRef(kind="request_global", values=("book",))
|
|
|
|
|
|
def test_parse_resource_rejects_misshapen_bindings() -> None:
|
|
from openultrasast.semantic.obligations.abstract_domain import parse_resource
|
|
|
|
with pytest.raises(ValueError):
|
|
parse_resource("")
|
|
with pytest.raises(ValueError):
|
|
parse_resource(())
|
|
with pytest.raises(ValueError):
|
|
parse_resource(["book", ""])
|
|
|
|
|
|
def test_join_resources_keeps_identical_exact_tokens_exact() -> None:
|
|
from openultrasast.semantic.obligations.abstract_domain import ResourceRef, join_resources
|
|
|
|
binding = ResourceRef(kind="exact", values=("book",))
|
|
assert join_resources(binding, ResourceRef(kind="exact", values=("book",))) == binding
|
|
|
|
|
|
def test_join_resources_merges_different_exact_tokens_into_an_alias_set() -> None:
|
|
# Req 2.5: a different resource never collapses onto one exact binding; it widens to the finite alias set.
|
|
from openultrasast.semantic.obligations.abstract_domain import ResourceRef, join_resources
|
|
|
|
joined = join_resources(
|
|
ResourceRef(kind="exact", values=("book",)),
|
|
ResourceRef(kind="exact", values=("volume",)),
|
|
)
|
|
assert joined == ResourceRef(kind="aliases", values=("book", "volume"))
|
|
|
|
|
|
def test_join_resources_adds_exact_tokens_into_alias_sets() -> None:
|
|
from openultrasast.semantic.obligations.abstract_domain import ResourceRef, join_resources
|
|
|
|
joined = join_resources(
|
|
ResourceRef(kind="exact", values=("shelf",)),
|
|
ResourceRef(kind="aliases", values=("book", "volume")),
|
|
)
|
|
assert joined == ResourceRef(kind="aliases", values=("book", "shelf", "volume"))
|
|
|
|
|
|
def test_join_resources_unions_alias_sets_to_their_superset() -> None:
|
|
from openultrasast.semantic.obligations.abstract_domain import ResourceRef, join_resources
|
|
|
|
subset = ResourceRef(kind="aliases", values=("book",))
|
|
superset = ResourceRef(kind="aliases", values=("book", "volume"))
|
|
assert join_resources(subset, superset) == superset
|
|
assert join_resources(superset, subset) == superset
|
|
disjoint = join_resources(subset, ResourceRef(kind="aliases", values=("volume",)))
|
|
assert disjoint == ResourceRef(kind="aliases", values=("book", "volume"))
|
|
|
|
|
|
def test_join_resources_request_global_absorbs_concrete_bindings() -> None:
|
|
# Req 2.6: request-global scope already covers every resource in the request.
|
|
from openultrasast.semantic.obligations.abstract_domain import (
|
|
REQUEST_GLOBAL_RESOURCE,
|
|
ResourceRef,
|
|
join_resources,
|
|
)
|
|
|
|
for concrete in (
|
|
ResourceRef(kind="exact", values=("book",)),
|
|
ResourceRef(kind="aliases", values=("book", "volume")),
|
|
):
|
|
assert join_resources(concrete, REQUEST_GLOBAL_RESOURCE) == REQUEST_GLOBAL_RESOURCE
|
|
assert join_resources(REQUEST_GLOBAL_RESOURCE, concrete) == REQUEST_GLOBAL_RESOURCE
|
|
assert join_resources(REQUEST_GLOBAL_RESOURCE, REQUEST_GLOBAL_RESOURCE) == REQUEST_GLOBAL_RESOURCE
|
|
|
|
|
|
def test_join_resources_unknown_absorbs_every_fixture() -> None:
|
|
# Req 2.7, 11.1: an unresolvable binding stays unresolvable; joining cannot manufacture precision.
|
|
from openultrasast.semantic.obligations.abstract_domain import UNKNOWN_RESOURCE, join_resources
|
|
|
|
for other in _resource_fixtures():
|
|
assert join_resources(other, UNKNOWN_RESOURCE) == UNKNOWN_RESOURCE, other
|
|
assert join_resources(UNKNOWN_RESOURCE, other) == UNKNOWN_RESOURCE, other
|
|
|
|
|
|
def test_join_resources_is_commutative_and_idempotent_over_fixtures() -> None:
|
|
from openultrasast.semantic.obligations.abstract_domain import join_resources
|
|
|
|
fixtures = _resource_fixtures()
|
|
for left, right in itertools.product(fixtures, repeat=2):
|
|
assert join_resources(left, right) == join_resources(right, left), (left, right)
|
|
for resource in fixtures:
|
|
assert join_resources(resource, resource) == resource, resource
|
|
|
|
|
|
def test_operation_fact_declares_request_global_sufficient_kinds(tmp_path: Path) -> None:
|
|
from openultrasast.semantic.obligations.facts import load_obligation_facts
|
|
|
|
(tmp_path / "python.toml").write_text(_OPERATION_TOML)
|
|
(row,) = load_obligation_facts(tmp_path).operations
|
|
assert row.request_global_kinds == ("path_guard",)
|
|
|
|
|
|
def test_operation_fact_without_sufficiency_declares_none(tmp_path: Path) -> None:
|
|
from openultrasast.semantic.obligations.facts import load_obligation_facts
|
|
|
|
(tmp_path / "python.toml").write_text(_OPERATION_TOML.replace('request_global_kinds = ["path_guard"]\n', ""))
|
|
(row,) = load_obligation_facts(tmp_path).operations
|
|
assert row.request_global_kinds == ()
|
|
|
|
|
|
def test_operation_fact_rejects_unknown_request_global_kind(tmp_path: Path) -> None:
|
|
from openultrasast.semantic.obligations.facts import ObligationFactsError, load_obligation_facts
|
|
|
|
(tmp_path / "python.toml").write_text(_OPERATION_TOML.replace('["path_guard"]', '["vibes"]'))
|
|
with pytest.raises(ObligationFactsError, match="vibes"):
|
|
load_obligation_facts(tmp_path)
|