Research-Stack/5-Applications/nodupe/tests/integration/test_system_security.py
2026-05-05 21:15:26 -05:00

583 lines
21 KiB
Python

# SPDX-License-Identifier: Apache-2.0
# Copyright (c) 2025 Allaun
"""System Security Tests - Security validation and penetration testing.
This module tests authentication and authorization, data protection mechanisms,
secure file handling, and tool security boundaries.
"""
import pytest
import sys
import os
import tempfile
import json
from unittest.mock import patch, MagicMock
from nodupe.core.main import main
from nodupe.tools.commands.scan import ScanTool
from nodupe.tools.commands.apply import ApplyTool
from nodupe.tools.commands.similarity import SimilarityCommandTool as SimilarityTool
class TestAuthenticationAuthorization:
"""Test authentication and authorization mechanisms."""
def test_cli_authentication_mechanisms(self):
"""Test CLI authentication mechanisms."""
# Test version command (should not require auth)
with patch('sys.argv', ['nodupe', 'version']):
result = main()
assert result == 0
# Test help command (should not require auth)
with patch('sys.argv', ['nodupe', '--help']):
with pytest.raises(SystemExit) as excinfo:
main()
assert excinfo.value.code == 0
def test_tool_authorization(self):
"""Test tool authorization mechanisms."""
# Test tool list command
with patch('sys.argv', ['nodupe', 'tool', '--list']):
result = main()
assert result == 0
# Test that tools can be loaded and executed
scan_tool = ScanTool()
assert scan_tool.name == "scan"
assert scan_tool.version == "1.0.0"
apply_tool = ApplyTool()
assert apply_tool.name == "apply"
assert apply_tool.version == "1.0.0"
class TestDataProtection:
"""Test data protection mechanisms."""
def test_scan_data_protection(self):
"""Test data protection during scan operations."""
with tempfile.TemporaryDirectory() as temp_dir:
# Create test files with sensitive content
sensitive_files = [
("PASSWORD_REMOVEDs.txt", "user:PASSWORD_REMOVED123\nadmin:admin456"),
("config.json", '{"API_KEY_REMOVED": "SECRET_REMOVED123", "TOKEN_REMOVED": "abc456"}'),
("CREDENTIAL_REMOVEDs.txt", "database:user:pass\nservice:api:key")
]
for filename, content in sensitive_files:
test_file = os.path.join(temp_dir, filename)
with open(test_file, "w") as f:
f.write(content)
# Mock container and services
mock_container = MagicMock()
mock_db_connection = MagicMock()
mock_container.get_service.return_value = mock_db_connection
scan_tool = ScanTool()
scan_args = MagicMock()
scan_args.paths = [temp_dir]
scan_args.min_size = 0
scan_args.max_size = None
scan_args.extensions = None
scan_args.exclude = None
scan_args.verbose = False
scan_args.container = mock_container
scan_result = scan_tool.execute_scan(scan_args)
assert scan_result == 0 # Should handle sensitive data properly
def test_apply_data_protection(self):
"""Test data protection during apply operations."""
with tempfile.TemporaryDirectory() as temp_dir:
# Create duplicates file with sensitive paths
sensitive_file = os.path.join(temp_dir, "sensitive_duplicates.json")
sensitive_data = {
"duplicate_groups": [
{
"hash": "sensitive_hash",
"files": [
{"path": "/secure/location/PASSWORD_REMOVEDs.txt", "size": 100, "type": "txt"},
{"path": "/secure/location/keys.txt", "size": 100, "type": "txt"}
]
}
]
}
with open(sensitive_file, "w") as f:
json.dump(sensitive_data, f)
apply_tool = ApplyTool()
apply_args = MagicMock()
apply_args.action = "list"
apply_args.input = sensitive_file
apply_args.target_dir = None
apply_args.dry_run = True
apply_args.verbose = False
apply_result = apply_tool.execute_apply(apply_args)
assert apply_result == 0 # Should handle sensitive paths properly
class TestSecureFileHandling:
"""Test secure file handling mechanisms."""
def test_scan_secure_file_handling(self):
"""Test secure file handling during scan operations."""
with tempfile.TemporaryDirectory() as temp_dir:
# Create files with various security characteristics
test_files = [
("normal.txt", "Normal file content"),
("empty.txt", ""),
("special_chars.txt", "Special: ñ, ü, é, ß, ©, ®"),
("unicode.txt", "Unicode: 🔒 🔑 🔐"),
("binary.dat", b'\x00\x01\x02\x03\x04\x05')
]
for filename, content in test_files:
test_file = os.path.join(temp_dir, filename)
if filename.endswith('.dat'):
with open(test_file, "wb") as f:
f.write(content)
else:
with open(test_file, "w", encoding='utf-8') as f:
f.write(content)
# Mock container and services
mock_container = MagicMock()
mock_db_connection = MagicMock()
mock_container.get_service.return_value = mock_db_connection
scan_tool = ScanTool()
scan_args = MagicMock()
scan_args.paths = [temp_dir]
scan_args.min_size = 0
scan_args.max_size = None
scan_args.extensions = None
scan_args.exclude = None
scan_args.verbose = False
scan_args.container = mock_container
scan_result = scan_tool.execute_scan(scan_args)
assert scan_result == 0 # Should handle various file types securely
def test_apply_secure_file_handling(self):
"""Test secure file handling during apply operations."""
with tempfile.TemporaryDirectory() as temp_dir:
# Create duplicates file with secure file references
secure_file = os.path.join(temp_dir, "secure_duplicates.json")
secure_data = {
"duplicate_groups": [
{
"hash": "secure_hash",
"files": [
{"path": "/var/secure/file1.txt", "size": 150, "type": "txt"},
{"path": "/var/secure/file2.txt", "size": 150, "type": "txt"}
]
}
]
}
with open(secure_file, "w") as f:
json.dump(secure_data, f)
apply_tool = ApplyTool()
apply_args = MagicMock()
apply_args.action = "list"
apply_args.input = secure_file
apply_args.target_dir = None
apply_args.dry_run = True
apply_args.verbose = False
apply_result = apply_tool.execute_apply(apply_args)
assert apply_result == 0 # Should handle secure file references properly
class TestToolSecurity:
"""Test tool security boundaries."""
def test_tool_security_boundaries(self):
"""Test security boundaries between tools."""
# Test that tools operate within their security boundaries
scan_tool = ScanTool()
apply_tool = ApplyTool()
similarity_tool = SimilarityTool()
# Each tool should have its own namespace
assert scan_tool.name == "scan"
assert apply_tool.name == "apply"
assert similarity_tool.name == "similarity"
# Tools should not interfere with each other
mock_subparsers = MagicMock()
scan_tool.register_commands(mock_subparsers)
apply_tool.register_commands(mock_subparsers)
similarity_tool.register_commands(mock_subparsers)
# Each tool should register its own commands
assert mock_subparsers.add_parser.call_count == 3
def test_tool_isolation(self):
"""Test tool isolation and security."""
# Test that tools maintain proper isolation
scan_tool = ScanTool()
apply_tool = ApplyTool()
# Create separate mock containers for each tool
scan_container = MagicMock()
apply_container = MagicMock()
# Each tool should work with its own container
with tempfile.TemporaryDirectory() as temp_dir:
# Test scan with its container
test_file = os.path.join(temp_dir, "scan_test.txt")
with open(test_file, "w") as f:
f.write("Scan isolation test")
scan_args = MagicMock()
scan_args.paths = [temp_dir]
scan_args.min_size = 0
scan_args.max_size = None
scan_args.extensions = None
scan_args.exclude = None
scan_args.verbose = False
scan_args.container = scan_container
scan_result = scan_tool.execute_scan(scan_args)
assert scan_result == 0
# Test apply with its container
duplicates_file = os.path.join(temp_dir, "apply_test.json")
apply_data = {
"duplicate_groups": [
{
"hash": "apply_hash",
"files": [
{"path": "/tmp/apply_test.txt", "size": 100, "type": "txt"}
]
}
]
}
with open(duplicates_file, "w") as f:
json.dump(apply_data, f)
apply_args = MagicMock()
apply_args.action = "list"
apply_args.input = duplicates_file
apply_args.target_dir = None
apply_args.dry_run = True
apply_args.verbose = False
apply_args.container = apply_container
apply_result = apply_tool.execute_apply(apply_args)
assert apply_result == 0
class TestSecurityValidation:
"""Test security validation procedures."""
def test_scan_security_validation(self):
"""Test security validation during scan operations."""
with tempfile.TemporaryDirectory() as temp_dir:
# Create test files
for i in range(5):
test_file = os.path.join(temp_dir, f"security_{i}.txt")
with open(test_file, "w") as f:
f.write(f"Security validation {i}")
# Test with various security scenarios
scan_tool = ScanTool()
security_scenarios = [
# Normal scenario
{
"paths": [temp_dir],
"container": MagicMock(),
"expected": 0
},
# Protected directory scenario
{
"paths": ["/etc"],
"container": None,
"expected": "int"
},
# Mixed valid/invalid scenario
{
"paths": [temp_dir, "/invalid"],
"container": MagicMock(),
"expected": 0
}
]
for scenario in security_scenarios:
mock_container = scenario["container"]
if mock_container:
mock_db_connection = MagicMock()
mock_container.get_service.return_value = mock_db_connection
scan_args = MagicMock()
scan_args.paths = scenario["paths"]
scan_args.min_size = 0
scan_args.max_size = None
scan_args.extensions = None
scan_args.exclude = None
scan_args.verbose = False
scan_args.container = scenario["container"]
scan_result = scan_tool.execute_scan(scan_args)
if scenario["expected"] == 0:
assert scan_result == 0
else:
assert isinstance(scan_result, int)
def test_apply_security_validation(self):
"""Test security validation during apply operations."""
with tempfile.TemporaryDirectory() as temp_dir:
# Create valid duplicates file
valid_file = os.path.join(temp_dir, "valid_security.json")
valid_data = {
"duplicate_groups": [
{
"hash": "valid_security_hash",
"files": [
{"path": "/tmp/valid1.txt", "size": 100, "type": "txt"},
{"path": "/tmp/valid2.txt", "size": 100, "type": "txt"}
]
}
]
}
with open(valid_file, "w") as f:
json.dump(valid_data, f)
# Test with various security scenarios
apply_tool = ApplyTool()
security_scenarios = [
# Normal scenario
{
"input": valid_file,
"action": "list",
"expected": 0
},
# Invalid file scenario
{
"input": "/etc/passwd",
"action": "list",
"expected": "int"
},
# Protected action scenario
{
"input": valid_file,
"action": "delete",
"expected": 0
}
]
for scenario in security_scenarios:
apply_args = MagicMock()
apply_args.action = scenario["action"]
apply_args.input = scenario["input"]
apply_args.target_dir = None
apply_args.dry_run = True
apply_args.verbose = False
apply_result = apply_tool.execute_apply(apply_args)
if scenario["expected"] == 0:
assert apply_result == 0
else:
assert isinstance(apply_result, int)
class TestPenetrationTesting:
"""Test basic penetration testing scenarios."""
def test_scan_penetration_testing(self):
"""Test scan penetration testing scenarios."""
# Test with potentially malicious paths
malicious_paths = [
["../../../etc/passwd"],
["../../../etc/shadow"],
["/dev/null"],
["/proc/self/mem"]
]
scan_tool = ScanTool()
for paths in malicious_paths:
scan_args = MagicMock()
scan_args.paths = paths
scan_args.min_size = 0
scan_args.max_size = None
scan_args.extensions = None
scan_args.exclude = None
scan_args.verbose = False
scan_args.container = None
scan_result = scan_tool.execute_scan(scan_args)
# Should handle potentially malicious paths securely
assert isinstance(scan_result, int)
def test_apply_penetration_testing(self):
"""Test apply penetration testing scenarios."""
with tempfile.TemporaryDirectory() as temp_dir:
# Test with potentially malicious input
malicious_scenarios = [
# Very large file size
{
"files": [
{"path": "/tmp/huge.txt", "size": 999999999999, "type": "txt"}
]
},
# Malicious path patterns
{
"files": [
{"path": "../../../../etc/passwd", "size": 100, "type": "txt"}
]
},
# Special characters in paths
{
"files": [
{"path": "/tmp/file;rm -rf;.txt", "size": 100, "type": "txt"}
]
}
]
apply_tool = ApplyTool()
for scenario in malicious_scenarios:
malicious_file = os.path.join(temp_dir, "malicious.json")
malicious_data = {
"duplicate_groups": [
{
"hash": "malicious_hash",
"files": scenario["files"]
}
]
}
with open(malicious_file, "w") as f:
json.dump(malicious_data, f)
apply_args = MagicMock()
apply_args.action = "list"
apply_args.input = malicious_file
apply_args.target_dir = None
apply_args.dry_run = True
apply_args.verbose = False
apply_result = apply_tool.execute_apply(apply_args)
# Should handle potentially malicious input securely
assert isinstance(apply_result, int)
class TestSecurityAudit:
"""Test security audit procedures."""
def test_comprehensive_security_audit(self):
"""Test comprehensive security audit procedures."""
with tempfile.TemporaryDirectory() as temp_dir:
# Create test dataset
for i in range(10):
test_file = os.path.join(temp_dir, f"audit_{i}.txt")
with open(test_file, "w") as f:
f.write(f"Security audit test {i}")
# Create duplicates file
audit_file = os.path.join(temp_dir, "audit_duplicates.json")
audit_data = {
"duplicate_groups": [
{
"hash": f"audit_hash_{i}",
"files": [
{"path": f"{temp_dir}/audit_{i}.txt", "size": 100 + i, "type": "txt"},
{"path": f"{temp_dir}/audit_{i+1}.txt", "size": 100 + i, "type": "txt"}
]
}
for i in range(0, 8, 2)
]
}
with open(audit_file, "w") as f:
json.dump(audit_data, f)
# Create query file
query_file = os.path.join(temp_dir, "audit_query.txt")
with open(query_file, "w") as f:
f.write("Security audit query")
# Mock container and services
mock_container = MagicMock()
mock_db_connection = MagicMock()
mock_container.get_service.return_value = mock_db_connection
# Perform comprehensive security audit
scan_tool = ScanTool()
apply_tool = ApplyTool()
similarity_tool = SimilarityTool()
audit_operations = [
("scan", scan_tool, [temp_dir]),
("apply", apply_tool, audit_file),
("similarity", similarity_tool, query_file)
]
security_results = []
for op_name, tool, data in audit_operations:
if op_name == "scan":
scan_args = MagicMock()
scan_args.paths = data
scan_args.min_size = 0
scan_args.max_size = None
scan_args.extensions = None
scan_args.exclude = None
scan_args.verbose = False
scan_args.container = mock_container
result = tool.execute_scan(scan_args)
elif op_name == "apply":
apply_args = MagicMock()
apply_args.action = "list"
apply_args.input = data
apply_args.target_dir = None
apply_args.dry_run = True
apply_args.verbose = False
result = tool.execute_apply(apply_args)
elif op_name == "similarity":
similarity_args = MagicMock()
similarity_args.query_file = data
similarity_args.database = None
similarity_args.k = 5
similarity_args.threshold = 0.7
similarity_args.backend = "brute_force"
similarity_args.output = "text"
similarity_args.verbose = False
result = tool.execute_similarity(similarity_args)
security_results.append(result)
# All operations should complete securely
for result in security_results:
assert isinstance(result, int)
def test_security_compliance_validation(self):
"""Test security compliance validation."""
# Test that system maintains security compliance
security_checks = [
# CLI security
("version", ['nodupe', 'version']),
("help", ['nodupe', '--help']),
("tool_list", ['nodupe', 'tool', '--list'])
]
for check_name, args in security_checks:
with patch('sys.argv', args):
if "help" in check_name:
with pytest.raises(SystemExit):
main()
else:
result = main()
assert result == 0
if __name__ == "__main__":
pytest.main([__file__, "-v"])