Files
SneakyScan/docs/API_REFERENCE.md
2025-11-18 13:23:06 -06:00

2305 lines
48 KiB
Markdown

# SneakyScanner Web API Reference
**Version:** 5.0 (Phase 5)
**Base URL:** `http://localhost:5000`
**Authentication:** Session-based (Flask-Login)
## Table of Contents
1. [Authentication](#authentication)
2. [Scans API](#scans-api)
3. [Configs API](#configs-api)
4. [Schedules API](#schedules-api)
5. [Stats API](#stats-api)
6. [Settings API](#settings-api)
7. [Alerts API](#alerts-api)
8. [Error Handling](#error-handling)
9. [Status Codes](#status-codes)
10. [Request/Response Examples](#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:**
```form-data
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:**
```bash
# 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:**
```form-data
password: "your-password-here"
remember: false (optional)
```
**Success Response:** Redirects to dashboard (302)
**Error Response:** Returns login page with error message
**Usage Example:**
```bash
# 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:**
```json
{
"config_file": "/app/configs/example-site.yaml"
}
```
**Success Response (201 Created):**
```json
{
"scan_id": 42,
"status": "running",
"message": "Scan queued successfully"
}
```
**Error Responses:**
*400 Bad Request* - Invalid config file:
```json
{
"error": "Invalid config file",
"message": "Config file does not exist or is not valid YAML"
}
```
*500 Internal Server Error* - Scan queue failure:
```json
{
"error": "Failed to queue scan",
"message": "Internal server error"
}
```
**Usage Example:**
```bash
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):**
```json
{
"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:
```json
{
"error": "Invalid pagination parameters",
"message": "Page and per_page must be positive integers"
}
```
**Usage Examples:**
```bash
# 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):**
```json
{
"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:
```json
{
"error": "Scan not found",
"message": "Scan with ID 42 does not exist"
}
```
**Usage Example:**
```bash
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:*
```json
{
"scan_id": 42,
"status": "running",
"started_at": "2025-11-14T10:30:00Z",
"completed_at": null,
"error_message": null
}
```
*Completed scan:*
```json
{
"scan_id": 42,
"status": "completed",
"started_at": "2025-11-14T10:30:00Z",
"completed_at": "2025-11-14T10:32:05Z",
"error_message": null
}
```
*Failed scan:*
```json
{
"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:
```json
{
"error": "Scan not found",
"message": "Scan with ID 42 does not exist"
}
```
**Usage Example:**
```bash
# 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):**
```json
{
"message": "Scan 42 deleted successfully"
}
```
**Error Responses:**
*404 Not Found* - Scan doesn't exist:
```json
{
"error": "Scan not found",
"message": "Scan with ID 42 does not exist"
}
```
**Usage Example:**
```bash
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):**
```json
{
"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:
```json
{
"error": "Not found",
"message": "One or both scans not found"
}
```
**Usage Example:**
```bash
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):**
```json
{
"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):**
```json
{
"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:**
```bash
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):**
```json
{
"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:
```json
{
"error": "Not found",
"message": "Config file not found"
}
```
*400 Bad Request* - Invalid YAML:
```json
{
"error": "Invalid config",
"message": "YAML parsing error details"
}
```
**Usage Example:**
```bash
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:**
```json
{
"title": "My Network Scan",
"cidr": "10.0.0.0/24",
"site_name": "Production Network",
"ping_default": false
}
```
**Success Response (200 OK):**
```json
{
"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:
```json
{
"error": "Validation error",
"message": "Invalid CIDR range"
}
```
**Usage Example:**
```bash
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:**
```multipart/form-data
file: <yaml file>
filename: "custom-name.yaml" (optional)
```
**Success Response (200 OK):**
```json
{
"success": true,
"filename": "custom-name.yaml"
}
```
**Error Responses:**
*400 Bad Request* - No file or invalid YAML:
```json
{
"error": "Validation error",
"message": "File must be a YAML file (.yaml or .yml extension)"
}
```
**Usage Example:**
```bash
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:**
```bash
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:**
```json
{
"content": "title: Updated Scan\nsites:\n - ..."
}
```
**Success Response (200 OK):**
```json
{
"success": true,
"message": "Config updated successfully"
}
```
**Error Responses:**
*404 Not Found* - Config file doesn't exist:
```json
{
"error": "Not found",
"message": "Config file not found"
}
```
*400 Bad Request* - Invalid YAML:
```json
{
"error": "Validation error",
"message": "Invalid YAML structure"
}
```
**Usage Example:**
```bash
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):**
```json
{
"success": true,
"message": "Config deleted successfully"
}
```
**Error Responses:**
*404 Not Found* - Config file doesn't exist:
```json
{
"error": "Not found",
"message": "Config file not found"
}
```
**Usage Example:**
```bash
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):**
```json
{
"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:**
```bash
# 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):**
```json
{
"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:
```json
{
"error": "Schedule not found"
}
```
**Usage Example:**
```bash
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:**
```json
{
"name": "Daily Production Scan",
"config_file": "/app/configs/prod-scan.yaml",
"cron_expression": "0 2 * * *",
"enabled": true
}
```
**Success Response (201 Created):**
```json
{
"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:
```json
{
"error": "Missing required fields: name, config_file"
}
```
**Usage Example:**
```bash
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:**
```json
{
"name": "Updated Schedule Name",
"cron_expression": "0 3 * * *",
"enabled": false
}
```
**Success Response (200 OK):**
```json
{
"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:
```json
{
"error": "Schedule not found"
}
```
*400 Bad Request* - Invalid cron expression or validation error:
```json
{
"error": "Invalid cron expression"
}
```
**Usage Example:**
```bash
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):**
```json
{
"message": "Schedule deleted successfully",
"schedule_id": 1
}
```
**Error Responses:**
*404 Not Found* - Schedule doesn't exist:
```json
{
"error": "Schedule not found"
}
```
**Usage Example:**
```bash
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):**
```json
{
"message": "Scan triggered successfully",
"schedule_id": 1,
"scan_id": 43
}
```
**Error Responses:**
*404 Not Found* - Schedule doesn't exist:
```json
{
"error": "Schedule not found"
}
```
**Usage Example:**
```bash
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):**
```json
{
"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):**
```json
{
"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:
```json
{
"error": "days parameter must be at least 1"
}
```
**Usage Example:**
```bash
# 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):**
```json
{
"total_scans": 150,
"completed_scans": 140,
"failed_scans": 5,
"running_scans": 5,
"scans_today": 3,
"scans_this_week": 15
}
```
**Usage Example:**
```bash
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):**
```json
{
"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:
```json
{
"error": "Scan not found"
}
```
**Usage Example:**
```bash
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):**
```json
{
"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:**
```bash
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:**
```json
{
"settings": {
"smtp_server": "smtp.example.com",
"smtp_port": 587,
"retention_days": 90
}
}
```
**Success Response (200 OK):**
```json
{
"status": "success",
"message": "Updated 3 settings"
}
```
**Error Responses:**
*400 Bad Request* - No settings provided:
```json
{
"status": "error",
"message": "No settings provided"
}
```
**Usage Example:**
```bash
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):**
```json
{
"status": "success",
"key": "smtp_server",
"value": "smtp.gmail.com"
}
```
**Error Responses:**
*404 Not Found* - Setting doesn't exist:
```json
{
"status": "error",
"message": "Setting \"invalid_key\" not found"
}
```
**Usage Example:**
```bash
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:**
```json
{
"value": "smtp.example.com"
}
```
**Success Response (200 OK):**
```json
{
"status": "success",
"message": "Setting \"smtp_server\" updated"
}
```
**Error Responses:**
*400 Bad Request* - Missing value:
```json
{
"status": "error",
"message": "No value provided"
}
```
**Usage Example:**
```bash
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):**
```json
{
"status": "success",
"message": "Setting \"custom_key\" deleted"
}
```
**Error Responses:**
*404 Not Found* - Setting doesn't exist:
```json
{
"status": "error",
"message": "Setting \"invalid_key\" not found"
}
```
**Usage Example:**
```bash
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:**
```json
{
"password": "newpassword123"
}
```
**Success Response (200 OK):**
```json
{
"status": "success",
"message": "Password updated successfully"
}
```
**Error Responses:**
*400 Bad Request* - Missing or invalid password:
```json
{
"status": "error",
"message": "Password must be at least 8 characters"
}
```
**Usage Example:**
```bash
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):**
```json
{
"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:**
```bash
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):**
```json
{
"status": "healthy",
"api": "settings",
"version": "1.0.0-phase1"
}
```
**Usage Example:**
```bash
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):**
```json
{
"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:**
```bash
# 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):**
```json
{
"acknowledged_by": "admin"
}
```
**Success Response (200 OK):**
```json
{
"status": "success",
"message": "Alert 1 acknowledged",
"acknowledged_by": "admin"
}
```
**Error Responses:**
*400 Bad Request* - Failed to acknowledge:
```json
{
"status": "error",
"message": "Failed to acknowledge alert 1"
}
```
**Usage Example:**
```bash
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):**
```json
{
"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:**
```bash
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:**
```json
{
"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):**
```json
{
"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:
```json
{
"status": "error",
"message": "rule_type is required"
}
```
*400 Bad Request* - Invalid rule type:
```json
{
"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:
```json
{
"status": "error",
"message": "Invalid severity. Must be one of: critical, warning, info"
}
```
**Usage Examples:**
```bash
# 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:**
```json
{
"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):**
```json
{
"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:
```json
{
"status": "error",
"message": "Alert rule 1 not found"
}
```
*400 Bad Request* - Invalid severity:
```json
{
"status": "error",
"message": "Invalid severity. Must be one of: critical, warning, info"
}
```
**Usage Example:**
```bash
# 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):**
```json
{
"status": "success",
"message": "Alert rule 1 deleted successfully"
}
```
**Error Responses:**
*404 Not Found* - Rule doesn't exist:
```json
{
"status": "error",
"message": "Alert rule 1 not found"
}
```
**Usage Example:**
```bash
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):**
```json
{
"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:**
```bash
# 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):**
```json
{
"status": "healthy",
"api": "alerts",
"version": "1.0.0-phase5"
}
```
**Usage Example:**
```bash
curl -X GET http://localhost:5000/api/alerts/health
```
---
## Error Handling
### Error Response Format
All error responses follow a consistent JSON format:
```json
{
"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:**
```bash
# 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
```bash
#!/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
```bash
#!/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
```bash
# 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/login` and `/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:
```bash
CORS_ORIGINS=https://scanner.example.com,https://admin.example.com
```
### HTTPS
For production deployments:
1. Use HTTPS (TLS/SSL) for all requests
2. Set `SESSION_COOKIE_SECURE=True` in Flask config
3. 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