test(s52): AST guard — socket calls confined to the _urllib_post seam
This commit is contained in:
parent
c782c3402c
commit
fde44ceeb6
1 changed files with 46 additions and 0 deletions
|
|
@ -57,6 +57,52 @@ def test_webhook_gate_refuses_without_optin_and_posts_with_optin() -> None:
|
|||
assert len(posts) == 1
|
||||
|
||||
|
||||
# --- LOAD-BEARING: socket-opening CALLS confined to the _urllib_post seam -------------------------
|
||||
|
||||
|
||||
def test_no_socket_call_outside_the_post_seam() -> None:
|
||||
"""LOAD-BEARING (socket confinement): (a) ``notify.py`` never imports ``socket`` at all (no raw
|
||||
sockets needed); (b) every CALL to ``urlopen`` / ``Request`` / ``socket.socket`` has
|
||||
``_urllib_post`` as its nearest-enclosing function — no other function opens a connection. The
|
||||
module-level ``urllib`` IMPORT is ALLOWED (the single sanctioned import, exactly as
|
||||
``ingest.py`` imports urllib at module scope) — the guard scopes the forbidden thing to
|
||||
socket-opening *calls*, not imports (AST, not substring — the ``test_okf.py:216`` lesson).
|
||||
Detach point: add a ``urlopen(...)`` call to any notifier body → RED."""
|
||||
tree = ast.parse((_SRC_DIR / "notify.py").read_text("utf-8"))
|
||||
|
||||
imported: set[str] = set()
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, ast.Import):
|
||||
imported |= {a.name.split(".")[0] for a in node.names}
|
||||
elif isinstance(node, ast.ImportFrom):
|
||||
imported.add((node.module or "").split(".")[0])
|
||||
assert "socket" not in imported, "notify.py must never import socket"
|
||||
|
||||
offending: list[str] = []
|
||||
|
||||
def visit(node: ast.AST, enclosing: str | None) -> None:
|
||||
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
||||
enclosing = node.name
|
||||
if isinstance(node, ast.Call):
|
||||
fn = node.func
|
||||
name = (
|
||||
fn.id
|
||||
if isinstance(fn, ast.Name)
|
||||
else fn.attr
|
||||
if isinstance(fn, ast.Attribute)
|
||||
else None
|
||||
)
|
||||
if name in {"urlopen", "Request", "socket"} and enclosing != "_urllib_post":
|
||||
offending.append(f"{name} called in {enclosing or '<module>'}")
|
||||
for child in ast.iter_child_nodes(node):
|
||||
visit(child, enclosing)
|
||||
|
||||
visit(tree, None)
|
||||
assert offending == [], (
|
||||
f"socket-opening calls must live ONLY in _urllib_post, found: {offending}"
|
||||
)
|
||||
|
||||
|
||||
# --- LOAD-BEARING: transport failures never leak the secret-bearing URL ---------------------------
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue