mirror of
https://github.com/AikidoSec/safe-chain.git
synced 2026-05-26 12:10:49 +00:00
Move pipCaBundle to central location
This commit is contained in:
parent
b1c09c6ff1
commit
c2a9cc2733
4 changed files with 12 additions and 12 deletions
90
packages/safe-chain/src/registryProxy/certBundle.js
Normal file
90
packages/safe-chain/src/registryProxy/certBundle.js
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import certifi from "certifi";
|
||||
import tls from "node:tls";
|
||||
import { X509Certificate } from "node:crypto";
|
||||
import { getCaCertPath } from "./certUtils.js";
|
||||
|
||||
/**
|
||||
* Check if a PEM string contains only parsable cert blocks.
|
||||
*/
|
||||
function isParsable(pem) {
|
||||
if (!pem || typeof pem !== "string") return false;
|
||||
const begin = "-----BEGIN CERTIFICATE-----";
|
||||
const end = "-----END CERTIFICATE-----";
|
||||
const blocks = [];
|
||||
|
||||
let idx = 0;
|
||||
while (idx < pem.length) {
|
||||
const start = pem.indexOf(begin, idx);
|
||||
if (start === -1) break;
|
||||
const stop = pem.indexOf(end, start + begin.length);
|
||||
if (stop === -1) break;
|
||||
const blockEnd = stop + end.length;
|
||||
blocks.push(pem.slice(start, blockEnd));
|
||||
idx = blockEnd;
|
||||
}
|
||||
|
||||
if (blocks.length === 0) return false;
|
||||
try {
|
||||
for (const b of blocks) {
|
||||
// throw if invalid
|
||||
new X509Certificate(b);
|
||||
}
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
let cachedPath = null;
|
||||
|
||||
/**
|
||||
* Build a combined CA bundle for Python and Node HTTPS flows.
|
||||
* - Includes Safe Chain CA (for MITM of known registries)
|
||||
* - Includes Mozilla roots via npm `certifi` (public HTTPS)
|
||||
* - Includes Node's built-in root certificates as a portable fallback
|
||||
*/
|
||||
export function getCombinedCaBundlePath() {
|
||||
if (cachedPath && fs.existsSync(cachedPath)) return cachedPath;
|
||||
|
||||
// Concatenate PEM files
|
||||
const parts = [];
|
||||
|
||||
// 1) Safe Chain CA (for MITM'd registries)
|
||||
const safeChainPath = getCaCertPath();
|
||||
try {
|
||||
const safeChainPem = fs.readFileSync(safeChainPath, "utf8");
|
||||
if (isParsable(safeChainPem)) parts.push(safeChainPem.trim());
|
||||
} catch {
|
||||
// Ignore if Safe Chain CA is not available
|
||||
}
|
||||
|
||||
// 2) certifi (Mozilla CA bundle for all public HTTPS)
|
||||
try {
|
||||
const certifiPem = fs.readFileSync(certifi, "utf8");
|
||||
if (isParsable(certifiPem)) parts.push(certifiPem.trim());
|
||||
} catch {
|
||||
// Ignore if certifi bundle is not available
|
||||
}
|
||||
|
||||
// 3) Node's built-in root certificates
|
||||
try {
|
||||
const nodeRoots = tls.rootCertificates;
|
||||
if (Array.isArray(nodeRoots) && nodeRoots.length) {
|
||||
for (const rootPem of nodeRoots) {
|
||||
if (typeof rootPem !== "string") continue;
|
||||
if (isParsable(rootPem)) parts.push(rootPem.trim());
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Ignore if unavailable
|
||||
}
|
||||
|
||||
const combined = parts.filter(Boolean).join("\n");
|
||||
const target = path.join(os.tmpdir(), "safe-chain-python-ca-bundle.pem");
|
||||
fs.writeFileSync(target, combined, { encoding: "utf8" });
|
||||
cachedPath = target;
|
||||
return cachedPath;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue