2860 lines
60 KiB
Markdown
2860 lines
60 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. [Webhooks API](#webhooks-api)
|
|
9. [Error Handling](#error-handling)
|
|
10. [Status Codes](#status-codes)
|
|
11. [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
|
|
```
|
|
|
|
---
|
|
|
|
## Webhooks API
|
|
|
|
Manage webhook configurations for alert notifications with support for various authentication methods and delivery tracking.
|
|
|
|
### List Webhooks
|
|
|
|
List all configured webhooks with pagination.
|
|
|
|
**Endpoint:** `GET /api/webhooks`
|
|
|
|
**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) |
|
|
| `enabled` | boolean | No | - | Filter by enabled status (true/false) |
|
|
|
|
**Success Response (200 OK):**
|
|
```json
|
|
{
|
|
"webhooks": [
|
|
{
|
|
"id": 1,
|
|
"name": "Slack Notifications",
|
|
"url": "https://hooks.slack.com/services/XXX/YYY/ZZZ",
|
|
"enabled": true,
|
|
"auth_type": "none",
|
|
"auth_token": null,
|
|
"custom_headers": null,
|
|
"alert_types": ["cert_expiry", "drift_detection"],
|
|
"severity_filter": ["critical", "warning"],
|
|
"timeout": 10,
|
|
"retry_count": 3,
|
|
"created_at": "2025-11-18T10:00:00Z",
|
|
"updated_at": "2025-11-18T10:00:00Z"
|
|
}
|
|
],
|
|
"total": 1,
|
|
"page": 1,
|
|
"per_page": 20,
|
|
"pages": 1
|
|
}
|
|
```
|
|
|
|
**Usage Examples:**
|
|
```bash
|
|
# List all webhooks
|
|
curl -X GET http://localhost:5000/api/webhooks \
|
|
-b cookies.txt
|
|
|
|
# List only enabled webhooks
|
|
curl -X GET "http://localhost:5000/api/webhooks?enabled=true" \
|
|
-b cookies.txt
|
|
```
|
|
|
|
### Get Webhook
|
|
|
|
Get details for a specific webhook.
|
|
|
|
**Endpoint:** `GET /api/webhooks/{id}`
|
|
|
|
**Authentication:** Required
|
|
|
|
**Path Parameters:**
|
|
|
|
| Parameter | Type | Required | Description |
|
|
|-----------|------|----------|-------------|
|
|
| `id` | integer | Yes | Webhook ID |
|
|
|
|
**Success Response (200 OK):**
|
|
```json
|
|
{
|
|
"webhook": {
|
|
"id": 1,
|
|
"name": "Slack Notifications",
|
|
"url": "https://hooks.slack.com/services/XXX/YYY/ZZZ",
|
|
"enabled": true,
|
|
"auth_type": "bearer",
|
|
"auth_token": "***ENCRYPTED***",
|
|
"custom_headers": null,
|
|
"alert_types": ["cert_expiry"],
|
|
"severity_filter": ["critical"],
|
|
"timeout": 10,
|
|
"retry_count": 3,
|
|
"created_at": "2025-11-18T10:00:00Z",
|
|
"updated_at": "2025-11-18T10:00:00Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Error Responses:**
|
|
|
|
*404 Not Found* - Webhook doesn't exist:
|
|
```json
|
|
{
|
|
"status": "error",
|
|
"message": "Webhook 1 not found"
|
|
}
|
|
```
|
|
|
|
**Usage Example:**
|
|
```bash
|
|
curl -X GET http://localhost:5000/api/webhooks/1 \
|
|
-b cookies.txt
|
|
```
|
|
|
|
### Create Webhook
|
|
|
|
Create a new webhook configuration.
|
|
|
|
**Endpoint:** `POST /api/webhooks`
|
|
|
|
**Authentication:** Required
|
|
|
|
**Request Body:**
|
|
```json
|
|
{
|
|
"name": "Slack Notifications",
|
|
"url": "https://hooks.slack.com/services/XXX/YYY/ZZZ",
|
|
"enabled": true,
|
|
"auth_type": "bearer",
|
|
"auth_token": "your-secret-token",
|
|
"custom_headers": {
|
|
"X-Custom-Header": "value"
|
|
},
|
|
"alert_types": ["cert_expiry", "drift_detection"],
|
|
"severity_filter": ["critical", "warning"],
|
|
"timeout": 10,
|
|
"retry_count": 3
|
|
}
|
|
```
|
|
|
|
**Request Body Fields:**
|
|
|
|
| Field | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| `name` | string | Yes | Webhook name |
|
|
| `url` | string | Yes | Webhook URL |
|
|
| `enabled` | boolean | No | Whether webhook is enabled (default: true) |
|
|
| `auth_type` | string | No | Authentication type: none, bearer, basic, custom (default: none) |
|
|
| `auth_token` | string | No | Authentication token (encrypted when stored) |
|
|
| `custom_headers` | object | No | JSON object with custom HTTP headers |
|
|
| `alert_types` | array | No | Array of alert types to filter (empty = all types) |
|
|
| `severity_filter` | array | No | Array of severities to filter (empty = all severities) |
|
|
| `timeout` | integer | No | Request timeout in seconds (default: 10) |
|
|
| `retry_count` | integer | No | Number of retry attempts (default: 3) |
|
|
|
|
**Success Response (201 Created):**
|
|
```json
|
|
{
|
|
"status": "success",
|
|
"message": "Webhook created successfully",
|
|
"webhook": {
|
|
"id": 1,
|
|
"name": "Slack Notifications",
|
|
"url": "https://hooks.slack.com/services/XXX/YYY/ZZZ",
|
|
"enabled": true,
|
|
"auth_type": "bearer",
|
|
"alert_types": ["cert_expiry"],
|
|
"severity_filter": ["critical"],
|
|
"custom_headers": null,
|
|
"timeout": 10,
|
|
"retry_count": 3,
|
|
"created_at": "2025-11-18T10:00:00Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Error Responses:**
|
|
|
|
*400 Bad Request* - Missing required fields:
|
|
```json
|
|
{
|
|
"status": "error",
|
|
"message": "name is required"
|
|
}
|
|
```
|
|
|
|
*400 Bad Request* - Invalid auth type:
|
|
```json
|
|
{
|
|
"status": "error",
|
|
"message": "Invalid auth_type. Must be one of: none, bearer, basic, custom"
|
|
}
|
|
```
|
|
|
|
**Usage Examples:**
|
|
```bash
|
|
# Create webhook with no authentication
|
|
curl -X POST http://localhost:5000/api/webhooks \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"name": "Slack Notifications",
|
|
"url": "https://hooks.slack.com/services/XXX/YYY/ZZZ",
|
|
"alert_types": ["cert_expiry", "drift_detection"],
|
|
"severity_filter": ["critical"]
|
|
}' \
|
|
-b cookies.txt
|
|
|
|
# Create webhook with bearer token authentication
|
|
curl -X POST http://localhost:5000/api/webhooks \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"name": "Custom API",
|
|
"url": "https://api.example.com/webhook",
|
|
"auth_type": "bearer",
|
|
"auth_token": "your-secret-token"
|
|
}' \
|
|
-b cookies.txt
|
|
```
|
|
|
|
### Update Webhook
|
|
|
|
Update an existing webhook configuration.
|
|
|
|
**Endpoint:** `PUT /api/webhooks/{id}`
|
|
|
|
**Authentication:** Required
|
|
|
|
**Path Parameters:**
|
|
|
|
| Parameter | Type | Required | Description |
|
|
|-----------|------|----------|-------------|
|
|
| `id` | integer | Yes | Webhook ID |
|
|
|
|
**Request Body:**
|
|
```json
|
|
{
|
|
"name": "Updated Name",
|
|
"enabled": false,
|
|
"timeout": 15
|
|
}
|
|
```
|
|
|
|
**Note:** All fields are optional. Only provided fields will be updated.
|
|
|
|
**Success Response (200 OK):**
|
|
```json
|
|
{
|
|
"status": "success",
|
|
"message": "Webhook updated successfully",
|
|
"webhook": {
|
|
"id": 1,
|
|
"name": "Updated Name",
|
|
"url": "https://hooks.slack.com/services/XXX/YYY/ZZZ",
|
|
"enabled": false,
|
|
"auth_type": "none",
|
|
"alert_types": ["cert_expiry"],
|
|
"severity_filter": ["critical"],
|
|
"custom_headers": null,
|
|
"timeout": 15,
|
|
"retry_count": 3,
|
|
"updated_at": "2025-11-18T11:00:00Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Error Responses:**
|
|
|
|
*404 Not Found* - Webhook doesn't exist:
|
|
```json
|
|
{
|
|
"status": "error",
|
|
"message": "Webhook 1 not found"
|
|
}
|
|
```
|
|
|
|
**Usage Example:**
|
|
```bash
|
|
# Disable a webhook
|
|
curl -X PUT http://localhost:5000/api/webhooks/1 \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"enabled":false}' \
|
|
-b cookies.txt
|
|
|
|
# Update timeout and retry count
|
|
curl -X PUT http://localhost:5000/api/webhooks/1 \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"timeout":20,"retry_count":5}' \
|
|
-b cookies.txt
|
|
```
|
|
|
|
### Delete Webhook
|
|
|
|
Delete a webhook and all associated delivery logs.
|
|
|
|
**Endpoint:** `DELETE /api/webhooks/{id}`
|
|
|
|
**Authentication:** Required
|
|
|
|
**Path Parameters:**
|
|
|
|
| Parameter | Type | Required | Description |
|
|
|-----------|------|----------|-------------|
|
|
| `id` | integer | Yes | Webhook ID |
|
|
|
|
**Success Response (200 OK):**
|
|
```json
|
|
{
|
|
"status": "success",
|
|
"message": "Webhook 1 deleted successfully"
|
|
}
|
|
```
|
|
|
|
**Error Responses:**
|
|
|
|
*404 Not Found* - Webhook doesn't exist:
|
|
```json
|
|
{
|
|
"status": "error",
|
|
"message": "Webhook 1 not found"
|
|
}
|
|
```
|
|
|
|
**Usage Example:**
|
|
```bash
|
|
curl -X DELETE http://localhost:5000/api/webhooks/1 \
|
|
-b cookies.txt
|
|
```
|
|
|
|
### Test Webhook
|
|
|
|
Send a test payload to a webhook to verify configuration.
|
|
|
|
**Endpoint:** `POST /api/webhooks/{id}/test`
|
|
|
|
**Authentication:** Required
|
|
|
|
**Path Parameters:**
|
|
|
|
| Parameter | Type | Required | Description |
|
|
|-----------|------|----------|-------------|
|
|
| `id` | integer | Yes | Webhook ID to test |
|
|
|
|
**Success Response (200 OK):**
|
|
```json
|
|
{
|
|
"status": "success",
|
|
"message": "HTTP 200",
|
|
"status_code": 200,
|
|
"response_body": "ok"
|
|
}
|
|
```
|
|
|
|
**Error Response (failure to connect):**
|
|
```json
|
|
{
|
|
"status": "error",
|
|
"message": "Connection error: Failed to resolve hostname",
|
|
"status_code": null
|
|
}
|
|
```
|
|
|
|
**Test Payload Format:**
|
|
```json
|
|
{
|
|
"event": "webhook.test",
|
|
"message": "This is a test webhook from SneakyScanner",
|
|
"timestamp": "2025-11-18T10:00:00Z",
|
|
"webhook": {
|
|
"id": 1,
|
|
"name": "Slack Notifications"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Usage Example:**
|
|
```bash
|
|
curl -X POST http://localhost:5000/api/webhooks/1/test \
|
|
-b cookies.txt
|
|
```
|
|
|
|
### Get Webhook Delivery Logs
|
|
|
|
Get delivery history for a specific webhook.
|
|
|
|
**Endpoint:** `GET /api/webhooks/{id}/logs`
|
|
|
|
**Authentication:** Required
|
|
|
|
**Path Parameters:**
|
|
|
|
| Parameter | Type | Required | Description |
|
|
|-----------|------|----------|-------------|
|
|
| `id` | integer | Yes | Webhook ID |
|
|
|
|
**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 (success/failed) |
|
|
|
|
**Success Response (200 OK):**
|
|
```json
|
|
{
|
|
"webhook_id": 1,
|
|
"webhook_name": "Slack Notifications",
|
|
"logs": [
|
|
{
|
|
"id": 101,
|
|
"alert_id": 42,
|
|
"alert_type": "cert_expiry",
|
|
"alert_message": "Certificate expires in 15 days",
|
|
"status": "success",
|
|
"response_code": 200,
|
|
"response_body": "ok",
|
|
"error_message": null,
|
|
"attempt_number": 1,
|
|
"delivered_at": "2025-11-18T10:30:00Z"
|
|
},
|
|
{
|
|
"id": 100,
|
|
"alert_id": 41,
|
|
"alert_type": "drift_detection",
|
|
"alert_message": "Port drift detected",
|
|
"status": "failed",
|
|
"response_code": null,
|
|
"response_body": null,
|
|
"error_message": "Request timeout after 10 seconds",
|
|
"attempt_number": 3,
|
|
"delivered_at": "2025-11-18T09:00:00Z"
|
|
}
|
|
],
|
|
"total": 2,
|
|
"page": 1,
|
|
"per_page": 20,
|
|
"pages": 1
|
|
}
|
|
```
|
|
|
|
**Error Responses:**
|
|
|
|
*404 Not Found* - Webhook doesn't exist:
|
|
```json
|
|
{
|
|
"status": "error",
|
|
"message": "Webhook 1 not found"
|
|
}
|
|
```
|
|
|
|
**Usage Examples:**
|
|
```bash
|
|
# Get all delivery logs
|
|
curl -X GET http://localhost:5000/api/webhooks/1/logs \
|
|
-b cookies.txt
|
|
|
|
# Get only failed deliveries
|
|
curl -X GET "http://localhost:5000/api/webhooks/1/logs?status=failed" \
|
|
-b cookies.txt
|
|
|
|
# Get page 2
|
|
curl -X GET "http://localhost:5000/api/webhooks/1/logs?page=2" \
|
|
-b cookies.txt
|
|
```
|
|
|
|
### Webhook Payload Format
|
|
|
|
When alerts are triggered, webhooks receive JSON payloads in this format:
|
|
|
|
```json
|
|
{
|
|
"event": "alert.created",
|
|
"alert": {
|
|
"id": 123,
|
|
"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,
|
|
"created_at": "2025-11-18T10:30:00Z"
|
|
},
|
|
"scan": {
|
|
"id": 42,
|
|
"title": "Production Network Scan",
|
|
"timestamp": "2025-11-18T10:00:00Z",
|
|
"status": "completed"
|
|
},
|
|
"rule": {
|
|
"id": 3,
|
|
"name": "Certificate Expiry Warning",
|
|
"type": "cert_expiry",
|
|
"threshold": 30
|
|
}
|
|
}
|
|
```
|
|
|
|
### Authentication Types
|
|
|
|
**None:**
|
|
- No authentication headers added
|
|
|
|
**Bearer Token:**
|
|
- Adds `Authorization: Bearer <token>` header
|
|
- Token is encrypted in database
|
|
|
|
**Basic Authentication:**
|
|
- Format: `username:password` in auth_token field
|
|
- Automatically converts to HTTP Basic Auth
|
|
- Credentials encrypted in database
|
|
|
|
**Custom Headers:**
|
|
- Define any custom HTTP headers
|
|
- Useful for API keys or custom authentication schemes
|
|
- Example:
|
|
```json
|
|
{
|
|
"X-API-Key": "your-api-key",
|
|
"X-Custom-Header": "value"
|
|
}
|
|
```
|
|
|
|
### Retry Logic
|
|
|
|
Failed webhook deliveries are automatically retried with exponential backoff:
|
|
|
|
- **Attempt 1:** Immediate
|
|
- **Attempt 2:** After 2 seconds
|
|
- **Attempt 3:** After 4 seconds
|
|
- **Attempt 4:** After 8 seconds
|
|
- **Maximum delay:** 60 seconds
|
|
|
|
Retry count is configurable per webhook (0-5 attempts).
|
|
|
|
### Health Check
|
|
|
|
Check API health status.
|
|
|
|
**Endpoint:** `GET /api/webhooks/health`
|
|
|
|
**Authentication:** Not required
|
|
|
|
**Success Response (200 OK):**
|
|
```json
|
|
{
|
|
"status": "healthy",
|
|
"api": "webhooks",
|
|
"version": "1.0.0-phase5"
|
|
}
|
|
```
|
|
|
|
**Usage Example:**
|
|
```bash
|
|
curl -X GET http://localhost:5000/api/webhooks/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
|
|
- **Webhooks API** - Webhook management, delivery tracking, authentication support, retry logic
|
|
|
|
### Endpoint Count
|
|
- Total endpoints: 65+
|
|
- Authenticated endpoints: 60+
|
|
- 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
|