Desktop Application Architecture

Desktop Application Architecture

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

Purpose and Scope

This document provides a complete technical reference for the WSHawk Desktop application architecture. It covers the dual-process sidecar model, inter-process communication protocols, component responsibilities, and the build pipeline that produces platform-specific installers.

For the WebSocket scanner engine internals, see Core Architecture. For individual web pentesting tools, see Desktop Web Pentest Toolkit. For the interceptor and advanced features, see Desktop Advanced Features.

Sources: docs/DESKTOP_V3_GUIDE.md:1-100


Dual-Process Sidecar Model

WSHawk Desktop uses a sidecar architecture where an Electron frontend (JavaScript/HTML/CSS) communicates with a Python backend server over HTTP and Socket.IO. The two processes run independently and communicate via localhost networking.

graph TB
    subgraph Electron["Electron App (JavaScript)"]
        Main["index.js<br/>(Main Process)"]
        Preload["preload.js<br/>(Secure Bridge)"]
        Renderer["renderer.js<br/>(UI Logic — 4,641 lines)"]
        Main -->|IPC| Preload
        Preload -->|contextBridge| Renderer
    end

    Main -->|"Spawns as child process"| Bridge
    Renderer -->|"REST API (fetch)"| Bridge
    Renderer <-->|"Socket.IO<br/>(real-time events)"| Bridge

    subgraph Backend["Python Backend (Port 8080)"]
        Bridge["gui_bridge.py<br/>(FastAPI + Socket.IO)"]
        Bridge --> Scanner["scanner_v2.py<br/>(WebSocket Scanner)"]
        Bridge --> Mutator["payload_mutator.py<br/>(Mutation Engine)"]
        Bridge --> Discovery["ws_discovery.py<br/>(Endpoint Finder)"]
        Bridge --> DB["db_manager.py<br/>(SQLite)"]
        Bridge --> WebPentest["web_pentest/<br/>(22 HTTP Engines)"]
        Bridge --> SmartPayloads["smart_payloads/<br/>(SPE Engine)"]
    end

    style Electron fill:#1a1a2e,stroke:#6c63ff,color:#fff
    style Backend fill:#0f3460,stroke:#e94560,color:#fff

Why a Sidecar?

The sidecar model was chosen over alternatives for several reasons:

| Alternative | Problem | |---|---| | Python GUI (tkinter/Qt) | Poor UI/UX, no modern web technologies, limited animation support | | Electron-only (no Python) | Would require rewriting the scanner, mutation engine, and all 22 web pentest tools in JavaScript | | Embedded Python (python-shell) | Fragile, no real-time streaming, blocks the main thread | | Sidecar (chosen) | Full Python ecosystem for security tools + full web stack for UI. Each process can crash independently |

The tradeoff is binary size (the compiled Python sidecar is ~80-120MB depending on platform) and startup time (~2-3 seconds for sidecar initialization).

Sources: docs/DESKTOP_V3_GUIDE.md:49-100


Component Breakdown

Electron Layer

| Component | File | Lines | Responsibility | |---|---|---|---| | Main Process | desktop/index.js | ~230 | Window management, Python sidecar lifecycle, IPC handlers for file dialogs and window controls | | Preload Script | desktop/preload.js | ~40 | Secure IPC bridge via contextBridge. Whitelisted channels only — no raw ipcRenderer exposure | | Renderer | desktop/src/renderer.js | 4,641 | All UI logic. Socket.IO client, tool panel interactions, real-time result rendering, theme system | | HTML Shell | desktop/src/index.html | ~2,200 | Single-page layout containing all views, modals, tool panels, and the sidebar navigation | | Styles | desktop/src/style.css | ~3,800 | Dark-mode design system with CSS custom properties, glassmorphism effects, and responsive layout |

Python Backend Layer

| Component | File | Lines | Responsibility | |---|---|---|---| | GUI Bridge | wshawk/gui_bridge.py | 1,342 | FastAPI server + Socket.IO for real-time communication. Exposes 50+ REST endpoints and 20+ Socket.IO events | | Scanner Engine | wshawk/scanner_v2.py | ~900 | WebSocket vulnerability scanner with 7 test suites | | Payload Mutator | wshawk/payload_mutator.py | ~400 | WAF-evasion mutation engine with 8 bypass strategies | | WS Discovery | wshawk/ws_discovery.py | ~300 | WebSocket endpoint discovery via HTTP probing and JS analysis | | DB Manager | wshawk/db_manager.py | ~250 | SQLite wrapper for scan history and session persistence | | Web Pentest | wshawk/web_pentest/ | 22 files | HTTP security tools (crawler, fuzzer, port scanner, etc.) | | Smart Payloads | wshawk/smart_payloads/ | 3 files | Genetic algorithm payload evolution system |

Sources: docs/DESKTOP_V3_GUIDE.md:82-108


Communication Protocol

The frontend and backend communicate through two channels: REST API for request-response operations and Socket.IO for real-time streaming.

sequenceDiagram
    participant UI as Renderer (Electron)
    participant Bridge as gui_bridge.py
    participant Engine as Scanner / Tool Engine

    UI->>Bridge: REST POST /scan/start
    Bridge->>Engine: asyncio.create_task()
    Bridge-->>UI: {status: "started"}

    loop Real-time streaming
        Engine-->>Bridge: Result found
        Bridge-->>UI: sio.emit('vulnerability_found')
    end

    Engine-->>Bridge: Scan complete
    Bridge-->>UI: sio.emit('scan_complete')

Channel Responsibilities

| Channel | Protocol | Use Cases | |---|---|---| | REST API | HTTP fetch() to http://127.0.0.1:8080 | Start scan, send request, get payloads, export report, stop scan | | Socket.IO | Persistent WebSocket connection | Scan progress, fuzzer results, port discoveries, intercepted frames, vulnerability findings | | IPC | Electron ipcMain / ipcRenderer | File dialogs (save/open project), window controls (minimize/maximize/close) |

REST API Surface

The gui_bridge.py exposes approximately 50 REST endpoints organized by feature area:

| Category | Example Endpoints | HTTP Methods | |---|---|---| | Scanner | /scan/start, /scan/stop, /scan/status | POST, GET | | Interceptor | /interceptor/action, /interceptor/toggle | POST | | Request Forge | /forge/send | POST | | Web Pentest | /web/crawl, /web/fuzz, /web/portscan, /web/dirscan | POST | | Payloads | /payloads/{category}, /payloads/custom | GET, POST | | Reports | /web/report, /report/export | POST | | Config | /system/info, /auth/build | GET, POST |

Socket.IO Events

| Event | Direction | Data | |---|---|---| | vulnerability_found | Server → Client | {type, severity, payload, evidence, cvss} | | scan_progress | Server → Client | {phase, current, total, percentage} | | intercepted_frame | Server → Client | {id, direction, data, timestamp} | | fuzzer_result | Server → Client | {payload, status_code, response_size, time} | | port_found | Server → Client | {port, service, banner, state} | | scan_complete | Server → Client | {total_vulns, duration, report_path} | | web_tool_result | Server → Client | {tool, finding_type, data} |

Sources: docs/DESKTOP_V3_GUIDE.md:104-140, wshawk/gui_bridge.py:1-100


Sidecar Lifecycle

Startup Sequence

sequenceDiagram
    participant App as app.whenReady()
    participant Main as index.js
    participant Sidecar as wshawk-bridge
    participant Window as BrowserWindow
    participant Renderer as renderer.js

    App->>Main: checkPythonDependency()
    Main->>Main: execSync('python3 --version')

    App->>Main: startPythonSidecar()

    alt Packaged (production)
        Main->>Main: Resolve resources/bin/wshawk-bridge
        Main->>Main: chmod +x (macOS/Linux)
        Main->>Sidecar: spawn(binary)
    else Development
        Main->>Sidecar: spawn('python3', ['-m', 'wshawk.gui_bridge'])
    end

    Sidecar->>Sidecar: FastAPI + Socket.IO init
    Sidecar->>Sidecar: uvicorn.run(port=8080)

    App->>Main: createWindow()
    Main->>Window: loadFile('src/index.html')
    Window->>Renderer: DOMContentLoaded
    Renderer->>Sidecar: io.connect('http://127.0.0.1:8080')
    Sidecar-->>Renderer: Socket.IO 'connect' event
    Renderer->>Renderer: connPill = 'Connected'

Platform-Specific Binary Resolution

In production (packaged) mode, index.js resolves the sidecar binary path based on the platform:

| Platform | Binary Path | Notes | |---|---|---| | Linux | process.resourcesPath/bin/wshawk-bridge | Permissions fixed via fs.chmodSync(0o755) | | macOS | process.resourcesPath/bin/wshawk-bridge | DMG packaging strips execute permissions; chmod required | | Windows | process.resourcesPath/bin/wshawk-bridge.exe | No permission fix needed |

In development mode, the renderer connects to python3 -m wshawk.gui_bridge running from the project root.

Shutdown Sequence

When the user closes the application:

  1. window-all-closed event fires in index.js
  2. pythonProcess.kill() terminates the sidecar
  3. On macOS, the app stays in the dock (process.platform !== 'darwin' check)
  4. app.quit() terminates the Electron process

Sources: desktop/index.js:31-120, docs/DESKTOP_V3_GUIDE.md:49-100


Security Model

Context Isolation

The Electron app uses strict context isolation to prevent the renderer from accessing Node.js APIs:

// index.js
webPreferences: {
    preload: path.join(__dirname, 'preload.js'),
    nodeIntegration: false,     // No require() in renderer
    contextIsolation: true,     // Separate JS contexts
}

The preload script exposes only whitelisted IPC channels:

// preload.js
contextBridge.exposeInMainWorld('electronAPI', {
    openProject:  () => ipcRenderer.invoke('dialog:openProject'),
    saveProject:  (data) => ipcRenderer.invoke('dialog:saveProject', data),
    exportReport: (html) => ipcRenderer.invoke('dialog:exportReport', html),
    exportExploit:(data) => ipcRenderer.invoke('dialog:exportExploit', data),
    minimize:     () => ipcRenderer.send('window:minimize'),
    maximize:     () => ipcRenderer.send('window:maximize'),
    close:        () => ipcRenderer.send('window:close'),
});

Network Isolation

The Python sidecar binds exclusively to 127.0.0.1:8080 — it is not accessible from the network. The CORSMiddleware allows * origins only because communication is localhost-only.

Binary Integrity

The sidecar is compiled by PyInstaller into a self-contained binary. All Python dependencies are bundled inside the binary — no system Python installation is required in production mode.

Sources: desktop/index.js:16-21, desktop/preload.js:1-40


Operating Modes

The Desktop application has three progressively expanding modes:

Standard Mode

Available tools:

  • WebSocket scanner dashboard
  • Request Forge (manual WebSocket frame construction)
  • Findings panel (vulnerability cards with severity badges)
  • Traffic history (chronological frame log)
  • System log (timestamped backend events)

Advanced Mode

Everything in Standard, plus:

  • Payload Blaster — High-speed WebSocket fuzzer
  • Interceptor — Full-duplex MitM proxy
  • Endpoint Map — Automated WebSocket discovery
  • Auth Builder — Multi-step authentication sequences
  • Mutation Lab — Interactive payload mutation
  • Scheduler — Recurring scan automation
  • Codec — Encoder/decoder toolkit
  • Comparer — Response diff viewer
  • Notes — In-app markdown notes

Web Pentest Mode

Everything in Advanced, plus all 22 HTTP security tools organized into six phases. See Desktop Web Pentest Toolkit for complete tool documentation.

Mode Selection: The user toggles between modes via the sidebar. Mode state is persisted in localStorage.

Sources: docs/DESKTOP_V3_GUIDE.md:118-200


Build Pipeline

PyInstaller Compilation

The Python backend is compiled into a standalone binary using PyInstaller:

pyinstaller wshawk-bridge.spec

The .spec file defines:

| Configuration | Value | |---|---| | Entry point | wshawk/gui_bridge.py | | Hidden imports | 80+ modules (uvicorn internals, starlette, socketio, all web_pentest engines) | | Excluded modules | tkinter, matplotlib, numpy, pandas, scipy, pytest, playwright | | Output | Single binary (dist/wshawk-bridge or dist/wshawk-bridge.exe) | | Binary size | ~80-120MB depending on platform |

Electron Packaging

The desktop app is packaged using electron-builder:

cd desktop && npx electron-builder --publish never

| Platform | Output Format | Size | |---|---|---| | Linux | .AppImage, .pacman, .deb | ~180-220MB | | Windows | .exe (NSIS installer) | ~160-200MB | | macOS | .dmg | ~170-210MB |

CI/CD Pipeline

The GitHub Actions workflow (.github/workflows/build.yml) automates the full build:

graph LR
    A["Checkout Code"] --> B["Setup Node 20<br/>+ Python 3.11"]
    B --> C["pip install -e .<br/>+ extra deps"]
    C --> D["pyinstaller<br/>wshawk-bridge.spec"]
    D --> E["Copy binary to<br/>desktop/bin/"]
    E --> F["npm install<br/>(Electron deps)"]
    F --> G["electron-builder<br/>--publish never"]
    G --> H["Upload artifacts"]
    H --> I["Create GitHub<br/>Release"]

    style A fill:#16213e,stroke:#0f3460,color:#fff
    style I fill:#1a1a2e,stroke:#e94560,color:#fff

The build matrix runs on ubuntu-latest, windows-latest, and macos-latest simultaneously with fail-fast: false so all platforms build independently.

extraResources Configuration

The compiled sidecar binary is bundled into the Electron app via package.json:

"extraResources": [
    {
        "from": "bin",
        "to": "bin",
        "filter": ["**/*"]
    }
]

This copies desktop/bin/wshawk-bridge into resources/bin/wshawk-bridge inside the packaged application. At runtime, index.js resolves this via process.resourcesPath.

Sources: wshawk-bridge.spec:1-100, desktop/package.json:75-86, .github/workflows/build.yml:1-87


State Persistence

Session Save/Load

Full application state can be saved to and loaded from .wshawk project files:

{
    "version": "3.0.1",
    "timestamp": "2026-02-24T03:00:00Z",
    "target": "ws://target.com",
    "findings": [...],
    "traffic": [...],
    "notes": "...",
    "auth_sequences": [...],
    "scheduled_scans": [...]
}

Save/load uses Electron's native file dialogs via IPC:

  1. Renderer calls window.electronAPI.saveProject(state)
  2. Main process shows native save dialog via dialog.showSaveDialog()
  3. Data is written as JSON to the chosen path

Scan History (SQLite)

The db_manager.py maintains a SQLite database at ~/.wshawk/scans.db with:

  • scans table: Target URL, timestamps, vulnerability counts, status
  • vulnerabilities table: Individual findings with CVSS scores and payloads

The desktop app provides a History panel with vulnerability regression diffing between scans of the same target.

localStorage Persistence

UI state is persisted in the browser's localStorage:

  • Active operating mode (Standard / Advanced / Web Pentest)
  • Sidebar collapse state
  • Theme preferences
  • Active scheduled scans (auto-resume on restart)

Sources: desktop/index.js:98-170, docs/DESKTOP_V3_GUIDE.md:435-465


Related Documentation

Sources: docs/DESKTOP_V3_GUIDE.md:1-960