17 KiB
SneakyScanner Web API Reference
Version: 2.0 (Phase 2)
Base URL: http://localhost:5000
Authentication: Session-based (Flask-Login)
Table of Contents
Authentication
SneakyScanner uses session-based authentication with Flask-Login. All API endpoints (except login) require authentication.
Login
Authenticate and create a session.
Endpoint: POST /auth/login
Request Body:
{
"password": "your-password-here"
}
Success Response (200 OK):
{
"message": "Login successful",
"redirect": "/dashboard"
}
Error Response (401 Unauthorized):
{
"error": "Invalid password"
}
Usage Example:
# Login and save session cookie
curl -X POST http://localhost:5000/auth/login \
-H "Content-Type: application/json" \
-d '{"password":"yourpassword"}' \
-c cookies.txt
# Use session cookie for subsequent requests
curl -X GET http://localhost:5000/api/scans \
-b cookies.txt
Logout
Destroy the current session.
Endpoint: GET /auth/logout
Success Response: Redirects to login page (302)
Scans API
Manage network scans: trigger, list, view, and delete.
Trigger Scan
Start a new background scan.
Endpoint: POST /api/scans
Authentication: Required
Request Body:
{
"config_file": "/app/configs/example-site.yaml"
}
Success Response (201 Created):
{
"scan_id": 42,
"status": "running",
"message": "Scan queued successfully"
}
Error Responses:
400 Bad Request - Invalid config file:
{
"error": "Invalid config file",
"message": "Config file does not exist or is not valid YAML"
}
500 Internal Server Error - Scan queue failure:
{
"error": "Failed to queue scan",
"message": "Internal server error"
}
Usage Example:
curl -X POST http://localhost:5000/api/scans \
-H "Content-Type: application/json" \
-d '{"config_file":"/app/configs/production.yaml"}' \
-b cookies.txt
List Scans
Retrieve a paginated list of scans with optional status filtering.
Endpoint: GET /api/scans
Authentication: Required
Query Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
page |
integer | No | 1 | Page number (1-indexed) |
per_page |
integer | No | 20 | Items per page (1-100) |
status |
string | No | - | Filter by status: running, completed, failed |
Success Response (200 OK):
{
"scans": [
{
"id": 42,
"timestamp": "2025-11-14T10:30:00Z",
"duration": 125.5,
"status": "completed",
"title": "Production Network Scan",
"config_file": "/app/configs/production.yaml",
"triggered_by": "manual",
"started_at": "2025-11-14T10:30:00Z",
"completed_at": "2025-11-14T10:32:05Z"
},
{
"id": 41,
"timestamp": "2025-11-13T15:00:00Z",
"duration": 98.2,
"status": "completed",
"title": "Development Network Scan",
"config_file": "/app/configs/dev.yaml",
"triggered_by": "scheduled",
"started_at": "2025-11-13T15:00:00Z",
"completed_at": "2025-11-13T15:01:38Z"
}
],
"total": 42,
"page": 1,
"per_page": 20,
"pages": 3
}
Error Responses:
400 Bad Request - Invalid parameters:
{
"error": "Invalid pagination parameters",
"message": "Page and per_page must be positive integers"
}
Usage Examples:
# List first page (default 20 items)
curl -X GET http://localhost:5000/api/scans \
-b cookies.txt
# List page 2 with 50 items per page
curl -X GET "http://localhost:5000/api/scans?page=2&per_page=50" \
-b cookies.txt
# List only running scans
curl -X GET "http://localhost:5000/api/scans?status=running" \
-b cookies.txt
Get Scan Details
Retrieve complete details for a specific scan, including all sites, IPs, ports, services, certificates, and TLS versions.
Endpoint: GET /api/scans/{id}
Authentication: Required
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
integer | Yes | Scan ID |
Success Response (200 OK):
{
"id": 42,
"timestamp": "2025-11-14T10:30:00Z",
"duration": 125.5,
"status": "completed",
"title": "Production Network Scan",
"config_file": "/app/configs/production.yaml",
"json_path": "/app/output/scan_report_20251114_103000.json",
"html_path": "/app/output/scan_report_20251114_103000.html",
"zip_path": "/app/output/scan_report_20251114_103000.zip",
"screenshot_dir": "/app/output/scan_report_20251114_103000_screenshots",
"triggered_by": "manual",
"started_at": "2025-11-14T10:30:00Z",
"completed_at": "2025-11-14T10:32:05Z",
"error_message": null,
"sites": [
{
"id": 101,
"site_name": "Production Web Servers",
"ips": [
{
"id": 201,
"ip_address": "192.168.1.10",
"ping_expected": true,
"ping_actual": true,
"ports": [
{
"id": 301,
"port": 443,
"protocol": "tcp",
"expected": true,
"state": "open",
"services": [
{
"id": 401,
"service_name": "https",
"product": "nginx",
"version": "1.24.0",
"extrainfo": null,
"ostype": "Linux",
"http_protocol": "https",
"screenshot_path": "scan_report_20251114_103000_screenshots/192_168_1_10_443.png",
"certificates": [
{
"id": 501,
"subject": "CN=example.com",
"issuer": "CN=Let's Encrypt Authority X3,O=Let's Encrypt,C=US",
"serial_number": "123456789012345678901234567890",
"not_valid_before": "2025-01-01T00:00:00+00:00",
"not_valid_after": "2025-04-01T23:59:59+00:00",
"days_until_expiry": 89,
"sans": "[\"example.com\", \"www.example.com\"]",
"is_self_signed": false,
"tls_versions": [
{
"id": 601,
"tls_version": "TLS 1.2",
"supported": true,
"cipher_suites": "[\"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\", \"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\"]"
},
{
"id": 602,
"tls_version": "TLS 1.3",
"supported": true,
"cipher_suites": "[\"TLS_AES_256_GCM_SHA384\", \"TLS_AES_128_GCM_SHA256\"]"
}
]
}
]
}
]
}
]
}
]
}
]
}
Error Responses:
404 Not Found - Scan doesn't exist:
{
"error": "Scan not found",
"message": "Scan with ID 42 does not exist"
}
Usage Example:
curl -X GET http://localhost:5000/api/scans/42 \
-b cookies.txt
Get Scan Status
Poll the current status of a running scan. Use this endpoint to track scan progress.
Endpoint: GET /api/scans/{id}/status
Authentication: Required
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
integer | Yes | Scan ID |
Success Response (200 OK):
Running scan:
{
"scan_id": 42,
"status": "running",
"started_at": "2025-11-14T10:30:00Z",
"completed_at": null,
"error_message": null
}
Completed scan:
{
"scan_id": 42,
"status": "completed",
"started_at": "2025-11-14T10:30:00Z",
"completed_at": "2025-11-14T10:32:05Z",
"error_message": null
}
Failed scan:
{
"scan_id": 42,
"status": "failed",
"started_at": "2025-11-14T10:30:00Z",
"completed_at": "2025-11-14T10:30:15Z",
"error_message": "Config file not found: /app/configs/missing.yaml"
}
Error Responses:
404 Not Found - Scan doesn't exist:
{
"error": "Scan not found",
"message": "Scan with ID 42 does not exist"
}
Usage Example:
# Poll status every 5 seconds
while true; do
curl -X GET http://localhost:5000/api/scans/42/status -b cookies.txt
sleep 5
done
Delete Scan
Delete a scan and all associated files (JSON, HTML, ZIP, screenshots).
Endpoint: DELETE /api/scans/{id}
Authentication: Required
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
integer | Yes | Scan ID |
Success Response (200 OK):
{
"message": "Scan 42 deleted successfully"
}
Error Responses:
404 Not Found - Scan doesn't exist:
{
"error": "Scan not found",
"message": "Scan with ID 42 does not exist"
}
Usage Example:
curl -X DELETE http://localhost:5000/api/scans/42 \
-b cookies.txt
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):
{
"smtp_server": "smtp.gmail.com",
"smtp_port": 587,
"smtp_username": "alerts@example.com",
"smtp_password": "********",
"smtp_from_email": "alerts@example.com",
"smtp_to_emails": "[\"admin@example.com\"]",
"retention_days": 90,
"app_password": "********"
}
Usage Example:
curl -X GET http://localhost:5000/api/settings \
-b cookies.txt
Update Setting
Update a specific setting value.
Endpoint: PUT /api/settings/{key}
Authentication: Required
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
key |
string | Yes | Setting key (e.g., smtp_server) |
Request Body:
{
"value": "smtp.example.com"
}
Success Response (200 OK):
{
"message": "Setting updated successfully",
"key": "smtp_server",
"value": "smtp.example.com"
}
Error Responses:
400 Bad Request - Missing value:
{
"error": "Missing required field: value"
}
Usage Example:
curl -X PUT http://localhost:5000/api/settings/smtp_server \
-H "Content-Type: application/json" \
-d '{"value":"smtp.example.com"}' \
-b cookies.txt
Health Check
Check if the API is running and database is accessible.
Endpoint: GET /api/settings/health
Authentication: Not required
Success Response (200 OK):
{
"status": "healthy",
"database": "connected"
}
Error Response (500 Internal Server Error):
{
"status": "unhealthy",
"database": "disconnected",
"error": "Connection error details"
}
Usage Example:
curl -X GET http://localhost:5000/api/settings/health
Error Handling
Error Response Format
All error responses follow a consistent JSON format:
{
"error": "Brief error type",
"message": "Detailed error message for debugging"
}
Content Negotiation
The API supports content negotiation based on the request:
- API Requests (Accept: application/json or /api/* path): Returns JSON errors
- Web Requests (Accept: text/html): Returns HTML error pages
Example:
# JSON error response
curl -X GET http://localhost:5000/api/scans/999 \
-H "Accept: application/json" \
-b cookies.txt
# HTML error page
curl -X GET http://localhost:5000/scans/999 \
-H "Accept: text/html" \
-b cookies.txt
Request ID Tracking
Every request receives a unique request ID for tracking and debugging:
Response Headers:
X-Request-ID: a1b2c3d4
X-Request-Duration-Ms: 125
Check application logs for detailed error information using the request ID:
2025-11-14 10:30:15 INFO [a1b2c3d4] GET /api/scans 200 125ms
Status Codes
Success Codes
| Code | Meaning | Usage |
|---|---|---|
| 200 | OK | Successful GET, PUT, DELETE requests |
| 201 | Created | Successful POST request that creates a resource |
| 302 | Found | Redirect (used for logout, login success) |
Client Error Codes
| Code | Meaning | Usage |
|---|---|---|
| 400 | Bad Request | Invalid request parameters or body |
| 401 | Unauthorized | Authentication required or failed |
| 403 | Forbidden | Authenticated but not authorized |
| 404 | Not Found | Resource doesn't exist |
| 405 | Method Not Allowed | HTTP method not supported for endpoint |
Server Error Codes
| Code | Meaning | Usage |
|---|---|---|
| 500 | Internal Server Error | Unexpected server error |
Request/Response Examples
Complete Workflow: Trigger and Monitor Scan
#!/bin/bash
# 1. Login and save session
curl -X POST http://localhost:5000/auth/login \
-H "Content-Type: application/json" \
-d '{"password":"yourpassword"}' \
-c cookies.txt
# 2. Trigger a new scan
RESPONSE=$(curl -s -X POST http://localhost:5000/api/scans \
-H "Content-Type: application/json" \
-d '{"config_file":"/app/configs/production.yaml"}' \
-b cookies.txt)
# Extract scan ID from response
SCAN_ID=$(echo $RESPONSE | jq -r '.scan_id')
echo "Scan ID: $SCAN_ID"
# 3. Poll status every 5 seconds until complete
while true; do
STATUS=$(curl -s -X GET http://localhost:5000/api/scans/$SCAN_ID/status \
-b cookies.txt | jq -r '.status')
echo "Status: $STATUS"
if [ "$STATUS" == "completed" ] || [ "$STATUS" == "failed" ]; then
break
fi
sleep 5
done
# 4. Get full scan results
curl -X GET http://localhost:5000/api/scans/$SCAN_ID \
-b cookies.txt | jq '.'
# 5. Logout
curl -X GET http://localhost:5000/auth/logout \
-b cookies.txt
Pagination Example
#!/bin/bash
# Get total number of scans
TOTAL=$(curl -s -X GET "http://localhost:5000/api/scans?per_page=1" \
-b cookies.txt | jq -r '.total')
echo "Total scans: $TOTAL"
# Calculate number of pages (50 items per page)
PER_PAGE=50
PAGES=$(( ($TOTAL + $PER_PAGE - 1) / $PER_PAGE ))
echo "Total pages: $PAGES"
# Fetch all pages
for PAGE in $(seq 1 $PAGES); do
echo "Fetching page $PAGE..."
curl -s -X GET "http://localhost:5000/api/scans?page=$PAGE&per_page=$PER_PAGE" \
-b cookies.txt | jq '.scans[] | {id, timestamp, title, status}'
done
Filter by Status
# Get all running scans
curl -X GET "http://localhost:5000/api/scans?status=running" \
-b cookies.txt | jq '.scans[] | {id, title, started_at}'
# Get all failed scans
curl -X GET "http://localhost:5000/api/scans?status=failed" \
-b cookies.txt | jq '.scans[] | {id, title, error_message}'
# Get all completed scans from last 24 hours
curl -X GET "http://localhost:5000/api/scans?status=completed" \
-b cookies.txt | jq '.scans[] | select(.completed_at > (now - 86400 | todate)) | {id, title, duration}'
Rate Limiting
Currently no rate limiting is implemented. For production deployments, consider:
- Adding nginx rate limiting
- Implementing application-level rate limiting with Flask-Limiter
- Setting connection limits in Gunicorn configuration
Security Considerations
Authentication
- All API endpoints (except
/auth/loginand/api/settings/health) require authentication - Session cookies are httpOnly and secure (in production with HTTPS)
- Passwords are hashed with bcrypt (cost factor 12)
- Sensitive settings values are encrypted at rest
CORS
CORS is configured via environment variable CORS_ORIGINS. Default: * (allow all).
For production, set to specific origins:
CORS_ORIGINS=https://scanner.example.com,https://admin.example.com
HTTPS
For production deployments:
- Use HTTPS (TLS/SSL) for all requests
- Set
SESSION_COOKIE_SECURE=Truein Flask config - Consider using a reverse proxy (nginx) with SSL termination
Input Validation
All inputs are validated:
- Config file paths are checked for existence and valid YAML
- Pagination parameters are sanitized (positive integers, max per_page: 100)
- Scan IDs are validated as integers
- Setting values are type-checked
Versioning
Current Version: 2.0 (Phase 2)
API versioning will be implemented in Phase 5. For now, the API is considered unstable and may change between phases.
Breaking Changes:
- Phase 2 → Phase 3: Possible schema changes for scan comparison
- Phase 3 → Phase 4: Possible authentication changes (token auth)
Support
For issues, questions, or feature requests:
- GitHub Issues: https://github.com/anthropics/sneakyscanner/issues
- Documentation:
/docs/ai/directory in repository
Last Updated: 2025-11-14 Phase: 2 - Flask Web App Core Next Update: Phase 3 - Dashboard & Scheduling