22 Commits
Author SHA1 Message Date
xenosandGitHub ba459cff40 Merge pull request #22 from tomaszk8266/master
Add macOS signing
2026-07-11 10:27:31 +08:00
Tomasz K. 8b54da6adf macOS: Sign the patched app before launching and increase timeout 2026-07-11 00:20:54 +02:00
xenosandGitHub 432f9a8e70 Merge pull request #18 from xenos1337/dependabot/npm_and_yarn/brace-expansion-5.0.5 2026-05-03 17:52:46 +02:00
xenosandGitHub 7d437d16e2 Merge pull request #20 from xenos1337/dependabot/npm_and_yarn/xmldom/xmldom-0.8.13 2026-04-26 23:04:44 +02:00
dependabot[bot]andGitHub 3ed4d62d6c chore(deps): bump @xmldom/xmldom from 0.8.11 to 0.8.13
Bumps [@xmldom/xmldom](https://github.com/xmldom/xmldom) from 0.8.11 to 0.8.13.
- [Release notes](https://github.com/xmldom/xmldom/releases)
- [Changelog](https://github.com/xmldom/xmldom/blob/master/CHANGELOG.md)
- [Commits](https://github.com/xmldom/xmldom/compare/0.8.11...0.8.13)

---
updated-dependencies:
- dependency-name: "@xmldom/xmldom"
  dependency-version: 0.8.13
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-23 03:33:12 +00:00
dependabot[bot]andGitHub 1adcd3c5cb chore(deps): bump brace-expansion from 5.0.4 to 5.0.5
Bumps [brace-expansion](https://github.com/juliangruber/brace-expansion) from 5.0.4 to 5.0.5.
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/v5.0.4...v5.0.5)

---
updated-dependencies:
- dependency-name: brace-expansion
  dependency-version: 5.0.5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-27 14:53:52 +00:00
xenosandGitHub 86c35f079a Merge pull request #16 from Shyzg/fix/macos-hash-patching 2026-03-22 01:35:45 +01:00
Al Jarreau Arman MaulanaandClaude Opus 4.6 24c8828e71 Fix macOS hash patching and update dependencies
On macOS, the integrity hash is stored in Info.plist, not embedded in
the executable binary. Added patchInfoPlistHash() and platform branching
so macOS patches Info.plist while Windows/Linux patch the binary.

Also updated dependencies for compatibility:
- @electron/asar 4.0.1 → 4.1.0 (named export change)
- chalk 4 → 5.6.2 (tagged template → function call syntax)
- @types/node 20 → 25

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 17:08:12 +07:00
xenos bbc7918678 Update package.json 2026-03-17 01:05:17 +01:00
xenos 68f7e1c1c0 update for 1.24.4 2026-03-17 00:29:26 +01:00
xenosandGitHub 0685a1e727 Merge pull request #10 from xenos1337/dependabot/npm_and_yarn/minimatch-10.2.4 2026-03-03 00:29:10 +01:00
dependabot[bot]andGitHub fa9dcf5361 chore(deps): bump minimatch from 10.2.2 to 10.2.4
Bumps [minimatch](https://github.com/isaacs/minimatch) from 10.2.2 to 10.2.4.
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/minimatch/compare/v10.2.2...v10.2.4)

---
updated-dependencies:
- dependency-name: minimatch
  dependency-version: 10.2.4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-28 13:26:35 +00:00
xenosandGitHub efcc89bab4 Merge pull request #9 from xenos1337/dependabot/npm_and_yarn/multi-4348f2d014 2026-02-24 16:49:12 +01:00
dependabot[bot]andGitHub 8215f4348b chore(deps): bump minimatch and @electron/asar
Bumps [minimatch](https://github.com/isaacs/minimatch) to 10.2.2 and updates ancestor dependency [@electron/asar](https://github.com/electron/asar). These dependencies need to be updated together.


Updates `minimatch` from 3.1.2 to 10.2.2
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/minimatch/compare/v3.1.2...v10.2.2)

Updates `@electron/asar` from 3.4.1 to 4.0.1
- [Release notes](https://github.com/electron/asar/releases)
- [Changelog](https://github.com/electron/asar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/electron/asar/compare/v3.4.1...v4.0.1)

---
updated-dependencies:
- dependency-name: minimatch
  dependency-version: 10.2.2
  dependency-type: indirect
- dependency-name: "@electron/asar"
  dependency-version: 4.0.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-21 10:32:42 +00:00
xenos 359b707b44 Update for 1.24.2 2025-12-18 17:38:56 +01:00
xenosandGitHub 7537b33593 Merge pull request #6 from jokelbaf/master 2025-12-11 21:37:37 +01:00
JokelBafandGitHub fe97c803f5 feat: Support newer electron import name 2025-12-11 21:31:42 +02:00
xenos 61b3bdeb5e chore(version): update version to 2.0.8 and add author information in package.json; remove unused version comparison documentation in index.js
Signed-off-by: xenos <66328734+xenos1337@users.noreply.github.com>
2025-11-29 00:08:02 +01:00
xenos 91333c1a3e refactor(version): read LOCAL_VERSION from package.json dynamically 2025-11-29 00:06:55 +01:00
xenos d1a64774da feat(version): add automatic update check from GitHub tags 2025-11-29 00:06:24 +01:00
xenos fe7cae9dd9 refactor(patcher): use local inject.js and simplify injection mechanism 2025-11-29 00:04:54 +01:00
xenos cb8fd663b7 docs(readme): remove 'Can They Fix It' section 2025-11-29 00:04:09 +01:00
5 changed files with 792 additions and 502 deletions
-4
View File
@@ -20,10 +20,6 @@ The patcher intercepts HTTP Toolkit's authentication functions:
By hooking these functions, we bypass the subscription checks entirely. By hooking these functions, we bypass the subscription checks entirely.
### Can They Fix It?
Yes, but they most likely won't. Fixing this would require changing their entire codebase architecture. And if they do? I'll just update the patcher.
## Installation ## Installation
1. Install Node.js (if not already installed) 1. Install Node.js (if not already installed)
+444 -161
View File
@@ -1,6 +1,6 @@
// @ts-check // @ts-check
import { spawn, execSync } from "child_process"; import { spawn, execSync } from "child_process";
import asar from "@electron/asar"; import * as asar from "@electron/asar";
import chalk from "chalk"; import chalk from "chalk";
import path from "path"; import path from "path";
import fs from "fs"; import fs from "fs";
@@ -23,6 +23,18 @@ const __filename = (() => {
return fileURLToPath(import.meta.url); return fileURLToPath(import.meta.url);
})(); })();
// Version info - read from package.json
const GITHUB_REPO = "xenos1337/httptoolkit-patcher";
const LOCAL_VERSION = (() => {
try {
const packageJsonPath = path.join(path.dirname(__filename), "package.json");
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
return packageJson.version || "0.0.0";
} catch {
return "0.0.0";
}
})();
// Check if running with elevated privileges // Check if running with elevated privileges
function isElevated() { function isElevated() {
if (isWin) { if (isWin) {
@@ -51,19 +63,79 @@ function prompt(question) {
}); });
} }
// Helper function to fetch content from URL /**
function fetchUrl(url) { * Fetch JSON from a URL
* @param {string} url
* @returns {Promise<Array<{name: string}>>}
*/
function fetchJson(url) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const options = {
headers: {
"User-Agent": "httptoolkit-patcher",
Accept: "application/vnd.github.v3+json",
},
};
https https
.get(url, res => { .get(url, options, res => {
if (res.statusCode !== 200) {
reject(new Error(`HTTP ${res.statusCode}`));
return;
}
let data = ""; let data = "";
res.on("data", chunk => (data += chunk)); res.on("data", chunk => (data += chunk));
res.on("end", () => resolve(data)); res.on("end", () => {
try {
resolve(JSON.parse(data));
} catch (e) {
reject(e);
}
});
}) })
.on("error", reject); .on("error", reject);
}); });
} }
function compareVersions(v1, v2) {
const normalize = (/** @type {string} */ v) => v.replace(/^v/, "").split(".").map(Number);
const parts1 = normalize(v1);
const parts2 = normalize(v2);
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
const num1 = parts1[i] || 0;
const num2 = parts2[i] || 0;
if (num1 > num2) return 1;
if (num1 < num2) return -1;
}
return 0;
}
/**
* Check for updates from GitHub
*/
async function checkForUpdates() {
try {
const tags = await fetchJson(`https://api.github.com/repos/${GITHUB_REPO}/tags`);
if (!tags || tags.length === 0) {
return; // No tags found, skip update check
}
// Tags are returned in order, first one is the latest
const latestTag = tags[0].name;
const latestVersion = latestTag.replace(/^v/, "");
if (compareVersions(latestVersion, LOCAL_VERSION) > 0) {
console.log(chalk.yellowBright('\n╔════════════════════════════════════════════════════════════╗'));
console.log(chalk.yellowBright('║') + chalk.white(' A new version is available: ') + chalk.greenBright(`v${latestVersion}`) + chalk.white(' (current: ') + chalk.gray(`v${LOCAL_VERSION}`) + chalk.white(') ') + chalk.yellowBright(' ║'));
console.log(chalk.yellowBright('║') + chalk.white(' Update: ') + chalk.cyanBright(`https://github.com/${GITHUB_REPO}`) + chalk.white(' ') + chalk.yellowBright('║'));
console.log(chalk.yellowBright('╚════════════════════════════════════════════════════════════╝\n'));
}
} catch (e) {
// Silently ignore update check errors (network issues, etc.)
}
}
// Helper function to remove directory recursively // Helper function to remove directory recursively
function rm(dirPath) { function rm(dirPath) {
if (!fs.existsSync(dirPath)) return; if (!fs.existsSync(dirPath)) return;
@@ -78,7 +150,11 @@ function rm(dirPath) {
// Find HTTP Toolkit installation path // Find HTTP Toolkit installation path
async function findAppPath() { async function findAppPath() {
const possiblePaths = isWin ? [path.join("C:", "Program Files", "HTTP Toolkit", "resources"), path.join("C:", "Program Files (x86)", "HTTP Toolkit", "resources")] : isMac ? ["/Applications/HTTP Toolkit.app/Contents/Resources"] : ["/opt/HTTP Toolkit/resources", "/opt/httptoolkit/resources"]; const possiblePaths = isWin
? [path.join("C:", "Program Files", "HTTP Toolkit", "resources"), path.join("C:", "Program Files (x86)", "HTTP Toolkit", "resources"), path.join(process.env.LOCALAPPDATA || path.join(process.env.USERPROFILE || "", "AppData", "Local"), "Programs", "HTTP Toolkit", "resources")]
: isMac
? ["/Applications/HTTP Toolkit.app/Contents/Resources"]
: ["/opt/HTTP Toolkit/resources", "/opt/httptoolkit/resources"];
for (const p of possiblePaths) { for (const p of possiblePaths) {
if (fs.existsSync(path.join(p, "app.asar"))) { if (fs.existsSync(path.join(p, "app.asar"))) {
@@ -86,11 +162,11 @@ async function findAppPath() {
} }
} }
console.log(chalk.yellowBright`[!] HTTP Toolkit not found in default locations`); console.log(chalk.yellowBright('[!] HTTP Toolkit not found in default locations'));
const userPath = await prompt("Please enter the path to HTTP Toolkit executable/app: "); const userPath = await prompt("Please enter the path to HTTP Toolkit executable/app: ");
if (!userPath) { if (!userPath) {
console.error(chalk.redBright`[-] No path provided`); console.error(chalk.redBright('[-] No path provided'));
process.exit(1); process.exit(1);
} }
@@ -101,7 +177,7 @@ async function findAppPath() {
if (!resourcesPath.endsWith("resources")) resourcesPath = path.join(resourcesPath, "resources"); if (!resourcesPath.endsWith("resources")) resourcesPath = path.join(resourcesPath, "resources");
if (!fs.existsSync(path.join(resourcesPath, "app.asar"))) { if (!fs.existsSync(path.join(resourcesPath, "app.asar"))) {
console.error(chalk.redBright`[-] app.asar not found at ${resourcesPath}`); console.error(chalk.redBright(`[-] app.asar not found at ${resourcesPath}`));
process.exit(1); process.exit(1);
} }
@@ -110,7 +186,7 @@ async function findAppPath() {
// Request elevated permissions // Request elevated permissions
async function requestElevation() { async function requestElevation() {
console.log(chalk.yellowBright`[!] Requesting elevated permissions...`); console.log(chalk.yellowBright('[!] Requesting elevated permissions...'));
// @ts-ignore - pkg adds this property at runtime // @ts-ignore - pkg adds this property at runtime
const isBundled = process.pkg; const isBundled = process.pkg;
@@ -125,28 +201,28 @@ async function requestElevation() {
} }
try { try {
console.log(chalk.greenBright`[+] Spawning PowerShell with script: ${script}`); console.log(chalk.greenBright(`[+] Spawning PowerShell with script: ${script}`));
execSync(`powershell -Command "${script}"`, { stdio: "inherit" }); execSync(`powershell -Command "${script}"`, { stdio: "inherit" });
console.log(chalk.blueBright`[+] Restarting with administrator privileges...`); console.log(chalk.blueBright('[+] Restarting with administrator privileges...'));
process.exit(0); process.exit(0);
} catch (e) { } catch (e) {
console.error(chalk.redBright`[-] Failed to elevate permissions: ${e.message}`); console.error(chalk.redBright(`[-] Failed to elevate permissions: ${e.message}`));
console.error(chalk.redBright`[-] Please run as administrator manually`); console.error(chalk.redBright('[-] Please run as administrator manually'));
process.exit(1); process.exit(1);
} }
} else if (isLinux) { } else if (isLinux) {
// Linux: Cannot auto-elevate with sudo, show instructions instead // Linux: Cannot auto-elevate with sudo, show instructions instead
console.log(chalk.yellowBright`[!] Elevated permissions are required for patching on Linux`); console.log(chalk.yellowBright('[!] Elevated permissions are required for patching on Linux'));
console.log(chalk.yellowBright`[!] Please re-run this script with sudo:`); console.log(chalk.yellowBright('[!] Please re-run this script with sudo:'));
if (isBundled) { if (isBundled) {
console.log(chalk.blueBright` sudo ${__filename}`); console.log(chalk.blueBright(` sudo ${__filename}`));
} else { } else {
console.log(chalk.blueBright` sudo node ${__filename}`); console.log(chalk.blueBright(` sudo node ${__filename}`));
} }
process.exit(1); process.exit(1);
} else { } else {
// macOS: Try to elevate with sudo // macOS: Try to elevate with sudo
console.log(chalk.blueBright`[+] Restarting with sudo...`); console.log(chalk.blueBright('[+] Restarting with sudo...'));
try { try {
let child; let child;
if (isBundled) { if (isBundled) {
@@ -160,8 +236,8 @@ async function requestElevation() {
} }
child.on("exit", code => process.exit(code || 0)); child.on("exit", code => process.exit(code || 0));
} catch (e) { } catch (e) {
console.error(chalk.redBright`[-] Failed to elevate permissions: ${e.message}`); console.error(chalk.redBright(`[-] Failed to elevate permissions: ${e.message}`));
console.error(chalk.redBright`[-] Please run with sudo manually`); console.error(chalk.redBright('[-] Please run with sudo manually'));
process.exit(1); process.exit(1);
} }
} }
@@ -172,83 +248,214 @@ function checkPermissions(filePath) {
try { try {
// Check write access to the file/directory // Check write access to the file/directory
fs.accessSync(filePath, fs.constants.W_OK); fs.accessSync(filePath, fs.constants.W_OK);
// Check if we can create directories // Check if we can create directories
const testDirPath = path.join(path.dirname(filePath), `.test_${Date.now()}`); const testDirPath = path.join(path.dirname(filePath), `.test_${Date.now()}`);
try { try {
fs.mkdirSync(testDirPath, { recursive: true }); fs.mkdirSync(testDirPath, { recursive: true });
fs.rmdirSync(testDirPath); fs.rmdirSync(testDirPath);
} catch (dirError) { } catch (dirError) {
console.error(chalk.redBright`[-] Cannot create directories in ${path.dirname(filePath)}: ${dirError.message}`); console.error(chalk.redBright(`[-] Cannot create directories in ${path.dirname(filePath)}: ${dirError.message}`));
return false; return false;
} }
console.log(chalk.greenBright`[+] Permissions check passed for ${filePath}`); console.log(chalk.greenBright(`[+] Permissions check passed for ${filePath}`));
return true; return true;
} catch (e) { } catch (e) {
console.error(chalk.redBright`[-] Permissions check failed for ${filePath}: ${e.message}`); console.error(chalk.redBright(`[-] Permissions check failed for ${filePath}: ${e.message}`));
return false; return false;
} }
} }
// Kill HTTP Toolkit processes // Kill HTTP Toolkit processes
async function killHttpToolkit() { async function killHttpToolkit() {
console.log(chalk.yellowBright`[+] Checking for running HTTP Toolkit processes...`); console.log(chalk.yellowBright('[+] Checking for running HTTP Toolkit processes...'));
try { try {
if (isWin) { if (isWin) {
// Windows: Use tasklist to find and taskkill to terminate // Windows: Use tasklist to find and taskkill to terminate
const output = execSync('tasklist /FI "IMAGENAME eq HTTP Toolkit.exe" /FO CSV /NH', { encoding: "utf-8" }); const output = execSync('tasklist /FI "IMAGENAME eq HTTP Toolkit.exe" /FO CSV /NH', { encoding: "utf-8" });
if (output.includes("HTTP Toolkit.exe")) { if (output.includes("HTTP Toolkit.exe")) {
console.log(chalk.yellowBright`[!] HTTP Toolkit is running, attempting to close it...`); console.log(chalk.yellowBright('[!] HTTP Toolkit is running, attempting to close it...'));
execSync('taskkill /F /IM "HTTP Toolkit.exe" /T', { stdio: "ignore" }); execSync('taskkill /F /IM "HTTP Toolkit.exe" /T', { stdio: "ignore" });
console.log(chalk.greenBright`[+] HTTP Toolkit processes terminated`); console.log(chalk.greenBright('[+] HTTP Toolkit processes terminated'));
// Wait a moment for the process to fully close // Wait a moment for the process to fully close
await new Promise(resolve => setTimeout(resolve, 2000)); await new Promise(resolve => setTimeout(resolve, 2000));
} else { } else {
console.log(chalk.greenBright`[+] HTTP Toolkit is not running`); console.log(chalk.greenBright('[+] HTTP Toolkit is not running'));
} }
} else if (isMac) { } else if (isMac) {
// macOS: Use pgrep and pkill // macOS: Use pgrep and pkill
try { try {
execSync('pgrep -f "HTTP Toolkit"', { stdio: "ignore" }); execSync('pgrep -f "HTTP Toolkit"', { stdio: "ignore" });
console.log(chalk.yellowBright`[!] HTTP Toolkit is running, attempting to close it...`); console.log(chalk.yellowBright('[!] HTTP Toolkit is running, attempting to close it...'));
execSync('pkill -f "HTTP Toolkit"', { stdio: "ignore" }); execSync('pkill -f "HTTP Toolkit"', { stdio: "ignore" });
console.log(chalk.greenBright`[+] HTTP Toolkit processes terminated`); console.log(chalk.greenBright('[+] HTTP Toolkit processes terminated'));
await new Promise(resolve => setTimeout(resolve, 2000)); await new Promise(resolve => setTimeout(resolve, 2000));
} catch (e) { } catch (e) {
console.log(chalk.greenBright`[+] HTTP Toolkit is not running`); console.log(chalk.greenBright('[+] HTTP Toolkit is not running'));
} }
} else { } else {
// Linux: Use pgrep and pkill // Linux: Use pgrep and pkill
try { try {
execSync('pgrep -f "HTTP Toolkit"', { stdio: "ignore" }); execSync('pgrep -f "HTTP Toolkit"', { stdio: "ignore" });
console.log(chalk.yellowBright`[!] HTTP Toolkit is running, attempting to close it...`); console.log(chalk.yellowBright('[!] HTTP Toolkit is running, attempting to close it...'));
execSync('pkill -f "HTTP Toolkit"', { stdio: "ignore" }); execSync('pkill -f "HTTP Toolkit"', { stdio: "ignore" });
console.log(chalk.greenBright`[+] HTTP Toolkit processes terminated`); console.log(chalk.greenBright('[+] HTTP Toolkit processes terminated'));
await new Promise(resolve => setTimeout(resolve, 2000)); await new Promise(resolve => setTimeout(resolve, 2000));
} catch (e) { } catch (e) {
console.log(chalk.greenBright`[+] HTTP Toolkit is not running`); console.log(chalk.greenBright('[+] HTTP Toolkit is not running'));
} }
} }
} catch (e) { } catch (e) {
console.log(chalk.yellowBright`[!] Could not check/kill processes: ${e.message}`); console.log(chalk.yellowBright(`[!] Could not check/kill processes: ${e.message}`));
console.log(chalk.yellowBright`[!] If HTTP Toolkit is running, please close it manually`); console.log(chalk.yellowBright('[!] If HTTP Toolkit is running, please close it manually'));
} }
} }
// Resolve the base installation directory (without the trailing resources folder)
function getBinaryBasePath(resourcesPath) {
const normalized = resourcesPath.replace(/[\\/]+$/, "");
if (normalized.toLowerCase().endsWith("resources")) {
return path.dirname(normalized);
}
return normalized;
}
// Determine the executable path for HTTP Toolkit based on platform
function getExecutablePath(resourcesPath) {
const basePath = getBinaryBasePath(resourcesPath);
const candidates = isWin ? [path.join(basePath, "HTTP Toolkit.exe"), path.join(basePath, "httptoolkit.exe")] : isMac ? [path.join(basePath, "MacOS", "HTTP Toolkit"), path.join(basePath, "MacOS", "HTTP Toolkit Preview")] : [path.join(basePath, "httptoolkit"), path.join(basePath, "HTTP Toolkit")];
for (const candidate of candidates) {
if (fs.existsSync(candidate)) {
return candidate;
}
}
throw new Error(`Could not locate HTTP Toolkit executable near ${resourcesPath}`);
}
// Extract hashes from integrity check output
function extractIntegrityHashes(output) {
const regex = /Integrity check failed for asar archive[\s\S]*?\(\s*([0-9a-f]{64})\s*vs\s*([0-9a-f]{64})\s*\)/i;
const match = output.match(regex);
if (!match) return null;
return {
originalHash: match[1],
newHash: match[2],
};
}
// Launch the app once to grab integrity hashes from its crash output
async function captureIntegrityHashes(executablePath) {
return new Promise((resolve, reject) => {
let output = "";
let finished = false;
const child = spawn(executablePath, {
cwd: path.dirname(executablePath),
stdio: ["ignore", "pipe", "pipe"],
windowsHide: true,
});
const timeout = setTimeout(() => {
if (!finished) {
finished = true;
child.kill();
reject(new Error("Timed out waiting for integrity check output"));
}
}, 30000);
const handleData = data => {
output += data.toString();
const hashes = extractIntegrityHashes(output);
if (hashes && !finished) {
finished = true;
clearTimeout(timeout);
child.kill();
resolve(hashes);
}
};
child.stdout.on("data", handleData);
child.stderr.on("data", handleData);
child.on("error", err => {
if (!finished) {
finished = true;
clearTimeout(timeout);
reject(err);
}
});
child.on("exit", () => {
if (!finished) {
clearTimeout(timeout);
const hashes = extractIntegrityHashes(output);
if (hashes) {
resolve(hashes);
} else {
reject(new Error("Could not find integrity hashes in HTTP Toolkit output"));
}
}
});
});
}
// Replace all occurrences of the original hash with the new hash inside the binary
function patchExecutableHash(executablePath, originalHash, newHash) {
if (originalHash.length !== newHash.length) {
throw new Error("Hash lengths do not match; cannot safely patch binary");
}
const binary = fs.readFileSync(executablePath);
const originalBuf = Buffer.from(originalHash, "utf-8");
const newBuf = Buffer.from(newHash, "utf-8");
let occurrences = 0;
let idx = binary.indexOf(originalBuf);
while (idx !== -1) {
newBuf.copy(binary, idx);
occurrences += 1;
idx = binary.indexOf(originalBuf, idx + originalBuf.length);
}
if (occurrences === 0) {
throw new Error("Original hash not found in binary");
}
fs.writeFileSync(executablePath, binary);
return occurrences;
}
// Replace the asar integrity hash inside the macOS Info.plist
function patchInfoPlistHash(resourcesPath, originalHash, newHash) {
const basePath = getBinaryBasePath(resourcesPath);
const plistPath = path.join(basePath, "Info.plist");
if (!fs.existsSync(plistPath)) {
throw new Error(`Info.plist not found at ${plistPath}`);
}
let content = fs.readFileSync(plistPath, "utf-8");
if (!content.includes(originalHash)) {
throw new Error("Original hash not found in Info.plist");
}
content = content.replaceAll(originalHash, newHash);
fs.writeFileSync(plistPath, content, "utf-8");
}
// Unpatch/restore function // Unpatch/restore function
async function unpatchApp() { async function unpatchApp() {
console.log(chalk.blueBright`[+] HTTP Toolkit Unpatcher Started`); console.log(chalk.blueBright('[+] HTTP Toolkit Unpatcher Started'));
// Step 1: Find app path
const appPath = await findAppPath(); const appPath = await findAppPath();
console.log(chalk.greenBright`[+] HTTP Toolkit found at ${appPath}`); console.log(chalk.greenBright(`[+] HTTP Toolkit found at ${appPath}`));
// Step 2: Kill HTTP Toolkit if running
await killHttpToolkit(); await killHttpToolkit();
// Step 3: Check permissions
const asarPath = path.join(appPath, "app.asar"); const asarPath = path.join(appPath, "app.asar");
const backupPath = path.join(appPath, "app.asar.bak"); const backupPath = path.join(appPath, "app.asar.bak");
const extractPath = path.join(appPath, "app.asar_extracted"); const extractPath = path.join(appPath, "app.asar_extracted");
@@ -257,196 +464,269 @@ async function unpatchApp() {
const hasPermissions = checkPermissions(appPath) && checkPermissions(asarPath); const hasPermissions = checkPermissions(appPath) && checkPermissions(asarPath);
if (!hasPermissions) { if (!hasPermissions) {
console.log(chalk.yellowBright`[!] No write permissions for ${appPath}`); console.log(chalk.yellowBright(`[!] No write permissions for ${appPath}`));
if (isElevated()) { if (isElevated()) {
console.error(chalk.redBright`[-] Still no permissions even with elevated privileges`); console.error(chalk.redBright('[-] Still no permissions even with elevated privileges'));
process.exit(1); process.exit(1);
} }
console.log(chalk.yellowBright`[!] Administrator/sudo privileges required for unpatching`); console.log(chalk.yellowBright('[!] Administrator/sudo privileges required for unpatching'));
await requestElevation(); await requestElevation();
} }
// Step 4: Check if backup exists
if (!fs.existsSync(backupPath)) { if (!fs.existsSync(backupPath)) {
console.error(chalk.redBright`[-] Backup file not found at ${backupPath}`); console.error(chalk.redBright(`[-] Backup file not found at ${backupPath}`));
console.error(chalk.redBright`[-] Cannot unpatch without backup file`); console.error(chalk.redBright('[-] Cannot unpatch without backup file'));
process.exit(1); process.exit(1);
} }
// Step 5: Restore from backup console.log(chalk.yellowBright('[+] Restoring from backup...'));
console.log(chalk.yellowBright`[+] Restoring from backup...`);
try { try {
fs.copyFileSync(backupPath, asarPath); fs.copyFileSync(backupPath, asarPath);
console.log(chalk.greenBright`[+] Restored app.asar from backup`); console.log(chalk.greenBright('[+] Restored app.asar from backup'));
} catch (e) { } catch (e) {
console.error(chalk.redBright`[-] Failed to restore backup: ${e.message}`); console.error(chalk.redBright(`[-] Failed to restore backup: ${e.message}`));
process.exit(1); process.exit(1);
} }
// Step 6: Clean up extracted files if they exist
if (fs.existsSync(extractPath)) { if (fs.existsSync(extractPath)) {
console.log(chalk.yellowBright`[+] Removing extracted files...`); console.log(chalk.yellowBright('[+] Removing extracted files...'));
rm(extractPath); rm(extractPath);
console.log(chalk.greenBright`[+] Cleaned up extracted files`); console.log(chalk.greenBright('[+] Cleaned up extracted files'));
} }
// Step 7: Optionally remove backup
const removeBackup = await prompt("Do you want to remove the backup file? (y/n): "); const removeBackup = await prompt("Do you want to remove the backup file? (y/n): ");
if (removeBackup.toLowerCase() === "y" || removeBackup.toLowerCase() === "yes") { if (removeBackup.toLowerCase() === "y" || removeBackup.toLowerCase() === "yes") {
fs.rmSync(backupPath, { force: true }); fs.rmSync(backupPath, { force: true });
console.log(chalk.greenBright`[+] Backup file removed`); console.log(chalk.greenBright('[+] Backup file removed'));
} }
console.log(chalk.greenBright`[+] Successfully unpatched!`); console.log(chalk.greenBright('[+] Successfully unpatched!'));
} }
// Main patching function // Main patching function
async function patchApp() { async function patchApp() {
console.log(chalk.blueBright`[+] HTTP Toolkit Patcher Started`); console.log(chalk.blueBright('[+] HTTP Toolkit Patcher Started'));
// Step 1: Find app path
const appPath = await findAppPath(); const appPath = await findAppPath();
console.log(chalk.greenBright`[+] HTTP Toolkit found at ${appPath}`); console.log(chalk.greenBright(`[+] HTTP Toolkit found at ${appPath}`));
// Step 2: Kill HTTP Toolkit if running
await killHttpToolkit(); await killHttpToolkit();
// Step 3: Check permissions
const asarPath = path.join(appPath, "app.asar"); const asarPath = path.join(appPath, "app.asar");
// Check if we have write permissions on both the directory and the file // Check if we have write permissions on both the directory and the file
const hasPermissions = checkPermissions(appPath) && checkPermissions(asarPath); const hasPermissions = checkPermissions(appPath) && checkPermissions(asarPath);
if (!hasPermissions) { if (!hasPermissions) {
console.log(chalk.yellowBright`[!] No write permissions for ${appPath}`); console.log(chalk.yellowBright(`[!] No write permissions for ${appPath}`));
if (isElevated()) { if (isElevated()) {
console.error(chalk.redBright`[-] Still no permissions even with elevated privileges`); console.error(chalk.redBright('[-] Still no permissions even with elevated privileges'));
process.exit(1); process.exit(1);
} }
console.log(chalk.yellowBright`[!] Administrator/sudo privileges required for patching`); console.log(chalk.yellowBright('[!] Administrator/sudo privileges required for patching'));
await requestElevation(); await requestElevation();
} }
// Step 4: Backup app.asar
const backupPath = path.join(appPath, "app.asar.bak"); const backupPath = path.join(appPath, "app.asar.bak");
if (!fs.existsSync(backupPath)) { if (!fs.existsSync(backupPath)) {
console.log(chalk.yellowBright`[+] Creating backup...`); console.log(chalk.yellowBright('[+] Creating backup...'));
fs.copyFileSync(asarPath, backupPath); fs.copyFileSync(asarPath, backupPath);
console.log(chalk.greenBright`[+] Backup created at ${backupPath}`); console.log(chalk.greenBright(`[+] Backup created at ${backupPath}`));
} }
// Step 5: Extract app.asar
const extractPath = path.join(appPath, "app.asar_extracted"); const extractPath = path.join(appPath, "app.asar_extracted");
console.log(chalk.yellowBright`[+] Extracting app.asar...`); console.log(chalk.yellowBright('[+] Extracting app.asar...'));
rm(extractPath); rm(extractPath);
asar.extractAll(asarPath, extractPath); asar.extractAll(asarPath, extractPath);
console.log(chalk.greenBright`[+] Extracted to ${extractPath}`); console.log(chalk.greenBright(`[+] Extracted to ${extractPath}`));
// Step 6: Check if preload.cjs or preload.js exists const preloadPath = path.join(extractPath, "build", "preload.cjs");
let preloadPath = path.join(extractPath, "build", "preload.cjs");
if (!fs.existsSync(preloadPath)) { if (!fs.existsSync(preloadPath)) {
console.log(chalk.yellowBright`[!] preload.cjs not found, checking for preload.js...`); console.error(chalk.redBright(`[-] preload.cjs not found in ${path.join(extractPath, "build")}`));
preloadPath = path.join(extractPath, "build", "preload.js"); console.error(chalk.yellowBright('[!] Please download the latest version of HTTP Toolkit from https://httptoolkit.com/'));
if (!fs.existsSync(preloadPath)) {
console.error(chalk.redBright`[-] Neither preload.cjs nor preload.js found in ${path.join(extractPath, "build")}`);
rm(extractPath);
process.exit(1);
}
console.log(chalk.greenBright`[+] Found preload.js`);
} else {
console.log(chalk.greenBright`[+] Found preload.cjs`);
}
// Step 7: Fetch inject.js from GitHub
console.log(chalk.yellowBright`[+] Fetching inject code from GitHub...`);
const injectCode = await fetchUrl("https://raw.githubusercontent.com/xenos1337/httptoolkit-patcher/refs/heads/master/inject.js");
if (!injectCode || !injectCode.includes("injectPageContextHooks")) {
console.error(chalk.redBright`[-] Failed to fetch inject.js from GitHub`);
rm(extractPath); rm(extractPath);
process.exit(1); process.exit(1);
} }
console.log(chalk.greenBright`[+] Inject code fetched successfully`); console.log(chalk.greenBright('[+] Found preload.cjs'));
console.log(chalk.yellowBright('[+] Reading inject code from local file...'));
const injectJsPath = path.join(path.dirname(__filename), "inject.js");
if (!fs.existsSync(injectJsPath)) {
console.error(chalk.redBright(`[-] inject.js not found at ${injectJsPath}`));
rm(extractPath);
process.exit(1);
}
const injectCode = fs.readFileSync(injectJsPath, "utf-8");
if (!injectCode || !injectCode.includes("PAGE-INJECT")) {
console.error(chalk.redBright('[-] Invalid inject.js file'));
rm(extractPath);
process.exit(1);
}
console.log(chalk.greenBright('[+] Inject code loaded successfully'));
// Step 8: Read preload file and check if already patched
let preloadContent = fs.readFileSync(preloadPath, "utf-8"); let preloadContent = fs.readFileSync(preloadPath, "utf-8");
const isPatched = preloadContent.includes("injectPageContextHooks");
if (isPatched) { const electronVarName = preloadContent.includes("electron_1") ? "electron_1" : "electron";
console.log(chalk.yellowBright`[!] File already patched`); console.log(chalk.greenBright(`[+] Detected electron variable: ${electronVarName}`));
const preloadPatchCode = `
(function loadInjectScript() {
const injectCode = ${JSON.stringify(injectCode)};
function injectViaWebFrame() {
try {
const { webFrame } = ${electronVarName};
if (webFrame && webFrame.executeJavaScript) {
webFrame.executeJavaScript(injectCode).then(() => console.log("[PRELOAD] Injected via webFrame.executeJavaScript")).catch(err => console.error("[PRELOAD] webFrame injection failed:", err));
return true;
}
} catch (e) {
console.error("[PRELOAD] webFrame not available:", e);
}
return false;
}
if (!injectViaWebFrame()) {
const tryInject = () => {
if (!injectViaWebFrame()) {
console.error("[PRELOAD] All injection methods failed");
}
};
if (document.readyState === 'complete' || document.readyState === 'interactive') {
tryInject();
} else {
document.addEventListener('DOMContentLoaded', tryInject, { once: true });
}
}
})();
`;
const isPreloadPatched = preloadContent.includes("loadInjectScript");
if (isPreloadPatched) {
console.log(chalk.yellowBright('[!] Files already patched'));
const answer = await prompt("Do you want to repatch? (y/n): "); const answer = await prompt("Do you want to repatch? (y/n): ");
if (answer.toLowerCase() !== "y" && answer.toLowerCase() !== "yes") { if (answer.toLowerCase() !== "y" && answer.toLowerCase() !== "yes") {
console.log(chalk.blueBright`[+] Patching cancelled`); console.log(chalk.blueBright('[+] Patching cancelled'));
rm(extractPath); rm(extractPath);
process.exit(0); process.exit(0);
} }
// Replace the existing injectPageContextHooks function // Remove existing patches
console.log(chalk.yellowBright`[+] Replacing existing patch...`); console.log(chalk.yellowBright('[+] Replacing existing patches...'));
const functionRegex = /\(function injectPageContextHooks\(\) \{[\s\S]*?\}\)\(\);/;
preloadContent = preloadContent.replace(functionRegex, injectCode);
} else {
// Find line with electron_1 and insert inject code below it
console.log(chalk.yellowBright`[+] Applying patch...`);
const lines = preloadContent.split("\n");
let insertIndex = -1;
for (let i = 0; i < lines.length; i++) { // Remove preload patch
if (lines[i].includes("electron_1")) { const preloadPatchRegex = /\n?\(function loadInjectScript\(\) \{[\s\S]*?\}\)\(\);/;
insertIndex = i + 1; preloadContent = preloadContent.replace(preloadPatchRegex, "");
break;
}
}
if (insertIndex === -1) {
console.error(chalk.redBright`[-] Could not find insertion point (electron_1) in ${path.basename(preloadPath)}`);
rm(extractPath);
process.exit(1);
}
lines.splice(insertIndex, 0, injectCode);
preloadContent = lines.join("\n");
} }
// Step 9: Write patched preload file console.log(chalk.yellowBright('[+] Applying preload patch...'));
const preloadLines = preloadContent.split("\n");
let preloadInsertIndex = -1;
for (let i = 0; i < preloadLines.length; i++) {
const line = preloadLines[i];
if (line.includes('require("electron")') || line.includes("require('electron')") || line.includes("electron_1")) {
preloadInsertIndex = i + 1;
break;
}
}
if (preloadInsertIndex === -1) {
console.error(chalk.redBright(`[-] Could not find insertion point (electron import) in ${path.basename(preloadPath)}`));
rm(extractPath);
process.exit(1);
}
preloadLines.splice(preloadInsertIndex, 0, preloadPatchCode);
preloadContent = preloadLines.join("\n");
// Write patched preload file
fs.writeFileSync(preloadPath, preloadContent, "utf-8"); fs.writeFileSync(preloadPath, preloadContent, "utf-8");
console.log(chalk.greenBright`[+] ${path.basename(preloadPath)} patched successfully`); console.log(chalk.greenBright(`[+] ${path.basename(preloadPath)} patched successfully`));
// Step 10: Repackage app.asar console.log(chalk.yellowBright('[+] Repackaging app.asar...'));
console.log(chalk.yellowBright`[+] Repackaging app.asar...`);
await asar.createPackage(extractPath, asarPath); await asar.createPackage(extractPath, asarPath);
console.log(chalk.greenBright`[+] app.asar repackaged successfully`); console.log(chalk.greenBright('[+] app.asar repackaged successfully'));
// Step 11: Clean up let executablePath;
console.log(chalk.yellowBright`[+] Cleaning up temporary files...`);
rm(extractPath);
console.log(chalk.greenBright`[+] Successfully patched!`);
// Step 12: Open HTTP Toolkit as detached process
console.log(chalk.blueBright`[+] Opening HTTP Toolkit...`);
try { try {
if (isLinux) { executablePath = getExecutablePath(appPath);
// Linux: Cannot auto-launch reliably, show manual instructions } catch (e) {
console.log(chalk.yellowBright`[!] HTTP Toolkit has been successfully patched`); rm(extractPath);
console.log(chalk.yellowBright`[!] Please manually launch HTTP Toolkit from your applications menu or using the command:`); console.error(chalk.redBright(`[-] ${e.message}`));
console.log(chalk.blueBright` httptoolkit`); process.exit(1);
}
// macOS: Sign the patched app
if (isMac) {
const appBundlePath = path.resolve(appPath, '..', '..');
console.log(chalk.yellowBright('[+] Signing HTTP Toolkit with ad-hoc signature...'));
try {
execSync(`codesign --deep --force --sign - "${appBundlePath}"`, {stdio: "pipe"});
console.log(chalk.greenBright('[+] HTTP Toolkit signed successfully'));
console.log(chalk.yellowBright(` Make sure to whitelist the app in privacy and security settings!`));
} catch (e) {
rm(extractPath);
console.error(chalk.redBright(`[!] Failed to sign HTTP Toolkit: ${e.message}`));
process.exit(1);
}
try {
execSync(`xattr -d com.apple.quarantine "${appBundlePath}" 2>/dev/null`, {stdio: 'pipe'});
} catch (e) {
console.log(chalk.redBright('[!] Failed to remove the quarantine attribute'));
}
}
console.log(chalk.yellowBright('[+] Launching HTTP Toolkit to read integrity hashes...'));
let hashes;
try {
hashes = await captureIntegrityHashes(executablePath);
} catch (e) {
rm(extractPath);
console.error(chalk.redBright(`[-] Failed to capture integrity hashes: ${e.message}`));
process.exit(1);
}
console.log(chalk.greenBright('[+] Integrity hashes captured'));
console.log(chalk.white(` Original hash: ${hashes.originalHash}`));
console.log(chalk.white(` New asar hash: ${hashes.newHash}`));
try {
if (isMac) {
patchInfoPlistHash(appPath, hashes.originalHash, hashes.newHash);
console.log(chalk.greenBright('[+] Patched Info.plist hash'));
} else { } else {
const command = isWin ? `"${path.resolve(appPath, "..", "HTTP Toolkit.exe")}"` : isMac ? 'open -a "HTTP Toolkit"' : "httptoolkit"; const replacements = patchExecutableHash(executablePath, hashes.originalHash, hashes.newHash);
const child = spawn(command, { console.log(chalk.greenBright(`[+] Patched binary hash (${replacements} replacement${replacements === 1 ? "" : "s"})`));
stdio: "ignore",
shell: true,
detached: true,
});
child.unref();
console.log(chalk.greenBright`[+] HTTP Toolkit launched successfully`);
} }
} catch (e) { } catch (e) {
console.error(chalk.yellowBright`[!] Could not auto-start HTTP Toolkit: ${e.message}`); rm(extractPath);
console.log(chalk.blueBright`[+] Please start HTTP Toolkit manually`); console.error(chalk.redBright(`[-] Failed to patch hash: ${e.message}`));
process.exit(1);
}
console.log(chalk.yellowBright('[+] Cleaning up temporary files...'));
rm(extractPath);
console.log(chalk.greenBright('[+] Successfully patched!'));
console.log(chalk.blueBright('[+] Opening HTTP Toolkit...'));
try {
const child = spawn(executablePath, {
stdio: "ignore",
shell: false,
detached: true,
});
child.unref();
console.log(chalk.greenBright('[+] HTTP Toolkit launched successfully'));
} catch (e) {
console.error(chalk.yellowBright(`[!] Could not auto-start HTTP Toolkit: ${e.message}`));
console.log(chalk.blueBright('[+] Please start HTTP Toolkit manually'));
} }
} }
@@ -465,33 +745,36 @@ const commandName = (() => {
// Run the appropriate command // Run the appropriate command
(async () => { (async () => {
try { try {
// Check for updates from GitHub
await checkForUpdates();
if (command === "unpatch" || command === "restore") { if (command === "unpatch" || command === "restore") {
await unpatchApp(); await unpatchApp();
} else if (command === "help" || command === "-h" || command === "--help") { } else if (command === "help" || command === "-h" || command === "--help") {
console.log(chalk.blueBright`HTTP Toolkit Patcher`); console.log(chalk.blueBright('HTTP Toolkit Patcher'));
console.log(chalk.white`\nUsage:`); console.log(chalk.white('\nUsage:'));
console.log(chalk.white` ${commandName} [command]`); console.log(chalk.white(` ${commandName} [command]`));
console.log(chalk.white`\nCommands:`); console.log(chalk.white('\nCommands:'));
console.log(chalk.white` patch ${chalk.gray`- Patch HTTP Toolkit (default)`}`); console.log(chalk.white(` patch ${chalk.gray('- Patch HTTP Toolkit (default)')}`));
console.log(chalk.white` unpatch ${chalk.gray`- Restore original HTTP Toolkit from backup`}`); console.log(chalk.white(` unpatch ${chalk.gray('- Restore original HTTP Toolkit from backup')}`));
console.log(chalk.white` restore ${chalk.gray`- Alias for unpatch`}`); console.log(chalk.white(` restore ${chalk.gray('- Alias for unpatch')}`));
console.log(chalk.white` help ${chalk.gray`- Show this help message`}`); console.log(chalk.white(` help ${chalk.gray('- Show this help message')}`));
process.exit(0); process.exit(0);
} else if (!command || command === "patch") { } else if (!command || command === "patch") {
// Ask for confirmation before patching // Ask for confirmation before patching
const answer = await prompt("Do you want to patch HTTP Toolkit? [Y/n]: "); const answer = await prompt("Do you want to patch HTTP Toolkit? [Y/n]: ");
if (answer.toLowerCase() === "n" || answer.toLowerCase() === "no") { if (answer.toLowerCase() === "n" || answer.toLowerCase() === "no") {
console.log(chalk.blueBright`[+] Patching cancelled`); console.log(chalk.blueBright('[+] Patching cancelled'));
process.exit(0); process.exit(0);
} }
await patchApp(); await patchApp();
} else { } else {
console.error(chalk.redBright`[-] Unknown command: ${command}`); console.error(chalk.redBright(`[-] Unknown command: ${command}`));
console.log(chalk.yellowBright`[!] Use 'help' to see available commands`); console.log(chalk.yellowBright('[!] Use \'help\' to see available commands'));
process.exit(1); process.exit(1);
} }
} catch (error) { } catch (error) {
console.error(chalk.redBright`[-] Error: ${error.message}`); console.error(chalk.redBright(`[-] Error: ${error.message}`));
process.exit(1); process.exit(1);
} }
})(); })();
+214 -184
View File
@@ -1,206 +1,236 @@
(function injectPageContextHooks() { (function () {
const injectionCode = ` console.log("[PAGE-INJECT] Installing hooks in page context");
(function() {
console.log("[PAGE-INJECT] Installing hooks in page context"); // Properties to hook directly on accountStore (still exist as computed properties)
const propertyHooks = {
const propertyHooks = { isLoggedIn: true,
isPaidUser: true, userEmail: "hi@httptoolkit.com",
isLoggedIn: true, mightBePaidUser: true,
userHasSubscription: true, userSubscription: {
userEmail: "hi@httptoolkit.com", state: "fulfilled",
mightBePaidUser: true, status: "active",
isPastDueUser: false, plan: "pro",
userSubscription: { sku: "sku",
status: "active", tierCode: "pro",
plan: "pro", interval: "monthly",
sku: "sku", quantity: 1,
tierCode: "pro", expiry: new Date(new Date().setFullYear(new Date().getFullYear() + 10)),
interval: "monthly", updateBillingDetailsUrl: "https://httptoolkit.com/",
quantity: 1, cancelSubscriptionUrl: "https://httptoolkit.com/",
expiry: new Date("2999-01-01"), lastReceiptUrl: "https://httptoolkit.com/",
updateBillingDetailsUrl: "https://httptoolkit.com/", canManageSubscription: true,
cancelSubscriptionUrl: "https://httptoolkit.com/", },
lastReceiptUrl: "https://httptoolkit.com/", };
canManageSubscription: true
// Methods to hook on the user object (moved from accountStore properties to user methods in v3.1.0)
const userMethodHooks = {
isPaidUser: true,
isPastDueUser: false,
userHasSubscription: true,
};
const hookedObjects = new WeakSet();
const hookedUsers = new WeakSet();
// Patch user object methods
function patchUserObject(user) {
if (!user || typeof user !== "object" || hookedUsers.has(user)) return;
for (const methodName of Object.keys(userMethodHooks)) {
if (typeof user[methodName] === "function") {
console.log("[PAGE-INJECT] Patching user." + methodName + "()");
user[methodName] = function () {
return userMethodHooks[methodName];
};
}
}
hookedUsers.add(user);
}
// Override Object.defineProperty to intercept all property definitions
const originalDefineProperty = Object.defineProperty;
Object.defineProperty = function (target, prop, descriptor) {
if (prop in propertyHooks) {
console.log("[PAGE-INJECT] Intercepting defineProperty for: " + prop);
if (descriptor && descriptor.get) {
const originalGetter = descriptor.get;
descriptor.get = function () {
const originalValue = originalGetter.call(this);
console.log("[PAGE-INJECT] " + prop + " getter called, original=" + originalValue + ", returning=" + JSON.stringify(propertyHooks[prop]));
return propertyHooks[prop];
};
} else if (descriptor && descriptor.value !== undefined) {
console.log("[PAGE-INJECT] " + prop + " value being defined, overriding to " + JSON.stringify(propertyHooks[prop]));
descriptor.value = propertyHooks[prop];
}
}
// Intercept the 'user' property to patch its methods when it's set/accessed
if (prop === "user") {
console.log("[PAGE-INJECT] Intercepting defineProperty for: user");
if (descriptor && descriptor.get) {
const originalGetter = descriptor.get;
descriptor.get = function () {
const user = originalGetter.call(this);
if (user) patchUserObject(user);
return user;
};
} else if (descriptor && descriptor.value !== undefined && descriptor.value) {
patchUserObject(descriptor.value);
}
}
return originalDefineProperty.call(this, target, prop, descriptor);
};
// Hook Object.defineProperties too
const originalDefineProperties = Object.defineProperties;
Object.defineProperties = function (target, props) {
for (let prop in props) {
if (prop in propertyHooks) {
console.log("[PAGE-INJECT] Intercepting defineProperties for: " + prop);
if (props[prop].get) {
const originalGetter = props[prop].get;
props[prop].get = function () {
const originalValue = originalGetter.call(this);
console.log("[PAGE-INJECT] " + prop + " getter called, original=" + originalValue + ", returning=" + JSON.stringify(propertyHooks[prop]));
return propertyHooks[prop];
};
} else if (props[prop].value !== undefined) {
props[prop].value = propertyHooks[prop];
} }
}; }
const hookedObjects = new WeakSet(); if (prop === "user") {
console.log("[PAGE-INJECT] Intercepting defineProperties for: user");
// Override Object.defineProperty to intercept all property definitions if (props[prop].get) {
const originalDefineProperty = Object.defineProperty; const originalGetter = props[prop].get;
Object.defineProperty = function(target, prop, descriptor) { props[prop].get = function () {
// Intercept our target properties const user = originalGetter.call(this);
if (prop in propertyHooks) { if (user) patchUserObject(user);
console.log("[PAGE-INJECT] Intercepting defineProperty for: " + prop); return user;
};
if (descriptor && descriptor.get) { } else if (props[prop].value !== undefined && props[prop].value) {
const originalGetter = descriptor.get; patchUserObject(props[prop].value);
descriptor.get = function() {
const originalValue = originalGetter.call(this);
console.log("[PAGE-INJECT] " + prop + " getter called, original=" + originalValue + ", returning=" + JSON.stringify(propertyHooks[prop]));
return propertyHooks[prop];
};
} else if (descriptor && descriptor.value !== undefined) {
console.log("[PAGE-INJECT] " + prop + " value being defined, overriding to " + JSON.stringify(propertyHooks[prop]));
descriptor.value = propertyHooks[prop];
}
} }
}
return originalDefineProperty.call(this, target, prop, descriptor); }
}; return originalDefineProperties.call(this, target, props);
};
// Hook Object.defineProperties too // Periodically scan and patch existing objects
const originalDefineProperties = Object.defineProperties; function scanAndPatch() {
Object.defineProperties = function(target, props) { // Search through window and common store locations
for (let prop in props) { const searchPaths = [window, window.accountStore, window.stores && window.stores.accountStore, window.appState && window.appState.accountStore];
if (prop in propertyHooks) {
console.log("[PAGE-INJECT] Intercepting defineProperties for: " + prop);
if (props[prop].get) {
const originalGetter = props[prop].get;
props[prop].get = function() {
const originalValue = originalGetter.call(this);
console.log("[PAGE-INJECT] " + prop + " getter called, original=" + originalValue + ", returning=" + JSON.stringify(propertyHooks[prop]));
return propertyHooks[prop];
};
} else if (props[prop].value !== undefined) {
props[prop].value = propertyHooks[prop];
}
}
}
return originalDefineProperties.call(this, target, props);
};
// Periodically scan and patch existing objects searchPaths.forEach((obj, idx) => {
function scanAndPatch() { if (!obj || hookedObjects.has(obj)) return;
// Search through window and common store locations
const searchPaths = [
window,
window.accountStore,
window.stores && window.stores.accountStore,
window.appState && window.appState.accountStore,
];
searchPaths.forEach((obj, idx) => { try {
if (!obj || hookedObjects.has(obj)) return; // Patch direct property hooks
Object.keys(propertyHooks).forEach(prop => {
try { try {
Object.keys(propertyHooks).forEach(prop => { const desc = Object.getOwnPropertyDescriptor(obj, prop);
try { if (desc && desc.configurable) {
const desc = Object.getOwnPropertyDescriptor(obj, prop); console.log("[PAGE-INJECT] Found " + prop + " on object #" + idx + ", patching...");
if (desc && desc.configurable) {
console.log("[PAGE-INJECT] Found " + prop + " on object #" + idx + ", patching..."); if (desc.get) {
const originalGetter = desc.get;
if (desc.get) { originalDefineProperty(obj, prop, {
get: function () {
const originalValue = originalGetter.call(this);
console.log("[PAGE-INJECT] " + prop + " getter intercepted, original=" + originalValue + ", returning=" + JSON.stringify(propertyHooks[prop]));
return propertyHooks[prop];
},
set: desc.set,
configurable: true,
enumerable: desc.enumerable,
});
} else if (desc.writable) {
obj[prop] = propertyHooks[prop];
console.log("[PAGE-INJECT] " + prop + " value set to " + JSON.stringify(propertyHooks[prop]));
}
}
} catch (e) {
// Ignore individual property errors
}
});
// Patch user object if present
try {
if (obj.user && typeof obj.user === "object") {
console.log("[PAGE-INJECT] Found user object on object #" + idx + ", patching methods...");
patchUserObject(obj.user);
}
} catch (e) {}
hookedObjects.add(obj);
} catch (e) {
// Ignore object access errors
}
});
// Also try to find accountStore by scanning window properties
try {
for (let key in window) {
try {
const obj = window[key];
if (obj && typeof obj === "object" && "accountStore" in obj) {
console.log("[PAGE-INJECT] Found accountStore in window." + key);
const store = obj.accountStore;
if (store && !hookedObjects.has(store)) {
Object.keys(propertyHooks).forEach(prop => {
try {
const desc = Object.getOwnPropertyDescriptor(store, prop);
if (desc && desc.configurable && desc.get) {
const originalGetter = desc.get; const originalGetter = desc.get;
Object.defineProperty(obj, prop, { originalDefineProperty(store, prop, {
get: function() { get: function () {
const originalValue = originalGetter.call(this); const originalValue = originalGetter.call(this);
console.log("[PAGE-INJECT] " + prop + " getter intercepted, original=" + originalValue + ", returning=" + JSON.stringify(propertyHooks[prop])); console.log("[PAGE-INJECT] accountStore." + prop + " intercepted, original=" + originalValue + ", returning=" + JSON.stringify(propertyHooks[prop]));
return propertyHooks[prop]; return propertyHooks[prop];
}, },
set: desc.set, set: desc.set,
configurable: true, configurable: true,
enumerable: desc.enumerable enumerable: desc.enumerable,
}); });
} else if (desc.writable) {
obj[prop] = propertyHooks[prop];
console.log("[PAGE-INJECT] " + prop + " value set to " + JSON.stringify(propertyHooks[prop]));
} }
} } catch (e) {}
} catch (e) { });
// Ignore individual property errors
}
});
hookedObjects.add(obj);
} catch (e) {
// Ignore object access errors
}
});
// Also try to find accountStore by scanning window properties // Patch user methods on the store
try { try {
for (let key in window) { if (store.user && typeof store.user === "object") {
try { console.log("[PAGE-INJECT] Found user object in accountStore, patching methods...");
const obj = window[key]; patchUserObject(store.user);
if (obj && typeof obj === 'object' && 'accountStore' in obj) {
console.log("[PAGE-INJECT] Found accountStore in window." + key);
const store = obj.accountStore;
if (store && !hookedObjects.has(store)) {
Object.keys(propertyHooks).forEach(prop => {
try {
const desc = Object.getOwnPropertyDescriptor(store, prop);
if (desc && desc.configurable && desc.get) {
const originalGetter = desc.get;
Object.defineProperty(store, prop, {
get: function() {
const originalValue = originalGetter.call(this);
console.log("[PAGE-INJECT] accountStore." + prop + " intercepted, original=" + originalValue + ", returning=" + JSON.stringify(propertyHooks[prop]));
return propertyHooks[prop];
},
set: desc.set,
configurable: true,
enumerable: desc.enumerable
});
}
} catch (e) {}
});
hookedObjects.add(store);
} }
} } catch (e) {}
} catch (e) {}
hookedObjects.add(store);
}
} }
} catch (e) {} } catch (e) {}
} }
} catch (e) {}
// Run initial scan
scanAndPatch();
// Scan periodically for late-initialized stores
let scanCount = 0;
const scanInterval = setInterval(() => {
scanCount++;
scanAndPatch();
if (scanCount >= 10) {
clearInterval(scanInterval);
console.log("[PAGE-INJECT] Stopped periodic scanning after 10 attempts");
}
}, 100);
console.log("[PAGE-INJECT] Hooks installed successfully");
})();
`;
function injectScript() {
try {
const script = document.createElement('script');
script.textContent = injectionCode;
(document.head || document.documentElement).appendChild(script);
script.remove(); // Clean up the script tag
console.log("[PRELOAD] Injected page context hooks");
} catch (e) {
console.error("[PRELOAD] Failed to inject hooks:", e);
}
} }
// Try to inject immediately if DOM is ready, otherwise wait // Run initial scan
if (document.documentElement) { scanAndPatch();
injectScript();
} else { // Scan periodically for late-initialized stores
// Wait for document to be created let scanCount = 0;
const observer = new MutationObserver(() => { const scanInterval = setInterval(() => {
if (document.documentElement) { scanCount++;
observer.disconnect(); scanAndPatch();
injectScript();
} if (scanCount >= 50) {
}); clearInterval(scanInterval);
observer.observe(document, { childList: true, subtree: true }); console.log("[PAGE-INJECT] Stopped periodic scanning after 50 attempts");
// Also try on DOMContentLoaded as backup
if (document.addEventListener) {
document.addEventListener('DOMContentLoaded', injectScript, { once: true });
} }
} }, 100);
})();
console.log("[PAGE-INJECT] Hooks installed successfully");
})();
+129 -149
View File
@@ -1,238 +1,218 @@
{ {
"name": "httptoolkit-patcher", "name": "httptoolkit-patcher",
"version": "2.0.5", "version": "2.0.11",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "httptoolkit-patcher", "name": "httptoolkit-patcher",
"version": "2.0.5", "version": "2.0.11",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@electron/asar": "^3.2.9", "@electron/asar": "^4.1.0",
"chalk": "4" "chalk": "5.6.2"
}, },
"bin": { "bin": {
"httptoolkit-patcher": "index.js" "httptoolkit-patcher": "index.js"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20.14.9" "@types/node": "^25.5.0"
} }
}, },
"node_modules/@electron/asar": { "node_modules/@electron/asar": {
"version": "3.4.1", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.4.1.tgz", "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-4.1.0.tgz",
"integrity": "sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA==", "integrity": "sha512-DflfXtTFuTYHcyupbhnCWi+hBFdhsWl878NuA1UmW7YVFlqPlY0SHdREVnigEO3zjS2vh4hp1A4dEl3I6mgd9w==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"commander": "^5.0.0", "commander": "^13.1.0",
"glob": "^7.1.6", "glob": "^13.0.2",
"minimatch": "^3.0.4" "minimatch": "^10.0.1",
"plist": "^3.1.0"
}, },
"bin": { "bin": {
"asar": "bin/asar.js" "asar": "bin/asar.mjs"
}, },
"engines": { "engines": {
"node": ">=10.12.0" "node": ">=22.12.0"
} }
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "20.19.23", "version": "25.5.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.23.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz",
"integrity": "sha512-yIdlVVVHXpmqRhtyovZAcSy0MiPcYWGkoO4CGe/+jpP0hmNuihm4XhHbADpK++MsiLHP5MVlv+bcgdF99kSiFQ==", "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"undici-types": "~6.21.0" "undici-types": "~7.18.0"
} }
}, },
"node_modules/ansi-styles": { "node_modules/@xmldom/xmldom": {
"version": "4.3.0", "version": "0.8.13",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.13.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "integrity": "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==",
"license": "MIT", "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": { "engines": {
"node": ">=8" "node": ">=10.0.0"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
} }
}, },
"node_modules/balanced-match": { "node_modules/balanced-match": {
"version": "1.0.2", "version": "4.0.4",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
"license": "MIT",
"engines": {
"node": "18 || 20 || >=22"
}
},
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT" "license": "MIT"
}, },
"node_modules/brace-expansion": { "node_modules/brace-expansion": {
"version": "1.1.12", "version": "5.0.5",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"balanced-match": "^1.0.0", "balanced-match": "^4.0.2"
"concat-map": "0.0.1" },
"engines": {
"node": "18 || 20 || >=22"
} }
}, },
"node_modules/chalk": { "node_modules/chalk": {
"version": "4.1.2", "version": "5.6.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
"license": "MIT", "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": { "engines": {
"node": ">=10" "node": "^12.17.0 || ^14.13 || >=16.0.0"
}, },
"funding": { "funding": {
"url": "https://github.com/chalk/chalk?sponsor=1" "url": "https://github.com/chalk/chalk?sponsor=1"
} }
}, },
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"license": "MIT"
},
"node_modules/commander": { "node_modules/commander": {
"version": "5.1.0", "version": "13.1.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz",
"integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">= 6" "node": ">=18"
} }
}, },
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"license": "MIT"
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"license": "ISC"
},
"node_modules/glob": { "node_modules/glob": {
"version": "7.2.3", "version": "13.0.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==",
"deprecated": "Glob versions prior to v9 are no longer supported", "license": "BlueOak-1.0.0",
"license": "ISC",
"dependencies": { "dependencies": {
"fs.realpath": "^1.0.0", "minimatch": "^10.2.2",
"inflight": "^1.0.4", "minipass": "^7.1.3",
"inherits": "2", "path-scurry": "^2.0.2"
"minimatch": "^3.1.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}, },
"engines": { "engines": {
"node": "*" "node": "18 || 20 || >=22"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/isaacs" "url": "https://github.com/sponsors/isaacs"
} }
}, },
"node_modules/has-flag": { "node_modules/lru-cache": {
"version": "4.0.0", "version": "11.2.7",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==",
"license": "MIT", "license": "BlueOak-1.0.0",
"engines": { "engines": {
"node": ">=8" "node": "20 || >=22"
} }
}, },
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
"license": "ISC",
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"license": "ISC"
},
"node_modules/minimatch": { "node_modules/minimatch": {
"version": "3.1.2", "version": "10.2.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
"license": "ISC", "license": "BlueOak-1.0.0",
"dependencies": { "dependencies": {
"brace-expansion": "^1.1.7" "brace-expansion": "^5.0.2"
}, },
"engines": { "engines": {
"node": "*" "node": "18 || 20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
} }
}, },
"node_modules/once": { "node_modules/minipass": {
"version": "1.4.0", "version": "7.1.3",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
"license": "ISC", "license": "BlueOak-1.0.0",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
"license": "MIT",
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=16 || 14 >=14.17"
} }
}, },
"node_modules/supports-color": { "node_modules/path-scurry": {
"version": "7.2.0", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==",
"license": "MIT", "license": "BlueOak-1.0.0",
"dependencies": { "dependencies": {
"has-flag": "^4.0.0" "lru-cache": "^11.0.0",
"minipass": "^7.1.2"
}, },
"engines": { "engines": {
"node": ">=8" "node": "18 || 20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/plist": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz",
"integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==",
"license": "MIT",
"dependencies": {
"@xmldom/xmldom": "^0.8.8",
"base64-js": "^1.5.1",
"xmlbuilder": "^15.1.1"
},
"engines": {
"node": ">=10.4.0"
} }
}, },
"node_modules/undici-types": { "node_modules/undici-types": {
"version": "6.21.0", "version": "7.18.2",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/wrappy": { "node_modules/xmlbuilder": {
"version": "1.0.2", "version": "15.1.1",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==",
"license": "ISC" "license": "MIT",
"engines": {
"node": ">=8.0"
}
} }
} }
} }
+5 -4
View File
@@ -1,10 +1,11 @@
{ {
"name": "httptoolkit-patcher", "name": "httptoolkit-patcher",
"version": "2.0.5", "version": "2.0.11",
"description": "HTTP Toolkit Pro Patcher", "description": "HTTP Toolkit Pro Patcher",
"main": "index.js", "main": "index.js",
"type": "module", "type": "module",
"bin": "index.js", "bin": "index.js",
"author": "xenos1337",
"scripts": { "scripts": {
"start": "node index.js", "start": "node index.js",
"patch": "node index.js patch", "patch": "node index.js patch",
@@ -13,10 +14,10 @@
}, },
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@electron/asar": "^3.2.9", "@electron/asar": "^4.1.0",
"chalk": "4" "chalk": "5.6.2"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20.14.9" "@types/node": "^25.5.0"
} }
} }