- by x32x01 ||
A chatbot greeting may look harmless, but if an application lets users control the greeting and then renders that value as Markdown, it can become an XSS attack surface.
A recently disclosed Shopify Help Center issue demonstrates the general pattern: an attacker-controlled
The interesting lesson is not the specific payload. It is the complete flow:
Attacker-controlled input → Markdown rendering → attacker-controlled URL → browser execution context → actions performed with the victim's session
⚠️ The dangerous exfiltration and account-manipulation payloads from the original report are intentionally not reproduced here. The focus is on understanding the vulnerability and how developers can prevent this class of attack.
When the chat interface loaded, a request contained a greeting parameter similar to:
The important observation was that the value was not simply static text.
Changing the parameter changed what appeared in the chatbot interface.
That immediately raises a useful security-testing question:
Where does this input go after the server receives it?
If attacker-controlled input is reflected into a page, the next step is to determine how that value is interpreted.
Is it rendered as:
For example, Markdown can represent:
A Markdown renderer can convert that into HTML containing an image element.
The security problem begins when an application assumes that Markdown is automatically safe.
Markdown is not a security boundary.
Depending on the renderer and its configuration, Markdown can generate HTML elements and URLs that require additional validation.
Shopify's current security guidance explicitly recommends treating external input as untrusted and validating URLs so that dangerous schemes such as
A harmless example is:
If the application renders that Markdown, the resulting interface may contain an element controlled partly by the attacker.
At this point, the vulnerability is no longer simply:
"I can change the chatbot greeting."
The security question becomes:
"Can I control the generated URL or HTML in a way that changes browser behavior?"
That distinction is critical during XSS research.
Why
One of the classic browser security problems is allowing an attacker-controlled URL to use the
A safe application normally expects links to use schemes such as:
or:
A dangerous URL scheme can instead cause the browser to interpret the URL as executable JavaScript.
For example, a security test should use a harmless proof of concept rather than code that steals data:
⚠️ Whether this executes depends on the browser, application, sanitization, link attributes, and the exact context in which the URL is opened.
This is why simply seeing a
The entire browser execution path has to be understood.
The
One interesting part of this vulnerability class is the behavior of links that open in a new browsing context.
A link may contain:
This tells the browser to open the destination in a new tab or window.
However, developers should not treat
It is primarily a browsing behavior.
Security depends on the URL scheme, sanitization, browser behavior, and the context in which the link is processed.
For links opened in a new tab, developers should also consider:
and, where appropriate:
These attributes address opener and referrer behavior. They should not be confused with input sanitization or XSS protection.
Shopify's security guidance similarly treats URLs as an active-content security boundary and recommends allowing only schemes that the application actually needs.
The next question is: "What can JavaScript do in the victim's browser?"
If the vulnerable page is running inside an authenticated session, malicious JavaScript may potentially be able to perform actions that the victim is authorized to perform.
Depending on the application's architecture, an XSS vulnerability can potentially be used to:
The exact impact must always be demonstrated within the authorized testing scope.
They are different vulnerabilities.
CSRF abuses a victim's authenticated browser to send an unwanted request.
XSS gives attacker-controlled JavaScript an execution context inside the target origin.
The difference matters because XSS can sometimes make traditional CSRF protections less effective.
For example, if an attacker can execute JavaScript on the same origin as the application, that script may be able to interact with application functionality in ways that a cross-origin attacker cannot.
Therefore, a developer should not think:
By itself, that may create a CSRF vulnerability depending on the endpoint, authentication mechanism, cookie policy, and request requirements.
If an attacker also has XSS on the same origin, however, the situation can become more serious because malicious JavaScript may be able to make authenticated requests from the victim's browser.
A simplified defensive model looks like this:
This is why security researchers often investigate what authenticated functionality is reachable after establishing script execution.
The browser may communicate with endpoints using:
For defensive testing, the important question is:
Which server-side actions are available to the current browser session?
A useful methodology is to inspect normal application traffic and identify:
It does not.
Base64 is an encoding format, not encryption and not sanitization.
For example:
can decode Base64 data in JavaScript.
If an application eventually passes decoded attacker-controlled data to an execution sink, Base64 merely hides the original content from casual inspection.
From a defensive perspective:
Encoded attacker input is still attacker input.
Developers should therefore decode and validate data according to the actual context in which it will be used.
If the greeting is supposed to be text, render it as text.
For example, prefer a text API such as:
instead of inserting untrusted content as HTML.
Shopify's security guidance specifically recommends text APIs such as
If Markdown is genuinely required, use a well-maintained Markdown parser with a strict security configuration.
Then:
A simplified defensive example is:
This is only a basic example. Real applications should define exactly which URL schemes, hosts, ports, and paths are required by the product.
The important principle is:
Validate the URL based on what the application actually needs, not based on a blacklist of dangerous strings.
Shopify's security documentation recommends this allowlist-oriented approach for URL handling.
Instead of stopping at: "I can control this parameter."
continue asking:
A chatbot still processes:
The fact that the interface is called an "AI chatbot" does not make traditional web security problems disappear.
Shopify's current security guidance explicitly says that both external input and AI output should be treated as untrusted data, and that model output should never be passed directly into dangerous execution contexts.
🎯 The core lesson is simple:
Never let convenience turn untrusted chatbot content into trusted browser code.
A greeting should be text.
A Markdown link should be a validated link.
A URL should use an allowed scheme.
And a state-changing API should enforce proper authentication and request protections independently of whatever the frontend happens to render.
Is
Not by itself. The actual impact depends on whether the value reaches an executable browser context and whether the application, browser, or security controls prevent execution.
Does
No.
A recently disclosed Shopify Help Center issue demonstrates the general pattern: an attacker-controlled
greeting value was reflected into the AI assistant interface, where Markdown image rendering could be abused to create an attacker-controlled link. The disclosed report was classified as Reflected XSS and was publicly disclosed in June 2026.The interesting lesson is not the specific payload. It is the complete flow:
Attacker-controlled input → Markdown rendering → attacker-controlled URL → browser execution context → actions performed with the victim's session
⚠️ The dangerous exfiltration and account-manipulation payloads from the original report are intentionally not reproduced here. The focus is on understanding the vulnerability and how developers can prevent this class of attack.
How the Attack Surface Started
The vulnerable functionality was associated with the AI assistant on the Shopify Help Center.When the chat interface loaded, a request contained a greeting parameter similar to:
greeting=Welcome ya dawlyThe important observation was that the value was not simply static text.
Changing the parameter changed what appeared in the chatbot interface.
That immediately raises a useful security-testing question:
Where does this input go after the server receives it?
If attacker-controlled input is reflected into a page, the next step is to determine how that value is interpreted.
Is it rendered as:
- Plain text?
- HTML?
- Markdown?
- A URL?
- JavaScript?
- A template?
- JSON that is later inserted into the DOM?
Why Markdown Matters
Markdown is normally used to make text easier to format.For example, Markdown can represent:
- Bold text
- Italic text
- Links
- Images
- Lists
[IMG alt="Example"]https://example.com/image.png[/IMG]A Markdown renderer can convert that into HTML containing an image element.
The security problem begins when an application assumes that Markdown is automatically safe.
Markdown is not a security boundary.
Depending on the renderer and its configuration, Markdown can generate HTML elements and URLs that require additional validation.
Shopify's current security guidance explicitly recommends treating external input as untrusted and validating URLs so that dangerous schemes such as
javascript: are rejected.From Controlled Text to a Controlled Link
The interesting discovery was that the attacker-controlled greeting could be turned into Markdown that generated a clickable link.A harmless example is:
[IMG alt="Click here"]https://example.com[/IMG]If the application renders that Markdown, the resulting interface may contain an element controlled partly by the attacker.
At this point, the vulnerability is no longer simply:
"I can change the chatbot greeting."
The security question becomes:
"Can I control the generated URL or HTML in a way that changes browser behavior?"
That distinction is critical during XSS research.
Why javascript: URLs Are Dangerous
One of the classic browser security problems is allowing an attacker-controlled URL to use the javascript: scheme.A safe application normally expects links to use schemes such as:
https:or:
http:A dangerous URL scheme can instead cause the browser to interpret the URL as executable JavaScript.
For example, a security test should use a harmless proof of concept rather than code that steals data:
javascript:alert(document.domain)⚠️ Whether this executes depends on the browser, application, sanitization, link attributes, and the exact context in which the URL is opened.
This is why simply seeing a
javascript: value reflected in HTML does not automatically prove exploitable XSS.The entire browser execution path has to be understood.
The target="_blank" Detail
One interesting part of this vulnerability class is the behavior of links that open in a new browsing context.A link may contain:
target="_blank"This tells the browser to open the destination in a new tab or window.
However, developers should not treat
target="_blank" as an XSS protection mechanism.It is primarily a browsing behavior.
Security depends on the URL scheme, sanitization, browser behavior, and the context in which the link is processed.
For links opened in a new tab, developers should also consider:
rel="noopener"and, where appropriate:
rel="noreferrer"These attributes address opener and referrer behavior. They should not be confused with input sanitization or XSS protection.
Shopify's security guidance similarly treats URLs as an active-content security boundary and recommends allowing only schemes that the application actually needs.
The Real Impact of XSS
Finding a JavaScript execution primitive is only part of an XSS report.The next question is: "What can JavaScript do in the victim's browser?"
If the vulnerable page is running inside an authenticated session, malicious JavaScript may potentially be able to perform actions that the victim is authorized to perform.
Depending on the application's architecture, an XSS vulnerability can potentially be used to:
- Read sensitive information accessible to the page.
- Make authenticated requests.
- Modify application data.
- Perform actions as the victim.
- Interact with internal APIs exposed to the browser.
- Access sensitive DOM content.
- Abuse application functionality available to the current user.
The exact impact must always be demonstrated within the authorized testing scope.
Why CSRF and XSS Are Different
A common mistake is to describe every browser-based attack as CSRF.They are different vulnerabilities.
CSRF abuses a victim's authenticated browser to send an unwanted request.
XSS gives attacker-controlled JavaScript an execution context inside the target origin.
The difference matters because XSS can sometimes make traditional CSRF protections less effective.
For example, if an attacker can execute JavaScript on the same origin as the application, that script may be able to interact with application functionality in ways that a cross-origin attacker cannot.
Therefore, a developer should not think:
CSRF defenses remain important, but they do not replace output encoding, sanitization, URL validation, and other XSS defenses."We have CSRF protection, so XSS is harmless."
Why a Missing CSRF Token Can Make a Chain Worse
Suppose an application exposes a state-changing endpoint that accepts a browser request but does not require a CSRF token or another strong anti-CSRF mechanism.By itself, that may create a CSRF vulnerability depending on the endpoint, authentication mechanism, cookie policy, and request requirements.
If an attacker also has XSS on the same origin, however, the situation can become more serious because malicious JavaScript may be able to make authenticated requests from the victim's browser.
A simplified defensive model looks like this:
Code:
Untrusted input
↓
Markdown renderer
↓
Unsafe URL
↓
JavaScript execution
↓
Authenticated browser requests
↓
Sensitive application actions The Importance of the Application's Internal APIs
Modern web applications frequently use APIs behind the user interface.The browser may communicate with endpoints using:
- REST APIs
- GraphQL
- Fetch requests
- XMLHttpRequest
- Form submissions
- Internal application routes
For defensive testing, the important question is:
Which server-side actions are available to the current browser session?
A useful methodology is to inspect normal application traffic and identify:
- Which requests load the page.
- Which requests retrieve user information.
- Which requests retrieve conversation data.
- Which requests modify application state.
- Which requests require CSRF protection.
- Which requests rely only on the user's authenticated session.
Why Base64 Does Not Make JavaScript Safe
Another common misconception is that encoding a JavaScript payload with Base64 makes it safer.It does not.
Base64 is an encoding format, not encryption and not sanitization.
For example:
atob()can decode Base64 data in JavaScript.
If an application eventually passes decoded attacker-controlled data to an execution sink, Base64 merely hides the original content from casual inspection.
From a defensive perspective:
Encoded attacker input is still attacker input.
Developers should therefore decode and validate data according to the actual context in which it will be used.
How Developers Should Prevent This Vulnerability
The most important defense is to decide whether the greeting actually needs Markdown.If the greeting is supposed to be text, render it as text.
For example, prefer a text API such as:
JavaScript:
element.textContent = greeting; Shopify's security guidance specifically recommends text APIs such as
textContent when the application does not need to render HTML.If Markdown is genuinely required, use a well-maintained Markdown parser with a strict security configuration.
Then:
- Sanitize generated HTML.
- Use an allowlist rather than trying to block a few known-bad strings.
- Validate every generated URL.
- Allow only required URL schemes.
- Reject
javascript:URLs. - Be careful with
data:URLs. - Treat Markdown image destinations as untrusted.
- Avoid dangerous HTML extensions unless they are explicitly required.
- Apply output encoding appropriate to the final rendering context.
A Safer URL Validation Model
If an application allows users to provide URLs, validation should happen before the URL reaches an HTML or Markdown renderer.A simplified defensive example is:
JavaScript:
function isSafeUrl(value) {
try {
const url = new URL(value, window.location.origin);
return url.protocol === 'https:' || url.protocol === 'http:';
} catch {
return false;
}
} The important principle is:
Validate the URL based on what the application actually needs, not based on a blacklist of dangerous strings.
Shopify's security documentation recommends this allowlist-oriented approach for URL handling.
What Security Researchers Can Learn From This
🔍 The most useful lesson from this bug class is the attack-chain mindset.Instead of stopping at: "I can control this parameter."
continue asking:
- Where is the value reflected?
- Is it treated as text or markup?
- Is Markdown supported?
- What HTML does the Markdown generate?
- Can the generated URL be controlled?
- Which URL schemes are accepted?
- Where does the link open?
- Does JavaScript execute in any supported browser path?
- What authenticated functionality is available from that origin?
- Are state-changing requests protected against CSRF?
The Bigger Lesson From AI Chatbots
🤖 AI chatbots are adding another layer to modern web applications, but the security fundamentals have not changed.A chatbot still processes:
- User input
- Server-side data
- Markdown
- HTML
- URLs
- Browser APIs
- Authentication state
- Internal API requests
The fact that the interface is called an "AI chatbot" does not make traditional web security problems disappear.
Shopify's current security guidance explicitly says that both external input and AI output should be treated as untrusted data, and that model output should never be passed directly into dangerous execution contexts.
🎯 The core lesson is simple:
Never let convenience turn untrusted chatbot content into trusted browser code.
A greeting should be text.
A Markdown link should be a validated link.
A URL should use an allowed scheme.
And a state-changing API should enforce proper authentication and request protections independently of whatever the frontend happens to render.
Frequently Asked Questions
-----------------Can Markdown cause XSS?
Yes. Markdown itself is not automatically a security boundary. Depending on the parser and configuration, Markdown can generate HTML and URLs that become dangerous if attacker-controlled input is not sanitized and validated.Is javascript: always an XSS vulnerability?
Not by itself. The actual impact depends on whether the value reaches an executable browser context and whether the application, browser, or security controls prevent execution.Does Base64 protect JavaScript payloads?
No. Base64 is encoding, not encryption or sanitization. An encoded malicious value can still become dangerous after decoding if it reaches an execution sink.Does target="_blank" prevent XSS?
No. target="_blank" controls where a link opens. It should not be considered an XSS defense.