mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2025-12-07 01:26:11 -08:00
Tests Passing
This commit is contained in:
@@ -8,8 +8,7 @@ properly addressed in the reporting.py module.
|
||||
|
||||
import sys
|
||||
import os
|
||||
import unittest
|
||||
from unittest.mock import Mock, patch, MagicMock
|
||||
import pytest
|
||||
|
||||
# Add parent directory to path
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'server'))
|
||||
@@ -19,203 +18,215 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'server', 'db')
|
||||
from sql_safe_builder import SafeConditionBuilder
|
||||
|
||||
|
||||
class TestSQLInjectionPrevention(unittest.TestCase):
|
||||
"""Test suite for SQL injection prevention."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures."""
|
||||
self.builder = SafeConditionBuilder()
|
||||
|
||||
def test_sql_injection_attempt_single_quote(self):
|
||||
"""Test that single quote injection attempts are blocked."""
|
||||
malicious_input = "'; DROP TABLE users; --"
|
||||
condition, params = self.builder.get_safe_condition_legacy(malicious_input)
|
||||
|
||||
# Should return empty condition when invalid
|
||||
self.assertEqual(condition, "")
|
||||
self.assertEqual(params, {})
|
||||
|
||||
def test_sql_injection_attempt_union(self):
|
||||
"""Test that UNION injection attempts are blocked."""
|
||||
malicious_input = "1' UNION SELECT * FROM passwords --"
|
||||
condition, params = self.builder.get_safe_condition_legacy(malicious_input)
|
||||
|
||||
# Should return empty condition when invalid
|
||||
self.assertEqual(condition, "")
|
||||
self.assertEqual(params, {})
|
||||
|
||||
def test_sql_injection_attempt_or_true(self):
|
||||
"""Test that OR 1=1 injection attempts are blocked."""
|
||||
malicious_input = "' OR '1'='1"
|
||||
condition, params = self.builder.get_safe_condition_legacy(malicious_input)
|
||||
|
||||
# Should return empty condition when invalid
|
||||
self.assertEqual(condition, "")
|
||||
self.assertEqual(params, {})
|
||||
|
||||
def test_valid_simple_condition(self):
|
||||
"""Test that valid simple conditions are handled correctly."""
|
||||
valid_input = "AND devName = 'Test Device'"
|
||||
condition, params = self.builder.get_safe_condition_legacy(valid_input)
|
||||
|
||||
# Should create parameterized query
|
||||
self.assertIn("AND devName = :", condition)
|
||||
self.assertEqual(len(params), 1)
|
||||
self.assertIn('Test Device', list(params.values()))
|
||||
|
||||
def test_empty_condition(self):
|
||||
"""Test that empty conditions are handled safely."""
|
||||
empty_input = ""
|
||||
condition, params = self.builder.get_safe_condition_legacy(empty_input)
|
||||
|
||||
# Should return empty condition
|
||||
self.assertEqual(condition, "")
|
||||
self.assertEqual(params, {})
|
||||
|
||||
def test_whitespace_only_condition(self):
|
||||
"""Test that whitespace-only conditions are handled safely."""
|
||||
whitespace_input = " \n\t "
|
||||
condition, params = self.builder.get_safe_condition_legacy(whitespace_input)
|
||||
|
||||
# Should return empty condition
|
||||
self.assertEqual(condition, "")
|
||||
self.assertEqual(params, {})
|
||||
|
||||
def test_multiple_conditions_valid(self):
|
||||
"""Test that single valid conditions are handled correctly."""
|
||||
# Test with a single condition first (our current parser handles single conditions well)
|
||||
valid_input = "AND devName = 'Device1'"
|
||||
condition, params = self.builder.get_safe_condition_legacy(valid_input)
|
||||
|
||||
# Should create parameterized query
|
||||
self.assertIn("devName = :", condition)
|
||||
self.assertEqual(len(params), 1)
|
||||
self.assertIn('Device1', list(params.values()))
|
||||
|
||||
def test_disallowed_column_name(self):
|
||||
"""Test that non-whitelisted column names are rejected."""
|
||||
invalid_input = "AND malicious_column = 'value'"
|
||||
condition, params = self.builder.get_safe_condition_legacy(invalid_input)
|
||||
|
||||
# Should return empty condition when column not in whitelist
|
||||
self.assertEqual(condition, "")
|
||||
self.assertEqual(params, {})
|
||||
|
||||
def test_disallowed_operator(self):
|
||||
"""Test that non-whitelisted operators are rejected."""
|
||||
invalid_input = "AND devName SOUNDS LIKE 'test'"
|
||||
condition, params = self.builder.get_safe_condition_legacy(invalid_input)
|
||||
|
||||
# Should return empty condition when operator not allowed
|
||||
self.assertEqual(condition, "")
|
||||
self.assertEqual(params, {})
|
||||
|
||||
def test_nested_select_attempt(self):
|
||||
"""Test that nested SELECT attempts are blocked."""
|
||||
malicious_input = "AND devName IN (SELECT password FROM users)"
|
||||
condition, params = self.builder.get_safe_condition_legacy(malicious_input)
|
||||
|
||||
# Should return empty condition when nested SELECT detected
|
||||
self.assertEqual(condition, "")
|
||||
self.assertEqual(params, {})
|
||||
|
||||
def test_hex_encoding_attempt(self):
|
||||
"""Test that hex-encoded injection attempts are blocked."""
|
||||
malicious_input = "AND 0x44524f50205441424c45"
|
||||
condition, params = self.builder.get_safe_condition_legacy(malicious_input)
|
||||
|
||||
# Should return empty condition when hex encoding detected
|
||||
self.assertEqual(condition, "")
|
||||
self.assertEqual(params, {})
|
||||
|
||||
def test_comment_injection_attempt(self):
|
||||
"""Test that comment injection attempts are handled."""
|
||||
malicious_input = "AND devName = 'test' /* comment */ --"
|
||||
condition, params = self.builder.get_safe_condition_legacy(malicious_input)
|
||||
|
||||
# Comments should be stripped and condition validated
|
||||
if condition:
|
||||
self.assertNotIn("/*", condition)
|
||||
self.assertNotIn("--", condition)
|
||||
|
||||
def test_special_placeholder_replacement(self):
|
||||
"""Test that {s-quote} placeholder is safely replaced."""
|
||||
input_with_placeholder = "AND devName = {s-quote}Test{s-quote}"
|
||||
condition, params = self.builder.get_safe_condition_legacy(input_with_placeholder)
|
||||
|
||||
# Should handle placeholder safely
|
||||
if condition:
|
||||
self.assertNotIn("{s-quote}", condition)
|
||||
self.assertIn("devName = :", condition)
|
||||
|
||||
def test_null_byte_injection(self):
|
||||
"""Test that null byte injection attempts are blocked."""
|
||||
malicious_input = "AND devName = 'test\x00' DROP TABLE --"
|
||||
condition, params = self.builder.get_safe_condition_legacy(malicious_input)
|
||||
|
||||
# Null bytes should be sanitized
|
||||
if condition:
|
||||
self.assertNotIn("\x00", condition)
|
||||
for value in params.values():
|
||||
self.assertNotIn("\x00", str(value))
|
||||
|
||||
def test_build_condition_with_allowed_values(self):
|
||||
"""Test building condition with specific allowed values."""
|
||||
conditions = [
|
||||
{"column": "eve_EventType", "operator": "=", "value": "Connected"},
|
||||
{"column": "devName", "operator": "LIKE", "value": "%test%"}
|
||||
]
|
||||
condition, params = self.builder.build_condition(conditions, "AND")
|
||||
|
||||
# Should create valid parameterized condition
|
||||
self.assertIn("eve_EventType = :", condition)
|
||||
self.assertIn("devName LIKE :", condition)
|
||||
self.assertEqual(len(params), 2)
|
||||
|
||||
def test_build_condition_with_invalid_column(self):
|
||||
"""Test that invalid columns in build_condition are rejected."""
|
||||
conditions = [
|
||||
{"column": "invalid_column", "operator": "=", "value": "test"}
|
||||
]
|
||||
condition, params = self.builder.build_condition(conditions)
|
||||
|
||||
# Should return empty when invalid column
|
||||
self.assertEqual(condition, "")
|
||||
self.assertEqual(params, {})
|
||||
|
||||
def test_case_variations_injection(self):
|
||||
"""Test that case variation injection attempts are blocked."""
|
||||
malicious_inputs = [
|
||||
"AnD 1=1",
|
||||
"oR 1=1",
|
||||
"UnIoN SeLeCt * FrOm users"
|
||||
]
|
||||
|
||||
for malicious_input in malicious_inputs:
|
||||
condition, params = self.builder.get_safe_condition_legacy(malicious_input)
|
||||
# Should handle case variations safely
|
||||
if "union" in condition.lower() or "select" in condition.lower():
|
||||
self.fail(f"Injection not blocked: {malicious_input}")
|
||||
|
||||
def test_time_based_injection_attempt(self):
|
||||
"""Test that time-based injection attempts are blocked."""
|
||||
malicious_input = "AND IF(1=1, SLEEP(5), 0)"
|
||||
condition, params = self.builder.get_safe_condition_legacy(malicious_input)
|
||||
|
||||
# Should return empty condition when SQL functions detected
|
||||
self.assertEqual(condition, "")
|
||||
self.assertEqual(params, {})
|
||||
|
||||
def test_stacked_queries_attempt(self):
|
||||
"""Test that stacked query attempts are blocked."""
|
||||
malicious_input = "'; INSERT INTO admin VALUES ('hacker', 'password'); --"
|
||||
condition, params = self.builder.get_safe_condition_legacy(malicious_input)
|
||||
|
||||
# Should return empty condition when semicolon detected
|
||||
self.assertEqual(condition, "")
|
||||
self.assertEqual(params, {})
|
||||
@pytest.fixture
|
||||
def builder():
|
||||
"""Fixture to provide a SafeConditionBuilder instance."""
|
||||
return SafeConditionBuilder()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Run the tests
|
||||
unittest.main(verbosity=2)
|
||||
def test_sql_injection_attempt_single_quote(builder):
|
||||
"""Test that single quote injection attempts are blocked."""
|
||||
malicious_input = "'; DROP TABLE users; --"
|
||||
condition, params = builder.get_safe_condition_legacy(malicious_input)
|
||||
|
||||
# Should return empty condition when invalid
|
||||
assert condition == ""
|
||||
assert params == {}
|
||||
|
||||
|
||||
def test_sql_injection_attempt_union(builder):
|
||||
"""Test that UNION injection attempts are blocked."""
|
||||
malicious_input = "1' UNION SELECT * FROM passwords --"
|
||||
condition, params = builder.get_safe_condition_legacy(malicious_input)
|
||||
|
||||
# Should return empty condition when invalid
|
||||
assert condition == ""
|
||||
assert params == {}
|
||||
|
||||
|
||||
def test_sql_injection_attempt_or_true(builder):
|
||||
"""Test that OR 1=1 injection attempts are blocked."""
|
||||
malicious_input = "' OR '1'='1"
|
||||
condition, params = builder.get_safe_condition_legacy(malicious_input)
|
||||
|
||||
# Should return empty condition when invalid
|
||||
assert condition == ""
|
||||
assert params == {}
|
||||
|
||||
|
||||
def test_valid_simple_condition(builder):
|
||||
"""Test that valid simple conditions are handled correctly."""
|
||||
valid_input = "AND devName = 'Test Device'"
|
||||
condition, params = builder.get_safe_condition_legacy(valid_input)
|
||||
|
||||
# Should create parameterized query
|
||||
assert "AND devName = :" in condition
|
||||
assert len(params) == 1
|
||||
assert 'Test Device' in list(params.values())
|
||||
|
||||
|
||||
def test_empty_condition(builder):
|
||||
"""Test that empty conditions are handled safely."""
|
||||
empty_input = ""
|
||||
condition, params = builder.get_safe_condition_legacy(empty_input)
|
||||
|
||||
# Should return empty condition
|
||||
assert condition == ""
|
||||
assert params == {}
|
||||
|
||||
|
||||
def test_whitespace_only_condition(builder):
|
||||
"""Test that whitespace-only conditions are handled safely."""
|
||||
whitespace_input = " \n\t "
|
||||
condition, params = builder.get_safe_condition_legacy(whitespace_input)
|
||||
|
||||
# Should return empty condition
|
||||
assert condition == ""
|
||||
assert params == {}
|
||||
|
||||
|
||||
def test_multiple_conditions_valid(builder):
|
||||
"""Test that single valid conditions are handled correctly."""
|
||||
# Test with a single condition first (our current parser handles single conditions well)
|
||||
valid_input = "AND devName = 'Device1'"
|
||||
condition, params = builder.get_safe_condition_legacy(valid_input)
|
||||
|
||||
# Should create parameterized query
|
||||
assert "devName = :" in condition
|
||||
assert len(params) == 1
|
||||
assert 'Device1' in list(params.values())
|
||||
|
||||
|
||||
def test_disallowed_column_name(builder):
|
||||
"""Test that non-whitelisted column names are rejected."""
|
||||
invalid_input = "AND malicious_column = 'value'"
|
||||
condition, params = builder.get_safe_condition_legacy(invalid_input)
|
||||
|
||||
# Should return empty condition when column not in whitelist
|
||||
assert condition == ""
|
||||
assert params == {}
|
||||
|
||||
|
||||
def test_disallowed_operator(builder):
|
||||
"""Test that non-whitelisted operators are rejected."""
|
||||
invalid_input = "AND devName SOUNDS LIKE 'test'"
|
||||
condition, params = builder.get_safe_condition_legacy(invalid_input)
|
||||
|
||||
# Should return empty condition when operator not allowed
|
||||
assert condition == ""
|
||||
assert params == {}
|
||||
|
||||
|
||||
def test_nested_select_attempt(builder):
|
||||
"""Test that nested SELECT attempts are blocked."""
|
||||
malicious_input = "AND devName IN (SELECT password FROM users)"
|
||||
condition, params = builder.get_safe_condition_legacy(malicious_input)
|
||||
|
||||
# Should return empty condition when nested SELECT detected
|
||||
assert condition == ""
|
||||
assert params == {}
|
||||
|
||||
|
||||
def test_hex_encoding_attempt(builder):
|
||||
"""Test that hex-encoded injection attempts are blocked."""
|
||||
malicious_input = "AND 0x44524f50205441424c45"
|
||||
condition, params = builder.get_safe_condition_legacy(malicious_input)
|
||||
|
||||
# Should return empty condition when hex encoding detected
|
||||
assert condition == ""
|
||||
assert params == {}
|
||||
|
||||
|
||||
def test_comment_injection_attempt(builder):
|
||||
"""Test that comment injection attempts are handled."""
|
||||
malicious_input = "AND devName = 'test' /* comment */ --"
|
||||
condition, params = builder.get_safe_condition_legacy(malicious_input)
|
||||
|
||||
# Comments should be stripped and condition validated
|
||||
if condition:
|
||||
assert "/*" not in condition
|
||||
assert "--" not in condition
|
||||
|
||||
|
||||
def test_special_placeholder_replacement(builder):
|
||||
"""Test that {s-quote} placeholder is safely replaced."""
|
||||
input_with_placeholder = "AND devName = {s-quote}Test{s-quote}"
|
||||
condition, params = builder.get_safe_condition_legacy(input_with_placeholder)
|
||||
|
||||
# Should handle placeholder safely
|
||||
if condition:
|
||||
assert "{s-quote}" not in condition
|
||||
assert "devName = :" in condition
|
||||
|
||||
|
||||
def test_null_byte_injection(builder):
|
||||
"""Test that null byte injection attempts are blocked."""
|
||||
malicious_input = "AND devName = 'test\x00' DROP TABLE --"
|
||||
condition, params = builder.get_safe_condition_legacy(malicious_input)
|
||||
|
||||
# Null bytes should be sanitized
|
||||
if condition:
|
||||
assert "\x00" not in condition
|
||||
for value in params.values():
|
||||
assert "\x00" not in str(value)
|
||||
|
||||
|
||||
def test_build_condition_with_allowed_values(builder):
|
||||
"""Test building condition with specific allowed values."""
|
||||
conditions = [
|
||||
{"column": "eve_EventType", "operator": "=", "value": "Connected"},
|
||||
{"column": "devName", "operator": "LIKE", "value": "%test%"}
|
||||
]
|
||||
condition, params = builder.build_condition(conditions, "AND")
|
||||
|
||||
# Should create valid parameterized condition
|
||||
assert "eve_EventType = :" in condition
|
||||
assert "devName LIKE :" in condition
|
||||
assert len(params) == 2
|
||||
|
||||
|
||||
def test_build_condition_with_invalid_column(builder):
|
||||
"""Test that invalid columns in build_condition are rejected."""
|
||||
conditions = [
|
||||
{"column": "invalid_column", "operator": "=", "value": "test"}
|
||||
]
|
||||
condition, params = builder.build_condition(conditions)
|
||||
|
||||
# Should return empty when invalid column
|
||||
assert condition == ""
|
||||
assert params == {}
|
||||
|
||||
|
||||
def test_case_variations_injection(builder):
|
||||
"""Test that case variation injection attempts are blocked."""
|
||||
malicious_inputs = [
|
||||
"AnD 1=1",
|
||||
"oR 1=1",
|
||||
"UnIoN SeLeCt * FrOm users"
|
||||
]
|
||||
|
||||
for malicious_input in malicious_inputs:
|
||||
condition, params = builder.get_safe_condition_legacy(malicious_input)
|
||||
# Should handle case variations safely
|
||||
if "union" in condition.lower() or "select" in condition.lower():
|
||||
assert False, f"Injection not blocked: {malicious_input}"
|
||||
|
||||
|
||||
def test_time_based_injection_attempt(builder):
|
||||
"""Test that time-based injection attempts are blocked."""
|
||||
malicious_input = "AND IF(1=1, SLEEP(5), 0)"
|
||||
condition, params = builder.get_safe_condition_legacy(malicious_input)
|
||||
|
||||
# Should return empty condition when SQL functions detected
|
||||
assert condition == ""
|
||||
assert params == {}
|
||||
|
||||
|
||||
def test_stacked_queries_attempt(builder):
|
||||
"""Test that stacked query attempts are blocked."""
|
||||
malicious_input = "'; INSERT INTO admin VALUES ('hacker', 'password'); --"
|
||||
condition, params = builder.get_safe_condition_legacy(malicious_input)
|
||||
|
||||
# Should return empty condition when semicolon detected
|
||||
assert condition == ""
|
||||
assert params == {}
|
||||
|
||||
Reference in New Issue
Block a user