mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-16 19:59:26 +00:00
feat(model): an endpoint its contract declares open carries no obligation
contributor-scan 2.8. VAmPI's last two false positives were register_user
and login_user, both declared open by its OpenAPI spec: the arbiter was
reporting a missing guard on endpoints whose contract says there is
nothing to miss.
**The fix keys on provenance, not on the value, and that distinction is
the whole task.** The mapper says "public" in two very different
situations. An OpenAPI operation with no `security` block is DECLARED
open, because the spec is a complete contract. A Flask handler with no
`@login_required` is "public" only because nothing was found -- which is
precisely the bug this family exists to report. Suppressing on the value
alone would have silenced the detector on its primary case, so
EntryPointRecord now carries `access_declared` and only a declaration
removes the obligation.
WordPress supplied the second source of declared access on the same day --
wp_ajax_nopriv_ for a logged-out handler, wp_ajax_ for a logged-in one,
permission_callback => __return_true for an open REST route. A rule resting
on one framework's convention would be a special case; two make it a shape.
vampi entailed 6 -> 4
access-control precision 2 of 4 -> 2 of 2
known vulnerabilities 3 of 3 still entailed
access_control offered to 6 of 14 handlers, exactly the six the spec
declares authenticated
**And the guard that decides whether it is a real improvement:** the
access-control pair corpus, which nobody tuned this against, is unmoved --
pair_correct 3 and leak 1 across all nine, pair_correct 2 and leak 0.0%
across the seven from projects unrelated to VAmPI, identical to task 2.6.
The precision was not bought by silencing the family. Pair excerpts carry
no route manifest and no hook, so nothing in them is declared open and the
rule correctly does not fire.
Gates byte-identical.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01T7XD7x5KonMdmGRkp63EQG
This commit is contained in:
co-authored by
Claude Opus 5
parent
3e1d13e4b6
commit
60d2a1a92c
@@ -9,7 +9,7 @@ language actually admit.
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
def _entry(path="api/users.py", function="update_password", access="public", boundary="network"): # type: ignore[no-untyped-def]
|
||||
def _entry(path="api/users.py", function="update_password", access="public", boundary="network", declared=False): # type: ignore[no-untyped-def]
|
||||
from openultrasast.mapping import EntryPointRecord
|
||||
|
||||
return EntryPointRecord(
|
||||
@@ -25,6 +25,7 @@ def _entry(path="api/users.py", function="update_password", access="public", bou
|
||||
conditions=[],
|
||||
provenance="decorator",
|
||||
rationale="",
|
||||
access_declared=declared,
|
||||
)
|
||||
|
||||
|
||||
@@ -238,3 +239,36 @@ def test_what_the_project_does_not_ship_ranks_below_what_it_does() -> None:
|
||||
ordered = [(r.path, r.shipped) for r in regions]
|
||||
assert ordered[0] == ("lib.py", True), "shipped code comes first whatever the rank says"
|
||||
assert ("example.py", False) in ordered, "not shipped is not unscanned"
|
||||
|
||||
|
||||
def test_a_declared_open_endpoint_carries_no_authorization_obligation() -> None:
|
||||
"""contributor-scan 2.8. VAmPI's OpenAPI spec declares register_user and login_user open, and the arbiter
|
||||
entailed a missing guard on both -- reporting an absence on endpoints whose contract says there is
|
||||
nothing to miss."""
|
||||
from openultrasast.model.regions import regions_for
|
||||
|
||||
declared_open = _entry(path="api.py", function="register_user", access="public", declared=True)
|
||||
assert declared_open.access_declared, "the fixture must actually carry the declaration it is testing"
|
||||
|
||||
regions = regions_for([declared_open], [_target(path="api.py")])
|
||||
handler = next(r for r in regions if r.function == "register_user")
|
||||
|
||||
assert "access_control" not in handler.families
|
||||
assert "injection" in handler.families, "only the obligation goes; the flow families stay"
|
||||
|
||||
|
||||
def test_an_inferred_public_endpoint_keeps_the_obligation() -> None:
|
||||
"""The case this rule must not break, and the reason it keys on provenance rather than on the level.
|
||||
|
||||
For a decorator framework "public" means only that no `@login_required` was found -- which is precisely
|
||||
the bug the access-control family exists to report. Suppressing on the value alone would silence the
|
||||
detector on its primary case.
|
||||
"""
|
||||
from openultrasast.model.regions import regions_for
|
||||
|
||||
inferred = _entry(path="app.py", function="handler", access="public") # access_declared defaults False
|
||||
|
||||
regions = regions_for([inferred], [_target(path="app.py")])
|
||||
handler = next(r for r in regions if r.function == "handler")
|
||||
|
||||
assert "access_control" in handler.families
|
||||
|
||||
Reference in New Issue
Block a user