Skip to content

Commit 7ce9fbb

Browse files
hi-ogawaAriPerkkio
andauthored
fix: backport #7317 to v1 (#7319)
Co-authored-by: Ari Perkkiö <[email protected]>
1 parent 73cb696 commit 7ce9fbb

File tree

18 files changed

+2541
-1083
lines changed

18 files changed

+2541
-1083
lines changed

.github/workflows/ci.yml

-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ on:
66
- main
77

88
pull_request:
9-
branches:
10-
- main
119

1210
concurrency:
1311
group: ci-${{ github.event.pull_request.number || github.ref }}

package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
"devDependencies": {
4040
"@antfu/eslint-config": "^2.15.0",
4141
"@antfu/ni": "^0.21.12",
42-
"@playwright/test": "^1.41.0",
42+
"@playwright/test": "^1.49.1",
4343
"@rollup/plugin-commonjs": "^25.0.7",
4444
"@rollup/plugin-json": "^6.0.1",
4545
"@rollup/plugin-node-resolve": "^15.2.3",
@@ -70,6 +70,8 @@
7070
},
7171
"pnpm": {
7272
"overrides": {
73+
"@vitest/browser": "$@vitest/browser",
74+
"@vitest/ui": "$@vitest/ui",
7375
"rollup": "$rollup",
7476
"vite": "$vite",
7577
"vitest": "workspace:*"

packages/browser/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@
7676
"@vitest/ws-client": "workspace:*",
7777
"@wdio/protocols": "^8.29.7",
7878
"periscopic": "^4.0.2",
79-
"playwright": "^1.41.0",
79+
"playwright": "^1.49.1",
8080
"playwright-core": "^1.41.0",
8181
"safaridriver": "^0.1.2",
8282
"vitest": "workspace:*",

packages/browser/src/client/client.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export const PORT = import.meta.hot ? '51204' : location.port
55
export const HOST = [location.hostname, PORT].filter(Boolean).join(':')
66
export const ENTRY_URL = `${
77
location.protocol === 'https:' ? 'wss:' : 'ws:'
8-
}//${HOST}/__vitest_api__`
8+
}//${HOST}/__vitest_api__?token=${(window as any).VITEST_API_TOKEN}`
99

1010
let setCancel = (_: CancelReason) => {}
1111
export const onCancel = new Promise<CancelReason>((resolve) => {

packages/browser/src/client/public/esm-client-injector.js

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ window.__vitest_browser_runner__ = {
3838
config: { __VITEST_CONFIG__ },
3939
files: { __VITEST_FILES__ },
4040
}
41+
window.VITEST_API_TOKEN = { __VITEST_API_TOKEN__ }
4142

4243
const config = __vitest_browser_runner__.config
4344

packages/browser/src/node/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export default (project: WorkspaceProject, base = '/'): Plugin[] => {
5959
const injector = replacer(await injectorJs, {
6060
__VITEST_CONFIG__: JSON.stringify(config),
6161
__VITEST_FILES__: JSON.stringify(files),
62+
__VITEST_API_TOKEN__: JSON.stringify(project.ctx.config.api.token),
6263
})
6364

6465
if (url.pathname === base) {

packages/ui/client/constants.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export const PORT = import.meta.hot ? '51204' : location.port
22
export const HOST = [location.hostname, PORT].filter(Boolean).join(':')
3-
export const ENTRY_URL = `${location.protocol === 'https:' ? 'wss:' : 'ws:'}//${HOST}/__vitest_api__`
3+
export const ENTRY_URL = `${location.protocol === 'https:' ? 'wss:' : 'ws:'}//${HOST}/__vitest_api__?token=${(window as any).VITEST_API_TOKEN}`
44
export const isReport = !!window.METADATA_PATH
55
export const BASE_PATH = isReport ? import.meta.env.BASE_URL : __BASE_PATH__

packages/ui/node/index.ts

+22
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import fs from 'node:fs'
12
import { fileURLToPath } from 'node:url'
23
import { basename, resolve } from 'pathe'
34
import sirv from 'sirv'
@@ -25,6 +26,27 @@ export default (ctx: Vitest): Plugin => {
2526
},
2627
}))
2728
const clientDist = resolve(fileURLToPath(import.meta.url), '../client')
29+
const clientIndexHtml = fs.readFileSync(resolve(clientDist, 'index.html'), 'utf-8')
30+
31+
// serve index.html with api token
32+
server.middlewares.use((req, res, next) => {
33+
if (req.url) {
34+
const url = new URL(req.url, 'http://localhost')
35+
if (url.pathname === base) {
36+
const html = clientIndexHtml.replace(
37+
'<!-- !LOAD_METADATA! -->',
38+
`<script>window.VITEST_API_TOKEN = ${JSON.stringify(ctx.config.api.token)}</script>`,
39+
)
40+
res.setHeader('Cache-Control', 'no-cache, max-age=0, must-revalidate')
41+
res.setHeader('Content-Type', 'text/html; charset=utf-8')
42+
res.write(html)
43+
res.end()
44+
return
45+
}
46+
}
47+
next()
48+
})
49+
2850
server.middlewares.use(base, sirv(clientDist, {
2951
single: true,
3052
dev: true,

packages/vitest/rollup.config.js

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ const external = [
7070
'node:fs',
7171
'node:stream',
7272
'node:vm',
73+
'node:http',
7374
'inspector',
7475
'vite-node/source-map',
7576
'vite-node/client',

packages/vitest/src/api/check.ts

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import type { IncomingMessage } from 'node:http'
2+
import crypto from 'node:crypto'
3+
import type { ResolvedConfig } from '../types/config'
4+
5+
export function isValidApiRequest(config: ResolvedConfig, req: IncomingMessage): boolean {
6+
const url = new URL(req.url ?? '', 'http://localhost')
7+
8+
// validate token. token is injected in ui/tester/orchestrator html, which is cross origin proteced.
9+
try {
10+
const token = url.searchParams.get('token')
11+
if (token && crypto.timingSafeEqual(
12+
Buffer.from(token),
13+
Buffer.from(config.api.token),
14+
))
15+
return true
16+
}
17+
// an error is thrown when the length is incorrect
18+
catch {}
19+
20+
return false
21+
}

packages/vitest/src/api/setup.ts

+6
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { getModuleGraph, isPrimitive, noop, stringifyReplace } from '../utils'
1616
import type { WorkspaceProject } from '../node/workspace'
1717
import { parseErrorStacktrace } from '../utils/source-map'
1818
import type { TransformResultWithSource, WebSocketEvents, WebSocketHandlers } from './types'
19+
import { isValidApiRequest } from './check'
1920

2021
export function setup(vitestOrWorkspace: Vitest | WorkspaceProject, _server?: ViteDevServer) {
2122
const ctx = 'ctx' in vitestOrWorkspace ? vitestOrWorkspace.ctx : vitestOrWorkspace
@@ -34,6 +35,11 @@ export function setup(vitestOrWorkspace: Vitest | WorkspaceProject, _server?: Vi
3435
if (pathname !== API_PATH)
3536
return
3637

38+
if (!isValidApiRequest(ctx.config, request)) {
39+
socket.destroy()
40+
return
41+
}
42+
3743
wss.handleUpgrade(request, socket, head, (ws) => {
3844
wss.emit('connection', ws, request)
3945
setupClient(ws)

packages/vitest/src/node/config.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import crypto from 'node:crypto'
12
import { resolveModule } from 'local-pkg'
23
import { normalize, relative, resolve } from 'pathe'
34
import c from 'picocolors'
@@ -408,7 +409,8 @@ export function resolveConfig(
408409
}
409410

410411
// the server has been created, we don't need to override vite.server options
411-
resolved.api = resolveApiServerConfig(options)
412+
const api = resolveApiServerConfig(options)
413+
resolved.api = { ...api, token: crypto.randomUUID() }
412414

413415
if (options.related)
414416
resolved.related = toArray(options.related).map(file => resolve(resolved.root, file))

packages/vitest/src/types/config.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -894,7 +894,7 @@ export interface ResolvedConfig extends Omit<Required<UserConfig>, 'config' | 'f
894894

895895
defines: Record<string, any>
896896

897-
api?: ApiConfig
897+
api: ApiConfig & { token: string }
898898
cliExclude?: string[]
899899

900900
benchmark?: Required<Omit<BenchmarkUserOptions, 'outputFile' | 'compare' | 'outputJson'>> & Pick<BenchmarkUserOptions, 'outputFile' | 'compare' | 'outputJson'>

0 commit comments

Comments
 (0)