| """ |
| Action Complexity Authorization Security Tests (SECU-02.2). |
| |
| Comprehensive tests for action complexity level enforcement: |
| - Level 1 (LOW): search, read, list, get, summarize, present_chart |
| - Level 2 (MODERATE): analyze, suggest, draft, generate, stream, submit |
| - Level 3 (HIGH): update, submit_form, send_email, create, post_message |
| - Level 4 (CRITICAL): delete, execute, deploy, payment |
| |
| These are CRITICAL security tests ensuring complexity-based gating. |
| Failure indicates agents can perform high-risk actions without proper maturity. |
| """ |
| import pytest |
| from sqlalchemy.orm import Session |
| from tests.factories.agent_factory import ( |
| StudentAgentFactory, |
| InternAgentFactory, |
| SupervisedAgentFactory, |
| AutonomousAgentFactory |
| ) |
| from core.agent_governance_service import AgentGovernanceService |
| from core.models import AgentStatus |
|
|
|
|
| |
| |
| |
|
|
|
|
| class TestLevel1LowComplexityActions: |
| """Test Level 1 (LOW) complexity actions. |
| |
| Risk: Read-only operations, no state changes |
| Allowed: STUDENT+ (all maturity levels) |
| """ |
|
|
| LEVEL_1_ACTIONS = [ |
| "search", "read", "list", "get", "summarize", "present_chart", |
| "present_markdown", "fetch" |
| ] |
|
|
| @pytest.mark.parametrize("action", LEVEL_1_ACTIONS) |
| def test_student_can_perform_level_1_actions(self, action, db_session: Session): |
| """STUDENT agents can perform Level 1 actions.""" |
| student = StudentAgentFactory(_session=db_session) |
| governance = AgentGovernanceService(db_session) |
|
|
| result = governance.can_perform_action(student.id, action) |
|
|
| assert result["allowed"] is True, f"STUDENT should be allowed to {action}" |
| assert result["action_complexity"] == 1 |
|
|
| @pytest.mark.parametrize("action", LEVEL_1_ACTIONS) |
| def test_all_maturity_levels_can_perform_level_1(self, action, db_session: Session): |
| """All maturity levels can perform Level 1 actions.""" |
| for factory in [StudentAgentFactory, InternAgentFactory, |
| SupervisedAgentFactory, AutonomousAgentFactory]: |
| agent = factory(_session=db_session) |
| governance = AgentGovernanceService(db_session) |
|
|
| result = governance.can_perform_action(agent.id, action) |
|
|
| assert result["allowed"] is True, \ |
| f"{agent.status} should be allowed to {action}" |
| assert result["action_complexity"] == 1 |
|
|
|
|
| |
| |
| |
|
|
|
|
| class TestLevel2ModerateComplexityActions: |
| """Test Level 2 (MODERATE) complexity actions. |
| |
| Risk: Content generation, no destructive actions |
| Allowed: INTERN+ (blocked for STUDENT) |
| """ |
|
|
| LEVEL_2_ACTIONS = [ |
| "analyze", "suggest", "draft", "generate", "recommend", |
| "stream_chat", "present_form", "llm_stream", "browser_navigate", |
| "browser_screenshot", "browser_extract", "device_camera_snap", |
| |
| "device_send_notification", "update_canvas" |
| ] |
|
|
| @pytest.mark.parametrize("action", LEVEL_2_ACTIONS) |
| def test_student_blocked_from_level_2_actions(self, action, db_session: Session): |
| """STUDENT agents blocked from Level 2 actions.""" |
| student = StudentAgentFactory(_session=db_session) |
| governance = AgentGovernanceService(db_session) |
|
|
| result = governance.can_perform_action(student.id, action) |
|
|
| assert result["allowed"] is False, f"STUDENT should be blocked from {action}" |
| assert result["action_complexity"] == 2 |
| assert "intern" in result["reason"].lower() |
|
|
| @pytest.mark.parametrize("action", LEVEL_2_ACTIONS) |
| def test_intern_can_perform_level_2_actions(self, action, db_session: Session): |
| """INTERN agents can perform Level 2 actions.""" |
| intern = InternAgentFactory(_session=db_session) |
| governance = AgentGovernanceService(db_session) |
|
|
| result = governance.can_perform_action(intern.id, action) |
|
|
| assert result["allowed"] is True, f"INTERN should be allowed to {action}" |
| assert result["action_complexity"] == 2 |
|
|
| @pytest.mark.parametrize("action", LEVEL_2_ACTIONS) |
| def test_supervised_and_autonomous_can_perform_level_2(self, action, db_session: Session): |
| """SUPERVISED and AUTONOMOUS can perform Level 2 actions.""" |
| for factory in [SupervisedAgentFactory, AutonomousAgentFactory]: |
| agent = factory(_session=db_session) |
| governance = AgentGovernanceService(db_session) |
|
|
| result = governance.can_perform_action(agent.id, action) |
|
|
| assert result["allowed"] is True, \ |
| f"{agent.status} should be allowed to {action}" |
| assert result["action_complexity"] == 2 |
|
|
|
|
| |
| |
| |
|
|
|
|
| class TestLevel3HighComplexityActions: |
| """Test Level 3 (HIGH) complexity actions. |
| |
| Risk: State changes, external effects (email, messages) |
| Allowed: SUPERVISED+ (blocked for STUDENT, INTERN) |
| """ |
|
|
| LEVEL_3_ACTIONS = [ |
| "create", "update", "send_email", "post_message", "schedule", |
| "submit_form", "device_screen_record", "device_screen_record_start", |
| "device_screen_record_stop" |
| ] |
|
|
| @pytest.mark.parametrize("action", LEVEL_3_ACTIONS) |
| def test_student_blocked_from_level_3_actions(self, action, db_session: Session): |
| """STUDENT agents blocked from Level 3 actions.""" |
| student = StudentAgentFactory(_session=db_session) |
| governance = AgentGovernanceService(db_session) |
|
|
| result = governance.can_perform_action(student.id, action) |
|
|
| assert result["allowed"] is False, f"STUDENT should be blocked from {action}" |
| assert result["action_complexity"] == 3 |
| assert "supervised" in result["reason"].lower() |
|
|
| @pytest.mark.parametrize("action", LEVEL_3_ACTIONS) |
| def test_intern_blocked_from_level_3_actions(self, action, db_session: Session): |
| """INTERN agents blocked from Level 3 actions.""" |
| intern = InternAgentFactory(_session=db_session) |
| governance = AgentGovernanceService(db_session) |
|
|
| result = governance.can_perform_action(intern.id, action) |
|
|
| assert result["allowed"] is False, f"INTERN should be blocked from {action}" |
| assert result["action_complexity"] == 3 |
| assert "supervised" in result["reason"].lower() |
|
|
| @pytest.mark.parametrize("action", LEVEL_3_ACTIONS) |
| def test_supervised_can_perform_level_3_actions(self, action, db_session: Session): |
| """SUPERVISED agents can perform Level 3 actions.""" |
| supervised = SupervisedAgentFactory(_session=db_session) |
| governance = AgentGovernanceService(db_session) |
|
|
| result = governance.can_perform_action(supervised.id, action) |
|
|
| assert result["allowed"] is True, f"SUPERVISED should be allowed to {action}" |
| assert result["action_complexity"] == 3 |
| |
| assert "requires_human_approval" in result |
|
|
| @pytest.mark.parametrize("action", LEVEL_3_ACTIONS) |
| def test_autonomous_can_perform_level_3_actions(self, action, db_session: Session): |
| """AUTONOMOUS agents can perform Level 3 actions without approval.""" |
| autonomous = AutonomousAgentFactory(_session=db_session) |
| governance = AgentGovernanceService(db_session) |
|
|
| result = governance.can_perform_action(autonomous.id, action) |
|
|
| assert result["allowed"] is True, f"AUTONOMOUS should be allowed to {action}" |
| assert result["action_complexity"] == 3 |
| assert result.get("requires_human_approval") is False |
|
|
|
|
| |
| |
| |
|
|
|
|
| class TestLevel4CriticalComplexityActions: |
| """Test Level 4 (CRITICAL) complexity actions. |
| |
| Risk: Destructive operations, payment processing, deployment |
| Allowed: AUTONOMOUS only (blocked for STUDENT, INTERN, SUPERVISED) |
| """ |
|
|
| LEVEL_4_ACTIONS = [ |
| "delete", "execute", "deploy", "transfer", "payment", "approve", |
| "device_execute_command", "canvas_execute_javascript" |
| ] |
|
|
| @pytest.mark.parametrize("action", LEVEL_4_ACTIONS) |
| def test_student_blocked_from_level_4_actions(self, action, db_session: Session): |
| """STUDENT agents blocked from Level 4 (CRITICAL) actions.""" |
| student = StudentAgentFactory(_session=db_session) |
| governance = AgentGovernanceService(db_session) |
|
|
| result = governance.can_perform_action(student.id, action) |
|
|
| assert result["allowed"] is False, f"STUDENT should be blocked from {action}" |
| assert result["action_complexity"] == 4 |
| assert "autonomous" in result["reason"].lower() |
|
|
| @pytest.mark.parametrize("action", LEVEL_4_ACTIONS) |
| def test_intern_blocked_from_level_4_actions(self, action, db_session: Session): |
| """INTERN agents blocked from Level 4 (CRITICAL) actions.""" |
| intern = InternAgentFactory(_session=db_session) |
| governance = AgentGovernanceService(db_session) |
|
|
| result = governance.can_perform_action(intern.id, action) |
|
|
| assert result["allowed"] is False, f"INTERN should be blocked from {action}" |
| assert result["action_complexity"] == 4 |
| assert "autonomous" in result["reason"].lower() |
|
|
| @pytest.mark.parametrize("action", LEVEL_4_ACTIONS) |
| def test_supervised_blocked_from_level_4_actions(self, action, db_session: Session): |
| """SUPERVISED agents blocked from Level 4 (CRITICAL) actions.""" |
| supervised = SupervisedAgentFactory(_session=db_session) |
| governance = AgentGovernanceService(db_session) |
|
|
| result = governance.can_perform_action(supervised.id, action) |
|
|
| assert result["allowed"] is False, f"SUPERVISED should be blocked from {action}" |
| assert result["action_complexity"] == 4 |
| assert "autonomous" in result["reason"].lower() |
|
|
| @pytest.mark.parametrize("action", LEVEL_4_ACTIONS) |
| def test_autonomous_can_perform_level_4_actions(self, action, db_session: Session): |
| """AUTONOMOUS agents can perform Level 4 (CRITICAL) actions.""" |
| autonomous = AutonomousAgentFactory(_session=db_session) |
| governance = AgentGovernanceService(db_session) |
|
|
| result = governance.can_perform_action(autonomous.id, action) |
|
|
| assert result["allowed"] is True, f"AUTONOMOUS should be allowed to {action}" |
| assert result["action_complexity"] == 4 |
| assert result.get("requires_human_approval") is False |
|
|
|
|
| |
| |
| |
|
|
|
|
| class TestComplexityMatrix: |
| """Test complete action complexity matrix enforcement.""" |
|
|
| def test_complexity_1_requires_student_or_higher(self, db_session: Session): |
| """Level 1 actions require STUDENT+ maturity.""" |
| governance = AgentGovernanceService(db_session) |
|
|
| |
| for factory in [StudentAgentFactory, InternAgentFactory, |
| SupervisedAgentFactory, AutonomousAgentFactory]: |
| agent = factory(_session=db_session) |
| result = governance.can_perform_action(agent.id, "search") |
|
|
| assert result["allowed"] is True |
| assert result["action_complexity"] == 1 |
|
|
| def test_complexity_2_requires_intern_or_higher(self, db_session: Session): |
| """Level 2 actions require INTERN+ maturity.""" |
| governance = AgentGovernanceService(db_session) |
|
|
| |
| student = StudentAgentFactory(_session=db_session) |
| result = governance.can_perform_action(student.id, "analyze") |
| assert result["allowed"] is False |
| assert result["action_complexity"] == 2 |
|
|
| |
| for factory in [InternAgentFactory, SupervisedAgentFactory, |
| AutonomousAgentFactory]: |
| agent = factory(_session=db_session) |
| result = governance.can_perform_action(agent.id, "analyze") |
|
|
| assert result["allowed"] is True |
| assert result["action_complexity"] == 2 |
|
|
| def test_complexity_3_requires_supervised_or_higher(self, db_session: Session): |
| """Level 3 actions require SUPERVISED+ maturity.""" |
| governance = AgentGovernanceService(db_session) |
|
|
| |
| for factory in [StudentAgentFactory, InternAgentFactory]: |
| agent = factory(_session=db_session) |
| result = governance.can_perform_action(agent.id, "create") |
|
|
| assert result["allowed"] is False |
| assert result["action_complexity"] == 3 |
|
|
| |
| for factory in [SupervisedAgentFactory, AutonomousAgentFactory]: |
| agent = factory(_session=db_session) |
| result = governance.can_perform_action(agent.id, "create") |
|
|
| assert result["allowed"] is True |
| assert result["action_complexity"] == 3 |
|
|
| def test_complexity_4_requires_autonomous_only(self, db_session: Session): |
| """Level 4 (CRITICAL) actions require AUTONOMOUS maturity.""" |
| governance = AgentGovernanceService(db_session) |
|
|
| |
| for factory in [StudentAgentFactory, InternAgentFactory, |
| SupervisedAgentFactory]: |
| agent = factory(_session=db_session) |
| result = governance.can_perform_action(agent.id, "delete") |
|
|
| assert result["allowed"] is False |
| assert result["action_complexity"] == 4 |
|
|
| |
| autonomous = AutonomousAgentFactory(_session=db_session) |
| result = governance.can_perform_action(autonomous.id, "delete") |
|
|
| assert result["allowed"] is True |
| assert result["action_complexity"] == 4 |
|
|
|
|
| |
| |
| |
|
|
|
|
| class TestComplexityEscalation: |
| """Test complexity level detection and escalation.""" |
|
|
| def test_action_complexity_correctly_detected(self, db_session: Session): |
| """Test action complexity is correctly classified.""" |
| autonomous = AutonomousAgentFactory(_session=db_session) |
| governance = AgentGovernanceService(db_session) |
|
|
| |
| for action in ["search", "read", "list", "get"]: |
| result = governance.can_perform_action(autonomous.id, action) |
| assert result["action_complexity"] == 1, \ |
| f"{action} should be complexity 1, got {result['action_complexity']}" |
|
|
| |
| for action in ["analyze", "stream_chat", "generate", "draft"]: |
| result = governance.can_perform_action(autonomous.id, action) |
| assert result["action_complexity"] == 2, \ |
| f"{action} should be complexity 2, got {result['action_complexity']}" |
|
|
| |
| for action in ["update", "submit_form", "send_email"]: |
| result = governance.can_perform_action(autonomous.id, action) |
| assert result["action_complexity"] == 3, \ |
| f"{action} should be complexity 3, got {result['action_complexity']}" |
|
|
| |
| for action in ["delete", "execute", "deploy", "payment"]: |
| result = governance.can_perform_action(autonomous.id, action) |
| assert result["action_complexity"] == 4, \ |
| f"{action} should be complexity 4, got {result['action_complexity']}" |
|
|
| def test_unknown_action_defaults_to_safe_complexity(self, db_session: Session): |
| """Test unknown actions default to safe complexity level.""" |
| autonomous = AutonomousAgentFactory(_session=db_session) |
| governance = AgentGovernanceService(db_session) |
|
|
| result = governance.can_perform_action(autonomous.id, "unknown_action_xyz") |
|
|
| |
| assert result["action_complexity"] in [1, 2] |
| assert "allowed" in result |
|
|
| def test_complexity_boundaries_enforced(self, db_session: Session): |
| """Test complexity boundaries cannot be bypassed.""" |
| |
| student = StudentAgentFactory(_session=db_session) |
| intern = InternAgentFactory(_session=db_session) |
| supervised = SupervisedAgentFactory(_session=db_session) |
| autonomous = AutonomousAgentFactory(_session=db_session) |
|
|
| governance = AgentGovernanceService(db_session) |
|
|
| |
| test_cases = [ |
| (student, 2, False), |
| (student, 3, False), |
| (student, 4, False), |
| (intern, 3, False), |
| (intern, 4, False), |
| (supervised, 4, False), |
| (autonomous, 4, True), |
| ] |
|
|
| for agent, complexity, allowed in test_cases: |
| |
| action_map = { |
| 2: "analyze", |
| 3: "create", |
| 4: "delete" |
| } |
| action = action_map[complexity] |
|
|
| result = governance.can_perform_action(agent.id, action) |
|
|
| assert result["allowed"] == allowed, \ |
| f"{agent.status} performing complexity {complexity} action {action}: " \ |
| f"expected allowed={allowed}, got {result['allowed']}" |
|
|
|
|
| |
| |
| |
|
|
|
|
| class TestApprovalRequirements: |
| """Test approval requirements based on complexity.""" |
|
|
| def test_supervised_requires_approval_for_level_3(self, db_session: Session): |
| """SUPERVISED agents require approval for Level 3 actions.""" |
| supervised = SupervisedAgentFactory(_session=db_session) |
| governance = AgentGovernanceService(db_session) |
|
|
| |
| for action in ["create", "update", "send_email"]: |
| result = governance.can_perform_action(supervised.id, action) |
|
|
| assert result["allowed"] is True |
| |
| assert result.get("requires_human_approval") is True |
|
|
| def test_autonomous_no_approval_required(self, db_session: Session): |
| """AUTONOMOUS agents don't require approval for any action.""" |
| autonomous = AutonomousAgentFactory(_session=db_session) |
| governance = AgentGovernanceService(db_session) |
|
|
| |
| for action in ["search", "analyze", "create", "delete"]: |
| result = governance.can_perform_action(autonomous.id, action) |
|
|
| assert result["allowed"] is True |
| |
| assert result.get("requires_human_approval") is False |
|
|
| def test_intern_approval_blocked_for_level_3(self, db_session: Session): |
| """INTERN agents blocked from Level 3 (not approval, outright denial).""" |
| intern = InternAgentFactory(_session=db_session) |
| governance = AgentGovernanceService(db_session) |
|
|
| result = governance.can_perform_action(intern.id, "create") |
|
|
| assert result["allowed"] is False |
| |
| assert "supervised" in result["reason"].lower() |
|
|
|
|
| |
| |
| |
|
|
|
|
| class TestComplexityEdgeCases: |
| """Test edge cases for action complexity.""" |
|
|
| def test_case_sensitive_action_matching(self, db_session: Session): |
| """Test action matching is case-sensitive or normalized.""" |
| autonomous = AutonomousAgentFactory(_session=db_session) |
| governance = AgentGovernanceService(db_session) |
|
|
| |
| result1 = governance.can_perform_action(autonomous.id, "delete") |
| assert result1["action_complexity"] == 4 |
|
|
| |
| result2 = governance.can_perform_action(autonomous.id, "DELETE") |
| |
| assert "action_complexity" in result2 |
|
|
| def test_action_with_underscores_and_hyphens(self, db_session: Session): |
| """Test actions with underscores and hyphens are handled.""" |
| autonomous = AutonomousAgentFactory(_session=db_session) |
| governance = AgentGovernanceService(db_session) |
|
|
| |
| for action in ["submit_form", "submit-form", "submitform"]: |
| result = governance.can_perform_action(autonomous.id, action) |
| assert "allowed" in result |
|
|
| def test_similar_action_names_have_correct_complexity(self, db_session: Session): |
| """Test similar action names have appropriate complexity.""" |
| autonomous = AutonomousAgentFactory(_session=db_session) |
| governance = AgentGovernanceService(db_session) |
|
|
| |
| result1 = governance.can_perform_action(autonomous.id, "stream_chat") |
| assert result1["action_complexity"] == 2 |
|
|
| result2 = governance.can_perform_action(autonomous.id, "send_email") |
| assert result2["action_complexity"] == 3 |
|
|
| def test_empty_action_handled_gracefully(self, db_session: Session): |
| """Test empty action string is handled gracefully.""" |
| autonomous = AutonomousAgentFactory(_session=db_session) |
| governance = AgentGovernanceService(db_session) |
|
|
| result = governance.can_perform_action(autonomous.id, "") |
|
|
| |
| assert "allowed" in result |
|
|
| def test_very_long_action_name(self, db_session: Session): |
| """Test very long action names are handled.""" |
| autonomous = AutonomousAgentFactory(_session=db_session) |
| governance = AgentGovernanceService(db_session) |
|
|
| long_action = "a" * 1000 |
|
|
| result = governance.can_perform_action(autonomous.id, long_action) |
|
|
| |
| assert "allowed" in result |
|
|
|
|
| |
| |
| |
|
|
|
|
| class TestCompleteComplexityMatrix: |
| """Verify complete 4x4 complexity matrix is enforced. |
| |
| Matrix: |
| Complexity 1 Complexity 2 Complexity 3 Complexity 4 |
| STUDENT ALLOWED BLOCKED BLOCKED BLOCKED |
| INTERN ALLOWED ALLOWED BLOCKED BLOCKED |
| SUPERVISED ALLOWED ALLOWED ALLOWED BLOCKED |
| AUTONOMOUS ALLOWED ALLOWED ALLOWED ALLOWED |
| """ |
|
|
| def test_complete_4x4_matrix(self, db_session: Session): |
| """Test complete 4x4 maturity x complexity matrix.""" |
| governance = AgentGovernanceService(db_session) |
|
|
| |
| matrix = { |
| AgentStatus.STUDENT: { |
| 1: True, 2: False, 3: False, 4: False |
| }, |
| AgentStatus.INTERN: { |
| 1: True, 2: True, 3: False, 4: False |
| }, |
| AgentStatus.SUPERVISED: { |
| 1: True, 2: True, 3: True, 4: False |
| }, |
| AgentStatus.AUTONOMOUS: { |
| 1: True, 2: True, 3: True, 4: True |
| } |
| } |
|
|
| |
| for maturity_level, complexity_map in matrix.items(): |
| for factory in [StudentAgentFactory, InternAgentFactory, |
| SupervisedAgentFactory, AutonomousAgentFactory]: |
| agent = factory(_session=db_session) |
|
|
| if agent.status != maturity_level.value: |
| continue |
|
|
| for complexity, expected_allowed in complexity_map.items(): |
| |
| action_map = {1: "search", 2: "analyze", 3: "create", 4: "delete"} |
| action = action_map[complexity] |
|
|
| result = governance.can_perform_action(agent.id, action) |
|
|
| assert result["allowed"] == expected_allowed, \ |
| f"Matrix mismatch: {agent.status} performing complexity {complexity} " \ |
| f"action {action}: expected allowed={expected_allowed}, got {result['allowed']}" |
|
|