mirror of
https://github.com/AikidoSec/safe-chain.git
synced 2026-05-26 12:10:49 +00:00
Fix cert issues in Virtual Environments
This commit is contained in:
parent
9c55a95eb9
commit
f5af26092a
3 changed files with 61 additions and 22 deletions
|
|
@ -12,17 +12,6 @@ export function getCaCertPath() {
|
|||
return path.join(certFolder, "ca-cert.pem");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {forge.pki.PublicKey} publicKey
|
||||
* @returns {string}
|
||||
*/
|
||||
function createKeyIdentifier(publicKey) {
|
||||
return forge.pki.getPublicKeyFingerprint(publicKey, {
|
||||
encoding: "binary",
|
||||
md: forge.md.sha1.create(),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} hostname
|
||||
* @returns {{privateKey: string, certificate: string}}
|
||||
|
|
@ -62,19 +51,39 @@ export function generateCertForHost(hostname) {
|
|||
},
|
||||
{
|
||||
/*
|
||||
extKeyUsage serverAuth is required for TLS server authentication.
|
||||
This is especially important for Python venv environments, which use their own
|
||||
certificate validation logic and will reject certificates lacking the serverAuth EKU.
|
||||
Adding serverAuth does not impact other usages
|
||||
Extended Key Usage (EKU) serverAuth extension
|
||||
|
||||
Needed for TLS server authentication. This extension indicates the certificate's
|
||||
public key may be used for TLS WWW server authentication.
|
||||
Python virtualenv environments (like pipx-installed Poetry) enforce this strictly
|
||||
https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.12
|
||||
*/
|
||||
name: "extKeyUsage",
|
||||
serverAuth: true,
|
||||
},
|
||||
{
|
||||
/*
|
||||
Subject Key Identifier (SKI)
|
||||
|
||||
Needed for Python virtualenv SSL validation and certificate chain building.
|
||||
This extension provides a means of identifying certificates containing a particular public key.
|
||||
Python virtualenv environments require this for proper certificate chain validation.
|
||||
System Python installations may be more lenient.
|
||||
https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.2
|
||||
*/
|
||||
name: "subjectKeyIdentifier",
|
||||
subjectKeyIdentifier: createKeyIdentifier(cert.publicKey),
|
||||
},
|
||||
{
|
||||
/*
|
||||
Authority Key Identifier (AKI)
|
||||
|
||||
Needed for Python virtualenv SSL validation and certificate path validation.
|
||||
This extension identifies the public key corresponding to the private key used to sign
|
||||
this certificate. It links this certificate to its issuing CA certificate.
|
||||
Without this, Python virtualenv certificate validation might fail
|
||||
https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.1
|
||||
*/
|
||||
name: "authorityKeyIdentifier",
|
||||
keyIdentifier: authorityKeyIdentifier,
|
||||
},
|
||||
|
|
@ -142,7 +151,7 @@ function generateCa() {
|
|||
|
||||
const attrs = [{ name: "commonName", value: "safe-chain proxy" }];
|
||||
cert.setSubject(attrs);
|
||||
cert.setIssuer(attrs);
|
||||
cert.setIssuer(attrs); // Self-signed: issuer === subject
|
||||
const keyIdentifier = createKeyIdentifier(cert.publicKey);
|
||||
cert.setExtensions([
|
||||
{
|
||||
|
|
@ -156,10 +165,28 @@ function generateCa() {
|
|||
digitalSignature: true,
|
||||
keyEncipherment: true,
|
||||
},
|
||||
/*
|
||||
Subject Key Identifier (SKI)
|
||||
|
||||
Needed for Python virtualenv SSL validation and certificate chain building.
|
||||
This extension provides a means of identifying certificates containing a particular public key.
|
||||
Python virtualenv environments require this for proper certificate chain validation.
|
||||
System Python installations may be more lenient.
|
||||
https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.2
|
||||
*/
|
||||
{
|
||||
name: "subjectKeyIdentifier",
|
||||
subjectKeyIdentifier: keyIdentifier,
|
||||
},
|
||||
/*
|
||||
Authority Key Identifier (AKI)
|
||||
|
||||
Needed for Python virtualenv SSL validation and certificate path validation.
|
||||
This extension identifies the public key corresponding to the private key used to sign
|
||||
this certificate. It links this certificate to its issuing CA certificate.
|
||||
Without this, Python virtualenv certificate validation might fail
|
||||
https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.1
|
||||
*/
|
||||
{
|
||||
name: "authorityKeyIdentifier",
|
||||
keyIdentifier,
|
||||
|
|
@ -172,3 +199,14 @@ function generateCa() {
|
|||
certificate: cert,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {forge.pki.PublicKey} publicKey
|
||||
* @returns {string}
|
||||
*/
|
||||
function createKeyIdentifier(publicKey) {
|
||||
return forge.pki.getPublicKeyFingerprint(publicKey, {
|
||||
encoding: "binary",
|
||||
md: forge.md.sha1.create(),
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,10 @@ describe("E2E: safe-chain setup-ci command for pip/pip3", () => {
|
|||
beforeEach(async () => {
|
||||
container = new DockerTestContainer();
|
||||
await container.start();
|
||||
|
||||
// Clear pip cache before each test to ensure fresh downloads through proxy
|
||||
const shell = await container.openShell("zsh");
|
||||
await shell.runCommand("pip3 cache purge");
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@ describe("E2E: pip coverage", () => {
|
|||
|
||||
const installationShell = await container.openShell("zsh");
|
||||
await installationShell.runCommand("safe-chain setup --include-python");
|
||||
|
||||
// Clear pip cache before each test to ensure fresh downloads through proxy
|
||||
await installationShell.runCommand("pip3 cache purge");
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
|
|
@ -118,9 +121,6 @@ describe("E2E: pip coverage", () => {
|
|||
|
||||
it(`safe-chain blocks installation of malicious Python packages`, async () => {
|
||||
const shell = await container.openShell("zsh");
|
||||
// Clear pip cache to ensure network download through proxy
|
||||
await shell.runCommand("pip3 cache purge");
|
||||
|
||||
const result = await shell.runCommand(
|
||||
"pip3 install --break-system-packages safe-chain-pi-test"
|
||||
);
|
||||
|
|
@ -247,9 +247,6 @@ describe("E2E: pip coverage", () => {
|
|||
|
||||
it(`pip3 successfully validates certificates for HTTPS downloads`, async () => {
|
||||
const shell = await container.openShell("zsh");
|
||||
// Clear cache to force network download through proxy
|
||||
await shell.runCommand("pip3 cache purge");
|
||||
|
||||
const result = await shell.runCommand(
|
||||
"pip3 install --break-system-packages certifi"
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue