Skip to content

Commit ed40f94

Browse files
committed
wip:
1 parent c415cfc commit ed40f94

File tree

12 files changed

+1279
-1445
lines changed

12 files changed

+1279
-1445
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"type": "module",
33
"version": "0.0.0-alpha.19",
44
"private": true,
5-
"packageManager": "pnpm@10.24.0",
5+
"packageManager": "pnpm@10.25.0",
66
"scripts": {
77
"build": "pnpm -r run build",
88
"build:debug": "NUXT_DEBUG_BUILD=true pnpm -r run build",

packages/core/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
"ws": "catalog:deps"
7171
},
7272
"devDependencies": {
73+
"@clack/prompts": "catalog:inlined",
7374
"@vitejs/devtools": "workspace:*",
7475
"@vitejs/devtools-vite": "workspace:*",
7576
"@vitejs/plugin-vue": "catalog:build",

packages/core/src/client/inject/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export async function init(): Promise<void> {
1010
// eslint-disable-next-line no-console
1111
console.log('[VITE DEVTOOLS] Client injected')
1212

13-
const rpcReturn = await getDevToolsRpcClient()
13+
const rpc = await getDevToolsRpcClient()
1414

1515
const state = useLocalStorage<DockPanelStorage>(
1616
'vite-devtools-dock-state',
@@ -28,7 +28,7 @@ export async function init(): Promise<void> {
2828

2929
const context = await createDocksContext(
3030
'embedded',
31-
rpcReturn,
31+
rpc,
3232
state,
3333
)
3434

packages/core/src/client/webcomponents/.generated/css.ts

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

packages/core/src/client/webcomponents/components/Dock.vue

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ function onPointerDown(e: PointerEvent) {
6464
draggingOffset.y = e.clientY - top - height / 2
6565
}
6666
67+
const isRpcTrusted = ref(context.rpc.isTrusted)
68+
context.rpc.events.on('rpc:is-trusted:updated', (isTrusted) => {
69+
isRpcTrusted.value = isTrusted
70+
})
71+
6772
onMounted(() => {
6873
windowSize.width = window.innerWidth
6974
windowSize.height = window.innerHeight
@@ -220,7 +225,9 @@ const panelStyle = computed(() => {
220225
})
221226
222227
onMounted(() => {
223-
if (context.rpc.isTrusted)
228+
if (context.panel.store.open && !isRpcTrusted.value)
229+
context.panel.store.open = false
230+
if (isRpcTrusted.value)
224231
bringUp()
225232
recalculateCounter.value++
226233
})
@@ -269,7 +276,11 @@ onMounted(() => {
269276
class="w-3 h-3 absolute left-1/2 top-1/2 translate-x--1/2 translate-y--1/2 transition-opacity duration-300"
270277
:class="isMinimized ? 'op100' : 'op0'"
271278
/>
272-
<div v-if="!context.rpc.isTrusted" class="p2">
279+
<div
280+
v-if="!isRpcTrusted"
281+
class="p2"
282+
:class="isMinimized ? 'opacity-0 pointer-events-none ws-nowrap flex items-center text-sm text-orange' : 'opacity-100'"
283+
>
273284
IS NOT TRUSTED
274285
</div>
275286
<DockEntries

packages/core/src/client/webcomponents/state/docks.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ export async function useDocksEntries(rpc: DevToolsRpcClient): Promise<Ref<DevTo
6868
// eslint-disable-next-line no-console
6969
console.log('[VITE DEVTOOLS] Docks Entries Updated', [...dockEntries.value])
7070
}
71+
rpc.events.on('rpc:is-trusted:updated', (isTrusted) => {
72+
if (isTrusted)
73+
updateDocksEntries()
74+
})
7175
rpc.client.register({
7276
name: 'vite:internal:docks:updated' satisfies keyof DevToolsRpcClientFunctions,
7377
type: 'action',

packages/core/src/node/rpc/anonymous/auth.ts

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import * as p from '@clack/prompts'
12
import { defineRpcFunction } from '@vitejs/devtools-kit'
3+
import c from 'ansis'
24

35
export interface DevToolsAuthInput {
46
authId: string
@@ -9,13 +11,58 @@ export interface DevToolsAuthReturn {
911
isTrusted: boolean
1012
}
1113

14+
const TEMPORARY_STORAGE = new Map<string, {
15+
authId: string
16+
ua: string
17+
timestamp: number
18+
}>()
19+
1220
export const anonymousAuth = defineRpcFunction({
1321
name: 'vite:anonymous:auth',
1422
type: 'action',
15-
setup: (context) => {
23+
setup: () => {
1624
return {
17-
handler: (query: DevToolsAuthInput): DevToolsAuthReturn => {
18-
// TODO: Implement the auth logic
25+
handler: async (query: DevToolsAuthInput): Promise<DevToolsAuthReturn> => {
26+
if (TEMPORARY_STORAGE.has(query.authId)) {
27+
return {
28+
isTrusted: true,
29+
}
30+
}
31+
32+
const message = [
33+
`A browser is requesting permissions to connect to the Vite DevTools.`,
34+
35+
`User Agent: ${c.yellow(c.bold(query.ua || 'Unknown'))}`,
36+
`Identifier: ${c.green(c.bold(query.authId))}`,
37+
'',
38+
'This will allow the browser to interact with the server, make file changes and run commands.',
39+
c.red(c.bold('You should only trust your local development browsers.')),
40+
]
41+
42+
p.note(
43+
c.reset(message.join('\n')),
44+
c.bold(c.yellow(' Vite DevTools Permission Request ')),
45+
)
46+
47+
const answer = await p.confirm({
48+
message: c.bold(`Do you trust this client (${c.green(c.bold(query.authId))})?`),
49+
initialValue: false,
50+
})
51+
52+
if (answer) {
53+
TEMPORARY_STORAGE.set(query.authId, {
54+
authId: query.authId,
55+
ua: query.ua,
56+
timestamp: Date.now(),
57+
})
58+
p.outro(c.green(c.bold('You have granted permissions to this client.')))
59+
60+
return {
61+
isTrusted: true,
62+
}
63+
}
64+
65+
p.outro(c.red(c.bold('You have denied permissions to this client.')))
1966
return {
2067
isTrusted: false,
2168
}

packages/kit/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"birpc-x": "catalog:deps"
4646
},
4747
"devDependencies": {
48+
"my-ua-parser": "catalog:frontend",
4849
"tsdown": "catalog:build",
4950
"vite": "catalog:build"
5051
}

packages/kit/src/client/docks.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,7 @@ export interface DockEntryStateEvents {
8383
'dom:panel:mounted': (panel: HTMLDivElement) => void
8484
'dom:iframe:mounted': (iframe: HTMLIFrameElement) => void
8585
}
86+
87+
export interface RpcClientEvents {
88+
'rpc:is-trusted:updated': (isTrusted: boolean) => void
89+
}

packages/kit/src/client/rpc.ts

Lines changed: 50 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import type { WebSocketRpcClientOptions } from '@vitejs/devtools-rpc/presets/ws/client'
22
import type { BirpcReturn, EventOptions } from 'birpc'
3-
import type { ConnectionMeta, DevToolsRpcClientFunctions, DevToolsRpcServerFunctions } from '../types'
4-
import type { DevToolsClientContext, DevToolsClientRpcHost } from './docks'
3+
import type { ConnectionMeta, DevToolsRpcClientFunctions, DevToolsRpcServerFunctions, EventEmitter } from '../types'
4+
import type { DevToolsClientContext, DevToolsClientRpcHost, RpcClientEvents } from './docks'
55
import { createRpcClient } from '@vitejs/devtools-rpc'
66
import { createWsRpcPreset } from '@vitejs/devtools-rpc/presets/ws/client'
77
import { RpcFunctionsCollectorBase } from 'birpc-x'
8+
import { UAParser } from 'my-ua-parser'
9+
import { createEventEmitter } from '../utils/events'
810
import { nanoid } from '../utils/nanoid'
911
import { promiseWithResolver } from '../utils/promise'
1012

@@ -25,10 +27,15 @@ export interface DevToolsRpcClientOptions {
2527
}
2628

2729
export interface DevToolsRpcClient {
30+
/**
31+
* The events of the client
32+
*/
33+
events: EventEmitter<RpcClientEvents>
34+
2835
/**
2936
* Whether the client is trusted
3037
*/
31-
readonly isTrusted: boolean
38+
readonly isTrusted: boolean | null
3239
/**
3340
* The connection meta
3441
*/
@@ -40,7 +47,12 @@ export interface DevToolsRpcClient {
4047
*
4148
* @param timeout - The timeout in milliseconds, default to 60 seconds
4249
*/
43-
ensureTrusted: (timeout?: number) => Promise<void>
50+
ensureTrusted: (timeout?: number) => Promise<boolean>
51+
52+
/**
53+
* Request trust from the server
54+
*/
55+
requestTrust: () => Promise<boolean>
4456

4557
/**
4658
* Call a RPC function on the server
@@ -109,6 +121,7 @@ export async function getDevToolsRpcClient(
109121
baseURL = '/.devtools/',
110122
rpcOptions = {},
111123
} = options
124+
const events = createEventEmitter<RpcClientEvents>()
112125
const bases = Array.isArray(baseURL) ? baseURL : [baseURL]
113126
let connectionMeta: ConnectionMeta | undefined = options.connectionMeta || findConnectionMetaFromWindows()
114127

@@ -142,21 +155,10 @@ export async function getDevToolsRpcClient(
142155
const authId = getConnectionAuthIdFromWindows()
143156

144157
let isTrusted = false
145-
const trustedPromise = promiseWithResolver<void>()
158+
const trustedPromise = promiseWithResolver<boolean>()
146159

147160
const clientRpc: DevToolsClientRpcHost = new RpcFunctionsCollectorBase<DevToolsRpcClientFunctions, DevToolsClientContext>(context)
148161

149-
// Builtin rpc functions
150-
clientRpc.register({
151-
name: 'vite:anonymous:trusted',
152-
type: 'event',
153-
handler: () => {
154-
isTrusted = true
155-
trustedPromise.resolve()
156-
return true
157-
},
158-
})
159-
160162
// Create the RPC client
161163
const serverRpc = createRpcClient<DevToolsRpcServerFunctions, DevToolsRpcClientFunctions>(
162164
clientRpc.functions,
@@ -169,15 +171,35 @@ export async function getDevToolsRpcClient(
169171
},
170172
)
171173

172-
// TODO: implement the trust logic
173-
serverRpc.$call('vite:anonymous:auth', {
174-
authId,
175-
ua: navigator.userAgent,
176-
})
174+
async function requestTrust() {
175+
if (isTrusted)
176+
return true
177+
178+
const info = new UAParser(navigator.userAgent).getResult()
179+
const ua = [
180+
info.browser.name,
181+
info.browser.version,
182+
'|',
183+
info.os.name,
184+
info.os.version,
185+
info.device.type,
186+
].filter(i => i).join(' ')
187+
188+
const result = await serverRpc
189+
.$call('vite:anonymous:auth', {
190+
authId,
191+
ua,
192+
})
193+
194+
isTrusted = result.isTrusted
195+
trustedPromise.resolve(isTrusted)
196+
events.emit('rpc:is-trusted:updated', isTrusted)
197+
return result.isTrusted
198+
}
177199

178-
async function ensureTrusted(timeout = 60_000): Promise<void> {
200+
async function ensureTrusted(timeout = 60_000): Promise<boolean> {
179201
if (isTrusted)
180-
trustedPromise.resolve()
202+
trustedPromise.resolve(true)
181203

182204
if (timeout <= 0)
183205
return trustedPromise.promise
@@ -192,14 +214,18 @@ export async function getDevToolsRpcClient(
192214
clear = () => clearTimeout(id)
193215
}),
194216
])
217+
218+
return isTrusted
195219
}
196220

197221
const rpc: DevToolsRpcClient = {
222+
events,
198223
get isTrusted() {
199224
return isTrusted
200225
},
201226
connectionMeta,
202227
ensureTrusted,
228+
requestTrust,
203229
call: (...args: any): any => {
204230
// @ts-expect-error casting
205231
return serverRpc.call(...args)
@@ -217,6 +243,7 @@ export async function getDevToolsRpcClient(
217243

218244
// @ts-expect-error assign to readonly property
219245
context.rpc = rpc
246+
requestTrust()
220247

221248
return rpc
222249
}

0 commit comments

Comments
 (0)