Files
vm-mcp/tests/governance/test_auth_fail_closed.py
Vault Sovereign e4871c2a29
Some checks are pending
Governance CI / Constitution Hash Gate (push) Waiting to run
Governance CI / Governance Tests (push) Blocked by required conditions
Governance CI / Golden Drill Mini (push) Blocked by required conditions
init: vaultmesh mcp server
2025-12-26 23:23:08 +00:00

141 lines
5.2 KiB
Python

"""
Test: Authentication Fail-Closed
Ensures unknown tools, profiles, and scopes are denied.
Authority must never be granted by default.
"""
import pytest
from vaultmesh_mcp.tools.auth import (
auth_check_permission,
auth_create_dev_session,
Profile,
check_profile_permission,
get_profile_for_scope,
SCOPE_TOOLS,
)
class TestFailClosed:
"""Fail-closed semantics - deny by default."""
def test_unknown_tool_denied(self):
"""Unknown tool must be denied regardless of scope."""
session = auth_create_dev_session(scope="sovereign")
token = session["token"]
result = auth_check_permission(token, "unknown_tool_xyz")
assert not result["allowed"], "Unknown tool should be denied"
def test_unknown_scope_maps_to_observer(self):
"""Unknown scope must map to OBSERVER (most restrictive)."""
profile = get_profile_for_scope("unknown_scope_xyz")
assert profile == Profile.OBSERVER, (
f"Unknown scope should map to OBSERVER, got {profile}"
)
def test_invalid_token_denied(self):
"""Invalid token must be denied."""
result = auth_check_permission("invalid_token_xyz", "cognitive_context")
assert not result["allowed"], "Invalid token should be denied"
def test_expired_session_denied(self):
"""Expired session must be denied (simulated via missing session)."""
result = auth_check_permission("expired_session_token", "cognitive_context")
assert not result["allowed"], "Expired session should be denied"
class TestProfileDeny:
"""Profile-based denials."""
def test_observer_denied_mutations(self):
"""OBSERVER cannot perform mutations."""
mutation_tools = [
"write_file",
"cognitive_decide",
"treasury_debit",
"offsec_tem_transmute",
]
for tool in mutation_tools:
result = check_profile_permission(Profile.OBSERVER, tool)
assert not result["allowed"], f"OBSERVER should be denied {tool}"
def test_operator_denied_tem(self):
"""OPERATOR cannot invoke Tem."""
result = check_profile_permission(Profile.OPERATOR, "cognitive_invoke_tem")
assert not result["allowed"], "OPERATOR should be denied Tem invocation"
def test_guardian_denied_phoenix_ops(self):
"""GUARDIAN cannot perform Phoenix operations."""
phoenix_ops = [
"offsec_phoenix_enable",
"offsec_phoenix_inject_crisis",
]
for tool in phoenix_ops:
result = check_profile_permission(Profile.GUARDIAN, tool)
assert not result["allowed"], f"GUARDIAN should be denied {tool}"
def test_phoenix_denied_treasury_create(self):
"""PHOENIX cannot create budgets (SOVEREIGN only)."""
result = check_profile_permission(Profile.PHOENIX, "treasury_create_budget")
assert not result["allowed"], "PHOENIX should be denied treasury creation"
class TestSovereignRequiresHuman:
"""SOVEREIGN profile requires human verification."""
def test_sovereign_cannot_be_auto_granted(self):
"""
SOVEREIGN authority cannot be granted through normal dev session.
This tests the constitutional invariant.
"""
# Dev session creates a session, but SOVEREIGN operations
# should still require additional human verification
session = auth_create_dev_session(scope="cognitive")
token = session["token"]
# Even with dev session, sovereign-only operations need proof
# The dev session scope is "cognitive", not "vault"
result = auth_check_permission(token, "treasury_create_budget")
# This should be denied because cognitive scope doesn't include
# treasury creation - that requires vault/sovereign scope
# The key point: sovereign authority isn't auto-granted
assert session["scope"] != "sovereign" or session.get("dev_mode"), (
"Production sessions should not auto-grant sovereign"
)
class TestCollapseSemantics:
"""Authority collapse tests - always downward, never upward."""
def test_insufficient_profile_collapses(self):
"""When profile is insufficient, result indicates collapse target."""
result = check_profile_permission(Profile.OBSERVER, "cognitive_decide")
assert not result["allowed"]
# The denial should indicate the profile level
assert result["profile"] == "observer"
def test_profile_hierarchy_is_strict(self):
"""Profile hierarchy: OBSERVER < OPERATOR < GUARDIAN < PHOENIX < SOVEREIGN."""
profiles = [
Profile.OBSERVER,
Profile.OPERATOR,
Profile.GUARDIAN,
Profile.PHOENIX,
Profile.SOVEREIGN,
]
# Each profile should have MORE tools than the one before
prev_count = 0
for profile in profiles:
from vaultmesh_mcp.tools.auth import PROFILE_TOOLS
tool_count = len(PROFILE_TOOLS.get(profile, set()))
assert tool_count >= prev_count, (
f"{profile.value} should have >= tools than previous profile"
)
prev_count = tool_count