mirror of
https://github.com/AikidoSec/safe-chain.git
synced 2026-05-26 12:10:49 +00:00
Some cleanup
This commit is contained in:
parent
97bbc77162
commit
c71320386e
13 changed files with 1601 additions and 240 deletions
|
|
@ -1,8 +1,3 @@
|
|||
/**
|
||||
* Generate certificate command for Safe Chain
|
||||
* Creates CA certificate and key for MITM proxy
|
||||
*/
|
||||
|
||||
import { generateCACertificate } from "../registryProxy/certUtils.js";
|
||||
import { writeFileSync, mkdirSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
|
|
@ -12,6 +7,7 @@ import chalk from "chalk";
|
|||
|
||||
/**
|
||||
* Generate certificate command
|
||||
* Allows us to call this independently, for instance from the installer.
|
||||
* @param {string[]} args - Command line arguments
|
||||
*/
|
||||
export async function generateCertCommand(args) {
|
||||
|
|
@ -26,9 +22,6 @@ export async function generateCertCommand(args) {
|
|||
}
|
||||
|
||||
try {
|
||||
ui.writeInformation(chalk.bold("Generating Safe Chain CA certificate..."));
|
||||
ui.emptyLine();
|
||||
|
||||
// Create output directory
|
||||
mkdirSync(outputDir, { recursive: true });
|
||||
|
||||
|
|
@ -41,20 +34,7 @@ export async function generateCertCommand(args) {
|
|||
|
||||
writeFileSync(certPath, cert);
|
||||
writeFileSync(keyPath, key);
|
||||
|
||||
ui.writeInformation(chalk.green("✓") + " Certificate generated successfully!");
|
||||
ui.emptyLine();
|
||||
ui.writeInformation(chalk.bold("Files created:"));
|
||||
ui.writeInformation(` Certificate: ${chalk.cyan(certPath)}`);
|
||||
ui.writeInformation(` Private Key: ${chalk.cyan(keyPath)}`);
|
||||
ui.emptyLine();
|
||||
ui.writeInformation(chalk.dim("To install this certificate in your system trust store:"));
|
||||
ui.writeInformation(chalk.dim(" macOS: sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain " + certPath));
|
||||
ui.writeInformation(chalk.dim(" Linux: sudo cp " + certPath + " /usr/local/share/ca-certificates/ && sudo update-ca-certificates"));
|
||||
ui.writeInformation(chalk.dim(" Windows: certutil -addstore -f ROOT " + certPath));
|
||||
ui.emptyLine();
|
||||
} catch (/** @type {any} */ error) {
|
||||
ui.writeError(`Failed to generate certificate: ${error.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,82 +0,0 @@
|
|||
import fs from "fs";
|
||||
import path from "path";
|
||||
import os from "os";
|
||||
|
||||
/**
|
||||
* Get the path to the proxy state file
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getProxyStateFilePath() {
|
||||
const homeDir = os.homedir();
|
||||
const safeChainDir = path.join(homeDir, ".safe-chain");
|
||||
|
||||
// Ensure directory exists
|
||||
if (!fs.existsSync(safeChainDir)) {
|
||||
fs.mkdirSync(safeChainDir, { recursive: true });
|
||||
}
|
||||
|
||||
return path.join(safeChainDir, "proxy-state.json");
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the proxy state to a file that shell scripts can read
|
||||
* @param {{port: number, url: string, pid: number, ecosystem: string, certPath: string}} state
|
||||
*/
|
||||
export function writeProxyState(state) {
|
||||
const statePath = getProxyStateFilePath();
|
||||
fs.writeFileSync(statePath, JSON.stringify(state, null, 2), "utf-8");
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the current proxy state
|
||||
* @returns {{port: number, url: string, pid: number, ecosystem: string, certPath: string} | null}
|
||||
*/
|
||||
export function readProxyState() {
|
||||
const statePath = getProxyStateFilePath();
|
||||
|
||||
if (!fs.existsSync(statePath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const content = fs.readFileSync(statePath, "utf-8");
|
||||
const state = JSON.parse(content);
|
||||
|
||||
// Verify the process is still running
|
||||
if (state.pid) {
|
||||
try {
|
||||
// Sending signal 0 checks if process exists without actually sending a signal
|
||||
process.kill(state.pid, 0);
|
||||
return state;
|
||||
} catch {
|
||||
// Process doesn't exist, clean up state file
|
||||
clearProxyState();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the proxy state file
|
||||
*/
|
||||
export function clearProxyState() {
|
||||
const statePath = getProxyStateFilePath();
|
||||
|
||||
if (fs.existsSync(statePath)) {
|
||||
fs.unlinkSync(statePath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a proxy is currently running
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isProxyRunning() {
|
||||
const state = readProxyState();
|
||||
return state !== null;
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ import { StandaloneProxyService } from "./standaloneProxy.js";
|
|||
import { ui } from "../environment/userInteraction.js";
|
||||
import chalk from "chalk";
|
||||
import { initializeCliArguments } from "../config/cliArguments.js";
|
||||
import { writeProxyState, clearProxyState } from "./proxyState.js";
|
||||
import { getCaCertPath } from "../registryProxy/certUtils.js";
|
||||
|
||||
/**
|
||||
|
|
@ -34,15 +33,6 @@ export async function runCommand(args) {
|
|||
|
||||
// Setup event listeners
|
||||
service.on("started", ({ port, url }) => {
|
||||
// Write proxy state to file so shell integration can detect it
|
||||
writeProxyState({
|
||||
port,
|
||||
url,
|
||||
pid: process.pid,
|
||||
ecosystem: 'all',
|
||||
certPath: getCaCertPath(),
|
||||
});
|
||||
|
||||
ui.emptyLine();
|
||||
ui.writeInformation(chalk.green("✔") + " Safe Chain proxy started successfully!");
|
||||
ui.emptyLine();
|
||||
|
|
@ -68,9 +58,6 @@ export async function runCommand(args) {
|
|||
});
|
||||
|
||||
service.on("stopped", ({ blockedPackages }) => {
|
||||
// Clear proxy state file
|
||||
clearProxyState();
|
||||
|
||||
ui.emptyLine();
|
||||
ui.writeInformation(chalk.yellow("Proxy stopped."));
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import { initializeCliArguments } from "./config/cliArguments.js";
|
|||
import { createSafeChainProxy } from "./registryProxy/registryProxy.js";
|
||||
import chalk from "chalk";
|
||||
import { getAuditStats } from "./scanning/audit/index.js";
|
||||
import { readProxyState } from "./agent/proxyState.js";
|
||||
|
||||
/**
|
||||
* @param {string[]} args
|
||||
|
|
@ -18,32 +17,15 @@ export async function main(args) {
|
|||
process.on("SIGTERM", handleProcessTermination);
|
||||
|
||||
// Check if a proxy is already running from 'safe-chain run'
|
||||
const existingProxy = readProxyState();
|
||||
const usingExistingProxy = existingProxy !== null;
|
||||
// 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;
|
||||
if (usingExistingProxy) {
|
||||
// Use the existing proxy - don't start a new one
|
||||
ui.writeInformation(`Safe-chain: Using existing proxy at ${existingProxy.url}`);
|
||||
// Create a proxy object that uses the existing proxy
|
||||
// We need to set the environment variables to point to the existing proxy
|
||||
const url = new URL(existingProxy.url);
|
||||
const port = parseInt(url.port);
|
||||
|
||||
// Import and set the proxy state so getSafeChainProxyEnvironmentVariables works
|
||||
const { setProxyState } = await import("./registryProxy/registryProxy.js");
|
||||
setProxyState(port, existingProxy.certPath);
|
||||
|
||||
proxy = {
|
||||
verifyNoMaliciousPackages: () => true, // Existing proxy handles this
|
||||
getBlockedRequests: () => [], // Can't access blocked requests from existing proxy
|
||||
stopServer: async () => {}, // Don't stop the existing proxy
|
||||
};
|
||||
} else {
|
||||
// No existing proxy, start one inline
|
||||
proxy = createSafeChainProxy();
|
||||
await proxy.startServer();
|
||||
}
|
||||
// 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) => {
|
||||
|
|
@ -91,19 +73,11 @@ export async function main(args) {
|
|||
const auditStats = getAuditStats();
|
||||
if (auditStats.totalPackages > 0) {
|
||||
ui.emptyLine();
|
||||
if (usingExistingProxy) {
|
||||
ui.writeInformation(
|
||||
`${chalk.green("✔")} Safe-chain: Scanned ${
|
||||
auditStats.totalPackages
|
||||
} packages via proxy, no malware found.`
|
||||
);
|
||||
} else {
|
||||
ui.writeInformation(
|
||||
`${chalk.green("✔")} Safe-chain: Scanned ${
|
||||
auditStats.totalPackages
|
||||
} packages, no malware found.`
|
||||
);
|
||||
}
|
||||
ui.writeInformation(
|
||||
`${chalk.green("✔")} Safe-chain: Scanned ${
|
||||
auditStats.totalPackages
|
||||
} packages, no malware found.`
|
||||
);
|
||||
}
|
||||
|
||||
// Returning the exit code back to the caller allows the promise
|
||||
|
|
@ -116,10 +90,7 @@ export async function main(args) {
|
|||
// to be awaited in the bin files and return the correct exit code
|
||||
return 1;
|
||||
} finally {
|
||||
// Only stop the proxy if we started it (not using existing proxy)
|
||||
if (!usingExistingProxy) {
|
||||
await proxy.stopServer();
|
||||
}
|
||||
await proxy.stopServer();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue