@@ -6,24 +6,27 @@ import { createBirpc } from 'birpc'
66import { parse , stringify } from 'flatted'
77import type { WebSocket } from 'ws'
88import { WebSocketServer } from 'ws'
9+ import { isFileServingAllowed } from 'vite'
910import type { ViteDevServer } from 'vite'
1011import type { StackTraceParserOptions } from '@vitest/utils/source-map'
1112import { API_PATH } from '../constants'
1213import type { Vitest } from '../node'
1314import type { File , ModuleGraphData , Reporter , TaskResultPack , UserConsoleLog } from '../types'
14- import { getModuleGraph , isPrimitive } from '../utils'
15+ import { getModuleGraph , isPrimitive , stringifyReplace } from '../utils'
1516import type { WorkspaceProject } from '../node/workspace'
1617import { parseErrorStacktrace } from '../utils/source-map'
1718import type { TransformResultWithSource , WebSocketEvents , WebSocketHandlers } from './types'
1819
19- export function setup ( vitestOrWorkspace : Vitest | WorkspaceProject , server ?: ViteDevServer ) {
20+ export function setup ( vitestOrWorkspace : Vitest | WorkspaceProject , _server ?: ViteDevServer ) {
2021 const ctx = 'ctx' in vitestOrWorkspace ? vitestOrWorkspace . ctx : vitestOrWorkspace
2122
2223 const wss = new WebSocketServer ( { noServer : true } )
2324
2425 const clients = new Map < WebSocket , BirpcReturn < WebSocketEvents , WebSocketHandlers > > ( )
2526
26- ; ( server || ctx . server ) . httpServer ?. on ( 'upgrade' , ( request , socket , head ) => {
27+ const server = _server || ctx . server
28+
29+ server . httpServer ?. on ( 'upgrade' , ( request , socket , head ) => {
2730 if ( ! request . url )
2831 return
2932
@@ -37,6 +40,11 @@ export function setup(vitestOrWorkspace: Vitest | WorkspaceProject, server?: Vit
3740 } )
3841 } )
3942
43+ function checkFileAccess ( path : string ) {
44+ if ( ! isFileServingAllowed ( path , server ) )
45+ throw new Error ( `Access denied to "${ path } ". See Vite config documentation for "server.fs": https://vitejs.dev/config/server-options.html#server-fs-strict.` )
46+ }
47+
4048 function setupClient ( ws : WebSocket ) {
4149 const rpc = createBirpc < WebSocketEvents , WebSocketHandlers > (
4250 {
@@ -73,7 +81,8 @@ export function setup(vitestOrWorkspace: Vitest | WorkspaceProject, server?: Vit
7381 return ctx . snapshot . resolveRawPath ( testPath , rawPath )
7482 } ,
7583 async readSnapshotFile ( snapshotPath ) {
76- if ( ! ctx . snapshot . resolvedPaths . has ( snapshotPath ) || ! existsSync ( snapshotPath ) )
84+ checkFileAccess ( snapshotPath )
85+ if ( ! existsSync ( snapshotPath ) )
7786 return null
7887 return fs . readFile ( snapshotPath , 'utf-8' )
7988 } ,
@@ -88,13 +97,13 @@ export function setup(vitestOrWorkspace: Vitest | WorkspaceProject, server?: Vit
8897 return fs . writeFile ( id , content , 'utf-8' )
8998 } ,
9099 async saveSnapshotFile ( id , content ) {
91- if ( ! ctx . snapshot . resolvedPaths . has ( id ) )
92- throw new Error ( `Snapshot file "${ id } " does not exist.` )
100+ checkFileAccess ( id )
93101 await fs . mkdir ( dirname ( id ) , { recursive : true } )
94102 return fs . writeFile ( id , content , 'utf-8' )
95103 } ,
96104 async removeSnapshotFile ( id ) {
97- if ( ! ctx . snapshot . resolvedPaths . has ( id ) || ! existsSync ( id ) )
105+ checkFileAccess ( id )
106+ if ( ! existsSync ( id ) )
98107 throw new Error ( `Snapshot file "${ id } " does not exist.` )
99108 return fs . unlink ( id )
100109 } ,
@@ -140,7 +149,7 @@ export function setup(vitestOrWorkspace: Vitest | WorkspaceProject, server?: Vit
140149 post : msg => ws . send ( msg ) ,
141150 on : fn => ws . on ( 'message' , fn ) ,
142151 eventNames : [ 'onUserConsoleLog' , 'onFinished' , 'onCollected' , 'onCancel' ] ,
143- serialize : stringify ,
152+ serialize : ( data : any ) => stringify ( data , stringifyReplace ) ,
144153 deserialize : parse ,
145154 } ,
146155 )
0 commit comments