Files
xamxam/tests/Security/SecurityTest.php
Pontoporeia b12ae73e91 tests: fix SecurityTest fatal TypeError — update searchTheses call to use array params
SecurityTest::Test1 was calling $db->searchTheses($string) with a plain
string, but searchTheses() was refactored to require array $params when
the tag M2M work landed.  This caused an immediate PHP fatal TypeError
before any SQL ever ran, killing the entire Security test suite with
exit code 255 and masking all three tests.

Fix: pass each malicious payload via ['query' => $string] which is the
correct API and properly exercises the parameterised query path through
validateSearchParams() + buildSearchConditions().  Added a clarifying
comment explaining why the array form is required.

All 4 test suites now pass:
  - Database (Unit):   7/7
  - Rate Limit (Unit): 5/5
  - Search (Integration): 6/6
  - Security:          3/3
2026-03-26 18:54:20 +01:00

73 lines
2.4 KiB
PHP

<?php
/**
* Security Test Suite
* Tests SQL injection protection and input sanitization
*/
require_once __DIR__ . '/../../src/Database.php';
echo "Security Test Suite\n";
echo "===================\n\n";
try {
$db = Database::getInstance();
// Test 1: SQL Injection in search
// searchTheses() takes an array of validated params; the 'query' key is the
// free-text search field that users control. Each malicious string must
// be passed as ['query' => $string] to exercise the actual parameterised
// query path rather than triggering a PHP TypeError before any SQL runs.
echo "Test 1: SQL Injection Protection (Search)\n";
$maliciousQueries = [
"' OR '1'='1",
"'; DROP TABLE theses; --",
"1' UNION SELECT * FROM authors--",
"<script>alert('xss')</script>",
];
foreach ($maliciousQueries as $query) {
try {
$results = $db->searchTheses(['query' => $query]);
// Should return a (possibly empty) result set without throwing
echo " ✓ Handled safely: " . substr($query, 0, 40) . "\n";
} catch (Exception $e) {
// A thrown exception is also acceptable (query rejected upstream)
echo " ✓ Exception (safe): " . substr($query, 0, 40) . "\n";
}
}
echo "✓ PASS: SQL injection attempts handled safely\n\n";
// Test 2: Invalid thesis ID
echo "Test 2: Invalid Thesis ID\n";
$invalidIds = ["abc", "'; DROP TABLE theses;", "-1", "999999"];
foreach ($invalidIds as $id) {
$result = $db->getThesisById($id);
if ($result === null || $result === false) {
echo " ✓ Rejected: " . $id . "\n";
} else {
throw new Exception("Invalid ID '$id' was not rejected");
}
}
echo "✓ PASS: Invalid IDs rejected\n\n";
// Test 3: XSS in output (checking data is escaped)
echo "Test 3: XSS Protection (Output Escaping)\n";
$theses = $db->getPublishedTheses(1, 0);
if (count($theses) > 0) {
$first = $theses[0];
// Check that HTML special chars would be handled
if (isset($first['title'])) {
echo " ✓ Title data retrieved safely\n";
}
}
echo "✓ PASS: Output handling verified\n\n";
echo "✅ All security tests passed!\n";
return true;
} catch (Exception $e) {
echo "❌ FAIL: " . $e->getMessage() . "\n";
return false;
}