import { isIP } from "node:net"; import {
matchesHostnameAllowlist,
normalizeHostname,
} from "openclaw/plugin-sdk/browser-security-runtime"; import {
isPrivateNetworkAllowedByPolicy,
resolvePinnedHostnameWithPolicy,
type LookupFn,
type SsrFPolicy,
} from "../infra/net/ssrf.js";
const NETWORK_NAVIGATION_PROTOCOLS = new Set(["http:", "https:"]); const SAFE_NON_NETWORK_URLS = new Set(["about:blank"]);
function isAllowedNonNetworkNavigationUrl(parsed: URL): boolean { // Keep non-network navigation explicit; about:blank is the only allowed bootstrap URL. return SAFE_NON_NETWORK_URLS.has(parsed.href);
}
function normalizeNavigationUrl(url: string): string { return url.trim();
}
export async function assertBrowserNavigationAllowed(
opts: {
url: string;
lookupFn?: LookupFn;
} & BrowserNavigationPolicyOptions,
): Promise<void> { const rawUrl = normalizeNavigationUrl(opts.url); if (!rawUrl) { thrownew InvalidBrowserNavigationUrlError("url is required");
}
let parsed: URL; try {
parsed = new URL(rawUrl);
} catch { thrownew InvalidBrowserNavigationUrlError(`Invalid URL: ${rawUrl}`);
}
if (!NETWORK_NAVIGATION_PROTOCOLS.has(parsed.protocol)) { if (isAllowedNonNetworkNavigationUrl(parsed)) { return;
} thrownew InvalidBrowserNavigationUrlError(
`Navigation blocked: unsupported protocol "${parsed.protocol}"`,
);
}
// Browser proxy routing hides the final connect target from this process. // Only block when the browser profile is known to be proxy-routed; Gateway // provider proxy env alone is not proof of browser page proxy behavior. if (
opts.browserProxyMode === "explicit-browser-proxy" &&
!isPrivateNetworkAllowedByPolicy(opts.ssrfPolicy)
) { thrownew InvalidBrowserNavigationUrlError( "Navigation blocked: strict browser SSRF policy cannot be enforced while this browser profile is proxy-routed",
);
}
// Browser navigations happen in Chromium's network stack, not Node's. In // strict mode, a hostname-based URL would be resolved twice by different // resolvers, so Node-side pinning cannot guarantee the browser connects to // the same address that passed policy checks. if (
opts.ssrfPolicy &&
opts.ssrfPolicy.dangerouslyAllowPrivateNetwork === false &&
!isPrivateNetworkAllowedByPolicy(opts.ssrfPolicy) &&
!isIpLiteralHostname(parsed.hostname) &&
!isExplicitlyAllowedBrowserHostname(parsed.hostname, opts.ssrfPolicy)
) { thrownew InvalidBrowserNavigationUrlError( "Navigation blocked: strict browser SSRF policy requires an IP-literal URL because browser DNS rebinding protections are unavailable for hostname-based navigation",
);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.