| """ |
| SQL Injection Attack Tests |
| |
| Tests SQL injection vulnerability prevention in AgentGovernanceService. |
| Verifies that parameterized queries are used and malicious inputs are escaped. |
| |
| OWASP Category: A03:2021 - Injection |
| CWE: CWE-89 (SQL Injection) |
| """ |
|
|
| import pytest |
| from sqlalchemy.exc import IntegrityError, ProgrammingError |
| from sqlalchemy.orm import Session |
|
|
| from core.agent_governance_service import AgentGovernanceService |
| from core.models import AgentRegistry, AgentStatus |
|
|
|
|
| |
| |
| |
|
|
|
|
| @pytest.mark.sql_injection |
| @pytest.mark.parametrize("malicious_id", [ |
| "'; DROP TABLE agents; --", |
| "' OR '1'='1", |
| "1' UNION SELECT * FROM users --", |
| "'; INSERT INTO agents VALUES ('hacked', 'admin'); --", |
| "1'; DELETE FROM episodes WHERE '1'='1' --", |
| "' OR '1'='1' --", |
| "admin'--", |
| "admin'/*", |
| ]) |
| def test_agent_id_sql_injection_blocked(malicious_id, db_session: Session): |
| """ |
| SECURITY: SQL injection via agent_id parameter. |
| |
| ATTACK: Attempt to inject SQL via agent_id string in can_perform_action(). |
| EXPECTED: Input escaped or parameterized, query returns "agent not found", |
| no SQL injection executed. |
| """ |
| service = AgentGovernanceService(db_session) |
|
|
| result = service.can_perform_action( |
| agent_id=malicious_id, |
| action_type="stream_chat" |
| ) |
|
|
| |
| assert result["allowed"] is False |
| assert "not found" in result["reason"].lower() |
|
|
|
|
| @pytest.mark.sql_injection |
| def test_agent_id_union_query_blocked(db_session: Session): |
| """ |
| SECURITY: UNION SELECT injection via agent_id. |
| |
| ATTACK: Attempt to extract data from other tables using UNION SELECT. |
| EXPECTED: Query fails, "agent not found" error. |
| """ |
| service = AgentGovernanceService(db_session) |
|
|
| malicious_id = "1' UNION SELECT * FROM users --" |
| result = service.can_perform_action( |
| agent_id=malicious_id, |
| action_type="stream_chat" |
| ) |
|
|
| |
| assert result["allowed"] is False |
| assert "not found" in result["reason"].lower() |
|
|
|
|
| @pytest.mark.sql_injection |
| def test_agent_id_drop_table_blocked(db_session: Session): |
| """ |
| SECURITY: DROP TABLE injection via agent_id. |
| |
| ATTACK: Attempt to DROP agents table. |
| EXPECTED: DROP TABLE not executed, agent not found error. |
| """ |
| service = AgentGovernanceService(db_session) |
|
|
| malicious_id = "'; DROP TABLE agents; --" |
| result = service.can_perform_action( |
| agent_id=malicious_id, |
| action_type="stream_chat" |
| ) |
|
|
| |
| agents = db_session.query(AgentRegistry).all() |
| assert agents is not None |
|
|
| |
| assert result["allowed"] is False |
| assert "not found" in result["reason"].lower() |
|
|
|
|
| @pytest.mark.sql_injection |
| def test_agent_id_insert_blocked(db_session: Session): |
| """ |
| SECURITY: INSERT injection via agent_id. |
| |
| ATTACK: Attempt to INSERT malicious agent record. |
| EXPECTED: INSERT not executed, agent not found error. |
| """ |
| service = AgentGovernanceService(db_session) |
|
|
| initial_count = db_session.query(AgentRegistry).count() |
|
|
| malicious_id = "'; INSERT INTO agents VALUES ('hacked', 'admin', 'AUTONOMOUS'); --" |
| result = service.can_perform_action( |
| agent_id=malicious_id, |
| action_type="stream_chat" |
| ) |
|
|
| |
| final_count = db_session.query(AgentRegistry).count() |
| assert initial_count == final_count |
|
|
| assert result["allowed"] is False |
|
|
|
|
| |
| |
| |
|
|
|
|
| @pytest.mark.sql_injection |
| @pytest.mark.parametrize("malicious_name", [ |
| "'; DROP TABLE users; --", |
| "' OR '1'='1", |
| "<script>alert('xss')</script>", |
| "$(whoami)", |
| "`cat /etc/passwd`", |
| ]) |
| def test_agent_name_sql_injection_blocked(malicious_name, db_session: Session): |
| """ |
| SECURITY: SQL injection via agent name parameter. |
| |
| ATTACK: Attempt to inject SQL via name field in register_or_update_agent(). |
| EXPECTED: Input escaped or parameterized, agent created safely, |
| malicious name stored as string not executed. |
| """ |
| service = AgentGovernanceService(db_session) |
|
|
| agent = service.register_or_update_agent( |
| name=malicious_name, |
| category="test", |
| module_path="test.module", |
| class_name="TestAgent" |
| ) |
|
|
| |
| assert agent is not None |
| |
| assert agent.name == malicious_name |
|
|
|
|
| @pytest.mark.sql_injection |
| def test_description_field_sql_injection_blocked(db_session: Session): |
| """ |
| SECURITY: SQL injection via description field. |
| |
| ATTACK: Attempt to inject SQL via description parameter. |
| EXPECTED: Input sanitized, description stored as string. |
| """ |
| service = AgentGovernanceService(db_session) |
|
|
| malicious_desc = "'; DROP TABLE agents; --" |
|
|
| agent = service.register_or_update_agent( |
| name="Test Agent", |
| category="test", |
| module_path="test.module", |
| class_name="TestAgent", |
| description=malicious_desc |
| ) |
|
|
| assert agent is not None |
| assert agent.description == malicious_desc |
|
|
| |
| agents = db_session.query(AgentRegistry).all() |
| assert agents is not None |
|
|
|
|
| @pytest.mark.sql_injection |
| def test_module_path_sql_injection_blocked(db_session: Session): |
| """ |
| SECURITY: SQL injection via module_path parameter. |
| |
| ATTACK: Attempt to inject SQL via module_path. |
| EXPECTED: Input validated, module_path stored safely. |
| """ |
| service = AgentGovernanceService(db_session) |
|
|
| malicious_module = "'; DROP TABLE agents; --" |
|
|
| |
| try: |
| agent = service.register_or_update_agent( |
| name="Test Agent", |
| category="test", |
| module_path=malicious_module, |
| class_name="TestAgent" |
| ) |
| assert agent is not None |
| |
| assert agent.module_path == malicious_module |
| except (ValueError, AttributeError): |
| |
| pass |
|
|
|
|
| |
| |
| |
|
|
|
|
| @pytest.mark.sql_injection |
| def test_filter_query_sql_injection_blocked(db_session: Session): |
| """ |
| SECURITY: SQL injection in filter() clause. |
| |
| ATTACK: Attempt to inject SQL in db.query().filter() with malicious agent_id. |
| EXPECTED: SQLAlchemy uses parameterized queries, injection blocked. |
| """ |
| malicious_id = "1' OR '1'='1" |
|
|
| |
| agent = db_session.query(AgentRegistry).filter( |
| AgentRegistry.id == malicious_id |
| ).first() |
|
|
| |
| assert agent is None |
|
|
| |
| |
|
|
|
|
| @pytest.mark.sql_injection |
| def test_order_by_sql_injection_blocked(db_session: Session): |
| """ |
| SECURITY: SQL injection in ORDER BY clause. |
| |
| ATTACK: Attempt to inject SQL in ORDER BY. |
| EXPECTED: Injection blocked, query returns empty or error handled. |
| """ |
| from sqlalchemy import text |
| from sqlalchemy.exc import CompileError |
|
|
| |
| malicious_order = "id; DROP TABLE agents; --" |
|
|
| try: |
| |
| agents = db_session.query(AgentRegistry).order_by(malicious_order).all() |
| |
| assert agents is not None |
| except (CompileError, ProgrammingError, AttributeError): |
| |
| pass |
|
|
| |
| agents = db_session.query(AgentRegistry).all() |
| assert agents is not None |
|
|
|
|
| @pytest.mark.sql_injection |
| def test_raw_query_sql_injection_blocked(db_session: Session): |
| """ |
| SECURITY: SQL injection in raw SQL text() queries. |
| |
| ATTACK: Attempt to inject SQL using text() with malicious input. |
| EXPECTED: Parameterization used, injection blocked. |
| """ |
| from sqlalchemy import text |
|
|
| malicious_id = "'; DROP TABLE agents; --" |
|
|
| |
| result = db_session.execute( |
| text("SELECT * FROM agent_registry WHERE id = :agent_id"), |
| {"agent_id": malicious_id} |
| ).fetchone() |
|
|
| |
| assert result is None |
|
|
| |
| agents = db_session.query(AgentRegistry).all() |
| assert agents is not None |
|
|
|
|
| |
| |
| |
|
|
|
|
| @pytest.mark.sql_injection |
| def test_parameterized_queries_used(db_session: Session): |
| """ |
| SECURITY: Verify SQLAlchemy uses parameterized queries. |
| |
| CHECK: Ensure ORM methods use parameterized queries, not string concatenation. |
| EXPECTED: SQLAlchemy automatically parameterizes, preventing injection. |
| """ |
| service = AgentGovernanceService(db_session) |
|
|
| |
| agent = service.register_or_update_agent( |
| name="Test Agent", |
| category="test", |
| module_path="test.module", |
| class_name="TestAgent" |
| ) |
|
|
| |
| queried = db_session.query(AgentRegistry).filter( |
| AgentRegistry.id == agent.id |
| ).first() |
|
|
| assert queried is not None |
| assert queried.id == agent.id |
|
|
|
|
| @pytest.mark.sql_injection |
| @pytest.mark.parametrize("special_char", [ |
| "'", |
| '"', |
| ";", |
| "--", |
| "/*", |
| "*/", |
| "\\", |
| "\0", |
| ]) |
| def test_special_characters_escaped(special_char, db_session: Session): |
| """ |
| SECURITY: Special characters properly escaped. |
| |
| CHECK: Quotes, backslashes, semicolons, comments don't break queries. |
| EXPECTED: Special characters escaped or parameterized. |
| """ |
| service = AgentGovernanceService(db_session) |
|
|
| malicious_name = f"test{special_char}name" |
|
|
| agent = service.register_or_update_agent( |
| name=malicious_name, |
| category="test", |
| module_path="test.module", |
| class_name="TestAgent" |
| ) |
|
|
| assert agent is not None |
| assert agent.name == malicious_name |
|
|
|
|
| @pytest.mark.sql_injection |
| def test_sql_error_not_exposed_to_user(db_session: Session): |
| """ |
| SECURITY: SQL error messages don't leak database schema. |
| |
| CHECK: SQL errors don't reveal table structure, column names, etc. |
| EXPECTED: Generic error message, no schema details. |
| """ |
| service = AgentGovernanceService(db_session) |
|
|
| |
| result = service.can_perform_action( |
| agent_id="nonexistent-agent-123", |
| action_type="stream_chat" |
| ) |
|
|
| |
| assert result["allowed"] is False |
| assert "not found" in result["reason"].lower() |
|
|
| |
| error_msg = str(result).lower() |
| assert "syntax error" not in error_msg |
| assert "table" not in error_msg or "not found" in result["reason"].lower() |
| assert "column" not in error_msg |
| assert "database" not in error_msg |
|
|
|
|
| |
| |
| |
|
|
|
|
| @pytest.mark.sql_injection |
| @pytest.mark.parametrize("malicious_id", [ |
| "'; DROP TABLE agents; --", |
| "' OR '1'='1", |
| "1' UNION SELECT * FROM users --", |
| "'; INSERT INTO agents VALUES ('hacked', 'admin'); --", |
| "1'; DELETE FROM episodes WHERE '1'='1' --", |
| ]) |
| def test_batch_agent_id_sql_injection(malicious_id, db_session: Session): |
| """ |
| SECURITY: Batch test SQL injection via agent_id. |
| |
| Tests all SQL injection payloads from conftest. |
| """ |
| service = AgentGovernanceService(db_session) |
|
|
| result = service.can_perform_action( |
| agent_id=malicious_id, |
| action_type="stream_chat" |
| ) |
|
|
| assert result["allowed"] is False |
|
|
|
|
| @pytest.mark.sql_injection |
| @pytest.mark.parametrize("malicious_name", [ |
| "'; DROP TABLE users; --", |
| "' OR '1'='1", |
| "<script>alert('xss')</script>", |
| "$(whoami)", |
| "`cat /etc/passwd`", |
| ]) |
| def test_batch_agent_name_sql_injection(malicious_name, db_session: Session): |
| """ |
| SECURITY: Batch test SQL injection via agent name. |
| |
| Tests all SQL injection payloads from conftest. |
| """ |
| service = AgentGovernanceService(db_session) |
|
|
| agent = service.register_or_update_agent( |
| name=malicious_name, |
| category="test", |
| module_path="test.module", |
| class_name="TestAgent" |
| ) |
|
|
| assert agent is not None |
| assert agent.name == malicious_name |
|
|