mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-16 13:03:31 +08:00
Keep desktop sidecar control local after H5 auth
The H5 LAN release made the desktop sidecar bind broadly and require auth for non-localhost hosts. The Tauri WebView still talks to the loopback control URL without an H5 bearer token, so startup requests were rejected before the app could load. Constraint: Desktop WebView control requests must stay tokenless for the local sidecar. Rejected: Reuse H5 token bootstrap for Tauri | would add remote-access state to the native app startup path. Confidence: high Scope-risk: narrow Directive: Tauri WebView bypass is limited to loopback control hosts; do not extend it to LAN or public hosts without re-reviewing H5 auth boundaries. Tested: bun test src/server/__tests__/h5-access-auth.test.ts Tested: bun run check:server
This commit is contained in:
parent
d1211e4dca
commit
4501ef2adb
@ -176,12 +176,31 @@ describe('remote H5 auth and CORS integration', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test('allows the Tauri desktop WebView origin to control the local sidecar without H5 token', async () => {
|
||||
const response = await fetch(`${baseUrl}/api/status`, {
|
||||
headers: {
|
||||
Origin: 'http://tauri.localhost',
|
||||
},
|
||||
})
|
||||
|
||||
expect(response.status).toBe(200)
|
||||
expect(response.headers.get('Access-Control-Allow-Origin')).toBe('http://tauri.localhost')
|
||||
await expect(response.json()).resolves.toMatchObject({
|
||||
status: 'ok',
|
||||
})
|
||||
})
|
||||
|
||||
test('only lets localhost WebUI origin bypass H5 auth for loopback or private server hosts', () => {
|
||||
expect(canBypassRemoteAuthForLocalBrowser('http://127.0.0.1:5179', '127.0.0.1')).toBe(true)
|
||||
expect(canBypassRemoteAuthForLocalBrowser('http://localhost:5179', '192.168.0.102')).toBe(true)
|
||||
expect(canBypassRemoteAuthForLocalBrowser('http://tauri.localhost', '127.0.0.1')).toBe(true)
|
||||
expect(canBypassRemoteAuthForLocalBrowser('tauri://localhost', '127.0.0.1')).toBe(true)
|
||||
expect(canBypassRemoteAuthForLocalBrowser('asset://localhost', '127.0.0.1')).toBe(true)
|
||||
expect(canBypassRemoteAuthForLocalBrowser('http://localhost:5179', '10.0.0.5')).toBe(true)
|
||||
expect(canBypassRemoteAuthForLocalBrowser('http://localhost:5179', '172.20.1.8')).toBe(true)
|
||||
expect(canBypassRemoteAuthForLocalBrowser('http://localhost:5179', 'public.example.com')).toBe(false)
|
||||
expect(canBypassRemoteAuthForLocalBrowser('http://tauri.localhost', 'public.example.com')).toBe(false)
|
||||
expect(canBypassRemoteAuthForLocalBrowser('http://tauri.localhost', '192.168.0.102')).toBe(false)
|
||||
expect(canBypassRemoteAuthForLocalBrowser('http://192.168.0.50:5179', '192.168.0.102')).toBe(false)
|
||||
})
|
||||
|
||||
|
||||
@ -64,6 +64,18 @@ function isLocalBrowserOrigin(origin: string | null): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
function isTauriWebViewOrigin(origin: string | null): boolean {
|
||||
if (!origin) return false
|
||||
|
||||
try {
|
||||
const url = new URL(origin)
|
||||
return url.hostname === 'tauri.localhost' ||
|
||||
((url.protocol === 'tauri:' || url.protocol === 'asset:') && url.hostname === 'localhost')
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function isPrivateNetworkHost(host: string): boolean {
|
||||
const normalized = host.trim().replace(/^\[/, '').replace(/\]$/, '').toLowerCase()
|
||||
|
||||
@ -93,6 +105,10 @@ function isPrivateNetworkHost(host: string): boolean {
|
||||
}
|
||||
|
||||
export function canBypassRemoteAuthForLocalBrowser(origin: string | null, requestHost: string): boolean {
|
||||
if (isTauriWebViewOrigin(origin)) {
|
||||
return isLocalServerHost(requestHost)
|
||||
}
|
||||
|
||||
return isLocalBrowserOrigin(origin) &&
|
||||
(isLocalServerHost(requestHost) || isPrivateNetworkHost(requestHost))
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user