Skip to content

Instantly share code, notes, and snippets.

@Inndy
Created May 8, 2026 17:14
Show Gist options
  • Select an option

  • Save Inndy/d3dbc72a1b2775833669480c889bab90 to your computer and use it in GitHub Desktop.

Select an option

Save Inndy/d3dbc72a1b2775833669480c889bab90 to your computer and use it in GitHub Desktop.
Node in the middle
{
"name": "node-in-the-middle",
"version": "1.0.0",
"description": "",
"main": "server.mjs",
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "pnpm@10.33.0",
"dependencies": {
"mockttp": "^4.3.1"
}
}
import mockttp from 'mockttp';
import { promises as fs } from 'fs';
import path from 'path';
import { spawn } from 'child_process';
const CA_KEY_PATH = './proxy-ca.key';
const CA_CERT_PATH = './proxy-ca.pem';
const LOG_FILE = './mitm.log';
async function ensureCAExists() {
try {
await fs.access(CA_KEY_PATH);
await fs.access(CA_CERT_PATH);
console.log('??CA certificate already exists');
return { keyPath: CA_KEY_PATH, certPath: CA_CERT_PATH };
} catch (error) {
console.log('? Generating new CA certificate...');
const caCertificate = await mockttp.generateCACertificate({
commonName: 'Proxy Testing CA - DO NOT TRUST',
organizationName: 'Mockttp Proxy',
countryName: 'US',
bits: 2048
});
await fs.writeFile(CA_KEY_PATH, caCertificate.key);
await fs.writeFile(CA_CERT_PATH, caCertificate.cert);
console.log('??CA certificate generated and saved');
return { keyPath: CA_KEY_PATH, certPath: CA_CERT_PATH };
}
}
async function spawnGeminiCli(proxyUrl) {
console.log('?? Spawning Gemini CLI...');
return new Promise((resolve, reject) => {
const env = {
...process.env,
NODE_EXTRA_CA_CERTS: path.resolve(CA_CERT_PATH),
https_proxy: proxyUrl,
HTTPS_PROXY: proxyUrl,
NODE_USE_ENV_PROXY: '1',
};
// Using 'pnpm dlx' to run gemini-cli.
// We'll run '--version' as a simple test that might trigger a check for updates or similar.
// If the user wants to test real traffic, they can run a command that requires network.
const worker = spawn('pnpx', ['@google/gemini-cli'], {
stdio: 'inherit',
env,
//shell: true
});
worker.on('error', reject);
worker.on('exit', (code) => {
console.log(`\nGemini CLI exited with code ${code}`);
resolve();
});
});
}
async function startProxy() {
const caConfig = await ensureCAExists();
const server = mockttp.getLocal({
https: caConfig,
debug: false
});
const captured = [];
await fs.writeFile(LOG_FILE, `MITM Log - ${new Date().toISOString()}\n\n`);
await server.start(3128);
await server.forAnyRequest().thenPassThrough({
beforeRequest: async (req) => {
const body = await req.body.getText();
const entry = {
url: req.url,
method: req.method,
requestBody: body,
timestamp: new Date().toISOString()
};
captured.push(entry);
await fs.appendFile(LOG_FILE, `[${entry.timestamp}] REQ: ${req.method} ${req.url}\nBody: ${body}\n\n`);
// Force no compression to make reading response body easier
const headers = { ...req.headers };
delete headers['accept-encoding'];
return { ...req, headers };
},
beforeResponse: async (res) => {
const body = await res.body.getText();
const lastEntry = captured.find(e => e.url === res.url && !e.responseBody);
if (lastEntry) {
lastEntry.responseBody = body;
lastEntry.statusCode = res.statusCode;
}
await fs.appendFile(LOG_FILE, `[${new Date().toISOString()}] RES: ${res.statusCode} ${res.url}\nBody: ${body}\n\n`);
return {
statusCode: res.statusCode,
headers: res.headers,
body: body
};
}
});
console.log(`?? Proxy running on: ${server.url}`);
try {
await spawnGeminiCli(server.url);
} catch (err) {
console.error('Error running Gemini CLI:', err.message);
}
// Print Summary
console.log('\n--- MITM Summary ---');
console.log(`Captured Requests: ${captured.length}`);
if (captured.length > 0) {
const last = captured[captured.length - 1];
console.log(`Last URL: ${last.url}`);
try {
const json = JSON.parse(last.responseBody || last.requestBody || '{}');
console.log('Last JSON data (sample):', JSON.stringify(json, null, 2).substring(0, 500) + (JSON.stringify(json).length > 500 ? '...' : ''));
} catch (e) {
console.log('Last body was not JSON or was empty.');
}
}
console.log(`Full log available at: ${LOG_FILE}`);
await server.stop();
process.exit();
}
startProxy().catch(console.error);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment