File size: 18,994 Bytes
81e3673 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 | """
Authorization security tests (SECU-02).
Tests cover:
- Agent maturity permission matrix (4x4 combinations)
- Action complexity enforcement
- Governance checks
- Permission boundaries
"""
import pytest
from fastapi.testclient import TestClient
from sqlalchemy.orm import Session
from tests.factories.agent_factory import (
StudentAgentFactory,
InternAgentFactory,
SupervisedAgentFactory,
AutonomousAgentFactory
)
from tests.factories.user_factory import AdminUserFactory
from core.models import AgentStatus
from core.auth import get_password_hash, create_access_token
# Test data: Maturity level x Action complexity = expected result
# Note: Complexity based on agent_governance_service.ACTION_COMPLEXITY
MATURITY_ACTION_MATRIX = [
# (agent_factory, action_type, complexity, allowed, description)
# STUDENT agents - blocked from all but read-only
(StudentAgentFactory, "search", 1, True, "STUDENT can search (read-only)"),
(StudentAgentFactory, "read", 1, True, "STUDENT can read (read-only)"),
(StudentAgentFactory, "list", 1, True, "STUDENT can list (read-only)"),
(StudentAgentFactory, "stream", 2, False, "STUDENT blocked from streaming"),
(StudentAgentFactory, "analyze", 2, False, "STUDENT blocked from analyze"),
(StudentAgentFactory, "delete", 4, False, "STUDENT blocked from deletions"),
(StudentAgentFactory, "execute", 4, False, "STUDENT blocked from execute"),
(StudentAgentFactory, "update", 3, False, "STUDENT blocked from state changes"),
# INTERN agents - can do level 1-2, blocked from 3-4
(InternAgentFactory, "search", 1, True, "INTERN can search"),
(InternAgentFactory, "analyze", 2, True, "INTERN can analyze"),
(InternAgentFactory, "draft", 2, True, "INTERN can draft"),
(InternAgentFactory, "delete", 4, False, "INTERN blocked from deletions"),
(InternAgentFactory, "execute", 4, False, "INTERN blocked from execute"),
(InternAgentFactory, "update", 3, False, "INTERN blocked from state changes"),
(InternAgentFactory, "send_email", 3, False, "INTERN blocked from send_email"),
(InternAgentFactory, "submit", 2, True, "INTERN can submit (complexity 2 - not submit_form)"),
# SUPERVISED agents - can do level 1-3, blocked from 4
(SupervisedAgentFactory, "search", 1, True, "SUPERVISED can search"),
(SupervisedAgentFactory, "stream", 2, True, "SUPERVISED can stream"),
(SupervisedAgentFactory, "analyze", 2, True, "SUPERVISED can analyze"),
(SupervisedAgentFactory, "update", 3, True, "SUPERVISED can update state"),
(SupervisedAgentFactory, "submit_form", 3, True, "SUPERVISED can submit_form"),
(SupervisedAgentFactory, "delete", 4, False, "SUPERVISED blocked from deletions"),
(SupervisedAgentFactory, "execute", 4, False, "SUPERVISED blocked from execute"),
(SupervisedAgentFactory, "send_email", 3, True, "SUPERVISED can send_email"),
# AUTONOMOUS agents - full execution
(AutonomousAgentFactory, "search", 1, True, "AUTONOMOUS can search"),
(AutonomousAgentFactory, "stream", 2, True, "AUTONOMOUS can stream"),
(AutonomousAgentFactory, "update", 3, True, "AUTONOMOUS can update state"),
(AutonomousAgentFactory, "delete", 4, True, "AUTONOMOUS can delete"),
(AutonomousAgentFactory, "execute", 4, True, "AUTONOMOUS can execute"),
(AutonomousAgentFactory, "submit", 2, True, "AUTONOMOUS can submit"),
(AutonomousAgentFactory, "send_email", 3, True, "AUTONOMOUS can send_email"),
(AutonomousAgentFactory, "analyze", 2, True, "AUTONOMOUS can analyze"),
]
class TestMaturityPermissionMatrix:
"""Test agent maturity permission matrix enforcement."""
@pytest.mark.parametrize("agent_factory,action,complexity,allowed,description", MATURITY_ACTION_MATRIX)
def test_maturity_action_combinations(
self,
db_session: Session,
agent_factory,
action,
complexity,
allowed,
description
):
"""
Test all maturity level x action complexity combinations.
This is a CRITICAL security test ensuring governance enforcement.
"""
# Given: Agent with specific maturity level
agent = agent_factory(_session=db_session)
# When: Check governance permission
from core.agent_governance_service import AgentGovernanceService
governance = AgentGovernanceService(db_session)
result = governance.can_perform_action(
agent_id=agent.id,
action_type=action
)
# Then: Enforce expected permission
assert result["allowed"] == allowed, \
f"Failed for {description}: got allowed={result['allowed']}, expected allowed={allowed}"
# Verify maturity level is correct
assert result["agent_status"] == agent.status
# Verify action complexity is detected
assert result["action_complexity"] == complexity
# Verify governance reason is provided
assert "reason" in result
assert len(result["reason"]) > 0
def test_student_blocked_from_all_triggers(self, db_session: Session):
"""Test STUDENT agent is completely blocked from automated triggers."""
agent = StudentAgentFactory(_session=db_session)
from core.agent_governance_service import AgentGovernanceService
governance = AgentGovernanceService(db_session)
# STUDENT should be blocked from high-complexity actions
for action in ["delete", "execute", "update", "send_email"]:
result = governance.can_perform_action(agent.id, action)
assert result["allowed"] == False, f"STUDENT should be blocked from {action}"
assert "lacks maturity" in result["reason"].lower() or "blocked" in result["reason"].lower()
def test_intern_requires_approval_for_state_changes(self, db_session: Session):
"""Test INTERN agent requires approval for state changes."""
agent = InternAgentFactory(_session=db_session)
from core.agent_governance_service import AgentGovernanceService
governance = AgentGovernanceService(db_session)
# INTERN should be blocked from high-complexity actions
for action in ["delete", "execute", "update", "send_email", "submit_form"]:
result = governance.can_perform_action(agent.id, action)
assert result["allowed"] == False, f"INTERN should be blocked from {action}"
def test_supervised_allowed_for_most_actions(self, db_session: Session):
"""Test SUPERVISED agent can perform most actions except critical."""
agent = SupervisedAgentFactory(_session=db_session)
from core.agent_governance_service import AgentGovernanceService
governance = AgentGovernanceService(db_session)
# SUPERVISED should be allowed for levels 1-3
for action in ["search", "stream", "analyze", "update", "submit", "send_email"]:
result = governance.can_perform_action(agent.id, action)
assert result["allowed"] == True, f"SUPERVISED should be allowed to {action}"
# But blocked from level 4
for action in ["delete", "execute"]:
result = governance.can_perform_action(agent.id, action)
assert result["allowed"] == False, f"SUPERVISED should be blocked from {action}"
def test_autonomous_full_execution(self, db_session: Session):
"""Test AUTONOMOUS agent has full execution权限."""
agent = AutonomousAgentFactory(_session=db_session)
from core.agent_governance_service import AgentGovernanceService
governance = AgentGovernanceService(db_session)
# AUTONOMOUS should be allowed for all actions
for action in ["search", "stream", "analyze", "update", "submit", "delete", "execute", "send_email"]:
result = governance.can_perform_action(agent.id, action)
assert result["allowed"] == True, \
f"AUTONOMOUS agent should be allowed to {action}, got: {result}"
class TestGovernanceCaching:
"""Test governance cache doesn't bypass security."""
def test_governance_cache_consistent_with_database(self, db_session: Session):
"""Test governance cache returns consistent decisions."""
from core.governance_cache import get_governance_cache
agent = StudentAgentFactory(_session=db_session)
# First check - database
from core.agent_governance_service import AgentGovernanceService
governance = AgentGovernanceService(db_session)
db_result = governance.can_perform_action(
agent_id=agent.id,
action_type="delete"
)
# Verify the result was cached
cache = get_governance_cache()
cached_result = cache.get(agent.id, "delete")
# Both should agree (STUDENT blocked from delete)
assert cached_result is not None
assert cached_result["allowed"] == db_result["allowed"]
assert db_result["allowed"] == False
def test_cache_invalidation_on_maturity_change(self, db_session: Session):
"""Test cache invalidates when agent maturity changes."""
agent = StudentAgentFactory(_session=db_session)
from core.governance_cache import get_governance_cache
from core.agent_governance_service import AgentGovernanceService
cache = get_governance_cache()
governance = AgentGovernanceService(db_session)
# Check initial permission (should be blocked)
result1 = governance.can_perform_action(agent.id, "delete")
assert result1["allowed"] == False
# Promote agent to AUTONOMOUS
agent.status = AgentStatus.AUTONOMOUS.value
agent.confidence_score = 0.95
db_session.commit()
# Invalidate cache
cache.invalidate(agent.id)
# Check new permission (should be allowed)
result2 = governance.can_perform_action(agent.id, "delete")
assert result2["allowed"] == True
def test_cache_hit_returns_same_result(self, db_session: Session):
"""Test cache hit returns identical result."""
agent = InternAgentFactory(_session=db_session)
from core.agent_governance_service import AgentGovernanceService
governance = AgentGovernanceService(db_session)
# First call - cache miss
result1 = governance.can_perform_action(agent.id, "stream")
assert "action_complexity" in result1
# Second call - cache hit
result2 = governance.can_perform_action(agent.id, "stream")
# Should return same result
assert result1["allowed"] == result2["allowed"]
assert result1["action_complexity"] == result2["action_complexity"]
assert result1["agent_status"] == result2["agent_status"]
class TestPermissionBoundaries:
"""Test permission boundaries between maturity levels."""
def test_maturity_hierarchy_enforced(self, db_session: Session):
"""Test maturity level hierarchy is properly enforced."""
from core.agent_governance_service import AgentGovernanceService
governance = AgentGovernanceService(db_session)
# Create one of each maturity level
student = StudentAgentFactory(_session=db_session)
intern = InternAgentFactory(_session=db_session)
supervised = SupervisedAgentFactory(_session=db_session)
autonomous = AutonomousAgentFactory(_session=db_session)
# Test a high-complexity action (delete)
for agent in [student, intern, supervised]:
result = governance.can_perform_action(agent.id, "delete")
assert result["allowed"] == False, \
f"{agent.status} should not be allowed to delete"
# Only AUTONOMOUS can delete
result = governance.can_perform_action(autonomous.id, "delete")
assert result["allowed"] == True
def test_confidence_score_consistent_with_maturity(self, db_session: Session):
"""Test confidence scores match maturity levels."""
student = StudentAgentFactory(_session=db_session)
intern = InternAgentFactory(_session=db_session)
supervised = SupervisedAgentFactory(_session=db_session)
autonomous = AutonomousAgentFactory(_session=db_session)
# STUDENT: <0.5
assert 0.0 <= student.confidence_score < 0.5
# INTERN: 0.5-0.7
assert 0.5 <= intern.confidence_score < 0.7
# SUPERVISED: 0.7-0.9
assert 0.7 <= supervised.confidence_score < 0.9
# AUTONOMOUS: >=0.9
assert 0.9 <= autonomous.confidence_score <= 1.0
def test_action_complexity_levels(self, db_session: Session):
"""Test action complexity is correctly classified."""
from core.agent_governance_service import AgentGovernanceService
governance = AgentGovernanceService(db_session)
agent = AutonomousAgentFactory(_session=db_session)
# Level 1 actions (low risk)
for action in ["search", "read", "list", "get", "summarize", "present_chart"]:
result = governance.can_perform_action(agent.id, action)
assert result["action_complexity"] == 1, \
f"{action} should be complexity 1, got {result['action_complexity']}"
# Level 2 actions (medium-low)
for action in ["analyze", "suggest", "draft", "generate", "stream", "submit"]:
result = governance.can_perform_action(agent.id, action)
assert result["action_complexity"] == 2, \
f"{action} should be complexity 2, got {result['action_complexity']}"
# Level 3 actions (high)
for action in ["update", "submit_form", "send_email", "create", "post_message"]:
result = governance.can_perform_action(agent.id, action)
assert result["action_complexity"] == 3, \
f"{action} should be complexity 3, got {result['action_complexity']}"
# Level 4 actions (critical)
for action in ["delete", "execute", "deploy", "payment"]:
result = governance.can_perform_action(agent.id, action)
assert result["action_complexity"] == 4, \
f"{action} should be complexity 4, got {result['action_complexity']}"
def test_get_agent_capabilities(self, db_session: Session):
"""Test getting agent capabilities based on maturity."""
from core.agent_governance_service import AgentGovernanceService
governance = AgentGovernanceService(db_session)
# Test STUDENT agent
student = StudentAgentFactory(_session=db_session)
capabilities = governance.get_agent_capabilities(student.id)
assert capabilities["maturity_level"] == AgentStatus.STUDENT.value
assert capabilities["max_complexity"] == 1
assert len(capabilities["allowed_actions"]) > 0
assert "search" in capabilities["allowed_actions"]
assert "delete" not in capabilities["allowed_actions"]
# Test AUTONOMOUS agent
autonomous = AutonomousAgentFactory(_session=db_session)
capabilities = governance.get_agent_capabilities(autonomous.id)
assert capabilities["maturity_level"] == AgentStatus.AUTONOMOUS.value
assert capabilities["max_complexity"] == 4
assert "delete" in capabilities["allowed_actions"]
assert "execute" in capabilities["allowed_actions"]
class TestGovernanceEnforcement:
"""Test governance enforcement at service level."""
def test_enforce_action_blocks_unauthorized(self, db_session: Session):
"""Test enforce_action blocks unauthorized agents."""
from core.agent_governance_service import AgentGovernanceService
governance = AgentGovernanceService(db_session)
student = StudentAgentFactory(_session=db_session)
# Try to enforce a blocked action
result = governance.enforce_action(
agent_id=student.id,
action_type="delete",
action_details={"target": "test"}
)
assert result["proceed"] == False
assert result["status"] in ["BLOCKED", "PENDING_APPROVAL"]
def test_enforce_action_allows_authorized(self, db_session: Session):
"""Test enforce_action allows authorized agents."""
from core.agent_governance_service import AgentGovernanceService
governance = AgentGovernanceService(db_session)
autonomous = AutonomousAgentFactory(_session=db_session)
# Try to enforce an allowed action
result = governance.enforce_action(
agent_id=autonomous.id,
action_type="delete",
action_details={"target": "test"}
)
assert result["proceed"] == True
assert result["status"] == "APPROVED"
def test_agent_not_found_returns_error(self, db_session: Session):
"""Test governance check for non-existent agent."""
from core.agent_governance_service import AgentGovernanceService
governance = AgentGovernanceService(db_session)
result = governance.can_perform_action(
agent_id="nonexistent-agent-id",
action_type="delete"
)
assert result["allowed"] == False
assert "not found" in result["reason"].lower()
class TestTriggerInterceptor:
"""Test trigger interceptor maturity-based routing."""
def test_routing_decision_exists(self, db_session: Session):
"""Test that RoutingDecision enum has expected values."""
from core.trigger_interceptor import RoutingDecision
# Verify all routing decisions exist
assert hasattr(RoutingDecision, 'TRAINING')
assert hasattr(RoutingDecision, 'PROPOSAL')
assert hasattr(RoutingDecision, 'SUPERVISION')
assert hasattr(RoutingDecision, 'EXECUTION')
def test_maturity_level_exists(self, db_session: Session):
"""Test that MaturityLevel enum has expected values."""
from core.trigger_interceptor import MaturityLevel
# Verify all maturity levels exist
assert hasattr(MaturityLevel, 'STUDENT')
assert hasattr(MaturityLevel, 'INTERN')
assert hasattr(MaturityLevel, 'SUPERVISED')
assert hasattr(MaturityLevel, 'AUTONOMOUS')
def test_trigger_source_exists(self, db_session: Session):
"""Test that TriggerSource enum has expected values."""
from core.trigger_interceptor import TriggerSource
# Verify trigger sources exist
assert hasattr(TriggerSource, 'MANUAL')
assert hasattr(TriggerSource, 'WORKFLOW_ENGINE')
assert hasattr(TriggerSource, 'DATA_SYNC')
def test_interceptor_initialization(self, db_session: Session):
"""Test TriggerInterceptor can be initialized."""
from core.trigger_interceptor import TriggerInterceptor
interceptor = TriggerInterceptor(db_session, "test_workspace")
assert interceptor is not None
assert interceptor.db == db_session
assert interceptor.workspace_id == "test_workspace"
|