AikidoSec-safe-chain/packages/safe-chain/src/main.js
2025-11-19 13:54:12 -08:00

99 lines
3.5 KiB
JavaScript

#!/usr/bin/env node
import { scanCommand, shouldScanCommand } from "./scanning/index.js";
import { ui } from "./environment/userInteraction.js";
import { getPackageManager } from "./packagemanager/currentPackageManager.js";
import { initializeCliArguments } from "./config/cliArguments.js";
import { createSafeChainProxy } from "./registryProxy/registryProxy.js";
import chalk from "chalk";
import { getAuditStats } from "./scanning/audit/index.js";
/**
* @param {string[]} args
* @returns {Promise<number>}
*/
export async function main(args) {
process.on("SIGINT", handleProcessTermination);
process.on("SIGTERM", handleProcessTermination);
// Check if a proxy is already running from 'safe-chain run'
// In the new agent architecture, we rely on system-wide environment variables
// so we don't need to detect or connect to an existing proxy here.
// The 'main' function is now only used when running 'aikido-npm' etc. directly
// (legacy wrapper mode) or when running 'safe-chain run' (which doesn't call main() directly)
let proxy;
// No existing proxy logic needed anymore as we don't wrap commands when using the agent
proxy = createSafeChainProxy();
await proxy.startServer();
// Global error handlers to log unhandled errors
process.on("uncaughtException", (error) => {
ui.writeError(`Safe-chain: Uncaught exception: ${error.message}`);
ui.writeVerbose(`Stack trace: ${error.stack}`);
process.exit(1);
});
process.on("unhandledRejection", (reason) => {
ui.writeError(`Safe-chain: Unhandled promise rejection: ${reason}`);
if (reason instanceof Error) {
ui.writeVerbose(`Stack trace: ${reason.stack}`);
}
process.exit(1);
});
try {
// This parses all the --safe-chain arguments and removes them from the args array
args = initializeCliArguments(args);
if (shouldScanCommand(args)) {
const commandScanResult = await scanCommand(args);
// Returning the exit code back to the caller allows the promise
// to be awaited in the bin files and return the correct exit code
if (commandScanResult !== 0) {
return commandScanResult;
}
}
// Buffer logs during package manager execution, this avoids interleaving
// of logs from the package manager and safe-chain
// Not doing this could cause bugs to disappear when cursor movement codes
// are written by the package manager while safe-chain is writing logs
ui.startBufferingLogs();
const packageManagerResult = await getPackageManager().runCommand(args);
// Write all buffered logs
ui.writeBufferedLogsAndStopBuffering();
if (!proxy.verifyNoMaliciousPackages()) {
return 1;
}
const auditStats = getAuditStats();
if (auditStats.totalPackages > 0) {
ui.emptyLine();
ui.writeInformation(
`${chalk.green("✔")} Safe-chain: Scanned ${
auditStats.totalPackages
} packages, no malware found.`
);
}
// Returning the exit code back to the caller allows the promise
// to be awaited in the bin files and return the correct exit code
return packageManagerResult.status;
} catch (/** @type any */ error) {
ui.writeError("Failed to check for malicious packages:", error.message);
// Returning the exit code back to the caller allows the promise
// to be awaited in the bin files and return the correct exit code
return 1;
} finally {
await proxy.stopServer();
}
}
function handleProcessTermination() {
ui.writeBufferedLogsAndStopBuffering();
}