Published June 29, 2026
There is a difference between a pen test that finds a vulnerability and one that proves what it actually enables. That difference matters when you're deciding what to fix first.
Most web application security tests will tell you that a vulnerability exists. The best ones prove what it actually enables — and those are not the same deliverable. A finding rated medium severity can represent a full account takeover. The gap between finding and proving is where remediation priorities get set correctly, or don't.
What "medium severity XSS" can actually mean
During a recent engagement, our team identified an unsanitized input field in a client's SaaS platform. A standard scanner would have flagged it. A quick tester would have popped an alert box in the browser, logged "stored XSS — medium severity," and moved on.
Our team kept going.
What followed required working through four successive layers of protection: bypassing a hardened Content Security Policy using a flaw in its script-src allowlist, working around a client-side rendering restriction that prevented standard payloads from executing, finding a side channel for data exfiltration when the obvious path was blocked, and rewriting the final payload entirely to survive a character blacklist on the endpoint being used.
At the end of that chain, the finding was no longer "XSS — medium." It was this: a low-privilege user could take over any admin account on the platform. No phishing. No escalation through another vulnerability. Just a single unsanitized form field and a practitioner willing to keep pulling threads.
That changes how you prioritize.
The insight worth paying attention to
Each bypass in that chain was made possible by a control that was almost tight enough. The Content Security Policy was well-configured — except that one of the trusted domains hosted a JSONP endpoint that effectively bypassed the entire script-src restriction. The exfiltration was blocked — except that font-src was set to allow any HTTPS source, which opened a side channel. The character blacklist was real — except that JavaScript offers enough flexibility to express the same logic without the blocked characters.
"Almost tight enough" is the most dangerous configuration in security. It passes automated checks. It fails manual ones.
This is the practical argument for practitioner-led testing. Not because practitioners are better at running tools, but because they ask the next question. An automated scanner confirms that a payload renders. A practitioner asks what that payload can be made to do — and keeps asking until the answer has business meaning.
What staying current makes possible
Going this deep requires practitioners who stay close to how the research community thinks about these problems. The engagement above drew on knowledge of a documented behavior in the WHATWG HTML specification, an understanding of how specific JSONP endpoints interact with CSP allowlists, and familiarity with how modern browsers handle font loading. None of that comes from a playbook. It comes from practitioners who read, test, and build on each other's work continuously.
That's the less visible part of a pen test: the methodology that exists before the engagement starts. The findings in a report reflect what a team knew how to look for.
What to ask your pen test partner
When you're evaluating a pen test provider, the question isn't whether they find vulnerabilities. Automated tools find vulnerabilities. The question is whether they prove impact.
Ask to see a sample report. A few things worth looking for:
Does severity reflect actual business impact, or just a CVSS score?
A CVSS formula rates a stored XSS finding the same whether it leads to a pop-up or a full account takeover. A practitioner-authored report tells you which one you're dealing with.
Does the report show what an attacker could do, or just what's broken?
There's a meaningful difference between "we found an injection point" and "we demonstrated that a low-privilege user could take over admin accounts without additional access." The second gives you something to act on with appropriate urgency.
Does the methodology show up in the findings?
A report that reflects genuine manual work looks different from one generated primarily by a scanner. The findings are more specific, the context is richer, and the remediation guidance is actionable rather than generic.
What this means for remediation
The client in this engagement left with a report that told them exactly what to fix, in what order, and why it mattered. The finding that a low-privilege user could take over admin accounts is a different remediation priority than a theoretical XSS with no demonstrated impact. That prioritization is what a security team needs to make good decisions.
The technical fix, by the way, was straightforward: render the label field using textContent instead of innerHTML. A single change at the render site would have eliminated every bypass in the chain. When the finding is understood at the right depth, the path forward is usually clearer than it looks from a scored list of vulnerabilities.
Trava's web application penetration tests are PTES and OWASP-aligned, practitioner-led, and reviewed by a vCISO before delivery. Every finding is framed in terms of what it actually enables, not just what the scanner caught. If you want to see what that looks like in practice, let's schedule a demo.
Technical Appendix: How the Chain Actually Worked
For practitioners who want the specifics, here is a condensed walkthrough of the four-bypass chain from the engagement above.
Injection point. The vulnerability lived in a label field inside an Activation Form — a surface accessible to both low-privilege users and admins. The field accepted an unsanitized payload, which was stored and rendered each time an admin opened a preview of the form. That shared-access surface is what made it dangerous: no escalation required, just a form field and an admin who previews their settings.
Bypass 1: The Content Security Policy. The application had a well-configured CSP that blocked inline script execution. However, its script-src directive used an allowlist that included https://www.google.com. That domain hosts a JSONP endpoint which reflects any callback parameter directly into its response. Loading that endpoint as an external script effectively let us execute arbitrary JavaScript under a trusted domain, bypassing the CSP entirely.
Bypass 2: The innerHTML restriction. The application used a client-side rendering framework that inserts dynamic content via innerHTML. Per the HTML specification, browsers intentionally do not execute <script> tags injected this way. The workaround was an <iframe srcdoc="..."> element. An iframe creates a separate browsing context, parsed as a full HTML document, where external script loads execute normally. Wrapping the JSONP script tag inside an iframe srcdoc attribute bypassed the innerHTML restriction cleanly.
Bypass 3: Exfiltration blocked by connect-src. With code executing in the admin's session, the next step was exfiltrating the Firebase authentication token (stored in IndexedDB). A standard fetch() request to an external server was blocked by the CSP's connect-src directive, which only allowed outbound connections to a fixed allowlist. The img-src directive was similarly restricted. However, font-src was set to https: — permitting any HTTPS source. By injecting a CSS @font-face rule pointing to an attacker-controlled server and forcing the browser to load it via a hidden div, we triggered an outbound request carrying the exfiltrated token. The browser's font lazy-loading behavior required that extra step: creating the style alone is not enough to trigger the network request.
Bypass 4: The JSONP character blacklist. Pasting the final payload into the JSONP endpoint's jsonp parameter revealed that the endpoint filtered out whitespace, <, >, ', and ". Rather than treating this as a dead end, we rewrote the payload to avoid all five character classes. JavaScript's template literals (backticks), function expressions, and flexible syntax meant the same logic was expressible without any of the blocked characters. The payload was then URL-encoded and delivered through the iframe srcdoc chain.
The full end-to-end payload turned an unsanitized label field into a stored XSS that fired in an admin session, extracted a Firebase auth token from IndexedDB, and exfiltrated it to an external server — all without triggering a single CSP violation in the console.
The fix was a one-line change: rendering the label value using textContent instead of innerHTML. Every bypass in the chain depended on that single render-site decision.
For the full payload chain, download our complete technical write-up below.

