mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
fix(web): share loopback hostname policy
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
|
||||
import type { IncomingHttpHeaders } from 'node:http'
|
||||
import { isLoopbackHostname } from './loopback-hostname.ts'
|
||||
|
||||
/** The request facts the fence reads (structural subset of IncomingMessage). */
|
||||
interface ApiTrustRequest {
|
||||
@@ -25,14 +26,6 @@ function header(headers: IncomingHttpHeaders, name: string): string | undefined
|
||||
return typeof value === 'string' ? value : undefined
|
||||
}
|
||||
|
||||
function isLoopbackHostname(hostname: string): boolean {
|
||||
if (hostname === 'localhost' || hostname === '[::1]') return true
|
||||
const parts = hostname.split('.')
|
||||
return parts.length === 4
|
||||
&& parts[0] === '127'
|
||||
&& parts.every(part => /^\d{1,3}$/.test(part) && Number(part) <= 255)
|
||||
}
|
||||
|
||||
/** Normalized URL of a Host-header authority (hostname lowercased, default port stripped, IPv6 bracketed), or undefined when unparsable. */
|
||||
function parseAuthority(authority: string): URL | undefined {
|
||||
try {
|
||||
|
||||
@@ -9,6 +9,8 @@ import { ConnectionController, type ConnectionConfig, type ConnectionSinks, type
|
||||
import { FixtureApiClient } from './fixture.ts'
|
||||
import { WebApiClient } from './web-api-client.ts'
|
||||
|
||||
export { isLoopbackHostname } from '../loopback-hostname.ts'
|
||||
|
||||
// ---- Contract re-exports (browser-safe apiproxy channels + core types) ----
|
||||
export type {
|
||||
ApiProxy, SessionsApi, SessionSearchItem, SessionSummary, HostApi, EventsApi, MuxFrame, HostFrame,
|
||||
|
||||
12
packages/client/connection/src/loopback-hostname.ts
Normal file
12
packages/client/connection/src/loopback-hostname.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Whether a normalized URL hostname names the local loopback authority.
|
||||
* @param hostname - WHATWG URL hostname (IPv6 literals retain brackets).
|
||||
* @returns true for localhost, IPv6 loopback, or any IPv4 address in 127/8.
|
||||
*/
|
||||
export function isLoopbackHostname(hostname: string): boolean {
|
||||
if (hostname === 'localhost' || hostname === '[::1]') return true
|
||||
const parts = hostname.split('.')
|
||||
return parts.length === 4
|
||||
&& parts[0] === '127'
|
||||
&& parts.every(part => /^\d{1,3}$/.test(part) && Number(part) <= 255)
|
||||
}
|
||||
18
packages/client/connection/tests/loopback-hostname.spec.ts
Normal file
18
packages/client/connection/tests/loopback-hostname.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
/** Shared loopback-hostname semantics for the Host fence and browser UI. */
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { isLoopbackHostname } from '../src/loopback-hostname.ts'
|
||||
|
||||
describe('isLoopbackHostname', () => {
|
||||
it('accepts localhost, IPv6 loopback, and the whole IPv4 127/8 block', () => {
|
||||
for (const hostname of ['localhost', '[::1]', '127.0.0.1', '127.8.9.10', '127.255.255.255']) {
|
||||
expect(isLoopbackHostname(hostname)).toBe(true)
|
||||
}
|
||||
})
|
||||
|
||||
it('refuses malformed and non-loopback hostnames', () => {
|
||||
for (const hostname of ['remote.localhost', '::1', '128.0.0.1', '127.0.0', '127.0.0.256', '127.0.0.-1']) {
|
||||
expect(isLoopbackHostname(hostname)).toBe(false)
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import { deferRegistration } from '@deepseek-ai/dsh-client-ui-slots'
|
||||
import type { ConnectionHandle } from '@deepseek-ai/dsh-client-connection/client'
|
||||
import { isLoopbackHostname, type ConnectionHandle } from '@deepseek-ai/dsh-client-connection/client'
|
||||
import { bindSnapshotSelector } from '@deepseek-ai/dsh-client-web-react'
|
||||
// Type-only: pulls the shell's SlotMap merges (trigger/header/section/item).
|
||||
import type {} from '@deepseek-ai/dsh-client-ui-settings/client'
|
||||
@@ -41,14 +41,6 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
|
||||
/** Dictionary namespace owned by this plugin (shell chrome + General copy). */
|
||||
const NS = 'settings'
|
||||
|
||||
function isLoopbackHostname(hostname: string): boolean {
|
||||
if (hostname === 'localhost' || hostname === '[::1]') return true
|
||||
const parts = hostname.split('.')
|
||||
return parts.length === 4
|
||||
&& parts[0] === '127'
|
||||
&& parts.every(part => /^\d{1,3}$/.test(part) && Number(part) <= 255)
|
||||
}
|
||||
|
||||
function welcomePersistence(): 'host' | 'memory' {
|
||||
return typeof location === 'undefined' || isLoopbackHostname(location.hostname) ? 'host' : 'memory'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user