# SneakyScanner Web API Reference **Version:** 4.0 (Phase 4) **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: 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 (200 OK):** ```json { "status": "not_implemented", "message": "Email testing endpoint - to be implemented in Phase 4" } ``` **Note:** This endpoint returns 501 Not Implemented. Full email functionality will be added in Phase 4. **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. Most functionality is planned for Phase 4. **Note:** The Alerts API endpoints are currently stub implementations returning placeholder data. Full alert functionality including email notifications and rule-based triggering will be implemented in Phase 4. ### List Alerts List recent alerts with pagination and filtering. **Endpoint:** `GET /api/alerts` **Authentication:** Required **Query Parameters:** | Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | `page` | integer | No | 1 | Page number | | `per_page` | integer | No | 20 | Items per page | | `alert_type` | string | No | - | Filter by alert type | | `severity` | string | No | - | Filter by severity (info, warning, critical) | | `start_date` | string | No | - | Filter alerts after this date | | `end_date` | string | No | - | Filter alerts before this date | **Success Response (200 OK):** ```json { "alerts": [], "total": 0, "page": 1, "per_page": 20, "message": "Alerts list endpoint - to be implemented in Phase 4" } ``` **Usage Example:** ```bash curl -X GET http://localhost:5000/api/alerts \ -b cookies.txt ``` ### List Alert Rules List all configured alert rules. **Endpoint:** `GET /api/alerts/rules` **Authentication:** Required **Success Response (200 OK):** ```json { "rules": [], "message": "Alert rules list endpoint - to be implemented in Phase 4" } ``` **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 { "rule_type": "cert_expiry", "threshold": 30, "enabled": true, "email_enabled": false } ``` **Success Response (501 Not Implemented):** ```json { "rule_id": null, "status": "not_implemented", "message": "Alert rule creation endpoint - to be implemented in Phase 4", "data": {...} } ``` **Usage Example:** ```bash curl -X POST http://localhost:5000/api/alerts/rules \ -H "Content-Type: application/json" \ -d '{"rule_type":"cert_expiry","threshold":30}' \ -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 { "threshold": 60, "enabled": true, "email_enabled": true } ``` **Success Response (501 Not Implemented):** ```json { "rule_id": 1, "status": "not_implemented", "message": "Alert rule update endpoint - to be implemented in Phase 4", "data": {...} } ``` **Usage Example:** ```bash curl -X PUT http://localhost:5000/api/alerts/rules/1 \ -H "Content-Type: application/json" \ -d '{"threshold":60}' \ -b cookies.txt ``` ### Delete Alert Rule Delete an alert rule. **Endpoint:** `DELETE /api/alerts/rules/{rule_id}` **Authentication:** Required **Path Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `rule_id` | integer | Yes | Alert rule ID | **Success Response (501 Not Implemented):** ```json { "rule_id": 1, "status": "not_implemented", "message": "Alert rule deletion endpoint - to be implemented in Phase 4" } ``` **Usage Example:** ```bash curl -X DELETE http://localhost:5000/api/alerts/rules/1 \ -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-phase1" } ``` **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:** 4.0 (Phase 4) API versioning will be implemented in Phase 5. For now, the API is considered unstable and may change between phases. **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 **Upcoming Changes:** - Phase 4 → Phase 5: Full alert implementation with email notifications - Phase 5: API versioning with backward compatibility guarantees --- ## API Summary ### Implemented APIs (Phase 4) - **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 ### Partially Implemented - **Alerts API** - Stub endpoints (full implementation in Phase 4 continuation) ### Endpoint Count - Total endpoints: 50+ - Authenticated endpoints: 45+ - 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-17 **Phase:** 4 - Alerts & Email Notifications **Next Update:** Phase 5 - API Versioning & Stability