AikidoSec-safe-chain/packages/safe-chain/src/main.js
2026-01-19 14:46:04 +01:00

119 lines
3.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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) {
if (isSafeChainVerify(args)) {
return 0;
}
process.on("SIGINT", handleProcessTermination);
process.on("SIGTERM", handleProcessTermination);
const 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}`);
ui.writeBufferedLogsAndStopBuffering();
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}`);
}
ui.writeBufferedLogsAndStopBuffering();
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.writeVerbose(
`${chalk.green("✔")} Safe-chain: Scanned ${
auditStats.totalPackages
} packages, no malware found.`,
);
}
if (proxy.hasSuppressedVersions()) {
ui.writeInformation(
`${chalk.yellow(
"",
)} Safe-chain: Some package versions were suppressed due to minimum age requirement.`,
);
ui.writeInformation(
` To disable this check, use: ${chalk.cyan(
"--safe-chain-skip-minimum-package-age",
)}`,
);
}
// 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);
ui.writeBufferedLogsAndStopBuffering();
// 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();
}
/** @param {string[]} args */
function isSafeChainVerify(args) {
const safeChainCheckCommand = "safe-chain-verify";
if (args.length > 0 && args[0] === safeChainCheckCommand) {
ui.writeInformation("OK: Safe-chain works!");
return true;
}
}