Files
SneakyScan/docs/API_REFERENCE.md
2025-11-17 15:50:15 -06:00

17 KiB

SneakyScanner Web API Reference

Version: 2.0 (Phase 2) Base URL: http://localhost:5000 Authentication: Session-based (Flask-Login)

Table of Contents

  1. Authentication
  2. Scans API
  3. Settings API
  4. Error Handling
  5. Status Codes
  6. Request/Response Examples

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/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:

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: 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:


Last Updated: 2025-11-14 Phase: 2 - Flask Web App Core Next Update: Phase 3 - Dashboard & Scheduling