Files
hermes-agent/tests/test_sql_injection.py
Peppi Littera 219af75704 fix(security): eliminate SQL string formatting in execute() calls
Closes #1911

- insights.py: Pre-compute SELECT queries as class constants instead of
  f-string interpolation at runtime. _SESSION_COLS is now evaluated once
  at class definition time.
- hermes_state.py: Add identifier quoting and whitelist validation for
  ALTER TABLE column names in schema migrations.
- Add 4 tests verifying no injection vectors in SQL query construction.
2026-03-19 15:16:35 +01:00

44 lines
1.5 KiB
Python

"""Tests that verify SQL injection mitigations in insights and state modules."""
import re
from agent.insights import InsightsEngine
def test_session_cols_no_injection_chars():
"""_SESSION_COLS must not contain SQL injection vectors."""
cols = InsightsEngine._SESSION_COLS
assert ";" not in cols
assert "--" not in cols
assert "'" not in cols
assert "DROP" not in cols.upper()
def test_get_sessions_all_query_is_parameterized():
"""_GET_SESSIONS_ALL must use a ? placeholder for the cutoff value."""
query = InsightsEngine._GET_SESSIONS_ALL
assert "?" in query
assert "started_at >= ?" in query
# Must not embed any runtime-variable content via brace interpolation
assert "{" not in query
def test_get_sessions_with_source_query_is_parameterized():
"""_GET_SESSIONS_WITH_SOURCE must use ? placeholders for both parameters."""
query = InsightsEngine._GET_SESSIONS_WITH_SOURCE
assert query.count("?") == 2
assert "started_at >= ?" in query
assert "source = ?" in query
assert "{" not in query
def test_session_col_names_are_safe_identifiers():
"""Every column name listed in _SESSION_COLS must be a simple identifier."""
cols = InsightsEngine._SESSION_COLS
identifiers = [c.strip() for c in cols.split(",")]
safe_identifier = re.compile(r"^[a-zA-Z_][a-zA-Z0-9_]*$")
for col in identifiers:
assert safe_identifier.match(col), (
f"Column name {col!r} is not a safe SQL identifier"
)