Make sure we don't override any environments

This commit is contained in:
Reinier Criel 2025-11-11 15:22:06 -08:00
parent f9d241e474
commit 6bcd3d3b8f
2 changed files with 48 additions and 17 deletions

View file

@ -20,29 +20,39 @@ export async function runPip(command, args) {
// so that any network request made by pip, including those outside explicit CLI args,
// validates correctly under both MITM'd and tunneled HTTPS.
const combinedCaPath = getCombinedCaBundlePath();
env.REQUESTS_CA_BUNDLE = combinedCaPath;
env.SSL_CERT_FILE = combinedCaPath;
if (!env.REQUESTS_CA_BUNDLE) {
env.REQUESTS_CA_BUNDLE = combinedCaPath;
}
if (!env.SSL_CERT_FILE) {
env.SSL_CERT_FILE = combinedCaPath;
}
// To counter behavior that is sometimes seen where pip ignores REQUESTS_CA_BUNDLE/SSL_CERT_FILE,
// We will set additional env vars for pip
env.PIP_CERT = combinedCaPath;
if (!env.PIP_CERT) {
env.PIP_CERT = combinedCaPath;
}
// Create a temporary pip config file
const tmpDir = os.tmpdir();
const pipConfigPath = path.join(tmpDir, `safe-chain-pip-${Date.now()}.ini`);
// Only create and set PIP_CONFIG_FILE if not already set
if (!env.PIP_CONFIG_FILE) {
const tmpDir = os.tmpdir();
const pipConfigPath = path.join(tmpDir, `safe-chain-pip-${Date.now()}.ini`);
// Proxy settings
const httpProxy = env.HTTP_PROXY || '';
const httpsProxy = env.HTTPS_PROXY || '';
// Proxy settings
const httpProxy = env.HTTP_PROXY || '';
const httpsProxy = env.HTTPS_PROXY || '';
// Build pip config INI
let pipConfig = '[global]\n';
pipConfig += `cert = ${combinedCaPath}\n`;
if (httpProxy) pipConfig += `proxy = ${httpProxy}\n`;
if (httpsProxy) pipConfig += `proxy = ${httpsProxy}\n`;
// Build pip config INI
let pipConfig = '[global]\n';
pipConfig += `cert = ${combinedCaPath}\n`;
if (httpProxy) pipConfig += `proxy = ${httpProxy}\n`;
if (httpsProxy) pipConfig += `proxy = ${httpsProxy}\n`;
await fs.writeFile(pipConfigPath, pipConfig);
env.PIP_CONFIG_FILE = pipConfigPath;
await fs.writeFile(pipConfigPath, pipConfig);
env.PIP_CONFIG_FILE = pipConfigPath;
}
const result = await safeSpawn(command, args, {
stdio: "inherit",

View file

@ -4,6 +4,7 @@ import assert from "node:assert";
describe("runPipCommand environment variable handling", () => {
let runPip;
let capturedArgs = null;
let customEnv = null;
beforeEach(async () => {
capturedArgs = null;
@ -18,11 +19,12 @@ describe("runPipCommand environment variable handling", () => {
},
});
// Mock proxy env merge
// Mock proxy env merge, allow custom env override
mock.module("../../registryProxy/registryProxy.js", {
namedExports: {
mergeSafeChainProxyEnvironmentVariables: (env) => ({
...env,
...(customEnv || {}),
HTTPS_PROXY: "http://localhost:8080",
}),
},
@ -43,6 +45,25 @@ describe("runPipCommand environment variable handling", () => {
mock.reset();
});
it("should not overwrite existing env vars for certs and config", async () => {
// Set custom env vars before merge
customEnv = {
REQUESTS_CA_BUNDLE: "/custom/ca-bundle.pem",
SSL_CERT_FILE: "/custom/ssl-cert.pem",
PIP_CERT: "/custom/pip-cert.pem",
PIP_CONFIG_FILE: "/custom/pip.conf"
};
const res = await runPip("pip3", ["install", "requests"]);
assert.strictEqual(res.status, 0);
assert.ok(capturedArgs, "safeSpawn should have been called");
// Should preserve custom env vars
assert.strictEqual(capturedArgs.options.env.REQUESTS_CA_BUNDLE, "/custom/ca-bundle.pem");
assert.strictEqual(capturedArgs.options.env.SSL_CERT_FILE, "/custom/ssl-cert.pem");
assert.strictEqual(capturedArgs.options.env.PIP_CERT, "/custom/pip-cert.pem");
assert.strictEqual(capturedArgs.options.env.PIP_CONFIG_FILE, "/custom/pip.conf");
customEnv = null;
});
it("should set PIP_CERT env var and create config file", async () => {
const res = await runPip("pip3", ["install", "requests"]);
assert.strictEqual(res.status, 0);