UNPKG

allproxy

Version:

AllProxy: MITM HTTP Debugging Tool.

238 lines (227 loc) 12.2 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.resetShellStartupScripts = exports.editShellStartupScripts = exports.getPowerShellScript = exports.getFishShellScript = exports.getGitBashShellScript = exports.getBashShellScript = void 0; const _ = __importStar(require("lodash")); const fs = __importStar(require("fs")); const util = __importStar(require("util")); const os = __importStar(require("os")); const path = __importStar(require("path")); const fs_1 = require("./util/fs"); const terminal_env_overrides_1 = require("./terminal-env-overrides"); // Generate POSIX paths for git-bash on Windows (or use the normal path everywhere else) const POSIX_OVERRIDE_BIN_PATH = process.platform === 'win32' ? terminal_env_overrides_1.OVERRIDE_BIN_PATH.replace(/\\/g, '/').replace(/^(\w+):/, (_all, driveLetter) => `/${driveLetter.toLowerCase()}`) : terminal_env_overrides_1.OVERRIDE_BIN_PATH; const SHELL = (process.env.SHELL || '').split('/').slice(-1)[0]; const appendOrCreateFile = util.promisify(fs.appendFile); const appendToFirstExisting = (paths, forceWrite, contents) => __awaiter(void 0, void 0, void 0, function* () { for (let path of paths) { // Follow the path through symlinks (relatively common for terminal config): const realPath = yield (0, fs_1.getRealPath)(path); if (!realPath) continue; // File (or linked file) does not exist if (yield (0, fs_1.canAccess)(realPath)) { // Found our first valid readable file - append our extra config return appendOrCreateFile(realPath, contents); } // ^ Small races here, if the file content/perms change between check and write, but // unlikely to be a major concern - we'll just fail to write, it'll work next time. } if (forceWrite) { // If force write is set, write the last file anyway, even though it didn't exist before: return appendOrCreateFile(paths.slice(-1)[0], contents); } }); const START_CONFIG_SECTION = '# --httptoolkit--'; const END_CONFIG_SECTION = '# --httptoolkit-end--'; // The shell config required to ensure every spawned shell always has the right // configuration, even if it has its env vars reset somehow. This also includes // fixes for winpty with git-bash. By default, winpty will intercept known // commands to manage them, so our PATH overrides never get run. We avoid that // with trivial aliases, and then run winpty ourselves inside the overrides. // Works in bash, zsh, dash, ksh, sh (not fish) const SH_SHELL_PATH_CONFIG = ` ${START_CONFIG_SECTION} # This section will be reset each time a HTTP Toolkit terminal is opened if [ -n "$HTTP_TOOLKIT_ACTIVE" ]; then # When HTTP Toolkit is active, we inject various overrides into PATH export PATH="${POSIX_OVERRIDE_BIN_PATH}:$PATH" if command -v winpty >/dev/null 2>&1; then # Work around for winpty's hijacking of certain commands alias php=php alias node=node fi fi ${END_CONFIG_SECTION}`; const FISH_SHELL_PATH_CONFIG = ` ${START_CONFIG_SECTION} # This section will be reset each time a HTTP Toolkit terminal is opened if [ -n "$HTTP_TOOLKIT_ACTIVE" ] # When HTTP Toolkit is active, we inject various overrides into PATH set -x PATH "${POSIX_OVERRIDE_BIN_PATH}" $PATH; if command -v winpty >/dev/null 2>&1 # Work around for winpty's hijacking of certain commands alias php=php alias node=node end end ${END_CONFIG_SECTION}`; // A source-able shell script. Should work for everything sh-compatible, e.g. Bash, Zsh, Ksh etc. const getBashShellScript = (callbackUrl, env) => `${_.map(env, (value, key) => ` export ${key}="${value.replace(/"/g, '\\"')}"`).join('\n')} if command -v winpty >/dev/null 2>&1; then # Work around for winpty's hijacking of certain commands alias php=php alias node=node fi if command -v curl >/dev/null 2>&1; then # Let the HTTP Toolkit app know this ran succesfully (curl --noproxy '*' -X POST "${callbackUrl}" >/dev/null 2>&1 &) &> /dev/null fi echo 'HTTP Toolkit interception enabled' `; exports.getBashShellScript = getBashShellScript; // Build a Bash script, but first just translate the $PATH (which Git Bash expects to include its own // path-format strings) from Windows to Git Bash format. const getGitBashShellScript = (callbackUrl, env) => (0, exports.getBashShellScript)(callbackUrl, Object.assign(Object.assign({}, env), { // We convert path from being the override bin & ; separator to /C/path/to/overrides. PATH: env['PATH'] .replace(`${terminal_env_overrides_1.OVERRIDE_BIN_PATH};`, POSIX_OVERRIDE_BIN_PATH + ':' // Unix format $PATH separator ) })); exports.getGitBashShellScript = getGitBashShellScript; const getFishShellScript = (callbackUrl, env) => `${_.map(env, (value, key) => ` set -x ${key} "${value.replace(/"/g, '\\"')}"`).join('\n')} if command -v winpty >/dev/null 2>&1 # Work around for winpty's hijacking of certain commands alias php=php alias node=node end if command -v curl >/dev/null 2>&1 # Let the HTTP Toolkit app know this ran succesfully curl --noproxy '*' -X POST "${callbackUrl}" >/dev/null 2>&1 & end echo 'HTTP Toolkit interception enabled' `; exports.getFishShellScript = getFishShellScript; const getPowerShellScript = (callbackUrl, env) => `${_.map(env, (value, key) => ` $Env:${key} = "${value.replace(/"/g, '`"')}"`).join('\n')} # We add a few special hooks just for Invoke-WebRequest. # First, we override the default proxy to use the env var value: $PSDefaultParameterValues["invoke-webrequest:proxy"] = $Env:HTTP_PROXY # Then we disable cert checks completely - all traffic will go to us, we'll handle HTTPS upstream $PSDefaultParameterValues["invoke-webrequest:SkipCertificateCheck"] = $True # Let the HTTP Toolkit app know this ran succesfully Start-Job -ScriptBlock { Invoke-WebRequest "${callbackUrl}" -NoProxy -Method 'POST' } | out-null Write-Host 'HTTP Toolkit interception enabled' `; exports.getPowerShellScript = getPowerShellScript; // Find the relevant user shell config file, add the above line to it, so that // shells launched with HTTP_TOOLKIT_ACTIVE set use the interception PATH. const editShellStartupScripts = () => __awaiter(void 0, void 0, void 0, function* () { yield (0, exports.resetShellStartupScripts)(); // The key risk here is that one of these scripts (or some other process) will be // overriding PATH itself, so we need to append some PATH reset logic. The main // offenders are: nvm config's in .bashrc/.bash_profile, OSX's path_helper and // git-bash ignoring the inherited $PATH. // .profile is used by Dash, Bash sometimes, and by Sh: appendToFirstExisting([ path.join(os.homedir(), '.profile') ], true, // We always write .profile SH_SHELL_PATH_CONFIG).catch(console.log); // Bash login shells use some other files by preference, if they exist. // Note that on OSX, all shells are login - elsewhere they only are at actual login time. appendToFirstExisting([ path.join(os.homedir(), '.bash_profile'), path.join(os.homedir(), '.bash_login') ], false, // Do nothing if they don't exist - it falls back to .profile SH_SHELL_PATH_CONFIG).catch(console.log); // Bash non-login shells use .bashrc, if it exists: appendToFirstExisting([ path.join(os.homedir(), '.bashrc') ], SHELL === 'bash', // If you use bash, we _always_ want to set this SH_SHELL_PATH_CONFIG).catch(console.log); // Zsh has its own files (both are actually used) appendToFirstExisting([ path.join(os.homedir(), '.zshenv'), path.join(os.homedir(), '.zshrc') ], SHELL === 'zsh', // If you use zsh, we _always_ write a config file SH_SHELL_PATH_CONFIG).catch(console.log); // Fish always uses the same config file appendToFirstExisting([ path.join(os.homedir(), '.config', 'fish', 'config.fish'), ], SHELL === 'fish' || (yield (0, fs_1.canAccess)(path.join(os.homedir(), '.config', 'fish'))), FISH_SHELL_PATH_CONFIG).catch(console.log); }); exports.editShellStartupScripts = editShellStartupScripts; const removeConfigSectionsFromFile = (path) => __awaiter(void 0, void 0, void 0, function* () { let fileLines; const targetPath = yield (0, fs_1.getRealPath)(path); // Follow symlinks, if present if (!targetPath) return; // File doesn't exist, no need to clean it up try { fileLines = (yield (0, fs_1.readFile)(targetPath, 'utf8')).split('\n'); } catch (e) { // Silently skip any files we can't read return; } // Remove everything between each pair of start/end section markers let sectionStart = _.findIndex(fileLines, (l) => l.startsWith(START_CONFIG_SECTION)); while (sectionStart !== -1) { let sectionEnd = _.findIndex(fileLines, (l) => l.startsWith(END_CONFIG_SECTION)); if (sectionEnd === -1 || sectionEnd <= sectionStart) return; // Odd config file state - don't edit it fileLines.splice(sectionStart, (sectionEnd - sectionStart) + 1); sectionStart = _.findIndex(fileLines, (l) => l.startsWith(START_CONFIG_SECTION)); } // Write & rename to ensure this is atomic, and avoid races here // as much as we reasonably can. const tempFile = targetPath + Date.now() + '.temp'; yield (0, fs_1.writeFile)(tempFile, fileLines.join('\n')); return (0, fs_1.renameFile)(tempFile, targetPath); }); // Cleanup: strip our extra config line from all config files // Good to do for tidiness, not strictly necessary (the config does nothing // unless HTTP_TOOLKIT_ACTIVE is set anyway). const resetShellStartupScripts = () => { // For each possible config file, remove our magic line, if present return Promise.all([ path.join(os.homedir(), '.profile'), path.join(os.homedir(), '.bash_profile'), path.join(os.homedir(), '.bash_login'), path.join(os.homedir(), '.bashrc'), path.join(os.homedir(), '.zshenv'), path.join(os.homedir(), '.zshrc'), path.join(os.homedir(), '.config', 'fish', 'config.fish'), ].map((configFile) => removeConfigSectionsFromFile(configFile).catch(console.log))); }; exports.resetShellStartupScripts = resetShellStartupScripts; //# sourceMappingURL=terminal-scripts.js.map