From 4aa3d639cdce19399bb9561035baf37bf15b167b Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Sat, 20 Jun 2026 16:42:57 -0300 Subject: [PATCH] chore(mitm): TEMP AvailableModels capture for #4295 reverse-engineering (NOT FOR MERGE) --- src/mitm/server.cjs | 58 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/src/mitm/server.cjs b/src/mitm/server.cjs index 8be6ad1b76..2c87313e97 100644 --- a/src/mitm/server.cjs +++ b/src/mitm/server.cjs @@ -418,6 +418,57 @@ async function passthrough(req, res, bodyBuffer) { forwardReq.end(); } +// TEMP CAPTURE (#4295) — NOT FOR MERGE. Forwards a Cursor AvailableModels request to +// the real upstream while dumping the full Connect/protobuf request+response (base64) +// so the AvailableModels wire schema can be reverse-engineered. Mirrors passthrough() +// but buffers the response instead of piping it. +async function captureAvailableModels(req, res, bodyBuffer, host) { + let targetIP; + try { + targetIP = await resolveTargetIP(host); + } catch (err) { + console.error(`[CURSOR-CAPTURE] DNS resolve failed for ${host}: ${err.message}`); + if (!res.headersSent) res.writeHead(502); + return res.end("Bad Gateway"); + } + console.log(`\n[CURSOR-CAPTURE] ===== ${req.method} ${host}${req.url} =====`); + console.log(`[CURSOR-CAPTURE] reqHeaders=${JSON.stringify(req.headers)}`); + console.log(`[CURSOR-CAPTURE] reqBody.base64(${bodyBuffer.length}B)=${bodyBuffer.toString("base64")}`); + const chunks = []; + const forwardReq = https.request( + { + hostname: targetIP, + port: 443, + path: req.url, + method: req.method, + headers: { ...req.headers, host }, + servername: host, + rejectUnauthorized: process.env.MITM_DISABLE_TLS_VERIFY !== "1", + }, + (forwardRes) => { + forwardRes.on("data", (c) => chunks.push(c)); + forwardRes.on("end", () => { + const respBody = Buffer.concat(chunks); + console.log(`[CURSOR-CAPTURE] respStatus=${forwardRes.statusCode}`); + console.log(`[CURSOR-CAPTURE] respHeaders=${JSON.stringify(forwardRes.headers)}`); + console.log( + `[CURSOR-CAPTURE] respBody.base64(${respBody.length}B)=${respBody.toString("base64")}` + ); + console.log(`[CURSOR-CAPTURE] ===== end =====\n`); + if (!res.headersSent) res.writeHead(forwardRes.statusCode, forwardRes.headers); + res.end(respBody); + }); + } + ); + forwardReq.on("error", (err) => { + console.error(`[CURSOR-CAPTURE] upstream error: ${err.message}`); + if (!res.headersSent) res.writeHead(502); + res.end("Bad Gateway"); + }); + if (bodyBuffer.length > 0) forwardReq.write(bodyBuffer); + forwardReq.end(); +} + // Build + fire-and-forget the inspector capture entry. NEVER throws — capture // must not be able to break proxy traffic. Bodies/headers are sent raw over the // token-gated loopback ingest endpoint, which masks secrets server-side. @@ -570,6 +621,13 @@ const server = https.createServer(sslOptions, async (req, res) => { if (bodyBuffer.length > 0) saveRequestLog(req.url, bodyBuffer); + // TEMP CAPTURE (#4295) — gated by MITM_CAPTURE_CURSOR_MODELS=1. Dumps the Cursor + // AvailableModels Connect/protobuf request+response in base64 so the AvailableModels + // schema can be reverse-engineered. NOT FOR MERGE — remove after capture. + if (process.env.MITM_CAPTURE_CURSOR_MODELS === "1" && req.url.includes("AvailableModels")) { + return captureAvailableModels(req, res, bodyBuffer, host); + } + if (req.headers["x-omniroute-source"] === "omniroute") { vlog(1, `[MITM] → PASSTHROUGH (OmniRoute source loop)`); return passthrough(req, res, bodyBuffer);