Bulwark Webmail: A cid Link in an Email Becomes Same-Origin Script

A cid: link is rewritten to a blob URL whose type the sender chooses, and on paths with a dot in the last segment the CSP is missing

Advisory ID: TP-2026-101
Product: Bulwark Webmail (web interface for the Stalwart mail server, Next.js)
Vulnerability type: Cross-site scripting through a blob URL (CWE-79)
CVE: requested on 19 September 2026 (GitHub CNA), assignment pending
CVSS 3.1: 9.3 (Critical) · CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:N
Affected versions: <= 1.9.2
Vendor advisory: GHSA-xvjh-v9c6-qcvc
Fixed in: 1.10.0
Reported: 16 September 2026

Summary

A link of the form cid: in an inbound email normally points at an embedded attachment. Bulwark rewrites it to a blob URL whose MIME type the sender of the email chooses, and passes it to window.open(). Normally that is inert, because a blob document inherits the Content-Security-Policy of the document that opens it. The proxy, however, sends no security headers at all for any path whose last segment contains a dot, while the fully signed-in application is still served there and the address bar shows the usual URL. Opened from such a page, the sender-typed blob runs in the webmail origin.

Root cause

The handler for links inside a message rejects only # and mailto:, so it checks neither the scheme nor the MIME type of the target. The cid: link has been translated beforehand into a blob URL whose type comes from the email's attachment and is therefore chosen by the sender. A blob document inherits the Content-Security-Policy of the document that opens it, which is why the attack fails on the canonical URL, where the browser reports two violations. The proxy returns no CSP, no X-Content-Type-Options and no X-Frame-Options for any path whose last segment contains a dot, while the localised catch-all routes render the signed-in application there. The same file shows the product knows the missing control: the attachment path forces a download for any type outside an inert allowlist.

Proof of Concept

# an email with a text/html attachment and a cid link pointing at it.
# the victim is signed in, sits on a path whose last segment contains a
# dot, opens the message and follows the link.

# the handler checks neither scheme nor type, window.open() opens the blob,
# and on the dotted path the CSP is absent
# -> the script runs in the webmail origin.

Both halves are needed: under the enforced CSP the typed blob is inert, and the missing CSP alone injects nothing. The victim must also be signed in with the remember-me option, which is off by default; without it the document on the dotted path cannot restore its session and lands on the login page, which carries the full CSP again.

Impact

  • Script execution in the webmail origin by anyone able to send the victim an email.
  • Access to the victim's entire mailbox and to the JMAP passthrough in their name.
  • No account and no login on the attacker's side, a single email suffices.
  • The address bar shows the usual URL, so nothing looks wrong to the victim.

References

Is Something Like This in Your Software?

Our team found this vulnerability in the course of its work. Have your applications tested by the same specialists, with a penetration test from turingpoint.