Fix python command returning -1 by selective wrapper interception and explicit bypass

This commit is contained in:
Reinier Criel 2025-12-10 15:01:35 -08:00
parent 14bb6899d8
commit 9417be1ac5
8 changed files with 354 additions and 44 deletions

View file

@ -2,31 +2,13 @@ import { ui } from "../../environment/userInteraction.js";
import { safeSpawn } from "../../utils/safeSpawn.js";
import { mergeSafeChainProxyEnvironmentVariables } from "../../registryProxy/registryProxy.js";
import { getCombinedCaBundlePath } from "../../registryProxy/certBundle.js";
import { PIP_COMMAND, PIP3_COMMAND, PYTHON_COMMAND, PYTHON3_COMMAND } from "./pipSettings.js";
import { PIP_COMMAND, PIP3_COMMAND } from "./pipSettings.js";
import fs from "node:fs/promises";
import fsSync from "node:fs";
import os from "node:os";
import path from "node:path";
import ini from "ini";
/**
* Checks if this pip invocation should bypass safe-chain and spawn directly.
* Returns true if the tool is python/python3 but NOT being run with -m pip/pip3.
* @param {string} command - The command executable
* @param {string[]} args - The arguments
* @returns {boolean}
*/
function shouldBypassSafeChain(command, args) {
if (command === PYTHON_COMMAND || command === PYTHON3_COMMAND) {
// Check if args start with -m pip
if (args.length >= 2 && args[0] === "-m" && (args[1] === PIP_COMMAND || args[1] === PIP3_COMMAND)) {
return false;
}
return true;
}
return false;
}
/**
* Sets fallback CA bundle environment variables used by Python libraries.
* These are applied in addition to the PIP_CONFIG_FILE to ensure all Python
@ -73,23 +55,6 @@ function setFallbackCaBundleEnvironmentVariables(env, combinedCaPath) {
* @returns {Promise<{status: number}>} Exit status of the pip command
*/
export async function runPip(command, args) {
// Check if we should bypass safe-chain (python/python3 without -m pip)
if (shouldBypassSafeChain(command, args)) {
ui.writeVerbose(`Safe-chain: Bypassing safe-chain for non-pip invocation: ${command} ${args.join(" ")}`);
// Spawn the ORIGINAL command with ORIGINAL args
const { spawn } = await import("child_process");
return new Promise((_resolve) => {
const proc = spawn(command, args, { stdio: "inherit" });
proc.on("exit", (/** @type {number | null} */ code) => {
process.exit(code ?? 0);
});
proc.on("error", (/** @type {Error} */ err) => {
ui.writeError(`Error executing command: ${err.message}`);
process.exit(1);
});
});
}
try {
const env = mergeSafeChainProxyEnvironmentVariables(process.env);

View file

@ -58,12 +58,22 @@ end
# `python -m pip`, `python -m pip3`.
function python
wrapSafeChainCommand "python" $argv
# Intercept only `python -m pip|pip3`; otherwise call real python
if test (count $argv) -ge 2; and test $argv[1] = "-m"; and (test $argv[2] = "pip" -o test $argv[2] = "pip3")
wrapSafeChainCommand "python" $argv
else
command python $argv
end
end
# `python3 -m pip`, `python3 -m pip3'.
function python3
wrapSafeChainCommand "python3" $argv
# Intercept only `python3 -m pip|pip3`; otherwise call real python3
if test (count $argv) -ge 2; and test $argv[1] = "-m"; and (test $argv[2] = "pip" -o test $argv[2] = "pip3")
wrapSafeChainCommand "python3" $argv
else
command python3 $argv
end
end
function printSafeChainWarning

View file

@ -54,12 +54,22 @@ function poetry() {
# `python -m pip`, `python -m pip3`.
function python() {
wrapSafeChainCommand "python" "$@"
# Intercept only `python -m pip|pip3`; otherwise run the real python to avoid loops/shadowing
if [[ "$1" == "-m" && ( "$2" == "pip" || "$2" == "pip3" ) ]]; then
wrapSafeChainCommand "python" "$@"
else
command python "$@"
fi
}
# `python3 -m pip`, `python3 -m pip3'.
function python3() {
wrapSafeChainCommand "python3" "$@"
# Intercept only `python3 -m pip|pip3`; otherwise run the real python3
if [[ "$1" == "-m" && ( "$2" == "pip" || "$2" == "pip3" ) ]]; then
wrapSafeChainCommand "python3" "$@"
else
command python3 "$@"
fi
}
function printSafeChainWarning() {

View file

@ -56,12 +56,22 @@ function poetry {
# `python -m pip`, `python -m pip3`.
function python {
Invoke-WrappedCommand 'python' $args
# Intercept only `python -m pip|pip3`; otherwise invoke real python
if (($args.Length -ge 2) -and ($args[0] -eq '-m') -and (($args[1] -eq 'pip') -or ($args[1] -eq 'pip3'))) {
Invoke-WrappedCommand 'python' $args
} else {
Invoke-RealCommand 'python' $args
}
}
# `python3 -m pip`, `python3 -m pip3'.
function python3 {
Invoke-WrappedCommand 'python3' $args
# Intercept only `python3 -m pip|pip3`; otherwise invoke real python3
if (($args.Length -ge 2) -and ($args[0] -eq '-m') -and (($args[1] -eq 'pip') -or ($args[1] -eq 'pip3'))) {
Invoke-WrappedCommand 'python3' $args
} else {
Invoke-RealCommand 'python3' $args
}
}