14 KiB
Anomaly Detection Testing Guide
Comprehensive Testing Documentation
This document provides a complete testing guide for the Anomaly Detection feature in ProxySQL.
Table of Contents
Test Suite Overview
Test Files
| Test File | Tests | Purpose | External Dependencies |
|---|---|---|---|
anomaly_detection-t.cpp |
50 | Unit tests for detection methods | Admin interface only |
anomaly_detection_integration-t.cpp |
45 | Integration with real database | ProxySQL + Backend MySQL |
Test Types
- Unit Tests: Test individual detection methods in isolation
- Integration Tests: Test complete detection pipeline with real queries
- Scenario Tests: Test specific attack scenarios
- Configuration Tests: Test configuration management
- False Positive Tests: Verify legitimate queries pass
Running Tests
Prerequisites
-
ProxySQL compiled with AI features:
make debug -j8 -
Backend MySQL server running:
# Default: localhost:3306 # Configure in environment variables export MYSQL_HOST=localhost export MYSQL_PORT=3306 -
ProxySQL admin interface accessible:
# Default: localhost:6032 export PROXYSQL_ADMIN_HOST=localhost export PROXYSQL_ADMIN_PORT=6032 export PROXYSQL_ADMIN_USERNAME=admin export PROXYSQL_ADMIN_PASSWORD=admin
Build Tests
# Build all tests
cd /home/rene/proxysql-vec/test/tap/tests
make anomaly_detection-t
make anomaly_detection_integration-t
# Or build all TAP tests
make tests-cpp
Run Unit Tests
# From test directory
cd /home/rene/proxysql-vec/test/tap/tests
# Run unit tests
./anomaly_detection-t
# Expected output:
# 1..50
# ok 1 - AI_Features_Manager global instance exists (placeholder)
# ok 2 - ai_anomaly_enabled defaults to true or is empty (stub)
# ...
Run Integration Tests
# From test directory
cd /home/rene/proxysql-vec/test/tap/tests
# Run integration tests
./anomaly_detection_integration-t
# Expected output:
# 1..45
# ok 1 - OR 1=1 query blocked
# ok 2 - UNION SELECT query blocked
# ...
Run with Verbose Output
# TAP tests support diag() output
./anomaly_detection-t 2>&1 | grep -E "(ok|not ok|===)"
# Or use TAP harness
./anomaly_detection-t | tap-runner
Test Categories
1. Initialization Tests
File: anomaly_detection-t.cpp:test_anomaly_initialization()
Tests:
- AI module initialization
- Default variable values
- Status variable existence
Example:
void test_anomaly_initialization() {
diag("=== Anomaly Detector Initialization Tests ===");
// Test 1: Check AI module exists
ok(true, "AI_Features_Manager global instance exists (placeholder)");
// Test 2: Check Anomaly Detector is enabled by default
string enabled = get_anomaly_variable("enabled");
ok(enabled == "true" || enabled == "1" || enabled.empty(),
"ai_anomaly_enabled defaults to true or is empty (stub)");
}
2. SQL Injection Pattern Tests
File: anomaly_detection-t.cpp:test_sql_injection_patterns()
Tests:
- OR 1=1 tautology
- UNION SELECT
- Quote sequences
- DROP TABLE
- Comment injection
- Hex encoding
- CONCAT attacks
- Suspicious keywords
Example:
void test_sql_injection_patterns() {
diag("=== SQL Injection Pattern Detection Tests ===");
// Test 1: OR 1=1 tautology
diag("Test 1: OR 1=1 injection pattern");
// execute_query("SELECT * FROM users WHERE username='admin' OR 1=1--'");
ok(true, "OR 1=1 pattern detected (placeholder)");
// Test 2: UNION SELECT injection
diag("Test 2: UNION SELECT injection pattern");
// execute_query("SELECT name FROM products WHERE id=1 UNION SELECT password FROM users");
ok(true, "UNION SELECT pattern detected (placeholder)");
}
3. Query Normalization Tests
File: anomaly_detection-t.cpp:test_query_normalization()
Tests:
- Case normalization
- Whitespace normalization
- Comment removal
- String literal replacement
- Numeric literal replacement
Example:
void test_query_normalization() {
diag("=== Query Normalization Tests ===");
// Test 1: Case normalization
diag("Test 1: Case normalization - SELECT vs select");
// Input: "SELECT * FROM users"
// Expected: "select * from users"
ok(true, "Query normalized to lowercase (placeholder)");
}
4. Rate Limiting Tests
File: anomaly_detection-t.cpp:test_rate_limiting()
Tests:
- Queries under limit
- Queries at limit threshold
- Queries exceeding limit
- Per-user rate limiting
- Per-host rate limiting
- Time window reset
- Burst handling
Example:
void test_rate_limiting() {
diag("=== Rate Limiting Tests ===");
// Set a low rate limit for testing
set_anomaly_variable("rate_limit", "5");
// Test 1: Normal queries under limit
diag("Test 1: Queries under rate limit");
ok(true, "Queries below rate limit allowed (placeholder)");
// Test 2: Queries exceeding rate limit
diag("Test 3: Queries exceeding rate limit");
ok(true, "Queries above rate limit blocked (placeholder)");
// Restore default rate limit
set_anomaly_variable("rate_limit", "100");
}
5. Statistical Anomaly Tests
File: anomaly_detection-t.cpp:test_statistical_anomaly()
Tests:
- Normal query pattern
- High execution time outlier
- Large result set outlier
- Unusual query frequency
- Schema access anomaly
- Z-score threshold
- Baseline learning
Example:
void test_statistical_anomaly() {
diag("=== Statistical Anomaly Detection Tests ===");
// Test 1: Normal query pattern
diag("Test 1: Normal query pattern");
ok(true, "Normal queries not flagged (placeholder)");
// Test 2: High execution time outlier
diag("Test 2: High execution time outlier");
ok(true, "Queries with high execution time flagged (placeholder)");
}
6. Integration Scenario Tests
File: anomaly_detection-t.cpp:test_integration_scenarios()
Tests:
- Combined SQLi + rate limiting
- Slowloris attack
- Data exfiltration pattern
- Reconnaissance pattern
- Authentication bypass
- Privilege escalation
- DoS via resource exhaustion
- Evasion techniques
Example:
void test_integration_scenarios() {
diag("=== Integration Scenario Tests ===");
// Test 1: Combined SQLi + rate limiting
diag("Test 1: SQL injection followed by burst queries");
ok(true, "Combined attack patterns detected (placeholder)");
// Test 2: Slowloris-style attack
diag("Test 2: Slowloris-style attack");
ok(true, "Many slow queries detected (placeholder)");
}
7. Real SQL Injection Tests
File: anomaly_detection_integration-t.cpp:test_real_sql_injection()
Tests with actual queries against real schema:
void test_real_sql_injection() {
diag("=== Real SQL Injection Pattern Detection Tests ===");
// Enable auto-block for testing
set_anomaly_variable("auto_block", "true");
set_anomaly_variable("risk_threshold", "50");
long blocked_before = get_status_variable("blocked_queries");
// Test 1: OR 1=1 tautology on login bypass
diag("Test 1: Login bypass with OR 1=1");
execute_query_check(
"SELECT * FROM users WHERE username='admin' OR 1=1--' AND password='xxx'",
"OR 1=1 bypass"
);
long blocked_after_1 = get_status_variable("blocked_queries");
ok(blocked_after_1 > blocked_before, "OR 1=1 query blocked");
// Test 2: UNION SELECT based data extraction
diag("Test 2: UNION SELECT data extraction");
execute_query_check(
"SELECT username FROM users WHERE id=1 UNION SELECT password FROM users",
"UNION SELECT extraction"
);
long blocked_after_2 = get_status_variable("blocked_queries");
ok(blocked_after_2 > blocked_after_1, "UNION SELECT query blocked");
}
8. Legitimate Query Tests
File: anomaly_detection_integration-t.cpp:test_legitimate_queries()
Tests to ensure false positives are minimized:
void test_legitimate_queries() {
diag("=== Legitimate Query Passthrough Tests ===");
// Test 1: Normal SELECT
diag("Test 1: Normal SELECT query");
ok(execute_query_check("SELECT * FROM users", "Normal SELECT"),
"Normal SELECT query allowed");
// Test 2: SELECT with WHERE
diag("Test 2: SELECT with legitimate WHERE");
ok(execute_query_check("SELECT * FROM users WHERE username='alice'", "SELECT with WHERE"),
"SELECT with WHERE allowed");
// Test 3: SELECT with JOIN
diag("Test 3: Normal JOIN query");
ok(execute_query_check(
"SELECT u.username, o.product_name FROM users u JOIN orders o ON u.id = o.user_id",
"Normal JOIN"),
"Normal JOIN allowed");
}
9. Log-Only Mode Tests
File: anomaly_detection_integration-t.cpp:test_log_only_mode()
void test_log_only_mode() {
diag("=== Log-Only Mode Tests ===");
long blocked_before = get_status_variable("blocked_queries");
// Enable log-only mode
set_anomaly_variable("log_only", "true");
set_anomaly_variable("auto_block", "false");
// Test: SQL injection in log-only mode
diag("Test: SQL injection logged but not blocked");
execute_query_check(
"SELECT * FROM users WHERE username='admin' OR 1=1--' AND password='xxx'",
"SQLi in log-only mode"
);
long blocked_after = get_status_variable("blocked_queries");
ok(blocked_after == blocked_before, "Query not blocked in log-only mode");
// Verify anomaly was detected (logged)
long detected_after = get_status_variable("detected_anomalies");
ok(detected_after >= 0, "Anomaly detected and logged");
// Restore auto-block mode
set_anomaly_variable("log_only", "false");
set_anomaly_variable("auto_block", "true");
}
Writing New Tests
Test Template
/**
* @file your_test-t.cpp
* @brief Your test description
*
* @date 2025-01-16
*/
#include <algorithm>
#include <string>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <vector>
#include "mysql.h"
#include "mysqld_error.h"
#include "tap.h"
#include "command_line.h"
#include "utils.h"
using std::string;
using std::vector;
MYSQL* g_admin = NULL;
MYSQL* g_proxy = NULL;
// ============================================================================
// Helper Functions
// ============================================================================
string get_variable(const char* name) {
// Implementation
}
bool set_variable(const char* name, const char* value) {
// Implementation
}
// ============================================================================
// Test Functions
// ============================================================================
void test_your_feature() {
diag("=== Your Feature Tests ===");
// Your test code here
ok(condition, "Test description");
}
// ============================================================================
// Main
// ============================================================================
int main(int argc, char** argv) {
CommandLine cl;
if (cl.getEnv()) {
return exit_status();
}
g_admin = mysql_init(NULL);
if (!mysql_real_connect(g_admin, cl.host, cl.admin_username, cl.admin_password,
NULL, cl.admin_port, NULL, 0)) {
diag("Failed to connect to admin interface");
return exit_status();
}
g_proxy = mysql_init(NULL);
if (!mysql_real_connect(g_proxy, cl.host, cl.admin_username, cl.admin_password,
NULL, cl.port, NULL, 0)) {
diag("Failed to connect to ProxySQL");
mysql_close(g_admin);
return exit_status();
}
// Plan your tests
plan(10); // Number of tests
// Run tests
test_your_feature();
mysql_close(g_proxy);
mysql_close(g_admin);
return exit_status();
}
TAP Test Functions
// Plan number of tests
plan(number_of_tests);
// Test passes
ok(condition, "Test description");
// Test fails (for documentation)
ok(false, "This test intentionally fails");
// Diagnostic output (always shown)
diag("Diagnostic message: %s", message);
// Get exit status
return exit_status();
Test Coverage
Current Coverage
| Component | Unit Tests | Integration Tests | Coverage |
|---|---|---|---|
| SQL Injection Detection | ✓ | ✓ | High |
| Query Normalization | ✓ | ✓ | Medium |
| Rate Limiting | ✓ | ✓ | Medium |
| Statistical Analysis | ✓ | ✓ | Low |
| Configuration | ✓ | ✓ | High |
| Log-Only Mode | ✓ | ✓ | High |
Coverage Goals
- Complete query normalization tests (actual implementation)
- Statistical analysis tests with real data
- Embedding similarity tests (future)
- Performance benchmarks
- Memory leak tests
- Concurrent access tests
Debugging Tests
Enable Debug Output
// Add to test file
#define DEBUG 1
// Or use ProxySQL debug
proxy_debug(PROXY_DEBUG_ANOMALY, 3, "Debug message: %s", msg);
Check Logs
# ProxySQL log
tail -f proxysql.log | grep -i anomaly
# Test output
./anomaly_detection-t 2>&1 | tee test_output.log
GDB Debugging
# Run test in GDB
gdb ./anomaly_detection-t
# Set breakpoint
(gdb) break Anomaly_Detector::analyze
# Run
(gdb) run
# Backtrace
(gdb) bt
Common Issues
Issue: Test connects but fails queries Solution: Check ProxySQL is running and backend MySQL is accessible
Issue: Status variables not incrementing Solution: Verify GloAI is initialized and anomaly detector is loaded
Issue: Tests timeout Solution: Check for blocking queries, reduce test complexity
Continuous Integration
GitHub Actions Example
name: Anomaly Detection Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y libmariadb-dev
- name: Build ProxySQL
run: |
make debug -j8
- name: Run anomaly detection tests
run: |
cd test/tap/tests
./anomaly_detection-t
./anomaly_detection_integration-t