Skip to content
RiftX

After the verdict

Take our word for none of it.

You get the whole run back, not an opinion with a screenshot attached: every step, every request, every capture, and a document you can put your own name on. If you think the verdict is wrong, everything you need to show it is in there.

Retest reportVT-2026-0847

Reflected XSS: attribute context bypass

Not Fixed · 0.96
Exports as PDF · HTML · DOCX9 sections
Screenshots11 frames
The search ran and matched nothing. The probe token is echoed back into the search field, which is the reflection the rest of the retest works on.
The search field shows only riftx, though riftx"probe was sent. The raw double quote ended the attribute early, and everything after it stopped being part of the value.
An svg onload payload sitting inert in the search field as visible text. The encoder is tag-agnostic, so this variant fails the same way the reported one did.
The double-encoded payload, back to visible text in the search field. The extra encoding is normalised away before the encoder runs, so it lands exactly where the plain payload did.
The support console dimmed behind a browser alert reading "127.0.0.1:8787 says" and "127.0.0.1", with an OK button. Behind it the search field is empty and focused: the payload closed the value attribute early, so the field renders blank and the injected autofocus took the cursor before firing the handler.
+6
Screen recording14 steps
Network capture112 pairs
GET
/search?q=riftx%22probe
200
19,088
GET
/search?q=%22%20autofocus%20onfocus%3Dalert(document.domain)%20x%3D%22
200
19,402
Key evidence
content-type: text/html; charset=utf-8
<input name="q" value="" autofocus onfocus=alert(document.domain) x="" class="search__input" autocomplete="off">
Tool output2 runs
python script.pystep 5
ENCODED : < > &
RAW : " ' ` / \ = space ( ) ; : \n
SCRIPT_COMPLETED: 18 probes, 3 characters encoded

The retest scores what it saw, not what you reported.

Severity, CVSS score and vector are the retest's own reading of what it observed. Nothing is carried across from the report you filed, so a rating you disagree with is a rating you can argue with.

Not Fixed

Medium

Confidence

0.96cleared the 0.90 floor

Why it's not fixed

The fix is real, and it is partial.

The reported payload no longer executes, so the fix is real but partial. Output encoding maps angle brackets and ampersands to entities on the reflection path and leaves the double quote raw. The reflection sits inside an attribute value, so a raw quote closes the value and admits an event handler. The handler was reflected uncoded and fired on load in a real browser, three times out of three. The finding reproduces by a different route than the one reported.

Impact

CVSS 6.1CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
Impact detail

Exploitability metrics

Attack VectorNetworkAV:N
The search endpoint is reachable over the internet and the payload rides in the query string, so delivery is a link an attacker sends.
Attack ComplexityLowAC:L
The encoder leaves the same character raw on every request, so there is no timing window to hit and no target-specific condition to discover.
Privileges RequiredNonePR:N
The reflection came back on a request that carried no session and no credentials, so nothing about reaching it depends on holding an account.
User InteractionRequiredUI:R
Nothing fires until a victim follows that link, which is what holds this at Medium rather than High.

Impact metrics

ScopeChangedS:C
The handler runs in the victim's browser under the application's origin, a different security authority from the server that reflected it.
ConfidentialityLowC:L
Script in the victim's session reads what that origin exposes to it, which is some of what the application holds rather than all of it.
IntegrityLowI:L
The same execution can alter what the victim's page shows and acts on, without reaching the records the application stores behind it.
AvailabilityNoneA:N
The endpoint answered every attempt with a normal response and kept serving, so nothing in the captured run touches availability.

The same run, read five different ways

A verdict you cannot check is a verdict you have to trust.

What it actually did, one row per motion, in the product's own words.

Execution path7 of 14 steps
1
Navigated

https://app.target.com/search

2
Typed into the "q" field

q = riftx-probe-8f21

3
Clicked "Search"

https://app.target.com/search?q=riftx-probe-8f21

4
NavigatedKey evidence

https://app.target.com/search?q=%3Cscript%3Ealert%281%29%3C%2Fscript%3E

Reported payload no longer fires

5
Navigated

https://app.target.com/search?q=riftx%22probe

6
Navigated

https://app.target.com/search?q=%22%20autofocus%20onfocus%3Dalert(document.domain)%20x%3D%22

7
NavigatedKey evidence

https://app.target.com/search?q=%22%20autofocus%20onfocus%3Dalert(document.domain)%20x%3D%22

Bypass reproduced the finding

urlhttps://app.target.com/search
step 1 of 7

The same run, asked questions

Everything above this is something you read. The Examiner answers out of these same captures, names the step or the request each answer came from, and says so when they do not cover what you asked.

The ExaminerVT-2026-0847
You asked

Why did this come back Not Fixed when the reported payload no longer fires?

The Examiner answered

The finding came back Not Fixed because while the reported payload was blocked, the underlying vulnerability still reproduces through a different attack vector that the partial fix leaves open.

Here's what happened: the target's fix encodes angle brackets and ampersands to entities, which blocks the original reported payload (<script>alert(1)</script>). But that encoding only address one reflection context. The actual HTML reflects the input inside an attribute value, and the fix left double quotes unencoded.

An attacker can break out of that attribute using a raw quote character, then inject an event handler like " autofocus onfocus=alert(document.domain) x=". That handler fires on page load in the real browser. The finding's evidence captured the JavaScript firing three times out of three in the retest, proving the XSS still executes, just by a route the reporter's original steps didn't follow.

The distinction matters because it shows the developer stopped at blocking one technique without fixing the underlying flaw. Reflected XSS prevention requires encoding all special characters for the context they land in, or using a framework that does it. Encoding only some characters (angle brackets, ampersands) while leaving quotes raw leaves the attribute-injection vector open. That is why the retest landed on Not Fixed rather than Fixed, even though the specific payload from the original report no longer works.

You asked

What did you send that got through, and where did it land in the response?

The Examiner answered

You sent the payload " autofocus onfocus=alert(document.domain) x=" , and it landed raw and unencoded inside the HTML attribute value of the search input field.

The response body shows where it broke through:

<input name="q" value="" autofocus onfocus=alert(document.domain) x="" class="search__input" autocomplete="off">

Here's what happened: the target encodes angle brackets and ampersands (which blocks <script> payloads), but the double-quote character passed through unencoded. That quote sits inside an attribute context — the value="" attribute of the input. Your payload starts with a double quote, which closes that attribute, then injects autofocus onfocus=alert(document.domain) as raw attributes on the same tag, and x="" closes the syntax. The onfocus event handler fires on page load, executing the JavaScript three times in the retest.

The fix addressed the tag-injection vector but missed the attribute-escape vector entirely, because the developer encoded for one reflection context without accounting for where that reflection actually lands in the HTML structure.

You asked

Which techniques did you try that the fix held against?

The Examiner answered

The fix held against two techniques, but the third one broke through.

Alternative tag names : You tried <svg onload=alert(1)>, and the response encoded the angle brackets to entities, rendering it as &lt;svg onload=alert(1)&gt; inside the attribute value. The fix blocked this vector entirely.

Encoding/parser differential : You tried double URL-encoded %253Cscript%253Ealert(1)%253C%252Fscript%253E, but the response still encoded the angle brackets to entities: &lt;script&gt;alert(1)&lt;/script&gt;. The fix held against the normalization bypass as well.

Context escape — this one succeeded. The payload " autofocus onfocus=alert(document.domain) x=" passed through the response unencoded in the attribute value, because the fix did not encode double quotes. That raw quote character breaks out of the attribute context and admits the event handler, which fired in the real browser.

So the fix's encoding strategy effectively blocked tag-injection and encoding-bypass techniques, but it was incomplete, because it left the attribute-escape vector open by not encoding quotes.

You asked

Did the fix introduce a regression anywhere else in the application?

The Examiner answered

Based on the evidence captured in this retest, there is no indication that the fix introduced regressions elsewhere in the application.

The verdict reasoning makes no mention of functional side effects or broken functionality, focusing entirely on the security implications of the encoding strategy. The observe stage captured full 200 responses with complete page content, showing the search interface remained functional , and the search input continued to accept and reflect user input normally.

The fix itself is selective, encoding only angle brackets and ampersands to entities, while leaving other characters (like double quotes) unencoded. A surgical character encoding strategy like this is unlikely to break legitimate search functionality or page rendering, because HTML search inputs typically do not rely on angle brackets or ampersands in normal user queries.

That said, the retest only verified the security angle and the live target's responses, not an exhaustive functional test suite. If your application has edge cases where legitimate users search for terms containing < or > or & characters, those searches would now reflect with entities instead of raw characters, which might affect display or matching logic if the front-end or results handler does not expect encoded text. The evidence here does not show whether such edge cases were tested or whether any functional impact assessment was performed beyond the security retest's scope.

The document you send, and what it leaves out

The report is written when the retest settles, and it exports on its own, because the person who reads the report is not the person who reads the evidence. When the fix holds, it gets shorter.

Contents

VT-2026-0847-retest-report.pdf

Vulnerability Retest Report

Reflected Cross-Site Scripting (XSS) Retest Report

https://app.target.com/search

Retest verdict
NOT FIXED
Still exploitable
Report IDVT-2026-0847
VulnerabilityReflected Cross-Site Scripting (XSS)
SeverityMedium
Retested02 Jul 2026

Executive summary

A previously reported reflected XSS on the search endpoint of app.target.com was retested after a fix was applied on 2026-06-30. The fix blocks the originally reported payload but does not close the underlying flaw.

Testing found that the search parameter still reflects attacker-controlled input into an HTML attribute without encoding the double quote character. An attacker can break out of the attribute and inject an event handler that executes automatically when the page loads, requiring only that a victim follow a crafted link. The finding is confirmed as still exploitable, by a different payload than originally reported.

1 Finding description

The search endpoint at https://app.target.com/search accepts a query value through the q URL parameter and reflects it into the value attribute of the search input field on the results page.

The application encodes angle brackets and ampersands on this reflection path, which stops payloads built from raw tag markup, but it does not encode the double quote character. Because the reflection sits inside a double-quoted attribute, an unencoded quote in the input lets an attacker close the attribute early and append new attributes, including an event handler, to the same input element.

2 Steps to reproduce

The fix can be bypassed by breaking out of the attribute value with a raw quote and adding an autofocus event handler, reproduced as follows.

  1. Navigate to https://app.target.com/search?q=%22%20autofocus%20onfocus%3Dalert(document.domain)%20x%3D%22
  2. Inspect the returned page: the q value is reflected unencoded into the search input's attribute value, so the leading double quote closes the existing attribute and the trailing text is parsed as new attributes on the same input element (Figure 2).
  3. Because the injected attributes include autofocus, the input receives focus as soon as the page renders, and the accompanying onfocus handler fires without any further user interaction, executing alert(document.domain) in the browser (Figure 3).

3 Evidence

Request

GET https://app.target.com/search?q=riftx%22probe

Response

HTTP/2 200
<input name="q" value="riftx"probe" class="search__input">
<!-- &lt; &gt; &amp; encoded · " passes raw -->
Figure 1: The encoder's coverage, one character short

Request

GET https://app.target.com/search?q=%22%20autofocus%20onfocus%3Dalert(document.domain)%20x%3D%22

Response

HTTP/2 200
content-type: text/html; charset=utf-8
<input name="q" value="" autofocus onfocus=alert(document.domain) x="" class="search__input" autocomplete="off">
Figure 2: The payload reflected uncoded inside the attribute value

Request

GET https://app.target.com/search?q=%22%20autofocus%20onfocus%3Dalert(document.domain)%20x%3D%22

Response

HTTP/2 200
[browser] focus event dispatched on input[name=q]
[browser] dialog opened, text = app.target.com
[browser] document.domain read by handler, origin intact
Figure 3: The handler executing in a real browser
The reported payload sits in the search field as visible text reading script alert(1) close script. It is inert: the page rendered it as characters rather than as markup.
Figure 4: Reported payload no longer fires
The support console dimmed behind a browser alert reading "127.0.0.1:8787 says" and "127.0.0.1", with an OK button. Behind it the search field is empty and focused: the payload closed the value attribute early, so the field renders blank and the injected autofocus took the cursor before firing the handler.
Figure 5: Bypass reproduced the finding

4 Affected URL & endpoint

URLhttps://app.target.com/search
MethodGET
Parameterq
Injection surfaceURL parameter
Reflection contextAttribute value of the search input
AuthenticationNone required; the reflection returns on an unauthenticated request

5 Retest results

The deployed fix HTML-encodes angle brackets and ampersands on the reflection path, which defeats the originally reported script-tag and svg-onload payloads (Figure 1).

The double quote character is left unencoded on the same path. Since the reflection lands inside a quoted attribute rather than in HTML body text, the quote alone is enough to escape the attribute and inject a new one. An autofocus attribute paired with an onfocus handler was used to trigger execution automatically on page load, and the handler fired in a real browser on repeated attempts.

#TechniqueResult
1Alternative tag namesBlocked
2Encoding / parser differentialBlocked
3Escape the current output / sink contextBypassed

Bypass assessment

The reported payload no longer executes, so the fix is real but partial. Output encoding maps angle brackets and ampersands to entities on the reflection path and leaves the double quote raw. The reflection sits inside an attribute value, so a raw quote closes the value and admits an event handler. The handler was reflected uncoded and fired on load in a real browser, three times out of three. The finding reproduces by a different route than the one reported.

6 Exploitability & impact

Impact

Severity Medium · CVSS 6.1 · CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N

Exploitation requires only that a victim follow a link containing the crafted q parameter; no authentication or account access is needed, and the endpoint returns the reflection on every request, so there is no timing dependency for an attacker to work around.

The injected handler executes in the application's origin, giving it access to read and manipulate what that origin exposes to the victim's session, though it does not reach stored data behind the endpoint. The endpoint continued serving normally throughout testing, so availability is unaffected. This combination of network-only delivery, no privileges required, but a dependency on user interaction keeps the severity at Medium.

7 Recommendation

Apply context-aware output encoding to the q parameter at the point it is written into the attribute value, specifically HTML attribute encoding that escapes the double quote character in addition to angle brackets and ampersands; a templating engine or output-encoding library with built-in attribute-context escaping is more reliable here than the current character-specific substitution.

Re-test the fix against the attribute-breakout payload used here, including autofocus/onfocus and other event-handler variants, before considering the endpoint remediated.

As defense in depth, consider a Content Security Policy that restricts inline event handlers, which would limit the impact of any future encoding gap on this endpoint.

Evidence provenance

Evidence bundle SHA-256ce6513717ebb441fbf702a64df24e0178729fcd37cc4d96634c8a0c48a7002a4
Forensic sealVerified
Capture window2026-07-02 09:14 UTC – 2026-07-02 09:24 UTC
Artifacts112 exchanges, 14 steps, 10 minutes
First reported 11 Jun 2026 · Retested 02 Jul 2026Confidential · for the named recipient only
Exports asPDFHTMLDOCX

The seal, and what it does not vouch for

The seal is there so you can stand behind the evidence later. Months after the run, to a client's security team you never met, you can show that nothing in the record moved.

Records are held for 365 days from the run in an encrypted store on infrastructure we operate, readable only by the account that owns them. The auditor, and why it can only ever be the cautious one, is on Trust and security.

Under seal
Tool output2 runsa30e52cc37
HTTP archive112 exchanges440dec07db
Screen recording365 KB72f6cf0f87
Screenshots11 frames884b81289f
Reproduction steps14 steps86a26befb6

Merkle root

6525719f59c943c9

Signed with HMAC-SHA256. Opening the retest checks it again.

Every open returns one of four readings

Verified
Every artifact still hashes to the digest recorded at capture, and the signature over the tree is valid.
Tampering detected
A digest no longer matches, or the signature does not validate. The record cannot be relied on.
Anomalous
Digests changed while the signature still validates. Treat the record as suspect and retest.
Hashed, not signed
Artifacts carry digests but this record was never signed, so there is nothing to verify cryptographically.

See it run

Watch it retest a finding you already know the answer to.

Hand us a finding your team already settled by hand. Read what comes back against the answer you already have, and see whether we reach it.