mirror of
https://github.com/AikidoSec/safe-chain.git
synced 2026-05-26 12:10:49 +00:00
mask credentials in malwarelist urls and prevent log poisoning when using custom urls
This commit is contained in:
parent
da9e3d475e
commit
8ee123c321
2 changed files with 75 additions and 4 deletions
|
|
@ -200,6 +200,25 @@ export function getMinimumPackageAgeExclusions() {
|
||||||
return [...new Set(allExclusions)];
|
return [...new Set(allExclusions)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Masks credentials in a URL for logging purposes.
|
||||||
|
* @param {string} url
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function maskCredentialsInUrl(url) {
|
||||||
|
if (!url || typeof url !== "string") {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mask credentials https://username:password@abc.example or https://username@abc.example by replacing with https://***@abc.example
|
||||||
|
let masked = url.replace(/:\/\/([^@]+)@/, "://***@");
|
||||||
|
|
||||||
|
// Remove control characters to prevent log poisoning
|
||||||
|
masked = masked.replace(/[\x00-\x1F\x7F]/g, "");
|
||||||
|
|
||||||
|
return masked;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the malware list base URL with priority: CLI argument > environment variable > config file > default
|
* Gets the malware list base URL with priority: CLI argument > environment variable > config file > default
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
|
|
@ -209,7 +228,7 @@ export function getMalwareListBaseUrl() {
|
||||||
const cliValue = cliArguments.getMalwareListBaseUrl();
|
const cliValue = cliArguments.getMalwareListBaseUrl();
|
||||||
if (cliValue) {
|
if (cliValue) {
|
||||||
const url = removeTrailingSlashes(cliValue);
|
const url = removeTrailingSlashes(cliValue);
|
||||||
ui.writeInformation(`Fetching malware lists from ${url} as defined by CLI argument --safe-chain-malware-list-base-url`);
|
ui.writeInformation(`Fetching malware lists from ${maskCredentialsInUrl(url)} as defined by CLI argument --safe-chain-malware-list-base-url`);
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -217,7 +236,7 @@ export function getMalwareListBaseUrl() {
|
||||||
const envValue = environmentVariables.getMalwareListBaseUrl();
|
const envValue = environmentVariables.getMalwareListBaseUrl();
|
||||||
if (envValue) {
|
if (envValue) {
|
||||||
const url = removeTrailingSlashes(envValue);
|
const url = removeTrailingSlashes(envValue);
|
||||||
ui.writeInformation(`Fetching malware lists from ${url} as defined by environment variable SAFE_CHAIN_MALWARE_LIST_BASE_URL`);
|
ui.writeInformation(`Fetching malware lists from ${maskCredentialsInUrl(url)} as defined by environment variable SAFE_CHAIN_MALWARE_LIST_BASE_URL`);
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -225,13 +244,13 @@ export function getMalwareListBaseUrl() {
|
||||||
const configValue = configFile.getMalwareListBaseUrl();
|
const configValue = configFile.getMalwareListBaseUrl();
|
||||||
if (configValue) {
|
if (configValue) {
|
||||||
const url = removeTrailingSlashes(configValue);
|
const url = removeTrailingSlashes(configValue);
|
||||||
ui.writeInformation(`Fetching malware lists from ${url} as defined by config file (malwareListBaseUrl)`);
|
ui.writeInformation(`Fetching malware lists from ${maskCredentialsInUrl(url)} as defined by config file (malwareListBaseUrl)`);
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default
|
// Default
|
||||||
const url = removeTrailingSlashes("https://malware-list.aikido.dev");
|
const url = removeTrailingSlashes("https://malware-list.aikido.dev");
|
||||||
ui.writeInformation(`Fetching malware lists from ${url} (default)`);
|
ui.writeInformation(`Fetching malware lists from ${maskCredentialsInUrl(url)} (default)`);
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { describe, it, beforeEach, afterEach, mock } from "node:test";
|
||||||
import assert from "node:assert";
|
import assert from "node:assert";
|
||||||
|
|
||||||
let configFileContent = undefined;
|
let configFileContent = undefined;
|
||||||
|
let loggedMessages = [];
|
||||||
mock.module("fs", {
|
mock.module("fs", {
|
||||||
namedExports: {
|
namedExports: {
|
||||||
existsSync: () => configFileContent !== undefined,
|
existsSync: () => configFileContent !== undefined,
|
||||||
|
|
@ -11,6 +12,14 @@ mock.module("fs", {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
mock.module("../environment/userInteraction.js", {
|
||||||
|
namedExports: {
|
||||||
|
ui: {
|
||||||
|
writeInformation: (message) => loggedMessages.push(message),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getNpmCustomRegistries,
|
getNpmCustomRegistries,
|
||||||
getPipCustomRegistries,
|
getPipCustomRegistries,
|
||||||
|
|
@ -545,6 +554,7 @@ describe("getMalwareListBaseUrl", () => {
|
||||||
delete process.env[envVarName];
|
delete process.env[envVarName];
|
||||||
// Reset CLI arguments state
|
// Reset CLI arguments state
|
||||||
initializeCliArguments([]);
|
initializeCliArguments([]);
|
||||||
|
loggedMessages = [];
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|
@ -644,4 +654,46 @@ describe("getMalwareListBaseUrl", () => {
|
||||||
|
|
||||||
assert.strictEqual(url, "https://cli-mirror.com");
|
assert.strictEqual(url, "https://cli-mirror.com");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should mask credentials in logged URL from CLI argument", () => {
|
||||||
|
initializeCliArguments(["--safe-chain-malware-list-base-url=https://user:pass@cli-mirror.com"]);
|
||||||
|
|
||||||
|
const url = getMalwareListBaseUrl();
|
||||||
|
|
||||||
|
assert.strictEqual(url, "https://user:pass@cli-mirror.com");
|
||||||
|
assert.strictEqual(loggedMessages.length, 1);
|
||||||
|
assert.strictEqual(loggedMessages[0], "Fetching malware lists from https://***@cli-mirror.com as defined by CLI argument --safe-chain-malware-list-base-url");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should mask credentials in logged URL from environment variable", () => {
|
||||||
|
process.env[envVarName] = "https://user:pass@env-mirror.com";
|
||||||
|
|
||||||
|
const url = getMalwareListBaseUrl();
|
||||||
|
|
||||||
|
assert.strictEqual(url, "https://user:pass@env-mirror.com");
|
||||||
|
assert.strictEqual(loggedMessages.length, 1);
|
||||||
|
assert.strictEqual(loggedMessages[0], "Fetching malware lists from https://***@env-mirror.com as defined by environment variable SAFE_CHAIN_MALWARE_LIST_BASE_URL");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should mask credentials in logged URL from config file", () => {
|
||||||
|
configFileContent = JSON.stringify({
|
||||||
|
malwareListBaseUrl: "https://user:pass@config-mirror.com",
|
||||||
|
});
|
||||||
|
|
||||||
|
const url = getMalwareListBaseUrl();
|
||||||
|
|
||||||
|
assert.strictEqual(url, "https://user:pass@config-mirror.com");
|
||||||
|
assert.strictEqual(loggedMessages.length, 1);
|
||||||
|
assert.strictEqual(loggedMessages[0], "Fetching malware lists from https://***@config-mirror.com as defined by config file (malwareListBaseUrl)");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should sanitize control characters in logged URL", () => {
|
||||||
|
initializeCliArguments(["--safe-chain-malware-list-base-url=https://user:pass@cli-mirror.com\nmalicious"]);
|
||||||
|
|
||||||
|
const url = getMalwareListBaseUrl();
|
||||||
|
|
||||||
|
assert.strictEqual(url, "https://user:pass@cli-mirror.com\nmalicious");
|
||||||
|
assert.strictEqual(loggedMessages.length, 1);
|
||||||
|
assert.strictEqual(loggedMessages[0], "Fetching malware lists from https://***@cli-mirror.commalicious as defined by CLI argument --safe-chain-malware-list-base-url");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue