mirror of https://github.com/sysown/proxysql
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
625 lines
14 KiB
625 lines
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
|
|
|
|
1. [Test Suite Overview](#test-suite-overview)
|
|
2. [Running Tests](#running-tests)
|
|
3. [Test Categories](#test-categories)
|
|
4. [Writing New Tests](#writing-new-tests)
|
|
5. [Test Coverage](#test-coverage)
|
|
6. [Debugging Tests](#debugging-tests)
|
|
|
|
---
|
|
|
|
## 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
|
|
|
|
1. **Unit Tests**: Test individual detection methods in isolation
|
|
2. **Integration Tests**: Test complete detection pipeline with real queries
|
|
3. **Scenario Tests**: Test specific attack scenarios
|
|
4. **Configuration Tests**: Test configuration management
|
|
5. **False Positive Tests**: Verify legitimate queries pass
|
|
|
|
---
|
|
|
|
## Running Tests
|
|
|
|
### Prerequisites
|
|
|
|
1. **ProxySQL compiled with AI features:**
|
|
```bash
|
|
make debug -j8
|
|
```
|
|
|
|
2. **Backend MySQL server running:**
|
|
```bash
|
|
# Default: localhost:3306
|
|
# Configure in environment variables
|
|
export MYSQL_HOST=localhost
|
|
export MYSQL_PORT=3306
|
|
```
|
|
|
|
3. **ProxySQL admin interface accessible:**
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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:**
|
|
```cpp
|
|
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:**
|
|
```cpp
|
|
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:**
|
|
```cpp
|
|
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:**
|
|
```cpp
|
|
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:**
|
|
```cpp
|
|
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:**
|
|
```cpp
|
|
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:
|
|
|
|
```cpp
|
|
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:
|
|
|
|
```cpp
|
|
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()`
|
|
|
|
```cpp
|
|
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
|
|
|
|
```cpp
|
|
/**
|
|
* @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
|
|
|
|
```cpp
|
|
// 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
|
|
|
|
```cpp
|
|
// Add to test file
|
|
#define DEBUG 1
|
|
|
|
// Or use ProxySQL debug
|
|
proxy_debug(PROXY_DEBUG_ANOMALY, 3, "Debug message: %s", msg);
|
|
```
|
|
|
|
### Check Logs
|
|
|
|
```bash
|
|
# ProxySQL log
|
|
tail -f proxysql.log | grep -i anomaly
|
|
|
|
# Test output
|
|
./anomaly_detection-t 2>&1 | tee test_output.log
|
|
```
|
|
|
|
### GDB Debugging
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```yaml
|
|
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
|
|
```
|