October 30, 2026
Stored XSS in Munshi: A Purchase Inquiry Message Could Rewrite the Page
Untrusted item descriptions and thread messages were rendered as executable HTML, allowing persistent JavaScript execution when a purchase…

By TheWildEye
5 min read
Untrusted item descriptions and thread messages were rendered as executable HTML, allowing persistent JavaScript execution when a purchase inquiry thread was opened.
During a security assessment of Munshi's Purchase Inquiry workflow, I identified a stored Cross-Site Scripting (XSS) vulnerability in the way inquiry descriptions and thread messages were rendered.
The issue was not limited to a script running in the browser where the content was first submitted. The malicious content was stored and executed again when the affected thread was viewed. Testing demonstrated full-page defacement, browser alert execution, and re-execution when an existing thread was reopened.
That persistence changed the risk. A thread is part of a business workflow, and another user opening it could trigger the stored content in their own browser context.
The Message Was Stored. The Script Was Executed.
The affected workflow was the Munshi Purchase Inquiry module:
https://munshi.petpooja.com/purchase-inquiry/
The affected thread route was:
https://munshi.petpooja.com/purchase-inquiry/thread/{thread-id}
A user can create a purchase inquiry, add an item description, and continue the discussion through a buyer-supplier thread. The application stores those values and later displays them inside the thread view.
During testing, I found that untrusted content placed in an item description could be interpreted as HTML rather than displayed strictly as text. The same behavior was also observed in messages posted to an existing thread.
Figure 1: The inquiry form where the test content was submitted. The payload and identifying details are covered with black rectangles.
The payload itself is intentionally redacted from the public screenshot. The important point is the input path: content submitted through an ordinary purchase inquiry was subsequently interpreted by the browser as active markup.
Opening the Thread Was Enough
The first proof of concept replaced the visible page with a full-screen defacement message. The interface became unusable until the page was reloaded.
Figure 2: The stored content replaced the thread page. The thread identifier is redacted.
This was not a case where a user had to copy a script into the browser console. The code path ran as part of rendering the stored thread content. Once the affected thread was opened, the browser processed the content and executed the injected behavior.
The assessment also confirmed that the payload remained in an existing thread and executed again when that thread was reopened. Persistence and repeatability are what distinguish this finding from a one-time client-side demonstration.
Figure 3: Evidence of the payload executing again in an existing thread. The thread ID and personal defacement text are redacted.
It Was Not Limited to Inquiry Creation
I also tested the message-posting path in an existing purchase inquiry thread. Content sent as a thread message triggered JavaScript execution when the thread was rendered.
The report captured a browser alert generated from that path. Supplier conversation content, participant details, and thread identifiers are hidden in the public version of the screenshot.
Figure 4: JavaScript alert triggered by content posted inside an existing thread. Thread identifiers and conversation content are redacted.
This showed that the issue affected more than one input path. Both the item description supplied during inquiry creation and message content posted within a thread reached unsafe rendering logic.
What the Browser Actually Did
The assessment demonstrated three outcomes:
- A stored payload could replace the thread page with a custom defacement message.
- The payload executed again when an existing thread containing it was reopened.
- Posting content inside an existing thread could trigger JavaScript execution, demonstrated by a browser alert.
The broader risk comes from execution in the application's browser origin. Depending on the victim's permissions and what data is accessible to page JavaScript, stored XSS may expose information shown in the application or enable actions in the victim's session. The report identifies those as potential impacts; it does not document a confirmed theft of application data or session tokens, so I am not presenting those outcomes as demonstrated.
Purchase inquiry threads may contain supplier communication, quotation details, and procurement context. A malicious message that runs when the thread is opened can therefore affect more than the person who submitted it.
The Technical Root Cause
The root cause was unsafe handling of untrusted content during thread rendering. The report identified a flow that temporarily parses input as HTML before the text-cleaning step is complete.
When untrusted data is assigned to innerHTML, the browser interprets it as markup. If that markup contains executable event handlers or elements that can trigger script execution, the browser processes them rather than treating the entire value as plain text.
The storage component makes the issue persistent. The content is saved during inquiry creation or message posting, then rendered later for users who open the thread. Without safe output handling at that point, the same content can execute across multiple views and sessions.
This is Stored Cross-Site Scripting (CWE-79): untrusted input is not properly neutralized before it is included in generated web content.
What Needs to Change
The fix should remove the unsafe rendering path, rather than relying only on filtering a few known payload patterns.
If the application only needs to display message text, it should render it as text and avoid parsing user-controlled content as HTML. In Angular-based rendering, ordinary interpolation is preferable to directly injecting HTML for this use case.
If rich HTML is required, sanitize it using a maintained, allowlist-based HTML sanitization library immediately before it reaches the HTML rendering sink. Apply context-appropriate output encoding wherever user-controlled content is rendered. Server-side validation and sanitization should provide additional protection, not replace safe rendering at the output layer.
The review should cover every rendering path for inquiry descriptions, thread messages, supplier replies, and message previews. Fixing only the creation form would leave the existing-thread message path exposed if it uses the same unsafe handling.
A strong Content Security Policy can provide defense in depth, but it is not a substitute for safe rendering. Regression testing should confirm that HTML and JavaScript-like input is displayed inertly, that previously stored malicious content no longer executes, and that reopening an old thread cannot trigger the payload again.
Vulnerability Reference
Vulnerability: Stored Cross-Site Scripting in Purchase Inquiry Thread
Product: Munshi (by Petpooja)
Affected Component: Purchase Inquiry Thread Rendering
Affected Workflow: Item descriptions and thread messages
Affected Route: /purchase-inquiry/thread/{thread-id}
Vulnerability Class: Stored Cross-Site Scripting (Stored XSS)
Primary CWE: CWE-79 — Improper Neutralization of Input During Web Page Generation
CVE ID: To be assigned
Closing Thoughts
This finding began with a message field that should have contained ordinary business text. Instead, content saved through the Purchase Inquiry workflow was later interpreted as executable HTML by the browser.
The most important detail was persistence: the content did not affect only the initial submitter. It executed when an existing thread was opened again, and the same unsafe behavior was observed through message posting.
Stored XSS is not just a formatting issue. In a business application, every user-generated field that is saved and rendered must be treated as untrusted. Safe text rendering, correctly applied sanitization where rich HTML is required, and regression tests across every thread-rendering path are essential to prevent stored content from becoming executable code.
Researcher: Vyom Nagpal