Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: backport #7317 to v1 #7319

Open
wants to merge 14 commits into
base: v1
Choose a base branch
from
2 changes: 0 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ on:
- main

pull_request:
branches:
- main

concurrency:
group: ci-${{ github.event.pull_request.number || github.ref }}
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"devDependencies": {
"@antfu/eslint-config": "^2.15.0",
"@antfu/ni": "^0.21.12",
"@playwright/test": "^1.41.0",
"@playwright/test": "^1.49.1",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-json": "^6.0.1",
"@rollup/plugin-node-resolve": "^15.2.3",
Expand Down Expand Up @@ -70,6 +70,8 @@
},
"pnpm": {
"overrides": {
"@vitest/browser": "$@vitest/browser",
"@vitest/ui": "$@vitest/ui",
"rollup": "$rollup",
"vite": "$vite",
"vitest": "workspace:*"
Expand Down
2 changes: 1 addition & 1 deletion packages/browser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
"@vitest/ws-client": "workspace:*",
"@wdio/protocols": "^8.29.7",
"periscopic": "^4.0.2",
"playwright": "^1.41.0",
"playwright": "^1.49.1",
"playwright-core": "^1.41.0",
"safaridriver": "^0.1.2",
"vitest": "workspace:*",
Expand Down
2 changes: 1 addition & 1 deletion packages/browser/src/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export const PORT = import.meta.hot ? '51204' : location.port
export const HOST = [location.hostname, PORT].filter(Boolean).join(':')
export const ENTRY_URL = `${
location.protocol === 'https:' ? 'wss:' : 'ws:'
}//${HOST}/__vitest_api__`
}//${HOST}/__vitest_api__?token=${(window as any).VITEST_API_TOKEN}`

let setCancel = (_: CancelReason) => {}
export const onCancel = new Promise<CancelReason>((resolve) => {
Expand Down
1 change: 1 addition & 0 deletions packages/browser/src/client/public/esm-client-injector.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ window.__vitest_browser_runner__ = {
config: { __VITEST_CONFIG__ },
files: { __VITEST_FILES__ },
}
window.VITEST_API_TOKEN = { __VITEST_API_TOKEN__ }

const config = __vitest_browser_runner__.config

Expand Down
1 change: 1 addition & 0 deletions packages/browser/src/node/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export default (project: WorkspaceProject, base = '/'): Plugin[] => {
const injector = replacer(await injectorJs, {
__VITEST_CONFIG__: JSON.stringify(config),
__VITEST_FILES__: JSON.stringify(files),
__VITEST_API_TOKEN__: JSON.stringify(project.ctx.config.api.token),
})

if (url.pathname === base) {
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/client/constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export const PORT = import.meta.hot ? '51204' : location.port
export const HOST = [location.hostname, PORT].filter(Boolean).join(':')
export const ENTRY_URL = `${location.protocol === 'https:' ? 'wss:' : 'ws:'}//${HOST}/__vitest_api__`
export const ENTRY_URL = `${location.protocol === 'https:' ? 'wss:' : 'ws:'}//${HOST}/__vitest_api__?token=${(window as any).VITEST_API_TOKEN}`
export const isReport = !!window.METADATA_PATH
export const BASE_PATH = isReport ? import.meta.env.BASE_URL : __BASE_PATH__
22 changes: 22 additions & 0 deletions packages/ui/node/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import fs from 'node:fs'
import { fileURLToPath } from 'node:url'
import { basename, resolve } from 'pathe'
import sirv from 'sirv'
Expand Down Expand Up @@ -25,6 +26,27 @@ export default (ctx: Vitest): Plugin => {
},
}))
const clientDist = resolve(fileURLToPath(import.meta.url), '../client')
const clientIndexHtml = fs.readFileSync(resolve(clientDist, 'index.html'), 'utf-8')

// serve index.html with api token
server.middlewares.use((req, res, next) => {
if (req.url) {
const url = new URL(req.url, 'http://localhost')
if (url.pathname === base) {
const html = clientIndexHtml.replace(
'<!-- !LOAD_METADATA! -->',
`<script>window.VITEST_API_TOKEN = ${JSON.stringify(ctx.config.api.token)}</script>`,
)
res.setHeader('Cache-Control', 'no-cache, max-age=0, must-revalidate')
res.setHeader('Content-Type', 'text/html; charset=utf-8')
res.write(html)
res.end()
return
}
}
next()
})

server.middlewares.use(base, sirv(clientDist, {
single: true,
dev: true,
Expand Down
1 change: 1 addition & 0 deletions packages/vitest/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ const external = [
'node:fs',
'node:stream',
'node:vm',
'node:http',
'inspector',
'vite-node/source-map',
'vite-node/client',
Expand Down
21 changes: 21 additions & 0 deletions packages/vitest/src/api/check.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { IncomingMessage } from 'node:http'
import crypto from 'node:crypto'
import type { ResolvedConfig } from '../types/config'

export function isValidApiRequest(config: ResolvedConfig, req: IncomingMessage): boolean {
const url = new URL(req.url ?? '', 'http://localhost')

// validate token. token is injected in ui/tester/orchestrator html, which is cross origin proteced.
try {
const token = url.searchParams.get('token')
if (token && crypto.timingSafeEqual(
Buffer.from(token),
Buffer.from(config.api.token),
))
return true
}
// an error is thrown when the length is incorrect
catch {}

return false
}
6 changes: 6 additions & 0 deletions packages/vitest/src/api/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { getModuleGraph, isPrimitive, noop, stringifyReplace } from '../utils'
import type { WorkspaceProject } from '../node/workspace'
import { parseErrorStacktrace } from '../utils/source-map'
import type { TransformResultWithSource, WebSocketEvents, WebSocketHandlers } from './types'
import { isValidApiRequest } from './check'

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

if (!isValidApiRequest(ctx.config, request)) {
socket.destroy()
return
}

wss.handleUpgrade(request, socket, head, (ws) => {
wss.emit('connection', ws, request)
setupClient(ws)
Expand Down
4 changes: 3 additions & 1 deletion packages/vitest/src/node/config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import crypto from 'node:crypto'
import { resolveModule } from 'local-pkg'
import { normalize, relative, resolve } from 'pathe'
import c from 'picocolors'
Expand Down Expand Up @@ -408,7 +409,8 @@ export function resolveConfig(
}

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

if (options.related)
resolved.related = toArray(options.related).map(file => resolve(resolved.root, file))
Expand Down
2 changes: 1 addition & 1 deletion packages/vitest/src/types/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -894,7 +894,7 @@ export interface ResolvedConfig extends Omit<Required<UserConfig>, 'config' | 'f

defines: Record<string, any>

api?: ApiConfig
api: ApiConfig & { token: string }
cliExclude?: string[]

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