mirror of
https://github.com/AikidoSec/safe-chain.git
synced 2026-05-26 12:10:49 +00:00
155 lines
4.1 KiB
JavaScript
155 lines
4.1 KiB
JavaScript
import { tmpdir } from "os";
|
|
import { unlinkSync } from "fs";
|
|
import { join } from "path";
|
|
import { execSync, spawnSync } from "child_process";
|
|
import { ui } from "../environment/userInteraction.js";
|
|
import { printVerboseAndSafeSpawn } from "../utils/safeSpawn.js";
|
|
import { downloadAgentToFile, getAgentVersion } from "./downloadAgent.js";
|
|
import chalk from "chalk";
|
|
|
|
const MACOS_PKG_IDENTIFIER = "com.aikidosecurity.safechainultimate";
|
|
|
|
/**
|
|
* Checks if root privileges are available and displays error message if not.
|
|
* @param {string} command - The sudo command to show in the error message
|
|
* @returns {boolean} True if running as root, false otherwise.
|
|
*/
|
|
function requireRootPrivileges(command) {
|
|
if (isRunningAsRoot()) {
|
|
return true;
|
|
}
|
|
|
|
ui.writeError("Root privileges required.");
|
|
ui.writeInformation("Please run this command with sudo:");
|
|
ui.writeInformation(` ${command}`);
|
|
return false;
|
|
}
|
|
|
|
function isRunningAsRoot() {
|
|
const rootUserUid = 0;
|
|
return process.getuid?.() === rootUserUid;
|
|
}
|
|
|
|
export async function installOnMacOS() {
|
|
if (!requireRootPrivileges("sudo safe-chain ultimate")) {
|
|
return;
|
|
}
|
|
|
|
const pkgPath = join(tmpdir(), `SafeChainUltimate-${Date.now()}.pkg`);
|
|
|
|
ui.emptyLine();
|
|
ui.writeInformation(`📥 Downloading SafeChain Ultimate ${getAgentVersion()}`);
|
|
ui.writeVerbose(`Destination: ${pkgPath}`);
|
|
|
|
const result = await downloadAgentToFile(pkgPath);
|
|
if (!result) {
|
|
ui.writeError("No download available for this platform/architecture.");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
ui.writeInformation("⚙️ Installing SafeChain Ultimate...");
|
|
await runPkgInstaller(pkgPath);
|
|
|
|
ui.emptyLine();
|
|
ui.writeInformation(
|
|
"✅ SafeChain Ultimate installed and started successfully!",
|
|
);
|
|
ui.emptyLine();
|
|
ui.writeInformation(
|
|
chalk.cyan("🔐 ") +
|
|
chalk.bold("ACTION REQUIRED: ") +
|
|
"macOS will show a popup to install our certificate.",
|
|
);
|
|
ui.writeInformation(
|
|
" " +
|
|
chalk.bold("Please accept the certificate") +
|
|
" to complete the installation.",
|
|
);
|
|
ui.emptyLine();
|
|
} finally {
|
|
ui.writeVerbose(`Cleaning up temporary file: ${pkgPath}`);
|
|
cleanup(pkgPath);
|
|
}
|
|
}
|
|
|
|
const MACOS_UNINSTALL_SCRIPT =
|
|
"/Library/Application\\ Support/AikidoSecurity/SafeChainUltimate/scripts/uninstall";
|
|
|
|
export async function uninstallOnMacOS() {
|
|
if (!requireRootPrivileges("sudo safe-chain ultimate uninstall")) {
|
|
return;
|
|
}
|
|
|
|
ui.emptyLine();
|
|
|
|
if (!isPackageInstalled()) {
|
|
ui.writeInformation("SafeChain Ultimate is not installed.");
|
|
return;
|
|
}
|
|
|
|
ui.writeInformation("🗑️ Uninstalling SafeChain Ultimate...");
|
|
ui.writeVerbose(`Running: ${MACOS_UNINSTALL_SCRIPT}`);
|
|
|
|
const result = spawnSync(MACOS_UNINSTALL_SCRIPT, {
|
|
stdio: "inherit",
|
|
shell: true,
|
|
});
|
|
|
|
if (result.status !== 0) {
|
|
ui.writeError(
|
|
`Uninstall script failed (exit code: ${result.status}). Please try again or remove manually.`,
|
|
);
|
|
return;
|
|
}
|
|
|
|
ui.emptyLine();
|
|
ui.writeInformation("✅ SafeChain Ultimate has been uninstalled.");
|
|
ui.emptyLine();
|
|
}
|
|
|
|
function isPackageInstalled() {
|
|
try {
|
|
const output = execSync(`pkgutil --pkg-info ${MACOS_PKG_IDENTIFIER}`, {
|
|
encoding: "utf8",
|
|
stdio: "pipe",
|
|
});
|
|
return output.includes(MACOS_PKG_IDENTIFIER);
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {string} pkgPath
|
|
*/
|
|
async function runPkgInstaller(pkgPath) {
|
|
// Uses installer to install the package (https://ss64.com/mac/installer.html)
|
|
// Options:
|
|
// -pkg (required): The package to be installed.
|
|
// -target (required): The target volume is specified with the -target parameter.
|
|
// --> "-target /" installs to the current boot volume.
|
|
|
|
const result = await printVerboseAndSafeSpawn(
|
|
"installer",
|
|
["-pkg", pkgPath, "-target", "/"],
|
|
{
|
|
stdio: "inherit",
|
|
},
|
|
);
|
|
|
|
if (result.status !== 0) {
|
|
throw new Error(`PKG installer failed (exit code: ${result.status})`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {string} pkgPath
|
|
*/
|
|
function cleanup(pkgPath) {
|
|
try {
|
|
unlinkSync(pkgPath);
|
|
} catch {
|
|
ui.writeVerbose("Failed to clean up temporary installer file.");
|
|
}
|
|
}
|