Merge pull request #178 from AikidoSec/feature/poetry-2

Add Poetry support
This commit is contained in:
bitterpanda 2025-12-05 15:56:20 +01:00 committed by GitHub
commit 15cc6ff7fe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 670 additions and 35 deletions

View file

@ -11,6 +11,7 @@ import {
import { createYarnPackageManager } from "./yarn/createPackageManager.js";
import { createPipPackageManager } from "./pip/createPackageManager.js";
import { createUvPackageManager } from "./uv/createUvPackageManager.js";
import { createPoetryPackageManager } from "./poetry/createPoetryPackageManager.js";
/**
* @type {{packageManagerName: PackageManager | null}}
@ -58,6 +59,8 @@ export function initializePackageManager(packageManagerName, context) {
state.packageManagerName = createPipPackageManager(context);
} else if (packageManagerName === "uv") {
state.packageManagerName = createUvPackageManager();
} else if (packageManagerName === "poetry") {
state.packageManagerName = createPoetryPackageManager();
} else {
throw new Error("Unsupported package manager: " + packageManagerName);
}

View file

@ -0,0 +1,77 @@
import { ui } from "../../environment/userInteraction.js";
import { safeSpawn } from "../../utils/safeSpawn.js";
import { mergeSafeChainProxyEnvironmentVariables } from "../../registryProxy/registryProxy.js";
import { getCombinedCaBundlePath } from "../../registryProxy/certBundle.js";
/**
* @returns {import("../currentPackageManager.js").PackageManager}
*/
export function createPoetryPackageManager() {
return {
runCommand: (args) => runPoetryCommand(args),
// MITM only approach for Poetry
isSupportedCommand: () => false,
getDependencyUpdatesForCommand: () => [],
};
}
/**
* Sets CA bundle environment variables used by Poetry and Python libraries.
* Poetry uses the Python requests library which respects these environment variables.
*
* @param {NodeJS.ProcessEnv} env - Environment object to modify
* @param {string} combinedCaPath - Path to the combined CA bundle
*/
function setPoetryCaBundleEnvironmentVariables(env, combinedCaPath) {
// SSL_CERT_FILE: Used by Python SSL libraries and requests
if (env.SSL_CERT_FILE) {
ui.writeWarning("Safe-chain: User defined SSL_CERT_FILE found in environment. It will be overwritten.");
}
env.SSL_CERT_FILE = combinedCaPath;
// REQUESTS_CA_BUNDLE: Used by the requests library (which Poetry uses)
if (env.REQUESTS_CA_BUNDLE) {
ui.writeWarning("Safe-chain: User defined REQUESTS_CA_BUNDLE found in environment. It will be overwritten.");
}
env.REQUESTS_CA_BUNDLE = combinedCaPath;
// PIP_CERT: Poetry may use pip internally
if (env.PIP_CERT) {
ui.writeWarning("Safe-chain: User defined PIP_CERT found in environment. It will be overwritten.");
}
env.PIP_CERT = combinedCaPath;
}
/**
* Runs a poetry command with safe-chain's certificate bundle and proxy configuration.
*
* Poetry respects standard HTTP_PROXY/HTTPS_PROXY environment variables through
* the Python requests library.
*
* @param {string[]} args - Command line arguments to pass to poetry
* @returns {Promise<{status: number}>} Exit status of the poetry command
*/
async function runPoetryCommand(args) {
try {
const env = mergeSafeChainProxyEnvironmentVariables(process.env);
const combinedCaPath = getCombinedCaBundlePath();
setPoetryCaBundleEnvironmentVariables(env, combinedCaPath);
const result = await safeSpawn("poetry", args, {
stdio: "inherit",
env,
});
return { status: result.status };
} catch (/** @type any */ error) {
if (error.status) {
return { status: error.status };
} else {
ui.writeError("Error executing command:", error.message);
ui.writeError("Is 'poetry' installed and available on your system?");
return { status: 1 };
}
}
}

View file

@ -0,0 +1,14 @@
import { test } from "node:test";
import assert from "node:assert";
import { createPoetryPackageManager } from "./createPoetryPackageManager.js";
test("createPoetryPackageManager", async (t) => {
await t.test("should create package manager with required interface", () => {
const pm = createPoetryPackageManager();
assert.ok(pm);
assert.strictEqual(typeof pm.runCommand, "function");
assert.strictEqual(typeof pm.isSupportedCommand, "function");
assert.strictEqual(typeof pm.getDependencyUpdatesForCommand, "function");
});
});