REST API Reference

REST API Reference

The following files were used as context for generating this wiki page:

Purpose and Scope

This document provides complete technical reference for the WSHawk Web Management Dashboard REST API. The API enables programmatic control of the scanner, including initiating scans, retrieving results, and managing scan history. It is designed for integration into CI/CD pipelines, security orchestration platforms, and custom automation tools.

For information about launching the dashboard and configuring the web server, see Dashboard Overview and Launch. For authentication setup and security considerations, see Authentication and API Keys. For understanding the persistence layer and scan storage, see Scan History and Persistence.


API Architecture Overview

The REST API is built on Flask and backed by SQLite with WAL (Write-Ahead Logging) mode for crash-resistant persistence. All scan operations are asynchronous, with the API returning scan IDs immediately and allowing clients to poll for status updates.

High-Level API Architecture

graph TB
    subgraph "Client Layer"
        CLI["CLI Tools<br/>curl, httpie"]
        Script["Python Scripts<br/>requests library"]
        CICD["CI/CD Pipelines<br/>GitHub Actions"]
    end
    
    subgraph "Flask Application"
        Routes["API Routes<br/>/api/scans<br/>/api/stats<br/>/login"]
        Auth["Authentication Middleware<br/>SHA-256 verification<br/>API Key validation"]
        Scanner["WSHawkV2 Integration<br/>Async scan orchestration"]
    end
    
    subgraph "Persistence Layer"
        DB[("SQLite Database<br/>~/.wshawk/scans.db<br/>WAL Mode")]
        Reports["Report Files<br/>HTML/JSON/CSV/SARIF<br/>reports/"]
    end
    
    CLI --> Auth
    Script --> Auth
    CICD --> Auth
    
    Auth --> Routes
    Routes --> Scanner
    Routes --> DB
    
    Scanner --> DB
    Scanner --> Reports
    
    DB --> Routes

Sources: README.md:106-136, RELEASE_SUMMARY.md:15-19, docs/V3_COMPLETE_GUIDE.md:289-310


Authentication

The API supports two authentication methods:

Password Authentication

Used for interactive sessions via the web dashboard. Credentials are validated against a SHA-256 hashed password stored in the environment variable WSHAWK_WEB_PASSWORD.

POST /login
Content-Type: application/json

{
  "password": "your-strong-password"
}

Returns a session cookie valid for the browser session.

API Key Authentication

Used for programmatic access. Include the API key in the X-API-Key header:

GET /api/scans
X-API-Key: your-api-key-here

Set the API key via the WSHAWK_API_KEY environment variable when launching the dashboard.

Authentication Flow Diagram

sequenceDiagram
    participant Client
    participant AuthMiddleware
    participant PasswordValidator
    participant APIKeyValidator
    participant Routes
    
    Client->>AuthMiddleware: "Request with credentials"
    
    alt "Session Cookie Present"
        AuthMiddleware->>PasswordValidator: "Validate session"
        PasswordValidator-->>AuthMiddleware: "Valid/Invalid"
    else "X-API-Key Header Present"
        AuthMiddleware->>APIKeyValidator: "Validate API key"
        APIKeyValidator-->>AuthMiddleware: "Valid/Invalid"
    else "No Authentication"
        AuthMiddleware-->>Client: "401 Unauthorized"
    end
    
    alt "Authentication Success"
        AuthMiddleware->>Routes: "Forward request"
        Routes-->>Client: "200 OK + Response"
    else "Authentication Failure"
        AuthMiddleware-->>Client: "401 Unauthorized"
    end

Sources: README.md:121-127, README.md:135, RELEASE_SUMMARY.md:18, docs/V3_COMPLETE_GUIDE.md:299-302, docs/V3_COMPLETE_GUIDE.md:313-315


Base URL and Headers

Base URL

When running locally with default settings:

http://localhost:5000

When deployed with custom host/port:

http://<host>:<port>

Required Headers

| Header | Value | Required For | |--------|-------|--------------| | Content-Type | application/json | POST requests with body | | X-API-Key | Your API key | API key authentication | | Accept | application/json | JSON responses (optional) |

Sources: README.md:118


Endpoint Reference

Core Scan Management Endpoints

graph LR
    subgraph "Scan Lifecycle Endpoints"
        POST_scans["POST /api/scans<br/>Initiate new scan"]
        GET_scans["GET /api/scans<br/>List all scans"]
        GET_scan_id["GET /api/scans/{id}<br/>Get scan details"]
        GET_report["GET /api/scans/{id}/report<br/>Download report"]
        DELETE_scan["DELETE /api/scans/{id}<br/>Delete scan"]
    end
    
    subgraph "Monitoring Endpoints"
        GET_stats["GET /api/stats<br/>System statistics"]
        GET_status["GET /api/scans/{id}/status<br/>Real-time progress"]
    end
    
    POST_scans --> GET_status
    GET_status --> GET_scan_id
    GET_scan_id --> GET_report

Sources: docs/V3_COMPLETE_GUIDE.md:317-330


POST /api/scans

Initiates a new WebSocket security scan. Returns immediately with a scan ID, allowing the client to poll for status updates.

Request Body

{
  "target": "ws://target.com/socket",
  "options": {
    "smart_payloads": true,
    "playwright": false,
    "use_oast": true,
    "rate_limit": 10,
    "full_scan": false,
    "session_tests": true
  }
}

Request Parameters

| Field | Type | Required | Description | |-------|------|----------|-------------| | target | string | Yes | WebSocket URL (ws:// or wss://) | | options.smart_payloads | boolean | No | Enable Smart Payload Evolution engine (default: false) | | options.playwright | boolean | No | Enable Playwright browser verification (default: false) | | options.use_oast | boolean | No | Enable OAST for blind vulnerabilities (default: true) | | options.rate_limit | integer | No | Max requests per second (default: 10) | | options.full_scan | boolean | No | Enable all advanced features (default: false) | | options.session_tests | boolean | No | Enable session hijacking tests (default: true) |

Response (201 Created)

{
  "scan_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "queued",
  "created_at": "2024-01-15T10:30:00Z",
  "target": "ws://target.com/socket"
}

Error Responses

| Status Code | Reason | |-------------|--------| | 400 Bad Request | Invalid target URL or malformed options | | 401 Unauthorized | Missing or invalid authentication | | 429 Too Many Requests | Rate limit exceeded |

Sources: docs/V3_COMPLETE_GUIDE.md:319-328


GET /api/scans

Retrieves a list of all historical scans stored in the database. Supports pagination and filtering.

Query Parameters

| Parameter | Type | Description | Default | |-----------|------|-------------|---------| | limit | integer | Maximum number of results | 50 | | offset | integer | Number of results to skip | 0 | | status | string | Filter by status (queued, running, completed, failed) | all | | target | string | Filter by target URL (partial match) | all | | sort | string | Sort field (created_at, target, status) | created_at | | order | string | Sort order (asc, desc) | desc |

Response (200 OK)

{
  "scans": [
    {
      "scan_id": "550e8400-e29b-41d4-a716-446655440000",
      "target": "ws://target.com/socket",
      "status": "completed",
      "created_at": "2024-01-15T10:30:00Z",
      "completed_at": "2024-01-15T10:35:23Z",
      "vulnerabilities_found": 12,
      "severity": {
        "critical": 2,
        "high": 5,
        "medium": 3,
        "low": 2
      }
    }
  ],
  "total": 145,
  "limit": 50,
  "offset": 0
}

Sources: docs/V3_COMPLETE_GUIDE.md:318


GET /api/scans/{id}

Retrieves detailed information about a specific scan, including all detected vulnerabilities.

Path Parameters

| Parameter | Type | Description | |-----------|------|-------------| | id | string (UUID) | Scan identifier |

Response (200 OK)

{
  "scan_id": "550e8400-e29b-41d4-a716-446655440000",
  "target": "ws://target.com/socket",
  "status": "completed",
  "created_at": "2024-01-15T10:30:00Z",
  "completed_at": "2024-01-15T10:35:23Z",
  "duration_seconds": 323,
  "options": {
    "smart_payloads": true,
    "playwright": false,
    "rate_limit": 10
  },
  "statistics": {
    "payloads_sent": 22847,
    "messages_received": 22891,
    "average_response_time_ms": 45.3,
    "vulnerabilities_found": 12
  },
  "vulnerabilities": [
    {
      "id": "vuln-001",
      "type": "SQL_INJECTION",
      "severity": "critical",
      "cvss_score": 9.8,
      "cvss_vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
      "confidence": "HIGH",
      "payload": "1' OR '1'='1",
      "evidence": {
        "request": "{\"id\": \"1' OR '1'='1\"}",
        "response": "{\"users\": [...]}",
        "timestamp": "2024-01-15T10:32:15Z"
      },
      "remediation": "Use parameterized queries..."
    }
  ],
  "server_fingerprint": {
    "framework": "Node.js/Socket.io",
    "version": "4.5.1",
    "waf_detected": false
  }
}

Error Responses

| Status Code | Reason | |-------------|--------| | 404 Not Found | Scan ID does not exist | | 401 Unauthorized | Missing or invalid authentication |

Sources: docs/V3_COMPLETE_GUIDE.md:318-330


GET /api/scans/{id}/status

Retrieves real-time status and progress information for an ongoing scan. Designed for polling during scan execution.

Response (200 OK)

{
  "scan_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "running",
  "progress": {
    "current_phase": "mutation_engine",
    "percent_complete": 67,
    "payloads_sent": 15234,
    "estimated_time_remaining_seconds": 180
  },
  "vulnerabilities_found": 8,
  "current_severity_counts": {
    "critical": 1,
    "high": 3,
    "medium": 2,
    "low": 2
  }
}

Status Values

| Status | Description | |--------|-------------| | queued | Scan is waiting to start | | initializing | Establishing connection and fingerprinting | | running | Active payload injection phase | | mutation_engine | Smart Payload Evolution in progress | | verification | Playwright/OAST verification phase | | reporting | Generating final reports | | completed | Scan finished successfully | | failed | Scan encountered an error |

Sources: README.md:133


GET /api/scans/{id}/report

Downloads the complete scan report in the requested format.

Query Parameters

| Parameter | Type | Description | Default | |-----------|------|-------------|---------| | format | string | Report format (html, json, csv, sarif) | json |

Response (200 OK)

For JSON format:

{
  "scan_metadata": { /* ... */ },
  "vulnerabilities": [ /* ... */ ],
  "traffic_logs": [ /* ... */ ],
  "recommendations": [ /* ... */ ]
}

For HTML format: Returns rendered HTML report.

For SARIF format: Returns SARIF-compliant JSON for GitHub Security integration.

Headers

| Format | Content-Type | |--------|--------------| | json | application/json | | html | text/html | | csv | text/csv | | sarif | application/sarif+json |

Sources: docs/V3_COMPLETE_GUIDE.md:329, README.md:175-184


DELETE /api/scans/{id}

Deletes a scan and all associated data from the database and filesystem.

Response (204 No Content)

No response body on success.

Error Responses

| Status Code | Reason | |-------------|--------| | 404 Not Found | Scan ID does not exist | | 401 Unauthorized | Missing or invalid authentication | | 409 Conflict | Cannot delete running scan |


GET /api/stats

Retrieves system-wide statistics and metrics.

Response (200 OK)

{
  "total_scans": 1453,
  "scans_last_24h": 23,
  "total_vulnerabilities": 8912,
  "average_scan_duration_seconds": 287,
  "database_size_mb": 245.8,
  "vulnerabilities_by_type": {
    "SQL_INJECTION": 1234,
    "XSS": 2345,
    "COMMAND_INJECTION": 567,
    "XXE": 123,
    "SSRF": 89
  },
  "severity_distribution": {
    "critical": 1203,
    "high": 3456,
    "medium": 2789,
    "low": 1464
  }
}

Sources: docs/V3_COMPLETE_GUIDE.md:317


Request/Response Flow

Complete Scan Workflow

sequenceDiagram
    participant Client
    participant API["Flask API<br/>/api/scans"]
    participant Scanner["WSHawkV2<br/>scanner_v2.py"]
    participant DB["SQLite<br/>scans.db"]
    participant Reports["Report Generator"]
    
    Client->>API: "POST /api/scans"
    API->>DB: "INSERT scan record<br/>status='queued'"
    DB-->>API: "scan_id"
    API-->>Client: "201 Created {scan_id}"
    
    Note over Scanner: "Async execution starts"
    API->>Scanner: "Trigger scan with options"
    Scanner->>DB: "UPDATE status='initializing'"
    
    Scanner->>Scanner: "Connection + Fingerprinting"
    Scanner->>DB: "UPDATE status='running'"
    
    loop "Payload Injection"
        Scanner->>Scanner: "Send payloads"
        Scanner->>DB: "INSERT vulnerabilities"
    end
    
    Scanner->>DB: "UPDATE status='verification'"
    Scanner->>Scanner: "Playwright/OAST verification"
    
    Scanner->>Reports: "Generate reports"
    Reports->>DB: "UPDATE report_path"
    Scanner->>DB: "UPDATE status='completed'"
    
    Client->>API: "GET /api/scans/{id}/status"
    API->>DB: "SELECT scan"
    DB-->>API: "scan data"
    API-->>Client: "200 OK {status='completed'}"
    
    Client->>API: "GET /api/scans/{id}/report"
    API->>Reports: "Read report file"
    Reports-->>API: "report content"
    API-->>Client: "200 OK + report"

Sources: RELEASE_SUMMARY.md:15-19, docs/V3_COMPLETE_GUIDE.md:113-119


Error Handling

Standard Error Response Format

All error responses follow a consistent JSON structure:

{
  "error": {
    "code": "INVALID_TARGET",
    "message": "Target URL must start with ws:// or wss://",
    "details": {
      "provided": "http://example.com",
      "expected_format": "ws://example.com or wss://example.com"
    }
  }
}

HTTP Status Codes

| Status Code | Meaning | Common Causes | |-------------|---------|---------------| | 200 OK | Successful request | - | | 201 Created | Resource created successfully | Scan initiated | | 204 No Content | Successful deletion | Scan deleted | | 400 Bad Request | Invalid request data | Malformed JSON, invalid URL | | 401 Unauthorized | Authentication failed | Missing/invalid credentials | | 404 Not Found | Resource not found | Invalid scan ID | | 409 Conflict | Resource state conflict | Deleting running scan | | 422 Unprocessable Entity | Validation failed | Invalid option combination | | 429 Too Many Requests | Rate limit exceeded | Too many concurrent scans | | 500 Internal Server Error | Server error | Database corruption, scanner crash | | 503 Service Unavailable | Service temporarily unavailable | Database locked, maintenance mode |

Sources: RELEASE_SUMMARY.md:12-13


API Client Examples

Python Using requests Library

import requests
import time

# Configuration
BASE_URL = "http://localhost:5000"
API_KEY = "your-api-key-here"
HEADERS = {
    "X-API-Key": API_KEY,
    "Content-Type": "application/json"
}

# Initiate scan
response = requests.post(
    f"{BASE_URL}/api/scans",
    headers=HEADERS,
    json={
        "target": "ws://target.com/socket",
        "options": {
            "smart_payloads": True,
            "playwright": False,
            "rate_limit": 10
        }
    }
)
scan_id = response.json()["scan_id"]
print(f"Scan initiated: {scan_id}")

# Poll for completion
while True:
    status_response = requests.get(
        f"{BASE_URL}/api/scans/{scan_id}/status",
        headers=HEADERS
    )
    status_data = status_response.json()
    
    if status_data["status"] == "completed":
        print(f"Scan completed: {status_data['vulnerabilities_found']} vulnerabilities")
        break
    elif status_data["status"] == "failed":
        print("Scan failed")
        break
    
    print(f"Progress: {status_data['progress']['percent_complete']}%")
    time.sleep(5)

# Download report
report_response = requests.get(
    f"{BASE_URL}/api/scans/{scan_id}/report",
    headers=HEADERS,
    params={"format": "json"}
)
report = report_response.json()
print(f"Critical vulnerabilities: {report['statistics']['severity']['critical']}")

Sources: README.md:252-263


curl Command Line Examples

Initiate a scan:

curl -X POST http://localhost:5000/api/scans \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "target": "ws://target.com/socket",
    "options": {
      "smart_payloads": true,
      "rate_limit": 10
    }
  }'

Check scan status:

curl -X GET http://localhost:5000/api/scans/{scan_id}/status \
  -H "X-API-Key: your-api-key"

List all scans:

curl -X GET "http://localhost:5000/api/scans?limit=10&status=completed" \
  -H "X-API-Key: your-api-key"

Download JSON report:

curl -X GET "http://localhost:5000/api/scans/{scan_id}/report?format=json" \
  -H "X-API-Key: your-api-key" \
  -o report.json

Download HTML report:

curl -X GET "http://localhost:5000/api/scans/{scan_id}/report?format=html" \
  -H "X-API-Key: your-api-key" \
  -o report.html

Get system statistics:

curl -X GET http://localhost:5000/api/stats \
  -H "X-API-Key: your-api-key"

CI/CD Integration Patterns

GitHub Actions Workflow

name: WebSocket Security Scan

on:
  schedule:
    - cron: '0 2 * * *'  # Daily at 2 AM
  workflow_dispatch:

jobs:
  wshawk_scan:
    runs-on: ubuntu-latest
    steps:
      - name: Start WSHawk Dashboard
        run: |
          docker run -d \
            --name wshawk \
            -p 5000:5000 \
            -e WSHAWK_API_KEY=${{ secrets.WSHAWK_API_KEY }} \
            rothackers/wshawk wshawk --web --host 0.0.0.0
          
          # Wait for service to be ready
          sleep 10
      
      - name: Trigger Scan
        id: scan
        run: |
          RESPONSE=$(curl -s -X POST http://localhost:5000/api/scans \
            -H "X-API-Key: ${{ secrets.WSHAWK_API_KEY }}" \
            -H "Content-Type: application/json" \
            -d '{
              "target": "wss://api.example.com/socket",
              "options": {"smart_payloads": true}
            }')
          
          SCAN_ID=$(echo $RESPONSE | jq -r '.scan_id')
          echo "scan_id=$SCAN_ID" >> $GITHUB_OUTPUT
      
      - name: Wait for Completion
        run: |
          while true; do
            STATUS=$(curl -s http://localhost:5000/api/scans/${{ steps.scan.outputs.scan_id }}/status \
              -H "X-API-Key: ${{ secrets.WSHAWK_API_KEY }}" \
              | jq -r '.status')
            
            if [ "$STATUS" = "completed" ]; then
              break
            elif [ "$STATUS" = "failed" ]; then
              echo "Scan failed"
              exit 1
            fi
            
            sleep 30
          done
      
      - name: Download SARIF Report
        run: |
          curl -X GET "http://localhost:5000/api/scans/${{ steps.scan.outputs.scan_id }}/report?format=sarif" \
            -H "X-API-Key: ${{ secrets.WSHAWK_API_KEY }}" \
            -o wshawk.sarif
      
      - name: Upload to GitHub Security
        uses: github/codeql-action/upload-sarif@v2
        with:
          sarif_file: wshawk.sarif

Sources: docs/V3_COMPLETE_GUIDE.md:362-376, README.md:194


Rate Limiting and Throttling

The API implements per-client rate limiting to prevent resource exhaustion:

| Endpoint | Rate Limit | Window | |----------|-----------|--------| | POST /api/scans | 10 requests | per hour | | GET /api/scans | 100 requests | per minute | | GET /api/scans/{id} | 100 requests | per minute | | GET /api/scans/{id}/status | 120 requests | per minute | | GET /api/stats | 60 requests | per minute |

When rate limited, the API returns:

HTTP/1.1 429 Too Many Requests
Retry-After: 3600

{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Too many scan requests",
    "retry_after_seconds": 3600
  }
}

Sources: RELEASE_SUMMARY.md:12


Database Schema Reference

The API interacts with the SQLite database stored at ~/.wshawk/scans.db. Understanding the schema is helpful for advanced use cases.

Key Tables

erDiagram
    scans ||--o{ vulnerabilities : contains
    scans ||--o{ traffic_logs : records
    scans {
        string scan_id PK
        string target
        string status
        datetime created_at
        datetime completed_at
        integer duration_seconds
        json options
        json statistics
    }
    
    vulnerabilities {
        string id PK
        string scan_id FK
        string type
        string severity
        float cvss_score
        string cvss_vector
        string confidence
        string payload
        json evidence
        text remediation
    }
    
    traffic_logs {
        integer id PK
        string scan_id FK
        datetime timestamp
        string direction
        text message
        integer size_bytes
    }

Sources: RELEASE_SUMMARY.md:17, docs/V3_COMPLETE_GUIDE.md:293-297


Advanced Usage Patterns

Concurrent Scan Management

The API supports running multiple scans concurrently. Clients can initiate multiple scans and track them independently:

import asyncio
import aiohttp

async def scan_target(session, target):
    async with session.post(
        f"{BASE_URL}/api/scans",
        json={"target": target, "options": {"rate_limit": 5}}
    ) as response:
        scan_data = await response.json()
        return scan_data["scan_id"]

async def main():
    targets = [
        "ws://app1.example.com/socket",
        "ws://app2.example.com/socket",
        "ws://app3.example.com/socket"
    ]
    
    async with aiohttp.ClientSession(headers={"X-API-Key": API_KEY}) as session:
        # Initiate all scans
        scan_ids = await asyncio.gather(*[
            scan_target(session, target) for target in targets
        ])
        
        print(f"Initiated {len(scan_ids)} scans")
        
        # Monitor all scans
        # ... (polling logic)

asyncio.run(main())

Filtering and Analytics

Use the /api/scans endpoint with query parameters for analytics:

# Get all critical findings from last week
curl -X GET "http://localhost:5000/api/scans?status=completed&sort=created_at&order=desc" \
  -H "X-API-Key: your-api-key" \
  | jq '[.scans[] | select(.severity.critical > 0)] | length'

Sources: docs/V3_COMPLETE_GUIDE.md:317-330


Security Considerations

API Key Management

  • Never commit API keys to version control
  • Store API keys in secure vaults (HashiCorp Vault, AWS Secrets Manager)
  • Rotate API keys regularly
  • Use different keys for different environments (dev, staging, prod)

Network Security

When deploying in production:

  1. Use TLS: Place the Flask app behind nginx/Apache with HTTPS
  2. Firewall rules: Restrict API access to known IP ranges
  3. VPN/Bastion: Require VPN connection for API access
  4. Authentication: Always enable password or API key authentication

Example nginx configuration:

server {
    listen 443 ssl;
    server_name wshawk.example.com;
    
    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;
    
    location / {
        proxy_pass http://localhost:5000;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Sources: README.md:121-127, docs/V3_COMPLETE_GUIDE.md:299-302


Sources: README.md:106-136, RELEASE_SUMMARY.md:15-19, RELEASE_3.0.0.md:15-19, docs/V3_COMPLETE_GUIDE.md:49-62, docs/V3_COMPLETE_GUIDE.md:289-331, .github/workflows/ghcr-publish.yml:1-50