mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-09-24 23:05:49 +00:00
fix(electron): preserve trusted navigation location
This commit is contained in:
@@ -206,6 +206,60 @@ describe("navigation event handlers", () => {
|
||||
|
||||
expect(on).toHaveBeenCalledWith("will-navigate", expect.any(Function));
|
||||
expect(on).toHaveBeenCalledWith("will-redirect", expect.any(Function));
|
||||
expect(on).toHaveBeenCalledWith("did-navigate", expect.any(Function));
|
||||
expect(setWindowOpenHandler).toHaveBeenCalledWith(expect.any(Function));
|
||||
});
|
||||
|
||||
it("does not trust a renderer-mutated URL as an exact reload", () => {
|
||||
let currentUrl = "file:///opt/Recordly/dist/index.html?windowType=editor";
|
||||
const on = vi.fn();
|
||||
const webContents = {
|
||||
getURL: () => currentUrl,
|
||||
on,
|
||||
setWindowOpenHandler: vi.fn(),
|
||||
};
|
||||
const openExternal = vi.fn(async () => undefined);
|
||||
|
||||
hardenWebContentsNavigation(webContents, openExternal);
|
||||
|
||||
// history.replaceState() changes getURL() without crossing a document-navigation boundary.
|
||||
currentUrl = "file:///opt/Recordly/dist/index.html?windowType=source-selector";
|
||||
const willNavigate = on.mock.calls.find(([eventName]) => eventName === "will-navigate")?.[1];
|
||||
if (typeof willNavigate !== "function") {
|
||||
throw new Error("will-navigate handler was not registered");
|
||||
}
|
||||
|
||||
const preventDefault = vi.fn();
|
||||
willNavigate({ url: currentUrl, preventDefault });
|
||||
|
||||
expect(preventDefault).toHaveBeenCalledOnce();
|
||||
expect(openExternal).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("trusts an exact reload after a completed document navigation", () => {
|
||||
const on = vi.fn();
|
||||
const webContents = {
|
||||
getURL: () => "",
|
||||
on,
|
||||
setWindowOpenHandler: vi.fn(),
|
||||
};
|
||||
|
||||
hardenWebContentsNavigation(
|
||||
webContents,
|
||||
vi.fn(async () => undefined),
|
||||
);
|
||||
|
||||
const didNavigate = on.mock.calls.find(([eventName]) => eventName === "did-navigate")?.[1];
|
||||
const willNavigate = on.mock.calls.find(([eventName]) => eventName === "will-navigate")?.[1];
|
||||
if (typeof didNavigate !== "function" || typeof willNavigate !== "function") {
|
||||
throw new Error("navigation handlers were not registered");
|
||||
}
|
||||
|
||||
const loadedUrl = "file:///opt/Recordly/dist/index.html?windowType=editor";
|
||||
didNavigate({}, loadedUrl);
|
||||
const preventDefault = vi.fn();
|
||||
willNavigate({ url: loadedUrl, preventDefault });
|
||||
|
||||
expect(preventDefault).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -88,21 +88,21 @@ const defaultReportOpenError: ReportOpenError = (url, error) => {
|
||||
};
|
||||
|
||||
export function createWillNavigateHandler(
|
||||
getCurrentUrl: () => string,
|
||||
getTrustedRendererUrl: () => string,
|
||||
openExternal: OpenExternal,
|
||||
reportOpenError: ReportOpenError = defaultReportOpenError,
|
||||
) {
|
||||
return (event: NavigationEvent): void => {
|
||||
const currentUrl = getCurrentUrl();
|
||||
const trustedRendererUrl = getTrustedRendererUrl();
|
||||
// Preserve an exact reload, but freeze all renderer-selected destination changes,
|
||||
// including same-origin query mutations that can carry privileged local paths.
|
||||
if (isExactRendererLocation(currentUrl, event.url)) {
|
||||
if (isExactRendererLocation(trustedRendererUrl, event.url)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The internal-target check only prevents app URLs from leaking into the system browser.
|
||||
event.preventDefault();
|
||||
if (isInternalRendererTarget(currentUrl, event.url)) {
|
||||
if (isInternalRendererTarget(trustedRendererUrl, event.url)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -134,9 +134,15 @@ export function hardenWebContentsNavigation(
|
||||
openExternal: OpenExternal,
|
||||
reportOpenError: ReportOpenError = defaultReportOpenError,
|
||||
): void {
|
||||
// Renderer history APIs mutate getURL() without a document navigation. Keep the last
|
||||
// main-frame document URL as the reload trust boundary instead of trusting that live value.
|
||||
let trustedRendererUrl = webContents.getURL();
|
||||
webContents.on("did-navigate", (_event, url) => {
|
||||
trustedRendererUrl = url;
|
||||
});
|
||||
webContents.on(
|
||||
"will-navigate",
|
||||
createWillNavigateHandler(() => webContents.getURL(), openExternal, reportOpenError),
|
||||
createWillNavigateHandler(() => trustedRendererUrl, openExternal, reportOpenError),
|
||||
);
|
||||
webContents.on("will-redirect", createWillRedirectHandler());
|
||||
webContents.setWindowOpenHandler(
|
||||
|
||||
Reference in New Issue
Block a user