fix: route every host-browser open through open_url_external
The Rust command existed but nothing called it. All four frontend call sites still used `openUrl` from `@tauri-apps/plugin-opener`, so the environment fix was inert and the three dialogs carried the same Linux bug as the terminal: DockerInstallDialog's docs link, ClaudeAuthModal's sign-in link and UpdateDialog's release link would all have reported success while launching nothing. `openUrlExternal` in tauri-commands.ts is now the single sink. There is no platform branch: Linux gets the sanitized spawn, macOS and Windows reach the same plugin as before but from Rust, and every platform picks up the Rust-side re-validation, which matters because these URLs originate in an untrusted container. Comments in urlRelay.ts and urlDetector.ts that named `openUrl` as the sink they guard are updated to match, and the two test files that mocked `@tauri-apps/plugin-opener` now mock the command instead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,8 +2,10 @@ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
import { render, fireEvent, cleanup, act } from "@testing-library/react";
|
||||
import TerminalView, { supersedes } from "./TerminalView";
|
||||
import { useAppState } from "../../store/appState";
|
||||
import { uploadHostFileToTerminal } from "../../lib/tauri-commands";
|
||||
import { openUrl } from "@tauri-apps/plugin-opener";
|
||||
import {
|
||||
uploadHostFileToTerminal,
|
||||
openUrlExternal,
|
||||
} from "../../lib/tauri-commands";
|
||||
import {
|
||||
chooseSignInTarget,
|
||||
resetBrowserSupportCache,
|
||||
@@ -65,6 +67,7 @@ vi.mock("../../lib/tauri-commands", () => ({
|
||||
uploadHostFileToTerminal: vi.fn(async () => ""),
|
||||
getAuthBridgeStatus: vi.fn(async () => containerEnv.bridge),
|
||||
checkBrowserViewSupport: vi.fn(async () => containerEnv.detection),
|
||||
openUrlExternal: vi.fn(async () => {}),
|
||||
}));
|
||||
|
||||
vi.mock("@tauri-apps/api/event", () => ({
|
||||
@@ -74,10 +77,6 @@ vi.mock("@tauri-apps/api/event", () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("@tauri-apps/plugin-opener", () => ({
|
||||
openUrl: vi.fn(async () => {}),
|
||||
}));
|
||||
|
||||
vi.mock("@tauri-apps/api/webview", () => ({
|
||||
getCurrentWebview: () => ({
|
||||
onDragDropEvent: async (cb: (event: unknown) => unknown) => {
|
||||
@@ -148,8 +147,8 @@ beforeEach(() => {
|
||||
vi.mocked(uploadHostFileToTerminal).mockResolvedValue("/workspace/api/dropped.txt");
|
||||
dragDrop.handler = null;
|
||||
ptyOutput.listeners.clear();
|
||||
vi.mocked(openUrl).mockReset();
|
||||
vi.mocked(openUrl).mockResolvedValue(undefined);
|
||||
vi.mocked(openUrlExternal).mockReset();
|
||||
vi.mocked(openUrlExternal).mockResolvedValue(undefined);
|
||||
containerEnv.bridge = { enabled: false, active_ports: [], conflicts: [] };
|
||||
containerEnv.detection = null;
|
||||
// The Playwright probe is memoized across mounts (it is a container exec), so
|
||||
@@ -760,7 +759,7 @@ describe("TerminalView — a host open that fails says so", () => {
|
||||
}
|
||||
|
||||
it("pushes a toast instead of a console line nobody reads", async () => {
|
||||
vi.mocked(openUrl).mockRejectedValueOnce(new Error("no opener"));
|
||||
vi.mocked(openUrlExternal).mockRejectedValueOnce(new Error("no opener"));
|
||||
await mountWithPrompt();
|
||||
await act(async () => {
|
||||
fireEvent.click(openButton());
|
||||
@@ -775,7 +774,7 @@ describe("TerminalView — a host open that fails says so", () => {
|
||||
it("keeps the prompt on screen, so the other route is still one click away", async () => {
|
||||
// Dismissing first is what this replaced: the toast vanished, nothing
|
||||
// opened, and the URL only existed in the container's transcript.
|
||||
vi.mocked(openUrl).mockRejectedValueOnce(new Error("no opener"));
|
||||
vi.mocked(openUrlExternal).mockRejectedValueOnce(new Error("no opener"));
|
||||
await mountWithPrompt();
|
||||
await act(async () => {
|
||||
fireEvent.click(openButton());
|
||||
@@ -790,7 +789,7 @@ describe("TerminalView — a host open that fails says so", () => {
|
||||
fireEvent.click(openButton());
|
||||
await Promise.resolve();
|
||||
});
|
||||
expect(openUrl).toHaveBeenCalledWith(URL);
|
||||
expect(openUrlExternal).toHaveBeenCalledWith(URL);
|
||||
expect(document.querySelector(URL_TOAST_SELECTOR)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user