UNPKG

allproxy

Version:

AllProxy: MITM HTTP Debugging Tool.

405 lines 18.3 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.FreshOpera = exports.FreshBrave = exports.FreshEdgeCanary = exports.FreshEdgeDev = exports.FreshEdgeBeta = exports.FreshEdge = exports.FreshChromiumDev = exports.FreshChromium = exports.FreshChromeCanary = exports.FreshChromeDev = exports.FreshChromeBeta = exports.ExistingChrome = exports.FreshChrome = void 0; const _ = __importStar(require("lodash")); const mockttp_1 = require("mockttp"); const browsers_1 = require("./browsers"); const promise_1 = require("./util/promise"); const fs_1 = require("./util/fs"); const process_management_1 = require("./util/process-management"); const hide_warning_server_1 = require("./hide-warning-server"); const getBrowserDetails = (config, variant) => __awaiter(void 0, void 0, void 0, function* () { const browsers = yield (0, browsers_1.getAvailableBrowsers)(config.configPath); // Get the details for the first of these browsers that is installed. return _.find(browsers, b => b.name === variant); }); const getChromiumLaunchOptions = (browser, config, proxyPort, hideWarningServer) => __awaiter(void 0, void 0, void 0, function* () { const certificatePem = yield (0, fs_1.readFile)(config.https.certPath, 'utf8'); const spkiFingerprint = (0, mockttp_1.generateSPKIFingerprint)(certificatePem); return { browser, proxy: `http://127.0.0.1:${proxyPort}`, noProxy: [ // Force even localhost requests to go through the proxy // See https://bugs.chromium.org/p/chromium/issues/detail?id=899126#c17 '<-loopback>', // Don't intercept our warning hiding requests. Note that this must be // the 2nd rule here, or <-loopback> would override it. hideWarningServer.host ], options: [ // Trust our CA certificate's fingerprint: `--ignore-certificate-errors-spki-list=${spkiFingerprint}`, `--ignore-certificate-errors`, // Disable annoying "What's New" page '--disable-features=ChromeWhatsNewUI', // Avoid annoying extra network noise: '--disable-background-networking', '--disable-component-update', '--check-for-update-interval=31536000' // Don't update for a year ] }; }); class FreshChromiumBasedInterceptor { constructor(config, variantName) { this.config = config; this.variantName = variantName; this.activeBrowsers = {}; } isActive(proxyPort) { const browser = this.activeBrowsers[proxyPort]; return !!browser && !!browser.pid; } isActivable() { return __awaiter(this, void 0, void 0, function* () { const browserDetails = yield getBrowserDetails(this.config, this.variantName); return !!browserDetails; }); } activate(proxyPort) { return __awaiter(this, void 0, void 0, function* () { if (this.isActive(proxyPort)) return; const hideWarningServer = new hide_warning_server_1.HideWarningServer(this.config); yield hideWarningServer.start('https://google.com'); const browserDetails = yield getBrowserDetails(this.config, this.variantName); const browser = yield (0, browsers_1.launchBrowser)(hideWarningServer.hideWarningUrl, yield getChromiumLaunchOptions(browserDetails ? browserDetails.name : this.variantName, this.config, proxyPort, hideWarningServer), this.config.configPath); if (browser.process.stdout) browser.process.stdout.pipe(process.stdout); if (browser.process.stderr) browser.process.stderr.pipe(process.stderr); yield hideWarningServer.completedPromise; yield hideWarningServer.stop(); this.activeBrowsers[proxyPort] = browser; browser.process.once('close', () => __awaiter(this, void 0, void 0, function* () { delete this.activeBrowsers[proxyPort]; // Opera has a launch proc that exits immediately in Windows, so we can't clear the profile there. if (process.platform === 'win32' && this.variantName === 'opera') return; yield (0, promise_1.delay)(1000); // No hurry, make sure the browser & related processes have all cleaned up if (Object.keys(this.activeBrowsers).length === 0 && browserDetails && _.isString(browserDetails.profile)) { // If we were the last browser, and we have a profile path, and it's in our config // (just in case something's gone wrong) -> delete the profile to reset everything. const profilePath = browserDetails.profile; if (!profilePath.startsWith(this.config.configPath)) { console.error(`Unexpected ${this.variantName} profile location, not deleting: ${profilePath}`); } else { const profileFolder = browserDetails.profile; (0, fs_1.deleteFolder)(profileFolder) .catch(() => __awaiter(this, void 0, void 0, function* () { // After 1 error, wait a little and retry yield (0, promise_1.delay)(1000); yield (0, fs_1.deleteFolder)(profileFolder); })).catch(console.warn); // If it still fails, just give up, not a big deal } } })); // Delay the approx amount of time it normally takes the browser to really open, just to be sure yield (0, promise_1.delay)(500); }); } deactivate(proxyPort) { return __awaiter(this, void 0, void 0, function* () { if (this.isActive(proxyPort)) { const browser = this.activeBrowsers[proxyPort]; const exitPromise = new Promise((resolve) => browser.process.once('close', resolve)); browser.stop(); yield exitPromise; } }); } deactivateAll() { return __awaiter(this, void 0, void 0, function* () { yield Promise.all(Object.keys(this.activeBrowsers).map((proxyPort) => this.deactivate(proxyPort))); }); } } ; class ExistingChromiumBasedInterceptor { constructor(config, variantName) { this.config = config; this.variantName = variantName; } browserDetails() { return __awaiter(this, void 0, void 0, function* () { return getBrowserDetails(this.config, this.variantName); }); } isActive(proxyPort) { const activeBrowser = this.activeBrowser; return !!activeBrowser && activeBrowser.proxyPort === proxyPort && !!activeBrowser.browser.pid; } isActivable() { return __awaiter(this, void 0, void 0, function* () { if (this.activeBrowser) return false; return !!(yield this.browserDetails()); }); } findExistingPid() { return __awaiter(this, void 0, void 0, function* () { const processes = yield (0, process_management_1.listRunningProcesses)(); const browserDetails = yield this.browserDetails(); if (!browserDetails) { throw new Error("Can't intercept existing browser without browser details"); } const browserProcesses = processes.filter((proc) => { if (process.platform === 'darwin') { if (!proc.command.startsWith(browserDetails.command)) return false; const appBundlePath = proc.command.substring(browserDetails.command.length); // Only *.app/Contents/MacOS/* is the main app process: return appBundlePath.match(/^\/Contents\/MacOS\//); } else { return proc.bin && ( // Find a binary that exactly matches the specific command: proc.bin === browserDetails.command || // Or whose binary who's matches the path for this specific variant: proc.bin.includes(`${browserDetails.name}/${browserDetails.type}`)); } }); const defaultRootProcess = browserProcesses.find(({ args }) => args !== undefined && // Find the main process, skipping any renderer/util processes !args.includes('--type=') && // Also skip any non-default profile processes (e.g. our own fresh Chromes) !args.includes('--user-data-dir')); return defaultRootProcess && defaultRootProcess.pid; }); } activate(proxyPort, options = { closeConfirmed: false }) { return __awaiter(this, void 0, void 0, function* () { if (!this.isActivable()) return; const hideWarningServer = new hide_warning_server_1.HideWarningServer(this.config); yield hideWarningServer.start('https://google.com'); const existingPid = yield this.findExistingPid(); if (existingPid) { if (!options.closeConfirmed) { // Fail, with metadata requesting the UI to confirm that Chrome should be killed throw Object.assign(new Error(`Not killing ${this.variantName}: not confirmed`), { metadata: { closeConfirmRequired: true }, reportable: false }); } if (process.platform === 'win32') { (0, process_management_1.windowsClose)(existingPid); try { yield (0, process_management_1.waitForExit)(existingPid); } catch (e) { // Try again, but less gently this time: process.kill(existingPid); yield (0, process_management_1.waitForExit)(existingPid); } } else { process.kill(existingPid); yield (0, process_management_1.waitForExit)(existingPid); } } const browserDetails = yield getBrowserDetails(this.config, this.variantName); const launchOptions = yield getChromiumLaunchOptions(browserDetails ? browserDetails.name : this.variantName, this.config, proxyPort, hideWarningServer); // Remove almost all default arguments. Each of these changes behaviour in maybe unexpected // ways, notably including --disable-restore which actively causes problems. launchOptions.skipDefaults = true; launchOptions.options.push('--no-default-browser-check', '--no-first-run', '--disable-popup-blocking', // Required for hide-warning -> amiusing hop // If we killed something, use --restore-last-session to ensure it comes back: ...(existingPid ? ['--restore-last-session'] : []), // Passing the URL here instead of passing it to launchBrowser ensures that it isn't // opened in a separate window when launching on Mac hideWarningServer.hideWarningUrl); const browser = yield (0, browsers_1.launchBrowser)("", Object.assign(Object.assign({}, launchOptions), { skipDefaults: true, profile: null // Enforce that we use the default profile }), this.config.configPath); if (browser.process.stdout) browser.process.stdout.pipe(process.stdout); if (browser.process.stderr) browser.process.stderr.pipe(process.stderr); yield hideWarningServer.completedPromise; yield hideWarningServer.stop(); this.activeBrowser = { browser, proxyPort }; browser.process.once('close', () => __awaiter(this, void 0, void 0, function* () { delete this.activeBrowser; })); // Delay the approx amount of time it normally takes the browser to really open, just to be sure yield (0, promise_1.delay)(500); }); } deactivate(proxyPort) { return __awaiter(this, void 0, void 0, function* () { if (this.isActive(proxyPort)) { const { browser } = this.activeBrowser; if (process.platform === 'win32') { // Try to cleanly close if we can, rather than killing Chrome directly: try { yield (0, process_management_1.windowsClose)(browser.pid) .then(() => (0, process_management_1.waitForExit)(browser.pid)); return; } catch (e) { } // If this fails/times out, kill like we do elsewhere: } const exitPromise = new Promise((resolve) => browser.process.once('close', resolve)); browser.stop(); yield exitPromise; } }); } deactivateAll() { return __awaiter(this, void 0, void 0, function* () { if (this.activeBrowser) { yield this.deactivate(this.activeBrowser.proxyPort); } }); } } ; class FreshChrome extends FreshChromiumBasedInterceptor { constructor(config) { super(config, 'chrome'); this.id = 'fresh-chrome'; this.version = '1.0.0'; } } exports.FreshChrome = FreshChrome; ; class ExistingChrome extends ExistingChromiumBasedInterceptor { constructor(config) { super(config, 'chrome'); this.id = 'existing-chrome'; this.version = '1.0.0'; } } exports.ExistingChrome = ExistingChrome; ; class FreshChromeBeta extends FreshChromiumBasedInterceptor { constructor(config) { super(config, 'chrome-beta'); this.id = 'fresh-chrome-beta'; this.version = '1.0.0'; } } exports.FreshChromeBeta = FreshChromeBeta; ; class FreshChromeDev extends FreshChromiumBasedInterceptor { constructor(config) { super(config, 'chrome-dev'); this.id = 'fresh-chrome-dev'; this.version = '1.0.0'; } } exports.FreshChromeDev = FreshChromeDev; ; class FreshChromeCanary extends FreshChromiumBasedInterceptor { constructor(config) { super(config, 'chrome-canary'); this.id = 'fresh-chrome-canary'; this.version = '1.0.0'; } } exports.FreshChromeCanary = FreshChromeCanary; ; class FreshChromium extends FreshChromiumBasedInterceptor { constructor(config) { super(config, 'chromium'); this.id = 'fresh-chromium'; this.version = '1.0.0'; } } exports.FreshChromium = FreshChromium; ; class FreshChromiumDev extends FreshChromiumBasedInterceptor { constructor(config) { super(config, 'chromium-dev'); this.id = 'fresh-chromium-dev'; this.version = '1.0.0'; } } exports.FreshChromiumDev = FreshChromiumDev; ; class FreshEdge extends FreshChromiumBasedInterceptor { constructor(config) { super(config, 'msedge'); this.id = 'fresh-edge'; this.version = '1.0.0'; } } exports.FreshEdge = FreshEdge; ; class FreshEdgeBeta extends FreshChromiumBasedInterceptor { constructor(config) { super(config, 'msedge-beta'); this.id = 'fresh-edge-beta'; this.version = '1.0.0'; } } exports.FreshEdgeBeta = FreshEdgeBeta; ; class FreshEdgeDev extends FreshChromiumBasedInterceptor { constructor(config) { super(config, 'msedge-dev'); this.id = 'fresh-edge-dev'; this.version = '1.0.0'; } } exports.FreshEdgeDev = FreshEdgeDev; ; class FreshEdgeCanary extends FreshChromiumBasedInterceptor { constructor(config) { super(config, 'msedge-canary'); this.id = 'fresh-edge-canary'; this.version = '1.0.0'; } } exports.FreshEdgeCanary = FreshEdgeCanary; ; class FreshBrave extends FreshChromiumBasedInterceptor { constructor(config) { super(config, 'brave'); this.id = 'fresh-brave'; this.version = '1.0.0'; } } exports.FreshBrave = FreshBrave; ; class FreshOpera extends FreshChromiumBasedInterceptor { constructor(config) { super(config, 'opera'); this.id = 'fresh-opera'; this.version = '1.0.3'; } } exports.FreshOpera = FreshOpera; ; //# sourceMappingURL=chromium-based-interceptors.js.map