🔍 The Modern SPA Testing Paradox
For more than two decades, the standard operating procedure for web application security assessments has centered around the interception proxy. Tools like Burp Suite, OWASP ZAP, and Caido position themselves between the browser and the target origin, capturing every HTTP request and response traversing the network socket.
In traditional server-rendered architectures (PHP, ASP.NET, Rails), this model was nearly exhaustive: every routing decision, session transition, and authorization challenge occurred strictly across the wire.
However, the rapid migration to modern Single-Page Applications (SPAs) and hybrid frameworks—powered by React, Next.js, Remix, Vue 3, Pinia, and Angular—has introduced a fundamental architectural blindspot into this workflow.
Modern SPAs do not merely display data received from an API; they maintain dynamic, complex, in-memory state machines that execute entirely within the client’s browser runtime:
Traditional SSR Paradigm:
[Browser UI] <================= (HTTP Wire / Proxy Visible) =================> [Backend Controller / DB]
Modern SPA Architecture:
[Browser UI] <---> [In-Memory State / Route Chunks / Sinks] <===(HTTP)===> [API Gateway / Microservices]
▲
└── [THE APPSEC BLINDSPOT: Invisible to Network Proxies]
The Three Blindspots of Network Interception
- Unlinked Route Chunk Manifests: When modern web bundlers (Webpack, Turbopack, Vite) build a production application with code-splitting, internal and administrative route definitions (e.g.,
/admin/tenant-provisioning,/internal/feature-flags) are compiled into separate JavaScript chunks. If a standard user has no navigational button pointing to these routes, they never emit an HTTP request during standard crawling. A network proxy sees zero bytes of traffic for these endpoints, yet their route signatures and parameter contracts sit in plain view within client-side memory. - Ephemeral In-Memory State & Secrets: Modern frontends heavily utilize reactive state stores (Redux, Pinia, Zustand, Vuex) and global window namespaces. Temporary bearer tokens, unmasked user identifiers, internal API gateway keys, and multi-tenant metadata frequently persist inside client memory closures without being explicitly stored in
localStorageordocument.cookie. - Cross-Origin Message Sinks (
postMessage): Single-page applications embedded in iframes or communicating with third-party authentication providers rely onwindow.postMessage. If an event listener processes inbound payloads without strictevent.originvalidation, it creates severe DOM-based Cross-Site Scripting (DOM XSS) and state poisoning vectors (CWE-345) that never touch the network layer.
Conversely, once an ethical security researcher or internal AppSec engineer identifies an undocumented endpoint or sensitive parameter, the opposite failure mode emerges: scope drift and compliance liability. Testing beyond authorized domain boundaries, hitting cloud metadata endpoints (169.254.169.254), or attaching unredacted Personally Identifiable Information (PII) to a bug bounty report exposes researchers to legal jeopardy and program disqualification.
To close this gap from discovery through verification, we developed and open-sourced a coordinated two-stage AppSec pipeline: StateHunter (in-browser client reconnaissance) and AuditGuard (terminal boundary enforcement and Safe Harbor certification).
🎯 Phase 1: In-Browser SPA State Reconnaissance with StateHunter
StateHunter is a high-performance Chrome DevTools extension architected under Manifest V3. Rather than injecting bulky, intrusive third-party scripts into the host page context, StateHunter runs within an isolated DevTools inspection bridge to passively introspect, parse, and categorize client-side JavaScript execution in real time.
flowchart LR
subgraph Browser ["Chrome Browser Context (Target App)"]
DOM["DOM & Window Object"]
Chunks["JS Chunks & Router Manifests"]
MsgHandler["window.addEventListener('message')"]
Stores["Reactive State (Redux / Pinia / Storage)"]
end
subgraph StateHunter ["StateHunter (Manifest V3 DevTools Panel)"]
Inspector["Runtime Introspector"]
ChunkParser["De-Obfuscation Engine"]
SinkMonitor["postMessage Sink Auditor"]
SecretRegex["High-Entropy Token Filter"]
YAMLGen["Scope YAML Exporter"]
end
DOM --> Inspector
Chunks --> ChunkParser
MsgHandler --> SinkMonitor
Stores --> SecretRegex
ChunkParser --> YAMLGen
SinkMonitor --> YAMLGen
SecretRegex --> YAMLGen
YAMLGen -->|scope.yaml| AuditGuardEngine["AuditGuard Terminal Engine"]
1. De-Obfuscating Unlinked Routes from Production Chunks
In modern frameworks like Next.js (App Router or Pages Router), route manifests are embedded across minified JavaScript chunks. StateHunter traverses loaded script tags and AST manifests to extract declared path strings, regular expression parameters, and dynamic slug masks:
| |
When activated on a target application, StateHunter instantly maps endpoints that do not appear in any visible navigation menu, such as internal administrative routes (/api/v2/internal/tenants/:id/diagnostics) or unlinked debug consoles.
2. Auditing Runtime postMessage Handlers
Cross-document messaging vulnerabilities arise when applications register message event listeners without verifying the origin of the calling frame. StateHunter hooks window.addEventListener('message') via an isolated instrumentation shim, recording incoming message signatures and analyzing the handler’s execution graph:
[!WARNING] If a handler uses wildcard target origins (
"*") or executeseval()orelement.innerHTML = event.datawithout strict schema sanitization, StateHunter flags the sink as a High-Risk DOM Poisoning Sink and captures the stack trace.
3. Deep Memory State & High-Entropy Key Extraction
Beyond routing, StateHunter recursively inspects accessible global window namespaces, sessionStorage keys, and reactive state stores. It applies Shannon entropy analysis alongside targeted regular expressions to flag:
- Leaked JSON Web Tokens (
eyJ...) - Cloud provider access keys (
AKIA...,AIza...) - Private tenant IDs and internal UUIDv4 identifiers
4. Direct Scope Export (scope.yaml)
Rather than forcing the tester to copy-paste findings into arbitrary notes, StateHunter compiles all discovered hosts, categorized endpoints, and sensitive parameters into an AuditGuard-compatible YAML manifest:
| |
🛡️ Phase 2: Mathematical Boundary Enforcement & Verification with AuditGuard
Finding an unlinked route or an undocumented parameter is only the first step. Conducting automated verification against that route without strict guardrails introduces substantial risks:
- Exceeding the authorized program scope (testing third-party SSO or cloud provider endpoints).
- Triggering unintended Denial-of-Service (DoS) via unthrottled concurrent requests.
- Exposing unauthorized sensitive data in public reports without cryptographic proof of compliance.
AuditGuard is a pure-Python, zero-dependency command-line framework built to solve these boundary problems through mathematical rigor.
flowchart TD
subgraph Ingest [1. Ingestion & Scope Validation]
SHYaml[StateHunter scope.yaml] --> CLI[AuditGuard CLI]
CLI --> ScopeEngine[Deterministic Scope Engine]
ScopeEngine -->|CIDR / Wildcard Filter| ScopeDecision{Target In-Scope?}
ScopeDecision -->|NO: Prohibited| AbortReq[🚨 Immediate Request Drop & Alert]
end
subgraph Execution [2. Safe Testing & Diagnostics]
ScopeDecision -->|YES: Validated| Bucket[Token-Bucket Rate Limiter]
Bucket --> Prober[Dual-Role IDOR Prober]
Bucket --> NucleiRunner[Pure-Python Nuclei Engine]
Prober --> EvidenceLog[(Audit Ledger: audit_log.jsonl)]
NucleiRunner --> EvidenceLog
end
subgraph Certification [3. Scoring & Legal Protection]
EvidenceLog --> CVSS[FIRST.org CVSS v3.1 Calculator]
EvidenceLog --> SafeHarbor[Disclose.io Proof-of-Adherence Engine]
SafeHarbor --> SOWReport[Cryptographic SOW Certificate]
CVSS --> PlatformExport[1-Click Triage Exporters: H1 / Bugcrowd / Jira]
end
1. Deterministic Scope Enforcement
AuditGuard does not rely on simple substring matching. It parses program scopes into mathematical boundary trees using standard CIDR blocks, wildcard domain hierarchies, and strict URL path exclusions:
| |
If an HTTP request targets a host outside the authorized boundary tree or attempts to touch the link-local metadata address (169.254.169.254), AuditGuard drops the socket connection instantly and appends an anomaly event to the tamper-evident audit ledger.
2. Dual-Role Authorization Matrix & IDOR / BOLA Probing
Broken Object Level Authorization (OWASP API1 / CWE-639) is among the most prevalent flaws in modern microservices. When StateHunter identifies dynamic route parameters (e.g. /api/v1/patients/{patientId}/records), AuditGuard automates safe, dual-role access verification.
By supplying credentials for two distinct researcher-controlled accounts (user_a and user_b):
- AuditGuard fetches a valid resource belonging to
user_a. - It re-executes the exact request using
user_b’s session token. - It performs status code, response length, and AST content differential analysis.
sequenceDiagram
autonumber
participant AG as AuditGuard Engine
participant API as Target API Gateway
participant DB as Backend Datastore
Note over AG,API: Dual-Role Comparative Matrix
AG->>API: GET /api/v1/patients/UUID-A/records (Token User A)
API->>DB: Query Tenant A
DB-->>API: 200 OK (User A Medical Data)
API-->>AG: 200 OK [Length: 1,420 bytes]
AG->>API: GET /api/v1/patients/UUID-A/records (Token User B)
alt Flaw Present (IDOR / BOLA)
API->>DB: Query Tenant A (Missing Tenancy Filter!)
DB-->>API: 200 OK (User A Data Leaked to User B)
API-->>AG: 200 OK [Length: 1,420 bytes]
Note over AG: 🚨 IDOR Confirmed! Flagged as High Severity
else Properly Hardened (Role-Enforced)
API->>DB: Check Context Ownership
API-->>AG: 403 Forbidden / 404 Not Found
Note over AG: ✅ Authorization Boundary Secure
end
3. Declarative Nuclei-Compatible YAML Diagnostics (Pure Python)
Rather than forcing users to bundle external Go runtime binaries, AuditGuard includes a native Python interpreter for declarative ProjectDiscovery Nuclei-compatible HTTP YAML templates.
It executes read-only diagnostic templates—verifying security headers, checking for CORS misconfigurations, and validating cache control policies—without external dependencies:
| |
4. Deterministic FIRST.org CVSS v3.1 Scoring
Hallucinated or inflated vulnerability severities damage credibility during triage. AuditGuard implements the official FIRST.org CVSS v3.1 metric specification entirely in pure Python:
| |
Every exported report includes the verifiable vector equation, calculated exploitability subscore, and impact metrics.
5. Cryptographic Safe Harbor Defense Certificates
Under the Disclose.io Core Vulnerability Disclosure Standard, ethical researchers who adhere strictly to program rules are afforded legal Safe Harbor protections against CFAA (18 U.S.C. § 1030) and DMCA (§ 1201) anti-circumvention claims.
AuditGuard writes every outbound request, timestamp, scope evaluation, and rate-limit throttle event into an append-only JSONL ledger (audit/audit_log.jsonl). When testing concludes, AuditGuard evaluates the log and produces a signed Proof-of-Adherence Certificate:
================================================================================
DISCLOSE.IO SAFE HARBOR ADHERENCE CERTIFICATE
================================================================================
Program Identifier: acuity-health-portal
Assessment Start: 2026-09-12 09:15:00 UTC
Assessment Conclusion: 2026-09-12 11:30:00 UTC
Total Requests Emitted: 184 HTTP transactions
Average Request Rate: 1.2 req/sec (Ceiling: 2.0 req/sec)
Out-of-Scope Attempts: 0 (100% boundary compliance)
Prohibited Route Drops: 0
Ledger SHA-256 Digest: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
LEGAL ATTESTATION:
All actions recorded in the referenced ledger strictly complied with the authorized
boundaries, testing constraints, and non-destructive protocols of the target VDP.
================================================================================
🚀 The Complete Recon-to-Report Workflow
Here is how the end-to-end testing pipeline operates in practice:
| |
📢 Call for Peer Review & Community RFC
Open-source security engineering relies on adversarial peer review and community scrutiny. We are publishing StateHunter and AuditGuard not just as finished tools, but as an open research framework for client-to-boundary application security.
We are actively seeking feedback from the application security community, penetration testers, bug bounty hunters, and academic researchers:
Key Areas for Community Feedback:
- AST & Route Chunk De-obfuscation:
- What edge cases exist in production Vite, Turbopack, or Webpack bundle chunking that evade regex-based route detection?
- How can we improve tree-shaking heuristic analysis in StateHunter without executing untrusted client code?
- Deterministic Scope Boundaries:
- Are there complex cloud networking topologies (e.g. multi-region AWS API Gateway custom domain mappings, Cloudflare Workers routes) where pure CIDR/regex boundary checks yield false negatives?
- Safe Harbor Cryptographic Ledger:
- How can the audit ledger be enhanced to support verifiable zero-knowledge or HMAC-based multi-party timestamping, further bolstering legal non-repudiation during disputed triage?
- Dual-Role Prober Differential Heuristics:
- What statistical or AST heuristics best eliminate false positives when testing dynamic responses with variable timestamps or CSRF tokens?
How to Participate:
- 🛡️ StateHunter Repository: github.com/SixFiveMil/statehunter
- Test the extension in Chrome DevTools against real-world SPAs.
- Submit issues, route de-obfuscation test fixtures, and PRs.
- ⚖️ AuditGuard Repository: github.com/SixFiveMil/auditguard
- Run the test suite:
pytest tests/(89 tests, 100% passing). - Review
docs/ARCHITECTURE.mdanddocs/SAFE_HARBOR_LEGAL.md.
- Run the test suite:
- 💬 Discussion & Feedback:
- Open an issue or join the discussion on GitHub.
- Share feedback on Reddit (
r/netsec,r/bugbounty) and Hacker News.
Both tools are released under the open-source MIT License, transmit zero telemetry, and execute 100% locally.