Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/encode-token-fragments.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": patch
---

Encode server Web UI token fragments before adding them to openable URLs.
2 changes: 1 addition & 1 deletion apps/kimi-code/src/cli/sub/server/access-urls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { formatHostForUrl, listNetworkAddresses, type NetworkAddress } from './n
*/
export function buildOpenableUrl(bareOrigin: string, token: string | undefined): string {
const base = bareOrigin.endsWith('/') ? bareOrigin.slice(0, -1) : bareOrigin;
return token === undefined ? `${base}/` : `${base}/#token=${token}`;
return token === undefined ? `${base}/` : `${base}/#token=${encodeURIComponent(token)}`;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion apps/kimi-code/src/tui/commands/web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,5 +88,5 @@ export async function handleWebCommand(host: SlashCommandHost): Promise<void> {
*/
export function webSessionUrl(origin: string, sessionId: string, token?: string): string {
const base = `${origin.replace(/\/+$/, '')}/sessions/${encodeURIComponent(sessionId)}`;
return token === undefined ? base : `${base}#token=${token}`;
return token === undefined ? base : `${base}#token=${encodeURIComponent(token)}`;
}
20 changes: 20 additions & 0 deletions apps/kimi-code/test/cli/server/server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1585,6 +1585,14 @@ describe('buildWebUrl', () => {
expect(parsed.search).not.toContain('abc123');
});

it('encodes token fragment values before Web UI parsing', async () => {
const { buildWebUrl } = await import('#/cli/sub/server/run');
const token = 'a&b a%20b #token';
const url = buildWebUrl('http://127.0.0.1:58627', token);
expect(url).toBe(`http://127.0.0.1:58627/#token=${encodeURIComponent(token)}`);
expect(new URLSearchParams(new URL(url).hash.slice(1)).get('token')).toBe(token);
});

it('normalizes a trailing slash', async () => {
const { buildWebUrl } = await import('#/cli/sub/server/run');
expect(buildWebUrl('http://127.0.0.1:58627/', 't')).toBe(
Expand Down Expand Up @@ -1613,6 +1621,18 @@ describe('accessUrlLines', () => {
]);
});

it('encodes token fragments in access URL lines', async () => {
const { accessUrlLines } = await import('#/cli/sub/server/access-urls');
const token = 'a&b%20c';
const lines = accessUrlLines('127.0.0.1', 58627, token);
expect(lines).toEqual([
{
label: 'Local: ',
url: `http://127.0.0.1:58627/#token=${encodeURIComponent(token)}`,
},
]);
});

it('returns a single URL line for a specific host (no token)', async () => {
const { accessUrlLines } = await import('#/cli/sub/server/access-urls');
const lines = accessUrlLines('192.168.1.5', 58627, undefined);
Expand Down
9 changes: 9 additions & 0 deletions apps/kimi-code/test/tui/commands/web.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,15 @@ describe('webSessionUrl', () => {
);
});

it('encodes bearer token fragments so URLSearchParams can read them back', () => {
const token = 'a&b a%20b #token';
const url = webSessionUrl('http://127.0.0.1:58627', 'abc123', token);
expect(url).toBe(
`http://127.0.0.1:58627/sessions/abc123#token=${encodeURIComponent(token)}`,
);
expect(new URLSearchParams(new URL(url).hash.slice(1)).get('token')).toBe(token);
});

it('omits the fragment when no token is available', () => {
expect(webSessionUrl('http://127.0.0.1:58627', 'abc123', undefined)).toBe(
'http://127.0.0.1:58627/sessions/abc123',
Expand Down