From 90a47c3f29743cb307cd9f4c94d9398fae9b3ccc Mon Sep 17 00:00:00 2001 From: decolua Date: Mon, 18 May 2026 15:27:29 +0700 Subject: [PATCH] Fix MITM --- src/mitm/config.js | 13 ++++++++++++- src/mitm/manager.js | 5 +++-- src/mitm/server.js | 4 ++-- src/shared/constants/providers.js | 4 ++-- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/mitm/config.js b/src/mitm/config.js index f047106..452704f 100644 --- a/src/mitm/config.js +++ b/src/mitm/config.js @@ -1,7 +1,18 @@ // All intercepted domains + URL patterns per tool +const fs = require("fs"); + const IS_DEV = process.env.NODE_ENV === "development"; +// Resolve lsof absolute path — packaged apps / sudo secure_path may strip /usr/sbin from PATH +const LSOF_BIN = (() => { + if (process.platform === "win32") return null; + for (const p of ["/usr/sbin/lsof", "/usr/bin/lsof", "/sbin/lsof"]) { + try { fs.accessSync(p, fs.constants.X_OK); return p; } catch { /* try next */ } + } + return "lsof"; // last-resort fallback (depends on PATH) +})(); + const TARGET_HOSTS = [ "daily-cloudcode-pa.googleapis.com", "cloudcode-pa.googleapis.com", @@ -53,4 +64,4 @@ function getToolForHost(host) { return null; } -module.exports = { IS_DEV, TARGET_HOSTS, URL_PATTERNS, MODEL_SYNONYMS, MODEL_PATTERNS, LOG_BLACKLIST_URL_PARTS, getToolForHost }; +module.exports = { IS_DEV, LSOF_BIN, TARGET_HOSTS, URL_PATTERNS, MODEL_SYNONYMS, MODEL_PATTERNS, LOG_BLACKLIST_URL_PARTS, getToolForHost }; diff --git a/src/mitm/manager.js b/src/mitm/manager.js index 6ff000d..be13603 100644 --- a/src/mitm/manager.js +++ b/src/mitm/manager.js @@ -15,6 +15,7 @@ const { installCert, uninstallCert } = require("./cert/install"); const { isCertExpired } = require("./cert/rootCA"); const { DATA_DIR, MITM_DIR } = require("./paths"); const { log, err } = require("./logger"); +const { LSOF_BIN } = require("./config"); const DEFAULT_MITM_ROUTER_BASE = "http://localhost:20128"; @@ -108,7 +109,7 @@ function getProcessUsingPort443() { if (processMatch) return processMatch[1].replace(".exe", ""); } } else { - const result = execSync("lsof -i :443", { encoding: "utf8", windowsHide: true }); + const result = execSync(`${LSOF_BIN} -i :443`, { encoding: "utf8", windowsHide: true }); const lines = result.trim().split("\n"); if (lines.length > 1) return lines[1].split(/\s+/)[0]; } @@ -298,7 +299,7 @@ function getPort443Owner(sudoPassword) { }); } else { // Only find process actually LISTENING on TCP port 443 - exec("lsof -nP -iTCP:443 -sTCP:LISTEN -t", { windowsHide: true }, (err, stdout) => { + exec(`${LSOF_BIN} -nP -iTCP:443 -sTCP:LISTEN -t`, { windowsHide: true }, (err, stdout) => { if (err || !stdout?.trim()) return resolve(null); const pid = parseInt(stdout.trim().split("\n")[0], 10); if (!pid || isNaN(pid)) return resolve(null); diff --git a/src/mitm/server.js b/src/mitm/server.js index f4f76e3..fe14321 100644 --- a/src/mitm/server.js +++ b/src/mitm/server.js @@ -5,7 +5,7 @@ const dns = require("dns"); const { promisify } = require("util"); const { execSync } = require("child_process"); const { log, err, dumpRequest, createResponseDumper, clearDumpDir } = require("./logger"); -const { IS_DEV, TARGET_HOSTS, URL_PATTERNS, MODEL_SYNONYMS, MODEL_PATTERNS, getToolForHost } = require("./config"); +const { IS_DEV, LSOF_BIN, TARGET_HOSTS, URL_PATTERNS, MODEL_SYNONYMS, MODEL_PATTERNS, getToolForHost } = require("./config"); const { DATA_DIR, MITM_DIR } = require("./paths"); const { getCertForDomain } = require("./cert/generate"); const { getMitmAlias } = require("./dbReader"); @@ -231,7 +231,7 @@ function killPort(port) { if (!out) return; pidList = out.split(/\r?\n/).map(s => s.trim()).filter(p => p && Number(p) !== process.pid && Number(p) > 4); } else { - const out = execSync(`lsof -nP -iTCP:${port} -sTCP:LISTEN -t`, { encoding: "utf-8", windowsHide: true }).trim(); + const out = execSync(`${LSOF_BIN} -nP -iTCP:${port} -sTCP:LISTEN -t`, { encoding: "utf-8", windowsHide: true }).trim(); if (!out) return; pidList = out.split("\n").filter(p => p && Number(p) !== process.pid); } diff --git a/src/shared/constants/providers.js b/src/shared/constants/providers.js index 8fb377b..a998430 100644 --- a/src/shared/constants/providers.js +++ b/src/shared/constants/providers.js @@ -5,12 +5,12 @@ const RISK_NOTICE = "⚠️ Risk Notice: This provider uses a subscription/OAuth // Free Providers (kiro first, iflow last) export const FREE_PROVIDERS = { kiro: { id: "kiro", alias: "kr", name: "Kiro AI", icon: "psychology_alt", color: "#FF6B35", deprecated: true, deprecationNotice: RISK_NOTICE, website: "https://kiro.dev", notice: { signupUrl: "https://kiro.dev" } }, - qwen: { id: "qwen", alias: "qw", name: "Qwen Code", icon: "psychology", color: "#10B981", mediaPriority: 999, deprecated: true, deprecationNotice: "Qwen OAuth free tier was discontinued by Alibaba on 2026-04-15. New connections will not work.", website: "https://chat.qwen.ai", notice: { signupUrl: "https://chat.qwen.ai" }, serviceKinds: ["llm", "tts"], ttsConfig: { baseUrl: "http://localhost:8000/v1/audio/speech", authType: "none", authHeader: "none", format: "openai", models: [{ id: "qwen3-tts", name: "Qwen3 TTS" }] } }, + // qwen: { id: "qwen", alias: "qw", name: "Qwen Code", icon: "psychology", color: "#10B981", mediaPriority: 999, deprecated: true, deprecationNotice: "Qwen OAuth free tier was discontinued by Alibaba on 2026-04-15. New connections will not work.", website: "https://chat.qwen.ai", notice: { signupUrl: "https://chat.qwen.ai" }, serviceKinds: ["llm", "tts"], ttsConfig: { baseUrl: "http://localhost:8000/v1/audio/speech", authType: "none", authHeader: "none", format: "openai", models: [{ id: "qwen3-tts", name: "Qwen3 TTS" }] } }, "gemini-cli": { id: "gemini-cli", alias: "gc", name: "Gemini CLI", icon: "terminal", color: "#4285F4", deprecated: true, deprecationNotice: RISK_NOTICE, website: "https://github.com/google-gemini/gemini-cli", notice: { signupUrl: "https://github.com/google-gemini/gemini-cli" } }, // gitlab: { id: "gitlab", alias: "gl", name: "GitLab Duo", icon: "code", color: "#FC6D26" }, // codebuddy: { id: "codebuddy", alias: "cb", name: "CodeBuddy", icon: "smart_toy", color: "#006EFF" }, // qoder: { id: "qoder", alias: "qd", name: "Qoder AI", icon: "water_drop", color: "#EC4899" }, - iflow: { id: "iflow", alias: "if", name: "iFlow AI", icon: "water_drop", color: "#6366F1", website: "https://iflow.cn", notice: { signupUrl: "https://iflow.cn" } }, + // iflow: { id: "iflow", alias: "if", name: "iFlow AI", icon: "water_drop", color: "#6366F1", website: "https://iflow.cn", notice: { signupUrl: "https://iflow.cn" } }, opencode: { id: "opencode", alias: "oc", name: "OpenCode Free", icon: "terminal", color: "#E87040", textIcon: "OC", noAuth: true, passthroughModels: true, modelsFetcher: { url: "https://opencode.ai/zen/v1/models", type: "opencode-free" } }, };