mirror of
https://github.com/Nezumi-2711/9router.git
synced 2026-09-22 13:38:31 +00:00
Fix : Antigravity MITM
This commit is contained in:
+25
-26
@@ -4,7 +4,7 @@ const path = require("path");
|
||||
const dns = require("dns");
|
||||
const { promisify } = require("util");
|
||||
const { execSync } = require("child_process");
|
||||
const { log, err } = require("./logger");
|
||||
const { log, err, dumpRequest, createResponseDumper } = require("./logger");
|
||||
const { TARGET_HOSTS, URL_PATTERNS, MODEL_SYNONYMS, getToolForHost } = require("./config");
|
||||
const { DATA_DIR, MITM_DIR } = require("./paths");
|
||||
const { getCertForDomain } = require("./cert/generate");
|
||||
@@ -12,12 +12,9 @@ const { getCertForDomain } = require("./cert/generate");
|
||||
const DB_FILE = path.join(DATA_DIR, "db.json");
|
||||
const LOCAL_PORT = 443;
|
||||
const IS_WIN = process.platform === "win32";
|
||||
const ENABLE_FILE_LOG = true;
|
||||
const LOG_DIR = path.join(DATA_DIR, "logs", "mitm");
|
||||
const ENABLE_FILE_LOG = false;
|
||||
const INTERNAL_REQUEST_HEADER = { name: "x-request-source", value: "local" };
|
||||
|
||||
if (ENABLE_FILE_LOG && !fs.existsSync(LOG_DIR)) fs.mkdirSync(LOG_DIR, { recursive: true });
|
||||
|
||||
// Load handlers — dev/ overrides handlers/ for private implementations
|
||||
function loadHandler(name) {
|
||||
try { return require(`./dev/${name}`); } catch {}
|
||||
@@ -34,13 +31,17 @@ const handlers = {
|
||||
// ── SSL / SNI ─────────────────────────────────────────────────
|
||||
|
||||
const certCache = new Map();
|
||||
let rootCAPem;
|
||||
|
||||
function sniCallback(servername, cb) {
|
||||
try {
|
||||
if (certCache.has(servername)) return cb(null, certCache.get(servername));
|
||||
const certData = getCertForDomain(servername);
|
||||
if (!certData) return cb(new Error(`Failed to generate cert for ${servername}`));
|
||||
const ctx = require("tls").createSecureContext({ key: certData.key, cert: certData.cert });
|
||||
const ctx = require("tls").createSecureContext({
|
||||
key: certData.key,
|
||||
cert: `${certData.cert}\n${rootCAPem}`
|
||||
});
|
||||
certCache.set(servername, ctx);
|
||||
log(`🔐 Cert generated: ${servername}`);
|
||||
cb(null, ctx);
|
||||
@@ -52,11 +53,10 @@ function sniCallback(servername, cb) {
|
||||
|
||||
let sslOptions;
|
||||
try {
|
||||
sslOptions = {
|
||||
key: fs.readFileSync(path.join(MITM_DIR, "rootCA.key")),
|
||||
cert: fs.readFileSync(path.join(MITM_DIR, "rootCA.crt")),
|
||||
SNICallback: sniCallback
|
||||
};
|
||||
const rootKey = fs.readFileSync(path.join(MITM_DIR, "rootCA.key"));
|
||||
const rootCert = fs.readFileSync(path.join(MITM_DIR, "rootCA.crt"));
|
||||
rootCAPem = rootCert.toString("utf8");
|
||||
sslOptions = { key: rootKey, cert: rootCert, SNICallback: sniCallback };
|
||||
} catch (e) {
|
||||
err(`Root CA not found: ${e.message}`);
|
||||
process.exit(1);
|
||||
@@ -116,24 +116,16 @@ function getMappedModel(tool, model) {
|
||||
} catch { return null; }
|
||||
}
|
||||
|
||||
function saveRequestLog(url, bodyBuffer) {
|
||||
if (!ENABLE_FILE_LOG) return;
|
||||
try {
|
||||
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
||||
const slug = url.replace(/[^a-zA-Z0-9]/g, "_").substring(0, 60);
|
||||
const body = JSON.parse(bodyBuffer.toString());
|
||||
fs.writeFileSync(path.join(LOG_DIR, `${ts}_${slug}.json`), JSON.stringify(body, null, 2));
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
|
||||
/**
|
||||
* Forward request to real upstream.
|
||||
* Optional onResponse(rawBuffer) callback — if provided, tees the response
|
||||
* so it's both forwarded to client AND passed to the callback for inspection.
|
||||
* Also tees full stream into a dump file when ENABLE_FILE_LOG is on.
|
||||
*/
|
||||
async function passthrough(req, res, bodyBuffer, onResponse) {
|
||||
const targetHost = (req.headers.host || TARGET_HOSTS[0]).split(":")[0];
|
||||
const targetIP = await resolveTargetIP(targetHost);
|
||||
const dumper = ENABLE_FILE_LOG ? createResponseDumper(req, "passthrough") : null;
|
||||
|
||||
const forwardReq = https.request({
|
||||
hostname: targetIP,
|
||||
@@ -145,23 +137,30 @@ async function passthrough(req, res, bodyBuffer, onResponse) {
|
||||
rejectUnauthorized: false
|
||||
}, (forwardRes) => {
|
||||
res.writeHead(forwardRes.statusCode, forwardRes.headers);
|
||||
if (dumper) dumper.writeHeader(forwardRes.statusCode, forwardRes.headers);
|
||||
|
||||
if (!onResponse) {
|
||||
if (!onResponse && !dumper) {
|
||||
forwardRes.pipe(res);
|
||||
return;
|
||||
}
|
||||
|
||||
// Tee: forward to client AND buffer for callback
|
||||
// Tee: forward to client AND optionally buffer + dump
|
||||
const chunks = [];
|
||||
forwardRes.on("data", chunk => { chunks.push(chunk); res.write(chunk); });
|
||||
forwardRes.on("data", chunk => {
|
||||
if (dumper) dumper.writeChunk(chunk);
|
||||
if (onResponse) chunks.push(chunk);
|
||||
res.write(chunk);
|
||||
});
|
||||
forwardRes.on("end", () => {
|
||||
if (dumper) dumper.end();
|
||||
res.end();
|
||||
try { onResponse(Buffer.concat(chunks), forwardRes.headers); } catch { /* ignore */ }
|
||||
if (onResponse) try { onResponse(Buffer.concat(chunks), forwardRes.headers); } catch { /* ignore */ }
|
||||
});
|
||||
});
|
||||
|
||||
forwardReq.on("error", (e) => {
|
||||
err(`Passthrough error: ${e.message}`);
|
||||
if (dumper) { dumper.writeChunk(`\n[ERROR] ${e.message}\n`); dumper.end(); }
|
||||
if (!res.headersSent) res.writeHead(502);
|
||||
res.end("Bad Gateway");
|
||||
});
|
||||
@@ -181,7 +180,7 @@ const server = https.createServer(sslOptions, async (req, res) => {
|
||||
}
|
||||
|
||||
const bodyBuffer = await collectBodyRaw(req);
|
||||
if (bodyBuffer.length > 0) saveRequestLog(req.url, bodyBuffer);
|
||||
if (ENABLE_FILE_LOG) dumpRequest(req, bodyBuffer, "raw");
|
||||
|
||||
// Anti-loop: skip requests from 9Router
|
||||
if (req.headers[INTERNAL_REQUEST_HEADER.name] === INTERNAL_REQUEST_HEADER.value) {
|
||||
|
||||
Reference in New Issue
Block a user