mirror of
https://github.com/AikidoSec/safe-chain.git
synced 2026-05-26 12:10:49 +00:00
Combine certificates
This commit is contained in:
parent
1755fe829c
commit
f38a12c6d5
8 changed files with 243 additions and 153 deletions
|
|
@ -1,80 +1,22 @@
|
|||
import { ui } from "../../environment/userInteraction.js";
|
||||
import { safeSpawn } from "../../utils/safeSpawn.js";
|
||||
import { mergeSafeChainProxyEnvironmentVariables } from "../../registryProxy/registryProxy.js";
|
||||
import { getCaCertPath } from "../../registryProxy/certUtils.js";
|
||||
import { knownPipRegistries } from "../../registryProxy/parsePackageFromUrl.js";
|
||||
import yargsParser from "yargs-parser";
|
||||
|
||||
function extractHostsFromPipArgs(args) {
|
||||
function hostFromString(input) {
|
||||
if (typeof input !== "string" || input.length === 0) return undefined;
|
||||
try {
|
||||
const u = new URL(input);
|
||||
return u.hostname || undefined;
|
||||
} catch {
|
||||
// ignore: not a valid absolute URL
|
||||
}
|
||||
// Try adding a scheme if it's a schemeless URL-like value
|
||||
try {
|
||||
const u2 = new URL(`https://${input}`);
|
||||
return u2.hostname || undefined;
|
||||
} catch {
|
||||
// ignore: not a valid schemeless URL either
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const parsed = yargsParser(args, {
|
||||
configuration: {
|
||||
"short-option-groups": true,
|
||||
"camel-case-expansion": false,
|
||||
"dot-notation": false,
|
||||
"duplicate-arguments-array": true,
|
||||
"flatten-duplicate-arrays": false,
|
||||
"greedy-arrays": false,
|
||||
"unknown-options-as-args": true,
|
||||
},
|
||||
});
|
||||
const toArray = (v) => (v == null ? [] : Array.isArray(v) ? v : [v]);
|
||||
const candidateUrls = [
|
||||
...toArray(parsed.i),
|
||||
...toArray(parsed["index-url"]),
|
||||
...toArray(parsed["extra-index-url"]),
|
||||
...toArray(parsed["find-links"]),
|
||||
...toArray(parsed._).filter(
|
||||
(a) => typeof a === "string" && (a.startsWith("https://") || a.startsWith("http://"))
|
||||
),
|
||||
];
|
||||
const hosts = new Set();
|
||||
for (const u of candidateUrls) {
|
||||
const h = hostFromString(u);
|
||||
if (h) hosts.add(h);
|
||||
}
|
||||
return Array.from(hosts);
|
||||
}
|
||||
|
||||
import { getCombinedCaBundlePath } from "./utils/pipCaBundle.js";
|
||||
// Always provide Python with a complete CA bundle (Safe Chain CA + Mozilla + Node built-in roots)
|
||||
// so that any network request made by pip, including those outside explicit CLI args,
|
||||
// validates correctly under both MITM'd and tunneled HTTPS.
|
||||
|
||||
export async function runPip(command, args) {
|
||||
try {
|
||||
const env = mergeSafeChainProxyEnvironmentVariables(process.env);
|
||||
// Re-introduce conditional --cert injection: only for known registries (MITM).
|
||||
// No global env overrides for Python trust.
|
||||
const hosts = extractHostsFromPipArgs(args);
|
||||
const allKnown = hosts.length === 0
|
||||
? true // No explicit sources => default PyPI (known) -> MITM
|
||||
: hosts.every((h) => knownPipRegistries.includes(h));
|
||||
|
||||
// Respect user-provided --cert: detect both "--cert <path>" and "--cert=<path>"
|
||||
const hasUserCert = args.some(
|
||||
(a) => a === "--cert" || (typeof a === "string" && a.startsWith("--cert="))
|
||||
);
|
||||
// Always set Python CA env vars to a combined bundle that includes Safe Chain CA,
|
||||
// Mozilla roots (certifi), and Node built-in root CAs.
|
||||
const combinedCaPath = getCombinedCaBundlePath();
|
||||
env.REQUESTS_CA_BUNDLE = combinedCaPath;
|
||||
env.SSL_CERT_FILE = combinedCaPath;
|
||||
|
||||
let finalArgs = [...args];
|
||||
if (allKnown && !hasUserCert) {
|
||||
finalArgs = [...args, "--cert", getCaCertPath()];
|
||||
}
|
||||
|
||||
const result = await safeSpawn(command, finalArgs, {
|
||||
const result = await safeSpawn(command, args, {
|
||||
stdio: "inherit",
|
||||
env,
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue