Merge pull request #311 from AikidoSec/add-log-cmd

add safe-chain ultimate logs & collect-logs
This commit is contained in:
Sander Declerck 2026-01-30 16:33:51 +01:00 committed by GitHub
commit b96bbc91a4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 1048 additions and 26 deletions

942
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -20,6 +20,7 @@ import {
installUltimate,
uninstallUltimate,
} from "../src/installation/installUltimate.js";
import {printUltimateLogs, troubleshootingExport } from "../src/ultimate/ultimateTroubleshooting.js";
/** @type {string} */
// This checks the current file's dirname in a way that's compatible with:
@ -72,6 +73,14 @@ if (tool) {
(async () => {
await uninstallUltimate();
})();
} else if (subCommand === "troubleshooting-logs") {
(async () => {
await printUltimateLogs();
})();
} else if (subCommand === "troubleshooting-export") {
(async () => {
await troubleshootingExport();
})();
} else {
(async () => {
await installUltimate();
@ -136,6 +145,16 @@ function writeHelp() {
"safe-chain ultimate",
)}: Install the ultimate version of safe-chain, enabling protection for more eco-systems.`,
);
ui.writeInformation(
`- ${chalk.cyan(
"safe-chain ultimate troubleshooting-logs",
)}: Prints standard and error logs for safe-chain ultimate and it's proxy.`,
);
ui.writeInformation(
`- ${chalk.cyan(
"safe-chain ultimate troubleshooting-export",
)}: Creates a zip archive of useful data for troubleshooting safe-chain ultimate, that can be shared with our support team.`,
);
ui.writeInformation(
`- ${chalk.cyan(
"safe-chain ultimate uninstall",

View file

@ -38,6 +38,7 @@
"license": "AGPL-3.0-or-later",
"description": "The Aikido Safe Chain wraps around the [npm cli](https://github.com/npm/cli), [npx](https://github.com/npm/cli/blob/latest/docs/content/commands/npx.md), [yarn](https://yarnpkg.com/), [pnpm](https://pnpm.io/), [pnpx](https://pnpm.io/cli/dlx), [bun](https://bun.sh/), [bunx](https://bun.sh/docs/cli/bunx), [uv](https://docs.astral.sh/uv/) (Python), and [pip](https://pip.pypa.io/) to provide extra checks before installing new packages. This tool will detect when a package contains malware and prompt you to exit, preventing npm, npx, yarn, pnpm, pnpx, bun, bunx, uv, or pip/pip3 from downloading or running the malware.",
"dependencies": {
"archiver": "^7.0.1",
"certifi": "14.5.15",
"chalk": "5.4.1",
"https-proxy-agent": "7.0.6",
@ -48,6 +49,7 @@
"semver": "7.7.2"
},
"devDependencies": {
"@types/archiver": "^7.0.0",
"@types/ini": "^4.1.1",
"@types/make-fetch-happen": "^10.0.4",
"@types/node": "^18.19.130",

View file

@ -0,0 +1,111 @@
import { platform } from 'os';
import { ui } from "../environment/userInteraction.js";
import { readFileSync, existsSync } from "node:fs";
import {randomUUID} from "node:crypto";
import {createWriteStream} from "fs";
import archiver from 'archiver';
import path from "node:path";
export async function printUltimateLogs() {
const { proxyLogPath, ultimateLogPath, proxyErrLogPath, ultimateErrLogPath } = getPathsPerPlatform();
await printLogs(
"SafeChain Proxy",
proxyLogPath,
proxyErrLogPath
);
await printLogs(
"SafeChain Ultimate",
ultimateLogPath,
ultimateErrLogPath
);
}
export async function troubleshootingExport() {
const { logDir } = getPathsPerPlatform();
return new Promise((resolve, reject) => {
if (!existsSync(logDir)) {
ui.writeError(`Log directory not found: ${logDir}`);
reject(new Error(`Log directory not found: ${logDir}`));
return;
}
const date = new Date().toISOString().split('T')[0];
const uuid = randomUUID();
const zipFileName = `safechain-ultimate-${date}-${uuid}.zip`;
const output = createWriteStream(zipFileName);
const archive = archiver('zip', { zlib: { level: 9 } });
output.on('close', () => {
ui.writeInformation(`Logs collected and zipped as: ${path.resolve(zipFileName)}`);
resolve(zipFileName);
});
archive.on('error', (err) => {
ui.writeError(`Failed to zip logs: ${err.message}`);
reject(err);
});
archive.pipe(output);
archive.directory(logDir, false);
archive.finalize();
});
}
function getPathsPerPlatform() {
const os = platform();
if (os === 'win32') {
const logDir = `C:\\ProgramData\\AikidoSecurity\\SafeChainUltimate\\logs`;
return {
logDir,
proxyLogPath: `${logDir}\\SafeChainProxy.log`,
ultimateLogPath: `${logDir}\\SafeChainUltimate.log`,
proxyErrLogPath: `${logDir}\\SafeChainProxy.err`,
ultimateErrLogPath: `${logDir}\\SafeChainUltimate.err`,
};
} else if (os === 'darwin') {
const logDir = `/Library/Logs/AikidoSecurity/SafeChainUltimate`;
return {
logDir,
proxyLogPath: `${logDir}/safechain-proxy.log`,
ultimateLogPath: `${logDir}/safechain-ultimate.log`,
proxyErrLogPath: `${logDir}/safechain-proxy.error.log`,
ultimateErrLogPath: `${logDir}/safechain-ultimate.error.log`,
};
} else {
throw new Error('Unsupported platform for log printing.');
}
}
/**
* @param {string} appName
* @param {string} logPath
* @param {string} errLogPath
*/
async function printLogs(appName, logPath, errLogPath) {
ui.writeInformation(`=== ${appName} Logs ===`);
try {
if (existsSync(logPath)) {
const logs = readFileSync(logPath, "utf-8");
ui.writeInformation(logs);
} else {
ui.writeWarning(`${appName} log file not found: ${logPath}`);
}
} catch (error) {
ui.writeError(`Failed to read ${appName} logs: ${error}`);
}
ui.writeInformation(`=== ${appName} Error Logs ===`);
try {
if (existsSync(errLogPath)) {
const errLogs = readFileSync(errLogPath, "utf-8");
ui.writeInformation(errLogs);
} else {
ui.writeInformation(`No error log file found for ${appName}.`);
}
} catch (error) {
ui.writeError(`Failed to read ${appName} error logs: ${error}`);
}
}