@@ -6,24 +6,27 @@ import { createBirpc } from 'birpc'
6
6
import { parse , stringify } from 'flatted'
7
7
import type { WebSocket } from 'ws'
8
8
import { WebSocketServer } from 'ws'
9
+ import { isFileServingAllowed } from 'vite'
9
10
import type { ViteDevServer } from 'vite'
10
11
import type { StackTraceParserOptions } from '@vitest/utils/source-map'
11
12
import { API_PATH } from '../constants'
12
13
import type { Vitest } from '../node'
13
14
import type { File , ModuleGraphData , Reporter , TaskResultPack , UserConsoleLog } from '../types'
14
- import { getModuleGraph , isPrimitive } from '../utils'
15
+ import { getModuleGraph , isPrimitive , stringifyReplace } from '../utils'
15
16
import type { WorkspaceProject } from '../node/workspace'
16
17
import { parseErrorStacktrace } from '../utils/source-map'
17
18
import type { TransformResultWithSource , WebSocketEvents , WebSocketHandlers } from './types'
18
19
19
- export function setup ( vitestOrWorkspace : Vitest | WorkspaceProject , server ?: ViteDevServer ) {
20
+ export function setup ( vitestOrWorkspace : Vitest | WorkspaceProject , _server ?: ViteDevServer ) {
20
21
const ctx = 'ctx' in vitestOrWorkspace ? vitestOrWorkspace . ctx : vitestOrWorkspace
21
22
22
23
const wss = new WebSocketServer ( { noServer : true } )
23
24
24
25
const clients = new Map < WebSocket , BirpcReturn < WebSocketEvents , WebSocketHandlers > > ( )
25
26
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 ) => {
27
30
if ( ! request . url )
28
31
return
29
32
@@ -37,6 +40,11 @@ export function setup(vitestOrWorkspace: Vitest | WorkspaceProject, server?: Vit
37
40
} )
38
41
} )
39
42
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
+
40
48
function setupClient ( ws : WebSocket ) {
41
49
const rpc = createBirpc < WebSocketEvents , WebSocketHandlers > (
42
50
{
@@ -73,7 +81,8 @@ export function setup(vitestOrWorkspace: Vitest | WorkspaceProject, server?: Vit
73
81
return ctx . snapshot . resolveRawPath ( testPath , rawPath )
74
82
} ,
75
83
async readSnapshotFile ( snapshotPath ) {
76
- if ( ! ctx . snapshot . resolvedPaths . has ( snapshotPath ) || ! existsSync ( snapshotPath ) )
84
+ checkFileAccess ( snapshotPath )
85
+ if ( ! existsSync ( snapshotPath ) )
77
86
return null
78
87
return fs . readFile ( snapshotPath , 'utf-8' )
79
88
} ,
@@ -88,13 +97,13 @@ export function setup(vitestOrWorkspace: Vitest | WorkspaceProject, server?: Vit
88
97
return fs . writeFile ( id , content , 'utf-8' )
89
98
} ,
90
99
async saveSnapshotFile ( id , content ) {
91
- if ( ! ctx . snapshot . resolvedPaths . has ( id ) )
92
- throw new Error ( `Snapshot file "${ id } " does not exist.` )
100
+ checkFileAccess ( id )
93
101
await fs . mkdir ( dirname ( id ) , { recursive : true } )
94
102
return fs . writeFile ( id , content , 'utf-8' )
95
103
} ,
96
104
async removeSnapshotFile ( id ) {
97
- if ( ! ctx . snapshot . resolvedPaths . has ( id ) || ! existsSync ( id ) )
105
+ checkFileAccess ( id )
106
+ if ( ! existsSync ( id ) )
98
107
throw new Error ( `Snapshot file "${ id } " does not exist.` )
99
108
return fs . unlink ( id )
100
109
} ,
@@ -140,7 +149,7 @@ export function setup(vitestOrWorkspace: Vitest | WorkspaceProject, server?: Vit
140
149
post : msg => ws . send ( msg ) ,
141
150
on : fn => ws . on ( 'message' , fn ) ,
142
151
eventNames : [ 'onUserConsoleLog' , 'onFinished' , 'onCollected' , 'onCancel' ] ,
143
- serialize : stringify ,
152
+ serialize : ( data : any ) => stringify ( data , stringifyReplace ) ,
144
153
deserialize : parse ,
145
154
} ,
146
155
)
0 commit comments