$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--", "", ]; 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; }