Merge pull request #123 from AikidoSec/logging-silent-mode

Introduce silent mode to disable logging
This commit is contained in:
Sander Declerck 2025-10-27 11:29:26 +01:00 committed by GitHub
commit 95d9cefcc9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 143 additions and 4 deletions

View file

@ -1,5 +1,6 @@
const state = {
malwareAction: undefined,
loggingLevel: undefined,
};
const SAFE_CHAIN_ARG_PREFIX = "--safe-chain-";
@ -7,6 +8,7 @@ const SAFE_CHAIN_ARG_PREFIX = "--safe-chain-";
export function initializeCliArguments(args) {
// Reset state on each call
state.malwareAction = undefined;
state.loggingLevel = undefined;
const safeChainArgs = [];
const remainingArgs = [];
@ -20,6 +22,7 @@ export function initializeCliArguments(args) {
}
setMalwareAction(safeChainArgs);
setLoggingLevel(safeChainArgs);
return remainingArgs;
}
@ -48,3 +51,17 @@ function getLastArgEqualsValue(args, prefix) {
export function getMalwareAction() {
return state.malwareAction;
}
function setLoggingLevel(args) {
const safeChainLoggingArg = SAFE_CHAIN_ARG_PREFIX + "logging=";
const level = getLastArgEqualsValue(args, safeChainLoggingArg);
if (!level) {
return;
}
state.loggingLevel = level.toLowerCase();
}
export function getLoggingLevel() {
return state.loggingLevel;
}

View file

@ -1,6 +1,10 @@
import { describe, it } from "node:test";
import assert from "node:assert";
import { initializeCliArguments, getMalwareAction } from "./cliArguments.js";
import {
initializeCliArguments,
getMalwareAction,
getLoggingLevel,
} from "./cliArguments.js";
describe("initializeCliArguments", () => {
it("should return all args when no safe-chain args are present", () => {
@ -105,4 +109,67 @@ describe("initializeCliArguments", () => {
assert.deepEqual(result, ["install"]);
assert.strictEqual(getMalwareAction(), "block");
});
it("should not set loggingLevel when no logging argument is passed", () => {
const args = ["install", "express", "--save"];
initializeCliArguments(args);
assert.strictEqual(getLoggingLevel(), undefined);
});
it("should parse logging=silent and set state", () => {
const args = ["--safe-chain-logging=silent", "install", "package"];
const result = initializeCliArguments(args);
assert.deepEqual(result, ["install", "package"]);
assert.strictEqual(getLoggingLevel(), "silent");
});
it("should parse logging=normal and set state", () => {
const args = ["--safe-chain-logging=normal", "install", "package"];
const result = initializeCliArguments(args);
assert.deepEqual(result, ["install", "package"]);
assert.strictEqual(getLoggingLevel(), "normal");
});
it("should handle multiple logging args, using the last one", () => {
const args = [
"--safe-chain-logging=normal",
"--safe-chain-logging=silent",
"install",
];
const result = initializeCliArguments(args);
assert.deepEqual(result, ["install"]);
assert.strictEqual(getLoggingLevel(), "silent");
});
it("should handle logging level case-insensitively", () => {
const args = ["--safe-chain-logging=SILENT", "install"];
initializeCliArguments(args);
assert.strictEqual(getLoggingLevel(), "silent");
});
it("should capture invalid logging level as-is (lowercased)", () => {
const args = ["--safe-chain-logging=invalid", "install"];
initializeCliArguments(args);
assert.strictEqual(getLoggingLevel(), "invalid");
});
it("should handle logging with other safe-chain args", () => {
const args = [
"--safe-chain-debug",
"--safe-chain-logging=silent",
"--safe-chain-malware-action=block",
"install",
];
const result = initializeCliArguments(args);
assert.deepEqual(result, ["install"]);
assert.strictEqual(getLoggingLevel(), "silent");
assert.strictEqual(getMalwareAction(), "block");
});
});

View file

@ -10,5 +10,18 @@ export function getMalwareAction() {
return MALWARE_ACTION_BLOCK;
}
export function getLoggingLevel() {
const level = cliArguments.getLoggingLevel();
if (level === LOGGING_SILENT) {
return LOGGING_SILENT;
}
return LOGGING_NORMAL;
}
export const MALWARE_ACTION_BLOCK = "block";
export const MALWARE_ACTION_PROMPT = "prompt";
export const LOGGING_SILENT = "silent";
export const LOGGING_NORMAL = "normal";

View file

@ -3,16 +3,27 @@ import chalk from "chalk";
import ora from "ora";
import { createInterface } from "readline";
import { isCi } from "./environment.js";
import { getLoggingLevel, LOGGING_SILENT } from "../config/settings.js";
function isSilentMode() {
return getLoggingLevel() === LOGGING_SILENT;
}
function emptyLine() {
if (isSilentMode()) return;
writeInformation("");
}
function writeInformation(message, ...optionalParams) {
if (isSilentMode()) return;
console.log(message, ...optionalParams);
}
function writeWarning(message, ...optionalParams) {
if (isSilentMode()) return;
if (!isCi()) {
message = chalk.yellow(message);
}
@ -26,7 +37,24 @@ function writeError(message, ...optionalParams) {
console.error(message, ...optionalParams);
}
function writeExitWithoutInstallingMaliciousPackages() {
let message = "Safe-chain: Exiting without installing malicious packages.";
if (!isCi()) {
message = chalk.red(message);
}
console.error(message);
}
function startProcess(message) {
if (isSilentMode()) {
return {
succeed: () => {},
fail: () => {},
stop: () => {},
setText: () => {},
};
}
if (isCi()) {
return {
succeed: (message) => {
@ -60,7 +88,7 @@ function startProcess(message) {
}
async function confirm(config) {
if (isCi()) {
if (isCi() || isSilentMode()) {
return Promise.resolve(config.default);
}
@ -91,6 +119,7 @@ export const ui = {
writeInformation,
writeWarning,
writeError,
writeExitWithoutInstallingMaliciousPackages,
emptyLine,
startProcess,
confirm,

View file

@ -153,7 +153,7 @@ function verifyNoMaliciousPackages() {
}
ui.emptyLine();
ui.writeError("Exiting without installing malicious packages.");
ui.writeExitWithoutInstallingMaliciousPackages();
ui.emptyLine();
return false;

View file

@ -93,7 +93,7 @@ async function onMalwareFound() {
}
}
ui.writeError("Exiting without installing malicious packages.");
ui.writeExitWithoutInstallingMaliciousPackages();
ui.emptyLine();
return 1;
}

View file

@ -46,6 +46,7 @@ describe("scanCommand", async () => {
writeError: () => {},
writeInformation: () => {},
writeWarning: () => {},
writeExitWithoutInstallingMaliciousPackages: () => {},
emptyLine: () => {},
confirm: mockConfirm,
},