mirror of
https://github.com/AikidoSec/safe-chain.git
synced 2026-05-26 12:10:49 +00:00
Add installer changes
This commit is contained in:
parent
e765ccf303
commit
2158478894
13 changed files with 674 additions and 21 deletions
|
|
@ -2,11 +2,15 @@
|
|||
|
||||
import chalk from "chalk";
|
||||
import { createRequire } from "module";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { dirname, join } from "node:path";
|
||||
import { ui } from "../src/environment/userInteraction.js";
|
||||
import { setup } from "../src/shell-integration/setup.js";
|
||||
import { teardown } from "../src/shell-integration/teardown.js";
|
||||
import { setupCi } from "../src/shell-integration/setup-ci.js";
|
||||
import { runCommand } from "../src/agent/runCommand.js";
|
||||
import { generateCertCommand } from "../src/agent/generateCert.js";
|
||||
|
||||
if (process.argv.length < 3) {
|
||||
ui.writeError("No command provided. Please provide a command to execute.");
|
||||
|
|
@ -32,6 +36,10 @@ if (command === "setup") {
|
|||
// Pass remaining arguments to runCommand
|
||||
const runArgs = process.argv.slice(3);
|
||||
runCommand(runArgs);
|
||||
} else if (command === "generate-cert") {
|
||||
// Pass remaining arguments to generateCertCommand
|
||||
const certArgs = process.argv.slice(3);
|
||||
generateCertCommand(certArgs);
|
||||
} else if (command === "--version" || command === "-v" || command === "-v") {
|
||||
ui.writeInformation(`Current safe-chain version: ${getVersion()}`);
|
||||
} else {
|
||||
|
|
@ -52,8 +60,8 @@ function writeHelp() {
|
|||
`Available commands: ${chalk.cyan("setup")}, ${chalk.cyan(
|
||||
"teardown"
|
||||
)}, ${chalk.cyan("setup-ci")}, ${chalk.cyan("run")}, ${chalk.cyan(
|
||||
"help"
|
||||
)}, ${chalk.cyan("--version")}`
|
||||
"generate-cert"
|
||||
)}, ${chalk.cyan("help")}, ${chalk.cyan("--version")}`
|
||||
);
|
||||
ui.emptyLine();
|
||||
ui.writeInformation(
|
||||
|
|
@ -74,7 +82,12 @@ function writeHelp() {
|
|||
ui.writeInformation(
|
||||
`- ${chalk.cyan(
|
||||
"safe-chain run"
|
||||
)}: Run the proxy as a standalone service. Options: --all (default), --js, --py, --ecosystem=<type>`
|
||||
)}: Run the proxy as a standalone service. Sets system-wide proxy environment variables. Options: --verbose`
|
||||
);
|
||||
ui.writeInformation(
|
||||
`- ${chalk.cyan(
|
||||
"safe-chain generate-cert"
|
||||
)}: Generate CA certificate for MITM proxy. Options: --output <directory>`
|
||||
);
|
||||
ui.writeInformation(
|
||||
`- ${chalk.cyan(
|
||||
|
|
@ -85,7 +98,15 @@ function writeHelp() {
|
|||
}
|
||||
|
||||
function getVersion() {
|
||||
const require = createRequire(import.meta.url);
|
||||
const packageJson = require("../package.json");
|
||||
return packageJson.version;
|
||||
try {
|
||||
// Try to load package.json from the expected location
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
const packageJsonPath = join(__dirname, '../package.json');
|
||||
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
||||
return packageJson.version;
|
||||
} catch (error) {
|
||||
// Fallback for bundled version
|
||||
return '1.0.0';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,5 +64,18 @@
|
|||
"type": "git",
|
||||
"url": "git+https://github.com/AikidoSec/safe-chain.git",
|
||||
"directory": "packages/safe-chain"
|
||||
},
|
||||
"pkg": {
|
||||
"assets": [
|
||||
"src/**/*.js",
|
||||
"node_modules/**/*"
|
||||
],
|
||||
"targets": [
|
||||
"node20-macos-arm64",
|
||||
"node20-macos-x64",
|
||||
"node20-linux-x64",
|
||||
"node20-linux-arm64",
|
||||
"node20-win-x64"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
60
packages/safe-chain/src/agent/generateCert.js
Normal file
60
packages/safe-chain/src/agent/generateCert.js
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
* 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";
|
||||
import { homedir } from "node:os";
|
||||
import { ui } from "../environment/userInteraction.js";
|
||||
import chalk from "chalk";
|
||||
|
||||
/**
|
||||
* Generate certificate command
|
||||
* @param {string[]} args - Command line arguments
|
||||
*/
|
||||
export async function generateCertCommand(args) {
|
||||
// Parse output directory from --output flag
|
||||
let outputDir = join(homedir(), ".safe-chain");
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] === "--output" && args[i + 1]) {
|
||||
outputDir = args[i + 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
ui.writeInformation(chalk.bold("Generating Safe Chain CA certificate..."));
|
||||
ui.emptyLine();
|
||||
|
||||
// Create output directory
|
||||
mkdirSync(outputDir, { recursive: true });
|
||||
|
||||
// Generate certificate
|
||||
const { cert, key } = generateCACertificate();
|
||||
|
||||
// Write certificate and key files
|
||||
const certPath = join(outputDir, "ca-cert.pem");
|
||||
const keyPath = join(outputDir, "ca-key.pem");
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,8 +4,6 @@ import chalk from "chalk";
|
|||
import { initializeCliArguments } from "../config/cliArguments.js";
|
||||
import { writeProxyState, clearProxyState } from "./proxyState.js";
|
||||
import { getCaCertPath } from "../registryProxy/certUtils.js";
|
||||
import { setup } from "../shell-integration/setup.js";
|
||||
import { teardown } from "../shell-integration/teardown.js";
|
||||
|
||||
/**
|
||||
* Run the Safe Chain proxy as a standalone service
|
||||
|
|
@ -26,9 +24,9 @@ export async function runCommand(args) {
|
|||
// Initialize logging from args
|
||||
initializeCliArguments(processedArgs);
|
||||
|
||||
// Automatically set up shell integration
|
||||
await setup();
|
||||
ui.emptyLine();
|
||||
// Note: We no longer call setup() here because the installer sets up
|
||||
// system-wide proxy environment variables via LaunchAgent on macOS
|
||||
// or systemd on Linux. The certificate is also installed at install time.
|
||||
|
||||
const service = new StandaloneProxyService({
|
||||
autoVerify: false
|
||||
|
|
@ -54,11 +52,14 @@ export async function runCommand(args) {
|
|||
ui.writeInformation(` PID: ${chalk.cyan(process.pid)}`);
|
||||
ui.emptyLine();
|
||||
|
||||
ui.writeInformation(chalk.bold("How to Use:"));
|
||||
ui.writeInformation(chalk.dim(" Restart your terminal, then run package managers normally:"));
|
||||
ui.writeInformation(chalk.cyan(" npm install <package>"));
|
||||
ui.writeInformation(chalk.cyan(" yarn add <package>"));
|
||||
ui.writeInformation(chalk.cyan(" pip3 install <package>"));
|
||||
ui.writeInformation(chalk.bold("Environment Variables Set:"));
|
||||
ui.writeInformation(` ${chalk.cyan("HTTPS_PROXY")}: http://localhost:${port}`);
|
||||
ui.writeInformation(` ${chalk.cyan("GLOBAL_AGENT_HTTP_PROXY")}: http://localhost:${port}`);
|
||||
ui.writeInformation(` ${chalk.cyan("NODE_EXTRA_CA_CERTS")}: ${getCaCertPath()}`);
|
||||
ui.emptyLine();
|
||||
|
||||
ui.writeInformation(chalk.bold("Package managers will use the proxy automatically."));
|
||||
ui.writeInformation(chalk.dim(" No shell wrappers or aliases needed."));
|
||||
ui.emptyLine();
|
||||
|
||||
ui.writeInformation(
|
||||
|
|
@ -104,9 +105,8 @@ export async function runCommand(args) {
|
|||
try {
|
||||
await service.stop();
|
||||
|
||||
// Remove shell integration
|
||||
ui.emptyLine();
|
||||
await teardown();
|
||||
// Note: We no longer call teardown() here because the environment
|
||||
// variables are managed by the system service (LaunchAgent/systemd)
|
||||
|
||||
process.exit(0);
|
||||
} catch (/** @type {any} */ error) {
|
||||
|
|
|
|||
|
|
@ -116,3 +116,15 @@ function generateCa() {
|
|||
certificate: cert,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate CA certificate and return as PEM strings
|
||||
* @returns {{cert: string, key: string}}
|
||||
*/
|
||||
export function generateCACertificate() {
|
||||
const { privateKey, certificate } = generateCa();
|
||||
return {
|
||||
cert: forge.pki.certificateToPem(certificate),
|
||||
key: forge.pki.privateKeyToPem(privateKey),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue