Restrict external browser URLs
This commit is contained in:
1 parent
d148146b91
commit
91991f7c4a
3 files changed
+25
-7
No files matched your search
@@ -0,0 +1,6 @@
|
||||
export function normalizeExternalBrowserUrl(value: string): string {
|
||||
const url = new URL(value);
|
||||
if (url.protocol !== "https:") throw new Error("External URLs must use HTTPS.");
|
||||
if (url.username || url.password) throw new Error("External URLs must not include credentials.");
|
||||
return url.toString();
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import { KnowledgeService } from "../services/knowledge-service";
|
||||
import { AutomationService } from "../services/automation-service";
|
||||
import { MeetingService } from "../services/meeting-service";
|
||||
import { SprintService } from "../services/sprint-service";
|
||||
import { normalizeExternalBrowserUrl } from "./external-url";
|
||||
|
||||
export interface IpcDeps {
|
||||
connection: DatabaseConnection;
|
||||
@@ -177,8 +178,9 @@ export function registerIpcHandlers(deps: IpcDeps): void {
|
||||
{ providerKey: input.providerKey, enterpriseDomain: input.enterpriseDomain ?? null },
|
||||
{
|
||||
onAuth: ({ url, instructions }) => {
|
||||
void shell.openExternal(url);
|
||||
emit({ phase: "authorize", url, instructions });
|
||||
const browserUrl = normalizeExternalBrowserUrl(url);
|
||||
void shell.openExternal(browserUrl);
|
||||
emit({ phase: "authorize", url: browserUrl, instructions });
|
||||
},
|
||||
onProgress: (message) => emit({ phase: "progress", message }),
|
||||
},
|
||||
@@ -209,11 +211,7 @@ export function registerIpcHandlers(deps: IpcDeps): void {
|
||||
});
|
||||
ipcMain.handle(IPC_CHANNELS.openExternalUrl, async (_event, url: string) => {
|
||||
try {
|
||||
const parsed = new URL(url);
|
||||
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
||||
return fail("external_url_blocked", "Only http(s) URLs may be opened externally.");
|
||||
}
|
||||
await shell.openExternal(parsed.toString());
|
||||
await shell.openExternal(normalizeExternalBrowserUrl(url));
|
||||
return ok(null);
|
||||
} catch (error) {
|
||||
return fail("external_url_failed", error instanceof Error ? error.message : String(error));
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { normalizeExternalBrowserUrl } from "../../src/main/app/external-url";
|
||||
|
||||
describe("external browser URL validation", () => {
|
||||
test("normalizes HTTPS URLs and preserves query strings", () => {
|
||||
expect(normalizeExternalBrowserUrl("https://example.com/docs?q=agent")).toBe("https://example.com/docs?q=agent");
|
||||
});
|
||||
|
||||
test("rejects non-HTTPS and credential-bearing URLs", () => {
|
||||
expect(() => normalizeExternalBrowserUrl("http://example.com")).toThrow("External URLs must use HTTPS.");
|
||||
expect(() => normalizeExternalBrowserUrl("mailto:security@example.com")).toThrow("External URLs must use HTTPS.");
|
||||
expect(() => normalizeExternalBrowserUrl("https://user:token@example.com")).toThrow("External URLs must not include credentials.");
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user