Over the course of eight days (March 17–24, 2026), we conducted a comprehensive multi-round security audit of every component in GhostPort OS — from the Express API server and frontend SPA to nftables firewall profiles, shell scripts, systemd services, and the EC2 fleet infrastructure. This log documents 242 bugs found, 113 patched across 13 audit rounds, a brand new color theming engine with RGB breathing mode, Family Shield parental controls with DNS + IP-level blocking, the Phase 2 fleet integration system with Stripe subscriptions, and a full-stack hardening pass across both the Pi and EC2.
/api/tools/backup exposes WiFi passphrase to authenticated users
177
MED
RaceCondition
withArsenal mutex swallows errors, stale data in subsequent calls
178
MED
Validation
WireGuard PostUp/PostDown strip is case-insensitive but wg-quick is case-sensitive
179
MED
Security
/api/tools/restore accepts hostapd config with minimal validation
180
MED
Auth
Lockout by req.ip — NAT shared IP locks out all LAN users
181
MED
RaceCondition
Concurrent mode switch requests read same previousMode
182
MED
Security
/api/ticket sends unsanitized description to Discord webhook
183
MED
Firewall
No DoQ/DoH blocking to unlisted servers beyond 7 IPs
184–185
MED
DNS
gp-dns-upstream sed not idempotent; wildcard catches invalid modes silently
186–187
MED
Logic
gp-dns-switch references unbound (inactive); no root check
188
MED
RaceCondition
gp-mode has no mutex/lockfile for concurrent invocations
189
MED
Security
nftables backup world-readable in /tmp
190
MED
DNS
gp-dns-watchdog restarts wg-quick@wg0 conflicting with gp-mode manual setup
191
MED
Security
gp-blog-generate interpolates results into HTML without escaping
192–198
MED
Logic/UI
Toggle desync on rapid clicks, stacked arm timers, missing res.ok checks, no domain/WG validation, hardcoded Pi-hole status & DNS resolver label
199–202
MED
Security
Backup import no schema validation, logo leaks referrer (missing noreferrer), WiFi password in stdout
203–225
LOW
Various
SSL cert never reloaded, run() errors ignored, double-sanitized password, login attempt count leak, unlimited sessions/schedules, fsIpBlock fire-and-forget, IPv6 input unfiltered, rollback PID writable, QUIC self-traffic, passcode show/reset mismatch, blog round detection, password complexity, sed without sudo, innerHTML race, DNS alert placement, WiFi charset, window.close blocked, polling overlap, no ARIA/a11y, 401 redirect bug, hardcoded version
Round 10 — March 23, 2026
3 parallel audit agents • 82 bugs found across server, frontend, and system scripts • 21 patches applied
Critical (11)
#
File
Bug
Fix
92–100
CRIT arsenal.js
9 XSS vulnerabilities — data.error, data.status, and speed test values injected unsanitized into innerHTML across DNS Leak, Ping, IP Leak, Speed Test, System Update, Blocked Domains, and Security Scan results
Reboot UX — No reconnection logic after reboot — shows “REBOOTING...” forever (noted)
Cumulative Security Posture
By Severity (All 10 Rounds)
Severity
Count
Examples
CRIT
22
Command injection via WireGuard config, DNS upstream timing, mutex deadlock, undeclared variable crash, XSS (setup flow, arsenal errors, speed test), flushTally crash-loop, hardcoded fleet token, doublehop ordering bug
HIGH
42
Arsenal race conditions, XSS across 5+ HTML files, IPv6 firewall bypass, mode string injection, DoS via speedtest, DHCP hostname XSS, SSH exposed on WAN, IP block startup failure, Google Fonts privacy leak, single-quote escape gap
MED
59
Error info leaks, trust proxy spoofing, session TTL, factory reset incomplete, webhook injection, DNS test rate limit, Pi-hole v6 API format, toggle race conditions, DNS watchdog mode-unaware, null pointer risks
Shared firewall rules + SSH WAN restriction + LAN port lockdown
gp-heartbeat
—
Fleet heartbeat agent (new)
gp-new
—
Fleet registration script (updated)
public/install.html
—
Customer activation page (new)
gp-fs-ipblock
—
Family Shield IP blocker (new, integrated into server)
fleet-api.py
5
EC2 fleet API — body limits, Stripe hardening, rate limiter, bridge pruning
010_ghostport-hardened
1
Sudoers — wildcard commands replaced with specific paths
New Feature — Family Shield (March 22, 2026)
🛡 Parental Controls with DNS + IP Blocking
Family Shield is a dual-layer parental control system that combines Pi-hole DNS filtering with nftables IP-range firewall rules. DNS blocking alone can’t stop apps like TikTok that use CDN fallbacks and hardcoded IPs — so we added firewall rules that block entire ASN-owned IP ranges at the packet level.
5 Categories — Adult Content, Gambling, Meta Apps (FB/IG/WhatsApp), TikTok, X (formerly Twitter)
DNS Blocking — Pi-hole v6 group-based filtering with 22,459+ domain blocklists per category
IP-Range Blocking — nftables sets blocking ByteDance (13 CIDR ranges), Meta (16 ranges), and X/Twitter (6 ranges) at the packet level
Per-Device Shielding — Shield specific devices without blocking yourself. DISCOVER button scans for connected clients
Master Toggle — One switch enables/disables all parental controls. Category toggles for granular control
Persistent Config — Settings saved to /etc/ghostport/family-shield.json, IP blocks restored on server restart
Family Shield — Bugs Fixed During Development
#
Severity
Component
Bug
Fix
85
HIGH
Server
Pi-hole v6 _suggestions API returns array format, not object — device discovery and shielding broken
Rewrote parsing for {clients: [{addresses, names}]} format in 3 endpoints
86
HIGH
Server
Pi-hole v6 lists API requires ?type=block as query parameter, not in JSON body
Added query parameter to all POST/PUT list endpoint URLs
87
MED
Frontend
Toggle race condition — load() polling overwrites toggle state during API calls
Added fs.busy flag, load() skips when busy
88
MED
Config
family-shield.json owned by root, server runs as ghostport-admin
Fixed ownership with chown ghostport-admin:ghostport-admin
89
MED
Pi-hole
Session exhaustion — default max 16 sessions burned by debug restarts
Bumped to 64 via pihole-FTL --config webserver.api.max_sessions 64
90
HIGH
Pi-hole
Gravity database corruption from repeated FTL restarts — tables missing
Rebuilt gravity.db from scratch, re-added blocklists and groups
91
MED
Blocking
TikTok DNS blocking ineffective — app uses Akamai CDN fallbacks
Added nftables IP-range blocking for ByteDance ASN ranges
Phase 2 — Fleet Integration (March 21, 2026)
Heartbeat Agent (gp-heartbeat)
Systemd timer-driven agent that checks in with the EC2 fleet server every 5 minutes. Each heartbeat sends the device’s firmware version, uptime, and current mode. The fleet server responds with subscription status and any pending commands (e.g., mode change, config update).
5-minute cadence — systemd timer triggers gp-heartbeat script on a reliable interval
Payload — firmware version, system uptime, current firewall mode, device serial
Customer-facing dark-themed activation page served at /install. Users enter their license key to activate the device. The page auto-provisions the router without requiring prior authentication.
Dark-themed UI — matches the GhostPort Command Deck aesthetic
License key input — validates format client-side, submits to fleet activation endpoint
Auto-provisioning — on success, generates credentials and completes device setup
/api/fleet/activate Endpoint
No-auth device setup endpoint. Accepts a license key, registers the device with the fleet server, and returns freshly generated credentials for the customer.
No authentication required — designed for first-boot activation before any passcode exists
Auto-generates — dashboard passcode, WiFi AP password, and Pi-hole admin password
Fleet registration — sends device serial + license key to EC2 fleet API, receives subscription tier
gp-new --auto
Non-interactive factory provisioning mode for the gp-new registration script. Enables headless device setup during manufacturing or bulk deployment without manual prompts.
Non-interactive — all prompts bypassed with sane defaults or pre-configured values
Factory use case — flash, boot, auto-register, ship
Stripe Subscription Integration
Three-tier subscription model with webhook-driven license management. Stripe events flow to the EC2 fleet server, which updates each device’s subscription status. The Pi’s heartbeat agent picks up changes on its next check-in.
Phantom Guardian — $10/mo — All modes including DoubleHop VPN, kill switch, MAC randomization
Unholy Covenant — $15/mo — Full feature set: ZHop, scheduled mode switching, priority support
EC2 Fleet API Pipeline
End-to-end subscription lifecycle: Stripe webhook fires on payment events → EC2 fleet server updates the device record → Pi heartbeat picks up the new subscription status on next 5-minute check-in.
Stripe → Fleet — Webhook receiver on EC2 validates Stripe signatures, maps customer to device
Fleet → Pi — Heartbeat response includes updated subscription tier, expiry, and feature flags
Grace period — failed payments trigger a grace window before feature downgrade
New Feature — Theme Color Engine
🎨 Infinite Color Picker + RGB Breathing Mode
The GhostPort Command Deck now supports full UI color customization. Every element — accents, backgrounds, borders, text, glows — adapts to your chosen color in real time.
8 Preset Swatches — Green (default), Purple, Blue, Red, White, Cyan, Amber, and Gunmetal. One-click to apply.
Custom Color Picker — Pick literally any color. The entire UI derives backgrounds, borders, text, and glow values from your chosen hue using a real-time HSL engine.
RGB Breathing Mode — Smooth, continuous hue rotation powered by requestAnimationFrame. A sine-wave brightness pulse creates a living, breathing effect. Full spectrum rotation every ~6.5 seconds at 30fps.
Achromatic Support — Grey, white, and black colors are handled correctly. The engine passes saturation through to prevent unwanted red tinting on neutral colors.
Persistence — Your choice saves to localStorage and applies across both the login screen and dashboard. Survives page reloads and browser restarts.
Login Screen Sync — The authentication page matches your chosen theme, including RGB breathing mode. First impression matches the rest of the UI.
Migration — Users with older named theme values in localStorage are automatically migrated to the new hex-based system.
Round 7 — March 21, 2026
Script: fix-bugs-round7.py • 18 patches across 4 categories
#
Severity
File
Bug
Fix
66–80
MED
ghostport-server.js
15 endpoints exposed raw e.message in error responses. Child process errors could leak filesystem paths, command names, or internal system details to authenticated clients
All error responses now return generic messages ("Operation failed", "Mode switch failed", etc.). Full details logged server-side with console.error()
81
HIGH
ghostport-server.js
DHCP lease hostname from dnsmasq not HTML-escaped in /api/arsenal/clients response. A device with <script> in its hostname could inject into the client list UI
Wrapped hostname in escapeHtml()
82
MED
ghostport-server.js
POST /api/arsenal/dnstest had no rate limiting. Could be spammed to flood external DNS lookups and ifconfig.me requests, causing resource exhaustion
Added 30-second cooldown (same pattern as speedtest's 2-minute throttle)
83
HIGH
ghostport-server.js
Restore endpoint wrote directly to /etc/ghostport/arsenal.json without the withArsenal() mutex. Concurrent arsenal operations (toggle, schedule add) could corrupt the config file
Wrapped in await withArsenal() to serialize writes
Bug Fix — March 21, 2026 (Server Crash-Loop)
Post-Round 7 hotfix • Server crash-loop recovery
#
Severity
File
Bug
Fix
84
CRIT
ghostport-server.js
Duplicate try{} block in flushTally() from a prior round patch caused a SyntaxError on startup — server entered a crash-loop with 162 restarts before detection
Collapsed duplicate try blocks into a single try/catch/finally structure
Round 6 — March 21, 2026
Script: fix-bugs-round6.py • 9 patches across server & firewall
#
Severity
File
Bug
Fix
57
HIGH
ghostport-server.js
Schedule deletion endpoint had copy-pasted finally block from Lynis scanner — resetting unrelated lynisScanRunning flag and returning wrong error message ("Security scan failed")
Removed wrong finally block, fixed error message to "Failed to delete schedule"
58
HIGH
ghostport-server.js
6 locations used predictable temp file names in /tmp/ (e.g. /tmp/gp-hostapd.conf) — symlink attack vector on world-writable directory
Added gpTmpFile() helper using crypto.randomBytes(8) for unique names. Applied to hostapd, WireGuard, restore, MAC randomization, and schedule writes
59
MED
ghostport-server.js
Restore endpoint exposed e.message in error response — could leak filesystem paths or command output
Replaced with generic "Config restore failed" message, logs details server-side
60
MED
ghostport-server.js
DNS leak check used curl -4 (IPv4 only) — wouldn't detect IPv6 DNS leaks
Switched to dual-stack curl -s to catch both v4 and v6 leak types
61
LOW
ghostport-server.js
Gateway and speedtest ping commands passed IPs without shell quoting
Added single-quote wrapping around interpolated IP variables for defense-in-depth
62
MED
ghostport-server.js
Restore endpoint used systemctl restart wg-quick@wg0 but gp-mode manages wg0 directly via ip/wg commands — service conflicts with existing interface
Replaced with gp-mode reapply for consistency with how WireGuard is actually managed
63
MED
ghostport-server.js
Kill switch nftables rules only allowed port 4200 (HTTP dashboard) but not 4201 (HTTPS) — would lose dashboard access over HTTPS during kill switch activation
Added port 4201 to kill switch accept rules
64
LOW
public/index.html
Three CSS classes (.mode-desc, .log-msg.info, .arsenal-desc) used hardcoded green hex values that didn't respond to theme color switching
Replaced #6a9a6a and #5a8a5a with var(--text-dim)
65
LOW
common.nft
Ports 80 and 443 had accept rules open to all interfaces — redundant since policy is accept but misleading
Already cleaned up (found pre-fixed during verification)
Round 5b — March 19, 2026
Script: fix-bugs-round5b.py • 9 bugs across 5 files • Deep audit of previously unaudited files
#
Severity
File
Bug
Fix
49
CRIT
public/pwa.html
XSS in setup summary — user-supplied name and email interpolated into innerHTML unescaped
Added esc() helper, escaped name, serial, and email fields
50
CRIT
public/pwa.html
XSS in log rendering — l.msg inserted into innerHTML without escaping
Wrapped in esc()
51
HIGH
public/da.html
XSS in QA error handler — e.message rendered as raw HTML
Added esc() helper, escaped error message
52
HIGH
public/da.html
XSS in log entries — l.message and l.timestamp unescaped
Escaped both fields
53
HIGH
public/da.html
XSS in device info — all API-sourced device fields rendered as raw HTML
Escaped labels and values
54
HIGH
public/da.html
XSS in QA checklist — c.name and c.detail unescaped in innerHTML
Escaped both fields
55
HIGH
isp.nft
Missing IPv6 drop in forward chain — clients can bypass routing via IPv6 tunnels
Added meta nfproto ipv6 counter drop to forward chain
56
HIGH
zerotrust.nft
Missing IPv6 drop in forward chain — DNS lock bypass via IPv6
Added meta nfproto ipv6 counter drop to forward chain
56b
MED
gp-dns-upstream
Silent failures — no error reporting if config file missing or sed replacement fails
Added file existence check and post-sed verification with error output
Round 5 — March 18, 2026
Script: fix-bugs-round5.py • 7 bugs, 10 patches
#
Severity
File
Bug
Fix
42
CRIT
ghostport-server.js
ispIpCacheTime used but never declared — DNS leak monitor crashes with ReferenceError on first run
Added let ispIpCacheTime = 0 declaration
43
HIGH
ghostport-server.js
No rate limit on /api/tools/speedtest — DoS vector via repeated 60-120 second system commands
Added 2-minute cooldown between speed tests
44
HIGH
ghostport-server.js
Lynis scan flag lynisScanRunning not reset on exception — one crash permanently locks scanning
Strip dangerous directives (PostUp, PostDown, PreUp, PreDown, SaveConfig) on upload
4
HIGH
ghostport-server.js
Hostapd SSID/password not validated — newline injection into config file
Added regex validation for SSID (1-32 chars, no newlines) and password (8-63 printable ASCII)
5
HIGH
ghostport-server.js
Schedule deletion command injection via unsanitized ID in sed command
Added regex validation: schedule IDs must match /^[a-z0-9]+$/
6
HIGH
ghostport-server.js
Missing input validation on mode switch — arbitrary string passed to shell
Whitelist validation: mode must be one of isp, zerotrust, doublehop, zhop
7
MED
ghostport-server.js
Hostapd config read via shell cat — unnecessary shell invocation
Switched to fs.readFileSync with sudo fallback
8
CRIT
gp-mode / nftables
DNS leak monitor nftables rules used invalid mixed tcp/udp syntax
Split into separate accept+drop rules matching kill switch pattern
9
CRIT
gp-mode / gp-dns-upstream
DNS upstream switch called before mode file update — reads OLD mode, applies wrong DNS
Moved gp-dns-upstream call to after modefile write in every mode case
10
MED
ghostport-server.js
Pi-hole API called via deprecated CLI syntax
Updated to Pi-hole v6 REST API
11
HIGH
ghostport-server.js
Arsenal config race condition — concurrent read-modify-write on kill switch, MAC, QUIC toggles
Introduced withArsenal(fn) mutex pattern for atomic operations
12
MED
ghostport-server.js
Arsenal.json not created on first boot — all toggles fail with ENOENT
Added default file creation with correct ownership on startup
Compliance Posture — March 24, 2026
Full-stack compliance audit covering PCI DSS, NIST Cybersecurity Framework, CIS Benchmarks, OWASP Top 10, and SOC 2 readiness. Assessed across both the GhostPort Pi router and EC2 fleet infrastructure.
80%
OWASP TOP 10
65%
NIST CSF
SAQ-A
PCI DSS
40%
SOC 2
PCI DSS — SAQ-A Compliant
Stripe Checkout handles all card data — GhostPort never touches PAN numbers. Webhook signatures verified with fail-closed + replay protection. Stripe keys stored mode 600, rotated successfully.