mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-30 02:40:18 +00:00
fix: support non-standard scheme origins (tauri://, capacitor://) in Control UI allowlist
The URL parser returns origin="null" for non-standard schemes like tauri:// and capacitor://, causing allowlist entries like "tauri://localhost" to never match. This broke Tauri iOS apps connecting to the gateway via Tailscale. Fall back to raw lowercased string comparison when URL.origin is "null", so non-standard scheme origins match their allowlist entries correctly. Adds test coverage for tauri://, capacitor://, case-insensitive matching, and rejection of unlisted non-standard origins.
This commit is contained in:
@@ -100,4 +100,46 @@ describe("checkBrowserOrigin", () => {
|
|||||||
});
|
});
|
||||||
expect(result.ok).toBe(true);
|
expect(result.ok).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("accepts non-standard scheme origins (tauri://) via raw string fallback", () => {
|
||||||
|
const result = checkBrowserOrigin({
|
||||||
|
requestHost: "gateway.tailnet.ts.net",
|
||||||
|
origin: "tauri://localhost",
|
||||||
|
allowedOrigins: ["tauri://localhost"],
|
||||||
|
});
|
||||||
|
expect(result.ok).toBe(true);
|
||||||
|
if (result.ok) {
|
||||||
|
expect(result.matchedBy).toBe("allowlist");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("accepts non-standard scheme origins (capacitor://) via raw string fallback", () => {
|
||||||
|
const result = checkBrowserOrigin({
|
||||||
|
requestHost: "gateway.tailnet.ts.net",
|
||||||
|
origin: "capacitor://localhost",
|
||||||
|
allowedOrigins: ["capacitor://localhost"],
|
||||||
|
});
|
||||||
|
expect(result.ok).toBe(true);
|
||||||
|
if (result.ok) {
|
||||||
|
expect(result.matchedBy).toBe("allowlist");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("rejects non-standard scheme origins not in allowlist", () => {
|
||||||
|
const result = checkBrowserOrigin({
|
||||||
|
requestHost: "gateway.tailnet.ts.net",
|
||||||
|
origin: "tauri://localhost",
|
||||||
|
allowedOrigins: ["https://control.example.com"],
|
||||||
|
});
|
||||||
|
expect(result.ok).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("matches non-standard scheme origins case-insensitively", () => {
|
||||||
|
const result = checkBrowserOrigin({
|
||||||
|
requestHost: "gateway.tailnet.ts.net",
|
||||||
|
origin: "Tauri://Localhost",
|
||||||
|
allowedOrigins: ["tauri://localhost"],
|
||||||
|
});
|
||||||
|
expect(result.ok).toBe(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,17 +9,23 @@ type OriginCheckResult =
|
|||||||
|
|
||||||
function parseOrigin(
|
function parseOrigin(
|
||||||
originRaw?: string,
|
originRaw?: string,
|
||||||
): { origin: string; host: string; hostname: string } | null {
|
): { origin: string; host: string; hostname: string; raw: string } | null {
|
||||||
const trimmed = (originRaw ?? "").trim();
|
const trimmed = (originRaw ?? "").trim();
|
||||||
if (!trimmed || trimmed === "null") {
|
if (!trimmed || trimmed === "null") {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const url = new URL(trimmed);
|
const url = new URL(trimmed);
|
||||||
|
const raw = trimmed.toLowerCase();
|
||||||
|
// Non-standard schemes (e.g. tauri://, capacitor://) produce a "null" origin
|
||||||
|
// from the URL parser. Preserve the raw input for allowlist matching so
|
||||||
|
// configured entries like "tauri://localhost" can still match.
|
||||||
|
const origin = url.origin === "null" ? raw : url.origin.toLowerCase();
|
||||||
return {
|
return {
|
||||||
origin: url.origin.toLowerCase(),
|
origin,
|
||||||
host: url.host.toLowerCase(),
|
host: url.host.toLowerCase(),
|
||||||
hostname: url.hostname.toLowerCase(),
|
hostname: url.hostname.toLowerCase(),
|
||||||
|
raw,
|
||||||
};
|
};
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
Reference in New Issue
Block a user