feat(inspector): add local network access support for HTTPS to HTTP connections

This commit is contained in:
Nathan Flurry 2026-01-28 01:37:56 -08:00
parent e984d07c28
commit d1cbd20b83
4 changed files with 83 additions and 2 deletions

View file

@ -341,6 +341,20 @@
color: var(--success);
}
.banner.warning {
display: flex;
align-items: flex-start;
gap: 8px;
background: rgba(255, 159, 10, 0.1);
border-left: 3px solid var(--warning);
color: var(--warning);
}
.banner.warning svg {
flex-shrink: 0;
margin-top: 1px;
}
.spinner {
width: 14px;
height: 14px;

View file

@ -154,8 +154,14 @@ export default function App() {
};
let logged = false;
// Add targetAddressSpace for local network access from HTTPS
const fetchInit = {
...init,
targetAddressSpace: "loopback"
};
try {
const response = await fetch(input, init);
const response = await fetch(input, fetchInit);
logRequest({ ...entry, status: response.status });
logged = true;
return response;

View file

@ -1,4 +1,5 @@
import { Zap } from "lucide-react";
import { AlertTriangle, Zap } from "lucide-react";
import { isHttpsToHttpConnection, isLocalNetworkTarget } from "../lib/permissions";
const ConnectScreen = ({
endpoint,
@ -50,6 +51,18 @@ const ConnectScreen = ({
{connectError && <div className="banner error">{connectError}</div>}
{isHttpsToHttpConnection(window.location.href, endpoint) &&
isLocalNetworkTarget(endpoint) && (
<div className="banner warning">
<AlertTriangle size={16} />
<span>
Connecting from HTTPS to a local HTTP server requires{" "}
<strong>local network access</strong> permission. Your browser may prompt you to
allow this connection.
</span>
</div>
)}
<label className="field">
<span className="label">Endpoint</span>
<input

View file

@ -0,0 +1,48 @@
export const askForLocalNetworkAccess = async (): Promise<boolean> => {
try {
const status = await navigator.permissions.query({
// @ts-expect-error - local-network-access is not in standard types
name: "local-network-access"
});
if (status.state === "granted") {
return true;
}
if (status.state === "denied") {
return false;
}
// If promptable, return true - browser will prompt on first request
return true;
} catch {
// Permissions API not supported or permission not recognized - try anyway
return true;
}
};
export const isHttpsToHttpConnection = (pageUrl: string, targetUrl: string): boolean => {
try {
const page = new URL(pageUrl);
const target = new URL(targetUrl);
return page.protocol === "https:" && target.protocol === "http:";
} catch {
return false;
}
};
export const isLocalNetworkTarget = (targetUrl: string): boolean => {
try {
const url = new URL(targetUrl);
const hostname = url.hostname.toLowerCase();
return (
hostname === "localhost" ||
hostname === "127.0.0.1" ||
hostname === "::1" ||
hostname.endsWith(".local") ||
// Private IPv4 ranges
/^10\./.test(hostname) ||
/^172\.(1[6-9]|2[0-9]|3[0-1])\./.test(hostname) ||
/^192\.168\./.test(hostname)
);
} catch {
return false;
}
};