Skip to content

Commit bf1d490

Browse files
committed
fix(browser): relax default hostname SSRF guard
1 parent a743b30 commit bf1d490

4 files changed

Lines changed: 22 additions & 6 deletions

File tree

extensions/browser/src/browser/navigation-guard.test.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,18 @@ describe("browser navigation guard", () => {
128128
expect(lookupFn).not.toHaveBeenCalled();
129129
});
130130

131+
it("allows hostname navigation when the default strict policy object is present", async () => {
132+
const lookupFn = createLookupFn("93.184.216.34");
133+
await expect(
134+
assertBrowserNavigationAllowed({
135+
url: "https://example.com",
136+
lookupFn,
137+
ssrfPolicy: {},
138+
}),
139+
).resolves.toBeUndefined();
140+
expect(lookupFn).toHaveBeenCalledWith("example.com", { all: true });
141+
});
142+
131143
it("allows explicitly allowed hostnames in strict mode", async () => {
132144
const lookupFn = createLookupFn("93.184.216.34");
133145
await expect(
@@ -300,8 +312,11 @@ describe("browser navigation guard", () => {
300312
).resolves.toBeUndefined();
301313
});
302314

303-
it("treats default browser SSRF mode as requiring redirect-hop inspection", () => {
304-
expect(requiresInspectableBrowserNavigationRedirects()).toBe(true);
315+
it("requires redirect-hop inspection only in explicit strict mode", () => {
316+
expect(requiresInspectableBrowserNavigationRedirects()).toBe(false);
317+
expect(
318+
requiresInspectableBrowserNavigationRedirects({ dangerouslyAllowPrivateNetwork: false }),
319+
).toBe(true);
305320
expect(requiresInspectableBrowserNavigationRedirects({ allowPrivateNetwork: true })).toBe(
306321
false,
307322
);

extensions/browser/src/browser/navigation-guard.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export function withBrowserNavigationPolicy(
4343
}
4444

4545
export function requiresInspectableBrowserNavigationRedirects(ssrfPolicy?: SsrFPolicy): boolean {
46-
return !isPrivateNetworkAllowedByPolicy(ssrfPolicy);
46+
return ssrfPolicy?.dangerouslyAllowPrivateNetwork === false;
4747
}
4848

4949
export function requiresInspectableBrowserNavigationRedirectsForUrl(
@@ -122,6 +122,7 @@ export async function assertBrowserNavigationAllowed(
122122
// the same address that passed policy checks.
123123
if (
124124
opts.ssrfPolicy &&
125+
opts.ssrfPolicy.dangerouslyAllowPrivateNetwork === false &&
125126
!isPrivateNetworkAllowedByPolicy(opts.ssrfPolicy) &&
126127
!isIpLiteralHostname(parsed.hostname) &&
127128
!isExplicitlyAllowedBrowserHostname(parsed.hostname, opts.ssrfPolicy)

extensions/browser/src/browser/routes/tabs.attach-only.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ describe("browser tab routes attachOnly loopback profiles", () => {
4242
{
4343
id: "PAGE-1",
4444
title: "WordPress",
45-
url: "https://example.test/wp-login.php",
45+
url: "https://example.com/wp-login.php",
4646
webSocketDebuggerUrl: "ws://127.0.0.1:9222/devtools/page/PAGE-1",
4747
type: "page",
4848
},
@@ -73,7 +73,7 @@ describe("browser tab routes attachOnly loopback profiles", () => {
7373
{
7474
targetId: "PAGE-1",
7575
title: "WordPress",
76-
url: "",
76+
url: "https://example.com/wp-login.php",
7777
wsUrl: "ws://127.0.0.1:9222/devtools/page/PAGE-1",
7878
type: "page",
7979
},

extensions/browser/src/browser/server-context.remote-profile-tab-ops.fallback.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ describe("browser remote profile fallback and attachOnly behavior", () => {
8080
it("fails closed for remote tab opens in strict mode without Playwright", async () => {
8181
vi.spyOn(deps.pwAiModule, "getPwAiModule").mockResolvedValue(null);
8282
const { state, remote, fetchMock } = deps.createRemoteRouteHarness();
83-
state.resolved.ssrfPolicy = {};
83+
state.resolved.ssrfPolicy = { dangerouslyAllowPrivateNetwork: false };
8484

8585
await expect(remote.openTab("https://example.com")).rejects.toBeInstanceOf(
8686
deps.InvalidBrowserNavigationUrlError,

0 commit comments

Comments
 (0)