fix(mitm): gate sudo prompts on server platform (#822)

This commit is contained in:
Rezky Hamid
2026-05-03 15:00:43 +07:00
committed by GitHub
parent a463ee00ff
commit e40f7ffb98
7 changed files with 62 additions and 37 deletions
@@ -88,11 +88,12 @@ export default function AntigravityToolCard({
}
};
// Windows uses UAC dialog, no sudo needed
const isWindows = typeof navigator !== "undefined" && navigator.userAgent?.includes("Windows");
// MITM elevation is decided by the server OS, not by this browser's OS.
const serverIsWindows = status?.isWin === true;
const canRunWithoutPassword = serverIsWindows || status?.hasCachedPassword || status?.needsSudoPassword === false;
const handleStart = () => {
if (isWindows || status?.hasCachedPassword) {
if (canRunWithoutPassword) {
doStart("");
} else {
setShowPasswordModal(true);
@@ -101,7 +102,7 @@ export default function AntigravityToolCard({
};
const handleStop = () => {
if (isWindows || status?.hasCachedPassword) {
if (canRunWithoutPassword) {
doStop("");
} else {
setShowPasswordModal(true);
@@ -385,7 +386,7 @@ export default function AntigravityToolCard({
)}
{/* Windows admin warning */}
{!isRunning && isWindows && (
{!isRunning && serverIsWindows && (
<div className="flex items-center gap-2 px-2 py-1.5 rounded text-xs bg-yellow-500/10 text-yellow-600 border border-yellow-500/20">
<span className="material-symbols-outlined text-[14px]">warning</span>
<span>Windows: Run terminal (9Router) as Administrator to enable MITM</span>
@@ -1,6 +1,6 @@
"use client";
import { useState, useEffect } from "react";
import { useState, useEffect, useCallback } from "react";
import { Card, Button, Badge, Input } from "@/shared/components";
const DEFAULT_MITM_ROUTER_BASE = "http://localhost:20128";
@@ -14,26 +14,17 @@ export default function MitmServerCard({ apiKeys, cloudEnabled, onStatusChange }
const [loading, setLoading] = useState(false);
const [showPasswordModal, setShowPasswordModal] = useState(false);
const [sudoPassword, setSudoPassword] = useState("");
const [selectedApiKey, setSelectedApiKey] = useState("");
const [selectedApiKey, setSelectedApiKey] = useState(() => apiKeys?.[0]?.key || "");
const [pendingAction, setPendingAction] = useState(null);
const [modalError, setModalError] = useState(null);
const [actionError, setActionError] = useState(null);
const [mitmRouterBaseUrl, setMitmRouterBaseUrl] = useState(DEFAULT_MITM_ROUTER_BASE);
const isWindows = typeof navigator !== "undefined" && navigator.userAgent?.includes("Windows");
const serverIsWindows = status?.isWin === true;
const canRunWithoutPassword = serverIsWindows || status?.hasCachedPassword || status?.needsSudoPassword === false;
const isAdmin = status?.isAdmin !== false;
useEffect(() => {
if (apiKeys?.length > 0 && !selectedApiKey) {
setSelectedApiKey(apiKeys[0].key);
}
}, [apiKeys, selectedApiKey]);
useEffect(() => {
fetchStatus();
}, []);
const fetchStatus = async () => {
const fetchStatus = useCallback(async () => {
try {
const res = await fetch("/api/cli-tools/antigravity-mitm");
if (res.ok) {
@@ -47,11 +38,17 @@ export default function MitmServerCard({ apiKeys, cloudEnabled, onStatusChange }
} catch {
setStatus({ running: false, certExists: false, dnsStatus: {} });
}
};
}, [onStatusChange]);
useEffect(() => {
queueMicrotask(() => {
fetchStatus();
});
}, [fetchStatus]);
const handleAction = (action) => {
setActionError(null);
if (isWindows || status?.hasCachedPassword) {
if (canRunWithoutPassword) {
doAction(action, "");
} else {
setPendingAction(action);
@@ -219,7 +216,7 @@ export default function MitmServerCard({ apiKeys, cloudEnabled, onStatusChange }
) : (
<button
onClick={() => handleAction("start")}
disabled={loading || (isWindows && !isAdmin)}
disabled={loading || (serverIsWindows && !isAdmin)}
className="flex w-full items-center justify-center gap-1.5 rounded-lg border border-primary/30 bg-primary/10 px-4 py-2 text-xs font-medium text-primary transition-colors hover:bg-primary/20 disabled:opacity-50 sm:w-auto sm:py-1.5"
>
<span className="material-symbols-outlined text-[16px]">play_circle</span>
@@ -240,7 +237,7 @@ export default function MitmServerCard({ apiKeys, cloudEnabled, onStatusChange }
)}
{/* Windows admin warning */}
{isWindows && !isAdmin && (
{serverIsWindows && !isAdmin && (
<div className="flex items-center gap-2 px-2 py-1.5 rounded text-xs bg-red-500/10 text-red-600 border border-red-500/20">
<span className="material-symbols-outlined text-[14px]">shield_lock</span>
<span>Administrator required restart 9Router as Administrator to use MITM</span>
@@ -18,6 +18,7 @@ export default function MitmToolCard({
serverRunning,
dnsActive,
hasCachedPassword,
needsSudoPassword,
apiKeys,
activeProviders,
hasActiveProviders,
@@ -36,7 +37,7 @@ export default function MitmToolCard({
const [currentEditingAlias, setCurrentEditingAlias] = useState(null);
const mitmHosts = TOOL_HOSTS[tool.id] ?? [];
const isWindows = typeof navigator !== "undefined" && navigator.userAgent?.includes("Windows");
const canRunWithoutPassword = hasCachedPassword || needsSudoPassword === false;
useEffect(() => {
if (isExpanded) loadSavedMappings();
@@ -85,7 +86,7 @@ export default function MitmToolCard({
const handleDnsToggle = () => {
if (!serverRunning) return;
const action = dnsActive ? "disable" : "enable";
if (isWindows || hasCachedPassword) {
if (canRunWithoutPassword) {
doDnsAction(action, "");
} else {
setPendingDnsAction(action);