48 KiB
SneakyScanner Web API Reference
Version: 5.0 (Phase 5)
Base URL: http://localhost:5000
Authentication: Session-based (Flask-Login)
Table of Contents
- Authentication
- Scans API
- Configs API
- Schedules API
- Stats API
- Settings API
- Alerts API
- Error Handling
- Status Codes
- Request/Response Examples
Authentication
SneakyScanner uses session-based authentication with Flask-Login. All API endpoints (except login, setup, and health checks) require authentication.
Setup
Initial password setup for first-time use.
Endpoint: GET|POST /auth/setup
Authentication: Not required (only accessible when no password is set)
GET Request: Returns the setup page template.
POST Request Body:
password: "your-password-here"
confirm_password: "your-password-here"
Success Response: Redirects to login page (302)
Error Responses:
- Password must be at least 8 characters
- Passwords must match
- Password is required
Usage Example:
# Setup initial password
curl -X POST http://localhost:5000/auth/setup \
-F "password=yourpassword" \
-F "confirm_password=yourpassword"
Login
Authenticate and create a session.
Endpoint: POST /auth/login
Authentication: Not required
Request Body:
password: "your-password-here"
remember: false (optional)
Success Response: Redirects to dashboard (302)
Error Response: Returns login page with error message
Usage Example:
# Login and save session cookie
curl -X POST http://localhost:5000/auth/login \
-F "password=yourpassword" \
-c cookies.txt
# Use session cookie for subsequent requests
curl -X GET http://localhost:5000/api/scans \
-b cookies.txt
Logout
Destroy the current session.
Endpoint: GET /auth/logout
Success Response: Redirects to login page (302)
Scans API
Manage network scans: trigger, list, view, and delete.
Trigger Scan
Start a new background scan.
Endpoint: POST /api/scans
Authentication: Required
Request Body:
{
"config_file": "/app/configs/example-site.yaml"
}
Success Response (201 Created):
{
"scan_id": 42,
"status": "running",
"message": "Scan queued successfully"
}
Error Responses:
400 Bad Request - Invalid config file:
{
"error": "Invalid config file",
"message": "Config file does not exist or is not valid YAML"
}
500 Internal Server Error - Scan queue failure:
{
"error": "Failed to queue scan",
"message": "Internal server error"
}
Usage Example:
curl -X POST http://localhost:5000/api/scans \
-H "Content-Type: application/json" \
-d '{"config_file":"/app/configs/production.yaml"}' \
-b cookies.txt
List Scans
Retrieve a paginated list of scans with optional status filtering.
Endpoint: GET /api/scans
Authentication: Required
Query Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
page |
integer | No | 1 | Page number (1-indexed) |
per_page |
integer | No | 20 | Items per page (1-100) |
status |
string | No | - | Filter by status: running, completed, failed |
Success Response (200 OK):
{
"scans": [
{
"id": 42,
"timestamp": "2025-11-14T10:30:00Z",
"duration": 125.5,
"status": "completed",
"title": "Production Network Scan",
"config_file": "/app/configs/production.yaml",
"triggered_by": "manual",
"started_at": "2025-11-14T10:30:00Z",
"completed_at": "2025-11-14T10:32:05Z"
},
{
"id": 41,
"timestamp": "2025-11-13T15:00:00Z",
"duration": 98.2,
"status": "completed",
"title": "Development Network Scan",
"config_file": "/app/configs/dev.yaml",
"triggered_by": "scheduled",
"started_at": "2025-11-13T15:00:00Z",
"completed_at": "2025-11-13T15:01:38Z"
}
],
"total": 42,
"page": 1,
"per_page": 20,
"total_pages": 3,
"has_prev": false,
"has_next": true
}
Error Responses:
400 Bad Request - Invalid parameters:
{
"error": "Invalid pagination parameters",
"message": "Page and per_page must be positive integers"
}
Usage Examples:
# List first page (default 20 items)
curl -X GET http://localhost:5000/api/scans \
-b cookies.txt
# List page 2 with 50 items per page
curl -X GET "http://localhost:5000/api/scans?page=2&per_page=50" \
-b cookies.txt
# List only running scans
curl -X GET "http://localhost:5000/api/scans?status=running" \
-b cookies.txt
Get Scan Details
Retrieve complete details for a specific scan, including all sites, IPs, ports, services, certificates, and TLS versions.
Endpoint: GET /api/scans/{id}
Authentication: Required
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
integer | Yes | Scan ID |
Success Response (200 OK):
{
"id": 42,
"timestamp": "2025-11-14T10:30:00Z",
"duration": 125.5,
"status": "completed",
"title": "Production Network Scan",
"config_file": "/app/configs/production.yaml",
"json_path": "/app/output/scan_report_20251114_103000.json",
"html_path": "/app/output/scan_report_20251114_103000.html",
"zip_path": "/app/output/scan_report_20251114_103000.zip",
"screenshot_dir": "/app/output/scan_report_20251114_103000_screenshots",
"triggered_by": "manual",
"started_at": "2025-11-14T10:30:00Z",
"completed_at": "2025-11-14T10:32:05Z",
"error_message": null,
"sites": [
{
"id": 101,
"site_name": "Production Web Servers",
"ips": [
{
"id": 201,
"ip_address": "192.168.1.10",
"ping_expected": true,
"ping_actual": true,
"ports": [
{
"id": 301,
"port": 443,
"protocol": "tcp",
"expected": true,
"state": "open",
"services": [
{
"id": 401,
"service_name": "https",
"product": "nginx",
"version": "1.24.0",
"extrainfo": null,
"ostype": "Linux",
"http_protocol": "https",
"screenshot_path": "scan_report_20251114_103000_screenshots/192_168_1_10_443.png",
"certificates": [
{
"id": 501,
"subject": "CN=example.com",
"issuer": "CN=Let's Encrypt Authority X3,O=Let's Encrypt,C=US",
"serial_number": "123456789012345678901234567890",
"not_valid_before": "2025-01-01T00:00:00+00:00",
"not_valid_after": "2025-04-01T23:59:59+00:00",
"days_until_expiry": 89,
"sans": "[\"example.com\", \"www.example.com\"]",
"is_self_signed": false,
"tls_versions": [
{
"id": 601,
"tls_version": "TLS 1.2",
"supported": true,
"cipher_suites": "[\"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\", \"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\"]"
},
{
"id": 602,
"tls_version": "TLS 1.3",
"supported": true,
"cipher_suites": "[\"TLS_AES_256_GCM_SHA384\", \"TLS_AES_128_GCM_SHA256\"]"
}
]
}
]
}
]
}
]
}
]
}
]
}
Error Responses:
404 Not Found - Scan doesn't exist:
{
"error": "Scan not found",
"message": "Scan with ID 42 does not exist"
}
Usage Example:
curl -X GET http://localhost:5000/api/scans/42 \
-b cookies.txt
Get Scan Status
Poll the current status of a running scan. Use this endpoint to track scan progress.
Endpoint: GET /api/scans/{id}/status
Authentication: Required
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
integer | Yes | Scan ID |
Success Response (200 OK):
Running scan:
{
"scan_id": 42,
"status": "running",
"started_at": "2025-11-14T10:30:00Z",
"completed_at": null,
"error_message": null
}
Completed scan:
{
"scan_id": 42,
"status": "completed",
"started_at": "2025-11-14T10:30:00Z",
"completed_at": "2025-11-14T10:32:05Z",
"error_message": null
}
Failed scan:
{
"scan_id": 42,
"status": "failed",
"started_at": "2025-11-14T10:30:00Z",
"completed_at": "2025-11-14T10:30:15Z",
"error_message": "Config file not found: /app/configs/missing.yaml"
}
Error Responses:
404 Not Found - Scan doesn't exist:
{
"error": "Scan not found",
"message": "Scan with ID 42 does not exist"
}
Usage Example:
# Poll status every 5 seconds
while true; do
curl -X GET http://localhost:5000/api/scans/42/status -b cookies.txt
sleep 5
done
Delete Scan
Delete a scan and all associated files (JSON, HTML, ZIP, screenshots).
Endpoint: DELETE /api/scans/{id}
Authentication: Required
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
integer | Yes | Scan ID |
Success Response (200 OK):
{
"message": "Scan 42 deleted successfully"
}
Error Responses:
404 Not Found - Scan doesn't exist:
{
"error": "Scan not found",
"message": "Scan with ID 42 does not exist"
}
Usage Example:
curl -X DELETE http://localhost:5000/api/scans/42 \
-b cookies.txt
Compare Scans
Compare two scans to identify differences in ports, services, and certificates.
Endpoint: GET /api/scans/{scan_id1}/compare/{scan_id2}
Authentication: Required
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
scan_id1 |
integer | Yes | First (older) scan ID |
scan_id2 |
integer | Yes | Second (newer) scan ID |
Success Response (200 OK):
{
"scan1": {
"id": 41,
"timestamp": "2025-11-13T15:00:00Z",
"title": "Development Network Scan"
},
"scan2": {
"id": 42,
"timestamp": "2025-11-14T10:30:00Z",
"title": "Production Network Scan"
},
"ports": {
"added": [{"ip": "192.168.1.10", "port": 8080, "protocol": "tcp"}],
"removed": [{"ip": "192.168.1.11", "port": 22, "protocol": "tcp"}],
"unchanged": [{"ip": "192.168.1.10", "port": 443, "protocol": "tcp"}]
},
"services": {
"added": [{"ip": "192.168.1.10", "port": 8080, "service_name": "http-proxy"}],
"removed": [{"ip": "192.168.1.11", "port": 22, "service_name": "ssh"}],
"changed": [
{
"ip": "192.168.1.10",
"port": 443,
"old_version": "1.23.0",
"new_version": "1.24.0"
}
]
},
"certificates": {
"added": [],
"removed": [],
"changed": [
{
"ip": "192.168.1.10",
"port": 443,
"old_expiry": "2025-04-01",
"new_expiry": "2025-07-01"
}
]
},
"drift_score": 0.15
}
Error Responses:
404 Not Found - One or both scans don't exist:
{
"error": "Not found",
"message": "One or both scans not found"
}
Usage Example:
curl -X GET http://localhost:5000/api/scans/41/compare/42 \
-b cookies.txt
Health Check
Check API health status.
Endpoint: GET /api/scans/health
Authentication: Not required
Success Response (200 OK):
{
"status": "healthy",
"api": "scans",
"version": "1.0.0-phase1"
}
Configs API
Manage scan configuration files including creation, upload, editing, and deletion.
List Configs
Retrieve a list of all available configuration files.
Endpoint: GET /api/configs
Authentication: Required
Success Response (200 OK):
{
"configs": [
{
"filename": "prod-scan.yaml",
"title": "Production Scan",
"path": "/app/configs/prod-scan.yaml",
"created_at": "2025-11-15T10:30:00Z",
"size_bytes": 1234,
"used_by_schedules": ["Daily Production Scan"]
}
]
}
Usage Example:
curl -X GET http://localhost:5000/api/configs \
-b cookies.txt
Get Config
Retrieve a specific configuration file's content and parsed data.
Endpoint: GET /api/configs/{filename}
Authentication: Required
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
filename |
string | Yes | Config filename |
Success Response (200 OK):
{
"filename": "prod-scan.yaml",
"content": "title: Production Scan\nsites:\n - ...",
"parsed": {
"title": "Production Scan",
"sites": [...]
}
}
Error Responses:
404 Not Found - Config file doesn't exist:
{
"error": "Not found",
"message": "Config file not found"
}
400 Bad Request - Invalid YAML:
{
"error": "Invalid config",
"message": "YAML parsing error details"
}
Usage Example:
curl -X GET http://localhost:5000/api/configs/prod-scan.yaml \
-b cookies.txt
Create Config from CIDR
Create a new configuration file from a CIDR range.
Endpoint: POST /api/configs/create-from-cidr
Authentication: Required
Request Body:
{
"title": "My Network Scan",
"cidr": "10.0.0.0/24",
"site_name": "Production Network",
"ping_default": false
}
Success Response (200 OK):
{
"success": true,
"filename": "my-network-scan.yaml",
"preview": "title: My Network Scan\nsites:\n - ..."
}
Error Responses:
400 Bad Request - Missing required fields or invalid CIDR:
{
"error": "Validation error",
"message": "Invalid CIDR range"
}
Usage Example:
curl -X POST http://localhost:5000/api/configs/create-from-cidr \
-H "Content-Type: application/json" \
-d '{"title":"My Scan","cidr":"10.0.0.0/24"}' \
-b cookies.txt
Upload YAML Config
Upload a YAML configuration file directly.
Endpoint: POST /api/configs/upload-yaml
Authentication: Required
Request Body:
file: <yaml file>
filename: "custom-name.yaml" (optional)
Success Response (200 OK):
{
"success": true,
"filename": "custom-name.yaml"
}
Error Responses:
400 Bad Request - No file or invalid YAML:
{
"error": "Validation error",
"message": "File must be a YAML file (.yaml or .yml extension)"
}
Usage Example:
curl -X POST http://localhost:5000/api/configs/upload-yaml \
-F "file=@/path/to/config.yaml" \
-b cookies.txt
Download Config
Download an existing configuration file.
Endpoint: GET /api/configs/{filename}/download
Authentication: Required
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
filename |
string | Yes | Config filename |
Success Response (200 OK): Returns YAML file as download.
Usage Example:
curl -X GET http://localhost:5000/api/configs/prod-scan.yaml/download \
-b cookies.txt \
-o prod-scan.yaml
Update Config
Update an existing configuration file with new YAML content.
Endpoint: PUT /api/configs/{filename}
Authentication: Required
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
filename |
string | Yes | Config filename |
Request Body:
{
"content": "title: Updated Scan\nsites:\n - ..."
}
Success Response (200 OK):
{
"success": true,
"message": "Config updated successfully"
}
Error Responses:
404 Not Found - Config file doesn't exist:
{
"error": "Not found",
"message": "Config file not found"
}
400 Bad Request - Invalid YAML:
{
"error": "Validation error",
"message": "Invalid YAML structure"
}
Usage Example:
curl -X PUT http://localhost:5000/api/configs/prod-scan.yaml \
-H "Content-Type: application/json" \
-d '{"content":"title: Updated\nsites: []"}' \
-b cookies.txt
Delete Config
Delete a configuration file and cascade delete associated schedules.
Endpoint: DELETE /api/configs/{filename}
Authentication: Required
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
filename |
string | Yes | Config filename |
Success Response (200 OK):
{
"success": true,
"message": "Config deleted successfully"
}
Error Responses:
404 Not Found - Config file doesn't exist:
{
"error": "Not found",
"message": "Config file not found"
}
Usage Example:
curl -X DELETE http://localhost:5000/api/configs/prod-scan.yaml \
-b cookies.txt
Schedules API
Manage scheduled scans including creation, updates, and manual triggering.
List Schedules
Retrieve a list of all schedules with pagination and filtering.
Endpoint: GET /api/schedules
Authentication: Required
Query Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
page |
integer | No | 1 | Page number (1-indexed) |
per_page |
integer | No | 20 | Items per page |
enabled |
boolean | No | - | Filter by enabled status (true/false) |
Success Response (200 OK):
{
"schedules": [
{
"id": 1,
"name": "Daily Production Scan",
"config_file": "/app/configs/prod-scan.yaml",
"cron_expression": "0 2 * * *",
"enabled": true,
"created_at": "2025-11-01T10:00:00Z",
"updated_at": "2025-11-01T10:00:00Z",
"last_run": "2025-11-15T02:00:00Z",
"next_run": "2025-11-16T02:00:00Z"
}
],
"total": 5,
"page": 1,
"per_page": 20,
"total_pages": 1
}
Usage Example:
# List all schedules
curl -X GET http://localhost:5000/api/schedules \
-b cookies.txt
# List only enabled schedules
curl -X GET "http://localhost:5000/api/schedules?enabled=true" \
-b cookies.txt
Get Schedule
Retrieve details for a specific schedule including execution history.
Endpoint: GET /api/schedules/{schedule_id}
Authentication: Required
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
schedule_id |
integer | Yes | Schedule ID |
Success Response (200 OK):
{
"id": 1,
"name": "Daily Production Scan",
"config_file": "/app/configs/prod-scan.yaml",
"cron_expression": "0 2 * * *",
"enabled": true,
"created_at": "2025-11-01T10:00:00Z",
"updated_at": "2025-11-01T10:00:00Z",
"last_run": "2025-11-15T02:00:00Z",
"next_run": "2025-11-16T02:00:00Z",
"execution_history": [
{
"scan_id": 42,
"timestamp": "2025-11-15T02:00:00Z",
"status": "completed"
}
]
}
Error Responses:
404 Not Found - Schedule doesn't exist:
{
"error": "Schedule not found"
}
Usage Example:
curl -X GET http://localhost:5000/api/schedules/1 \
-b cookies.txt
Create Schedule
Create a new scheduled scan.
Endpoint: POST /api/schedules
Authentication: Required
Request Body:
{
"name": "Daily Production Scan",
"config_file": "/app/configs/prod-scan.yaml",
"cron_expression": "0 2 * * *",
"enabled": true
}
Success Response (201 Created):
{
"schedule_id": 1,
"message": "Schedule created successfully",
"schedule": {
"id": 1,
"name": "Daily Production Scan",
"config_file": "/app/configs/prod-scan.yaml",
"cron_expression": "0 2 * * *",
"enabled": true,
"created_at": "2025-11-01T10:00:00Z"
}
}
Error Responses:
400 Bad Request - Missing required fields or invalid cron expression:
{
"error": "Missing required fields: name, config_file"
}
Usage Example:
curl -X POST http://localhost:5000/api/schedules \
-H "Content-Type: application/json" \
-d '{"name":"Daily Scan","config_file":"/app/configs/prod.yaml","cron_expression":"0 2 * * *"}' \
-b cookies.txt
Update Schedule
Update an existing schedule.
Endpoint: PUT /api/schedules/{schedule_id}
Authentication: Required
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
schedule_id |
integer | Yes | Schedule ID to update |
Request Body:
{
"name": "Updated Schedule Name",
"cron_expression": "0 3 * * *",
"enabled": false
}
Success Response (200 OK):
{
"message": "Schedule updated successfully",
"schedule": {
"id": 1,
"name": "Updated Schedule Name",
"config_file": "/app/configs/prod-scan.yaml",
"cron_expression": "0 3 * * *",
"enabled": false,
"updated_at": "2025-11-15T10:00:00Z"
}
}
Error Responses:
404 Not Found - Schedule doesn't exist:
{
"error": "Schedule not found"
}
400 Bad Request - Invalid cron expression or validation error:
{
"error": "Invalid cron expression"
}
Usage Example:
curl -X PUT http://localhost:5000/api/schedules/1 \
-H "Content-Type: application/json" \
-d '{"enabled":false}' \
-b cookies.txt
Delete Schedule
Delete a schedule. Associated scans are not deleted.
Endpoint: DELETE /api/schedules/{schedule_id}
Authentication: Required
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
schedule_id |
integer | Yes | Schedule ID to delete |
Success Response (200 OK):
{
"message": "Schedule deleted successfully",
"schedule_id": 1
}
Error Responses:
404 Not Found - Schedule doesn't exist:
{
"error": "Schedule not found"
}
Usage Example:
curl -X DELETE http://localhost:5000/api/schedules/1 \
-b cookies.txt
Trigger Schedule
Manually trigger a scheduled scan immediately.
Endpoint: POST /api/schedules/{schedule_id}/trigger
Authentication: Required
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
schedule_id |
integer | Yes | Schedule ID to trigger |
Success Response (201 Created):
{
"message": "Scan triggered successfully",
"schedule_id": 1,
"scan_id": 43
}
Error Responses:
404 Not Found - Schedule doesn't exist:
{
"error": "Schedule not found"
}
Usage Example:
curl -X POST http://localhost:5000/api/schedules/1/trigger \
-b cookies.txt
Health Check
Check API health status.
Endpoint: GET /api/schedules/health
Authentication: Not required
Success Response (200 OK):
{
"status": "healthy",
"api": "schedules",
"version": "1.0.0-phase1"
}
Stats API
Retrieve dashboard statistics, trends, and analytics data.
Scan Trend
Get scan activity trend data for charts.
Endpoint: GET /api/stats/scan-trend
Authentication: Required
Query Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
days |
integer | No | 30 | Number of days to include (1-365) |
Success Response (200 OK):
{
"labels": ["2025-01-01", "2025-01-02", "2025-01-03"],
"values": [5, 3, 7],
"start_date": "2025-01-01",
"end_date": "2025-01-30",
"total_scans": 150
}
Error Responses:
400 Bad Request - Invalid days parameter:
{
"error": "days parameter must be at least 1"
}
Usage Example:
# Get last 30 days trend
curl -X GET http://localhost:5000/api/stats/scan-trend \
-b cookies.txt
# Get last 90 days trend
curl -X GET "http://localhost:5000/api/stats/scan-trend?days=90" \
-b cookies.txt
Summary Statistics
Get dashboard summary statistics.
Endpoint: GET /api/stats/summary
Authentication: Required
Success Response (200 OK):
{
"total_scans": 150,
"completed_scans": 140,
"failed_scans": 5,
"running_scans": 5,
"scans_today": 3,
"scans_this_week": 15
}
Usage Example:
curl -X GET http://localhost:5000/api/stats/summary \
-b cookies.txt
Scan History
Get historical trend data for scans with the same configuration.
Endpoint: GET /api/stats/scan-history/{scan_id}
Authentication: Required
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
scan_id |
integer | Yes | Reference scan ID |
Query Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
limit |
integer | No | 10 | Max historical scans (1-50) |
Success Response (200 OK):
{
"scans": [
{
"id": 40,
"timestamp": "2025-11-10T12:00:00",
"title": "Production Scan",
"port_count": 25,
"ip_count": 5
},
{
"id": 42,
"timestamp": "2025-11-15T12:00:00",
"title": "Production Scan",
"port_count": 26,
"ip_count": 5
}
],
"labels": ["2025-11-10 12:00", "2025-11-15 12:00"],
"port_counts": [25, 26],
"config_file": "/app/configs/prod-scan.yaml"
}
Error Responses:
404 Not Found - Scan doesn't exist:
{
"error": "Scan not found"
}
Usage Example:
curl -X GET http://localhost:5000/api/stats/scan-history/42 \
-b cookies.txt
Settings API
Manage application settings including SMTP configuration, encryption keys, and preferences.
Get All Settings
Retrieve all application settings. Sensitive values (passwords, keys) are masked.
Endpoint: GET /api/settings
Authentication: Required
Success Response (200 OK):
{
"status": "success",
"settings": {
"smtp_server": "smtp.gmail.com",
"smtp_port": 587,
"smtp_username": "alerts@example.com",
"smtp_password": "***ENCRYPTED***",
"smtp_from_email": "alerts@example.com",
"smtp_to_emails": "[\"admin@example.com\"]",
"retention_days": 90,
"app_password": "***ENCRYPTED***"
}
}
Usage Example:
curl -X GET http://localhost:5000/api/settings \
-b cookies.txt
Update Multiple Settings
Update multiple settings at once.
Endpoint: PUT /api/settings
Authentication: Required
Request Body:
{
"settings": {
"smtp_server": "smtp.example.com",
"smtp_port": 587,
"retention_days": 90
}
}
Success Response (200 OK):
{
"status": "success",
"message": "Updated 3 settings"
}
Error Responses:
400 Bad Request - No settings provided:
{
"status": "error",
"message": "No settings provided"
}
Usage Example:
curl -X PUT http://localhost:5000/api/settings \
-H "Content-Type: application/json" \
-d '{"settings":{"smtp_server":"smtp.example.com","smtp_port":587}}' \
-b cookies.txt
Get Single Setting
Retrieve a specific setting by key.
Endpoint: GET /api/settings/{key}
Authentication: Required
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
key |
string | Yes | Setting key |
Success Response (200 OK):
{
"status": "success",
"key": "smtp_server",
"value": "smtp.gmail.com"
}
Error Responses:
404 Not Found - Setting doesn't exist:
{
"status": "error",
"message": "Setting \"invalid_key\" not found"
}
Usage Example:
curl -X GET http://localhost:5000/api/settings/smtp_server \
-b cookies.txt
Update Single Setting
Update a specific setting value.
Endpoint: PUT /api/settings/{key}
Authentication: Required
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
key |
string | Yes | Setting key |
Request Body:
{
"value": "smtp.example.com"
}
Success Response (200 OK):
{
"status": "success",
"message": "Setting \"smtp_server\" updated"
}
Error Responses:
400 Bad Request - Missing value:
{
"status": "error",
"message": "No value provided"
}
Usage Example:
curl -X PUT http://localhost:5000/api/settings/smtp_server \
-H "Content-Type: application/json" \
-d '{"value":"smtp.example.com"}' \
-b cookies.txt
Delete Setting
Delete a specific setting.
Endpoint: DELETE /api/settings/{key}
Authentication: Required
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
key |
string | Yes | Setting key to delete |
Success Response (200 OK):
{
"status": "success",
"message": "Setting \"custom_key\" deleted"
}
Error Responses:
404 Not Found - Setting doesn't exist:
{
"status": "error",
"message": "Setting \"invalid_key\" not found"
}
Usage Example:
curl -X DELETE http://localhost:5000/api/settings/custom_key \
-b cookies.txt
Set Application Password
Set or update the application password.
Endpoint: POST /api/settings/password
Authentication: Required
Request Body:
{
"password": "newpassword123"
}
Success Response (200 OK):
{
"status": "success",
"message": "Password updated successfully"
}
Error Responses:
400 Bad Request - Missing or invalid password:
{
"status": "error",
"message": "Password must be at least 8 characters"
}
Usage Example:
curl -X POST http://localhost:5000/api/settings/password \
-H "Content-Type: application/json" \
-d '{"password":"newpassword123"}' \
-b cookies.txt
Test Email Configuration
Test email settings by sending a test email.
Endpoint: POST /api/settings/test-email
Authentication: Required
Success Response (501 Not Implemented):
{
"status": "not_implemented",
"message": "Email testing endpoint - to be implemented in Phase 4"
}
Note: This endpoint returns 501 Not Implemented. Full email testing functionality will be added in a future phase.
Usage Example:
curl -X POST http://localhost:5000/api/settings/test-email \
-b cookies.txt
Health Check
Check API health status.
Endpoint: GET /api/settings/health
Authentication: Not required
Success Response (200 OK):
{
"status": "healthy",
"api": "settings",
"version": "1.0.0-phase1"
}
Usage Example:
curl -X GET http://localhost:5000/api/settings/health
Alerts API
Manage alert history and alert rules with full support for filtering, acknowledgment, and statistics.
List Alerts
List recent alerts with pagination and extensive filtering options.
Endpoint: GET /api/alerts
Authentication: Required
Query Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
page |
integer | No | 1 | Page number (1-indexed) |
per_page |
integer | No | 20 | Items per page (1-100) |
alert_type |
string | No | - | Filter by alert type (unexpected_port, drift_detection, cert_expiry, weak_tls, ping_failed) |
severity |
string | No | - | Filter by severity (info, warning, critical) |
acknowledged |
boolean | No | - | Filter by acknowledgment status (true/false) |
scan_id |
integer | No | - | Filter by specific scan ID |
start_date |
string | No | - | Filter alerts after this date (ISO format) |
end_date |
string | No | - | Filter alerts before this date (ISO format) |
Success Response (200 OK):
{
"alerts": [
{
"id": 1,
"scan_id": 42,
"scan_title": "Production Network Scan",
"rule_id": 3,
"alert_type": "cert_expiry",
"severity": "warning",
"message": "Certificate for 192.168.1.10:443 expires in 15 days",
"ip_address": "192.168.1.10",
"port": 443,
"acknowledged": false,
"acknowledged_at": null,
"acknowledged_by": null,
"email_sent": true,
"email_sent_at": "2025-11-18T10:30:00Z",
"webhook_sent": false,
"webhook_sent_at": null,
"created_at": "2025-11-18T10:30:00Z"
}
],
"total": 1,
"page": 1,
"per_page": 20,
"pages": 1
}
Usage Examples:
# List all alerts
curl -X GET http://localhost:5000/api/alerts \
-b cookies.txt
# List only critical unacknowledged alerts
curl -X GET "http://localhost:5000/api/alerts?severity=critical&acknowledged=false" \
-b cookies.txt
# List alerts for a specific scan
curl -X GET "http://localhost:5000/api/alerts?scan_id=42" \
-b cookies.txt
# List alerts from last week
START_DATE=$(date -d '7 days ago' -Iseconds)
curl -X GET "http://localhost:5000/api/alerts?start_date=$START_DATE" \
-b cookies.txt
Acknowledge Alert
Mark an alert as acknowledged.
Endpoint: POST /api/alerts/{alert_id}/acknowledge
Authentication: Required
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
alert_id |
integer | Yes | Alert ID to acknowledge |
Request Body (Optional):
{
"acknowledged_by": "admin"
}
Success Response (200 OK):
{
"status": "success",
"message": "Alert 1 acknowledged",
"acknowledged_by": "admin"
}
Error Responses:
400 Bad Request - Failed to acknowledge:
{
"status": "error",
"message": "Failed to acknowledge alert 1"
}
Usage Example:
curl -X POST http://localhost:5000/api/alerts/1/acknowledge \
-H "Content-Type: application/json" \
-d '{"acknowledged_by":"admin"}' \
-b cookies.txt
List Alert Rules
List all configured alert rules.
Endpoint: GET /api/alerts/rules
Authentication: Required
Success Response (200 OK):
{
"rules": [
{
"id": 1,
"name": "Certificate Expiry Warning",
"rule_type": "cert_expiry",
"enabled": true,
"threshold": 30,
"email_enabled": true,
"webhook_enabled": false,
"severity": "warning",
"filter_conditions": {
"ip_pattern": "192.168.*"
},
"config_file": "/app/configs/production.yaml",
"created_at": "2025-11-01T10:00:00Z",
"updated_at": "2025-11-15T08:30:00Z"
}
],
"total": 1
}
Usage Example:
curl -X GET http://localhost:5000/api/alerts/rules \
-b cookies.txt
Create Alert Rule
Create a new alert rule.
Endpoint: POST /api/alerts/rules
Authentication: Required
Request Body:
{
"name": "Certificate Expiry Warning",
"rule_type": "cert_expiry",
"threshold": 30,
"enabled": true,
"email_enabled": true,
"webhook_enabled": false,
"severity": "warning",
"filter_conditions": {
"ip_pattern": "192.168.*"
},
"config_file": "/app/configs/production.yaml"
}
Request Body Fields:
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | No | User-friendly rule name (defaults to "{rule_type} rule") |
rule_type |
string | Yes | Type of alert (unexpected_port, drift_detection, cert_expiry, weak_tls, ping_failed) |
threshold |
integer | No | Threshold value (e.g., days for cert expiry, percentage for drift) |
enabled |
boolean | No | Whether rule is active (default: true) |
email_enabled |
boolean | No | Send email for this rule (default: false) |
webhook_enabled |
boolean | No | Send webhook for this rule (default: false) |
severity |
string | No | Alert severity: critical, warning, info (default: warning) |
filter_conditions |
object | No | JSON object with filter conditions |
config_file |
string | No | Config file to apply rule to (null for all configs) |
Success Response (201 Created):
{
"status": "success",
"message": "Alert rule created successfully",
"rule": {
"id": 1,
"name": "Certificate Expiry Warning",
"rule_type": "cert_expiry",
"enabled": true,
"threshold": 30,
"email_enabled": true,
"webhook_enabled": false,
"severity": "warning",
"filter_conditions": {
"ip_pattern": "192.168.*"
},
"config_file": "/app/configs/production.yaml",
"created_at": "2025-11-18T10:00:00Z",
"updated_at": "2025-11-18T10:00:00Z"
}
}
Error Responses:
400 Bad Request - Missing or invalid fields:
{
"status": "error",
"message": "rule_type is required"
}
400 Bad Request - Invalid rule type:
{
"status": "error",
"message": "Invalid rule_type. Must be one of: unexpected_port, drift_detection, cert_expiry, weak_tls, ping_failed"
}
400 Bad Request - Invalid severity:
{
"status": "error",
"message": "Invalid severity. Must be one of: critical, warning, info"
}
Usage Examples:
# Create certificate expiry rule
curl -X POST http://localhost:5000/api/alerts/rules \
-H "Content-Type: application/json" \
-d '{
"name": "Cert Expiry Warning",
"rule_type": "cert_expiry",
"threshold": 30,
"severity": "warning",
"email_enabled": true
}' \
-b cookies.txt
# Create drift detection rule for specific config
curl -X POST http://localhost:5000/api/alerts/rules \
-H "Content-Type: application/json" \
-d '{
"name": "Production Drift Alert",
"rule_type": "drift_detection",
"threshold": 10,
"severity": "critical",
"config_file": "/app/configs/production.yaml",
"email_enabled": true,
"webhook_enabled": true
}' \
-b cookies.txt
Update Alert Rule
Update an existing alert rule.
Endpoint: PUT /api/alerts/rules/{rule_id}
Authentication: Required
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
rule_id |
integer | Yes | Alert rule ID |
Request Body:
{
"name": "Updated Rule Name",
"threshold": 60,
"enabled": true,
"email_enabled": true,
"severity": "critical"
}
Note: All fields are optional. Only provided fields will be updated.
Success Response (200 OK):
{
"status": "success",
"message": "Alert rule updated successfully",
"rule": {
"id": 1,
"name": "Updated Rule Name",
"rule_type": "cert_expiry",
"enabled": true,
"threshold": 60,
"email_enabled": true,
"webhook_enabled": false,
"severity": "critical",
"filter_conditions": null,
"config_file": "/app/configs/production.yaml",
"created_at": "2025-11-01T10:00:00Z",
"updated_at": "2025-11-18T10:00:00Z"
}
}
Error Responses:
404 Not Found - Rule doesn't exist:
{
"status": "error",
"message": "Alert rule 1 not found"
}
400 Bad Request - Invalid severity:
{
"status": "error",
"message": "Invalid severity. Must be one of: critical, warning, info"
}
Usage Example:
# Disable a rule
curl -X PUT http://localhost:5000/api/alerts/rules/1 \
-H "Content-Type: application/json" \
-d '{"enabled":false}' \
-b cookies.txt
# Update threshold and enable email
curl -X PUT http://localhost:5000/api/alerts/rules/1 \
-H "Content-Type: application/json" \
-d '{"threshold":15,"email_enabled":true}' \
-b cookies.txt
Delete Alert Rule
Delete an alert rule. Associated alerts are also deleted via cascade.
Endpoint: DELETE /api/alerts/rules/{rule_id}
Authentication: Required
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
rule_id |
integer | Yes | Alert rule ID |
Success Response (200 OK):
{
"status": "success",
"message": "Alert rule 1 deleted successfully"
}
Error Responses:
404 Not Found - Rule doesn't exist:
{
"status": "error",
"message": "Alert rule 1 not found"
}
Usage Example:
curl -X DELETE http://localhost:5000/api/alerts/rules/1 \
-b cookies.txt
Get Alert Statistics
Get alert statistics for a specified time period.
Endpoint: GET /api/alerts/stats
Authentication: Required
Query Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
days |
integer | No | 7 | Number of days to look back (1-365) |
Success Response (200 OK):
{
"stats": {
"total_alerts": 42,
"unacknowledged_count": 5,
"alerts_by_severity": {
"critical": 3,
"warning": 15,
"info": 24
},
"alerts_by_type": {
"cert_expiry": 10,
"drift_detection": 8,
"unexpected_port": 12,
"weak_tls": 7,
"ping_failed": 5
},
"date_range": {
"start": "2025-11-11T10:00:00Z",
"end": "2025-11-18T10:00:00Z",
"days": 7
}
}
}
Usage Examples:
# Get stats for last 7 days
curl -X GET http://localhost:5000/api/alerts/stats \
-b cookies.txt
# Get stats for last 30 days
curl -X GET "http://localhost:5000/api/alerts/stats?days=30" \
-b cookies.txt
Health Check
Check API health status.
Endpoint: GET /api/alerts/health
Authentication: Not required
Success Response (200 OK):
{
"status": "healthy",
"api": "alerts",
"version": "1.0.0-phase5"
}
Usage Example:
curl -X GET http://localhost:5000/api/alerts/health
Error Handling
Error Response Format
All error responses follow a consistent JSON format:
{
"error": "Brief error type",
"message": "Detailed error message for debugging"
}
Content Negotiation
The API supports content negotiation based on the request:
- API Requests (Accept: application/json or /api/* path): Returns JSON errors
- Web Requests (Accept: text/html): Returns HTML error pages
Example:
# JSON error response
curl -X GET http://localhost:5000/api/scans/999 \
-H "Accept: application/json" \
-b cookies.txt
# HTML error page
curl -X GET http://localhost:5000/scans/999 \
-H "Accept: text/html" \
-b cookies.txt
Request ID Tracking
Every request receives a unique request ID for tracking and debugging:
Response Headers:
X-Request-ID: a1b2c3d4
X-Request-Duration-Ms: 125
Check application logs for detailed error information using the request ID:
2025-11-14 10:30:15 INFO [a1b2c3d4] GET /api/scans 200 125ms
Status Codes
Success Codes
| Code | Meaning | Usage |
|---|---|---|
| 200 | OK | Successful GET, PUT, DELETE requests |
| 201 | Created | Successful POST request that creates a resource |
| 302 | Found | Redirect (used for logout, login success) |
Client Error Codes
| Code | Meaning | Usage |
|---|---|---|
| 400 | Bad Request | Invalid request parameters or body |
| 401 | Unauthorized | Authentication required or failed |
| 403 | Forbidden | Authenticated but not authorized |
| 404 | Not Found | Resource doesn't exist |
| 405 | Method Not Allowed | HTTP method not supported for endpoint |
Server Error Codes
| Code | Meaning | Usage |
|---|---|---|
| 500 | Internal Server Error | Unexpected server error |
Request/Response Examples
Complete Workflow: Trigger and Monitor Scan
#!/bin/bash
# 1. Login and save session
curl -X POST http://localhost:5000/auth/login \
-H "Content-Type: application/json" \
-d '{"password":"yourpassword"}' \
-c cookies.txt
# 2. Trigger a new scan
RESPONSE=$(curl -s -X POST http://localhost:5000/api/scans \
-H "Content-Type: application/json" \
-d '{"config_file":"/app/configs/production.yaml"}' \
-b cookies.txt)
# Extract scan ID from response
SCAN_ID=$(echo $RESPONSE | jq -r '.scan_id')
echo "Scan ID: $SCAN_ID"
# 3. Poll status every 5 seconds until complete
while true; do
STATUS=$(curl -s -X GET http://localhost:5000/api/scans/$SCAN_ID/status \
-b cookies.txt | jq -r '.status')
echo "Status: $STATUS"
if [ "$STATUS" == "completed" ] || [ "$STATUS" == "failed" ]; then
break
fi
sleep 5
done
# 4. Get full scan results
curl -X GET http://localhost:5000/api/scans/$SCAN_ID \
-b cookies.txt | jq '.'
# 5. Logout
curl -X GET http://localhost:5000/auth/logout \
-b cookies.txt
Pagination Example
#!/bin/bash
# Get total number of scans
TOTAL=$(curl -s -X GET "http://localhost:5000/api/scans?per_page=1" \
-b cookies.txt | jq -r '.total')
echo "Total scans: $TOTAL"
# Calculate number of pages (50 items per page)
PER_PAGE=50
PAGES=$(( ($TOTAL + $PER_PAGE - 1) / $PER_PAGE ))
echo "Total pages: $PAGES"
# Fetch all pages
for PAGE in $(seq 1 $PAGES); do
echo "Fetching page $PAGE..."
curl -s -X GET "http://localhost:5000/api/scans?page=$PAGE&per_page=$PER_PAGE" \
-b cookies.txt | jq '.scans[] | {id, timestamp, title, status}'
done
Filter by Status
# Get all running scans
curl -X GET "http://localhost:5000/api/scans?status=running" \
-b cookies.txt | jq '.scans[] | {id, title, started_at}'
# Get all failed scans
curl -X GET "http://localhost:5000/api/scans?status=failed" \
-b cookies.txt | jq '.scans[] | {id, title, error_message}'
# Get all completed scans from last 24 hours
curl -X GET "http://localhost:5000/api/scans?status=completed" \
-b cookies.txt | jq '.scans[] | select(.completed_at > (now - 86400 | todate)) | {id, title, duration}'
Rate Limiting
Currently no rate limiting is implemented. For production deployments, consider:
- Adding nginx rate limiting
- Implementing application-level rate limiting with Flask-Limiter
- Setting connection limits in Gunicorn configuration
Security Considerations
Authentication
- All API endpoints (except
/auth/loginand/api/settings/health) require authentication - Session cookies are httpOnly and secure (in production with HTTPS)
- Passwords are hashed with bcrypt (cost factor 12)
- Sensitive settings values are encrypted at rest
CORS
CORS is configured via environment variable CORS_ORIGINS. Default: * (allow all).
For production, set to specific origins:
CORS_ORIGINS=https://scanner.example.com,https://admin.example.com
HTTPS
For production deployments:
- Use HTTPS (TLS/SSL) for all requests
- Set
SESSION_COOKIE_SECURE=Truein Flask config - Consider using a reverse proxy (nginx) with SSL termination
Input Validation
All inputs are validated:
- Config file paths are checked for existence and valid YAML
- Pagination parameters are sanitized (positive integers, max per_page: 100)
- Scan IDs are validated as integers
- Setting values are type-checked
Versioning
Current Version: 5.0 (Phase 5)
API versioning will be implemented in future phases. For now, the API is considered stable for Phase 5 features.
Recent Breaking Changes:
- Phase 2 → Phase 3: Added scan comparison endpoint, schedules API
- Phase 3 → Phase 4: Added configs API, stats API, multiple settings endpoints
- Phase 4 → Phase 5: Full alerts API implementation with filtering, acknowledgment, and statistics
Upcoming Changes:
- Phase 6+: API versioning with backward compatibility guarantees
API Summary
Implemented APIs (Phase 5)
- Authentication API - Login, logout, setup
- Scans API - Full CRUD, status polling, comparison
- Configs API - File management, CIDR generation, YAML upload
- Schedules API - CRUD operations, manual triggering, APScheduler integration
- Stats API - Trends, summaries, historical data
- Settings API - Application configuration, password management
- Alerts API - Full implementation with filtering, acknowledgment, rules management, and statistics
Endpoint Count
- Total endpoints: 55+
- Authenticated endpoints: 50+
- Public endpoints: 5 (login, setup, health checks)
Support
For issues, questions, or feature requests:
- GitHub Issues: https://github.com/anthropics/sneakyscanner/issues
- Documentation:
/docs/directory in repository
Last Updated: 2025-11-18 Phase: 5 - Alerts Management Next Update: Phase 6 - Future Enhancements