mirror of
https://github.com/AikidoSec/safe-chain.git
synced 2026-05-26 20:20:49 +00:00
Add better error handling, tests and type checks for configFile.js
This commit is contained in:
parent
aa5c74c477
commit
5304a7744a
2 changed files with 165 additions and 5 deletions
|
|
@ -3,14 +3,32 @@ import path from "path";
|
||||||
import os from "os";
|
import os from "os";
|
||||||
import { ui } from "../environment/userInteraction.js";
|
import { ui } from "../environment/userInteraction.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} SafeChainConfig
|
||||||
|
* @property {any} scanTimeout // This should be a number
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
export function getScanTimeout() {
|
export function getScanTimeout() {
|
||||||
const config = /** @type {{scanTimeout?: number}} */ (readConfigFile());
|
const config = readConfigFile();
|
||||||
|
|
||||||
// @ts-expect-error values of process.env can be string | undefined
|
if (process.env.AIKIDO_SCAN_TIMEOUT_MS) {
|
||||||
return parseInt(process.env.AIKIDO_SCAN_TIMEOUT_MS) || config.scanTimeout || 10000 // Default to 10 seconds
|
const scanTimeout = Number(process.env.AIKIDO_SCAN_TIMEOUT_MS);
|
||||||
|
if (!Number.isNaN(scanTimeout) && scanTimeout > 0) {
|
||||||
|
return scanTimeout;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.scanTimeout) {
|
||||||
|
const scanTimeout = Number(config.scanTimeout);
|
||||||
|
if (!Number.isNaN(scanTimeout) && scanTimeout > 0) {
|
||||||
|
return scanTimeout;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 10000; // Default to 10 seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -68,13 +86,15 @@ export function readDatabaseFromLocalCache() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {unknown}
|
* @returns {SafeChainConfig}
|
||||||
*/
|
*/
|
||||||
function readConfigFile() {
|
function readConfigFile() {
|
||||||
const configFilePath = getConfigFilePath();
|
const configFilePath = getConfigFilePath();
|
||||||
|
|
||||||
if (!fs.existsSync(configFilePath)) {
|
if (!fs.existsSync(configFilePath)) {
|
||||||
return {};
|
return {
|
||||||
|
scanTimeout: undefined,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = fs.readFileSync(configFilePath, "utf8");
|
const data = fs.readFileSync(configFilePath, "utf8");
|
||||||
|
|
|
||||||
140
packages/safe-chain/src/config/configFile.spec.js
Normal file
140
packages/safe-chain/src/config/configFile.spec.js
Normal file
|
|
@ -0,0 +1,140 @@
|
||||||
|
import { describe, it, beforeEach, afterEach } from "node:test";
|
||||||
|
import assert from "node:assert";
|
||||||
|
import { getScanTimeout } from "./configFile.js";
|
||||||
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
|
import os from "os";
|
||||||
|
|
||||||
|
describe("getScanTimeout", () => {
|
||||||
|
let originalEnv;
|
||||||
|
let aikidoDir;
|
||||||
|
let configPath;
|
||||||
|
let configBackupPath;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// Save original environment
|
||||||
|
originalEnv = process.env.AIKIDO_SCAN_TIMEOUT_MS;
|
||||||
|
|
||||||
|
// Use the actual .aikido directory
|
||||||
|
aikidoDir = path.join(os.homedir(), ".aikido");
|
||||||
|
configPath = path.join(aikidoDir, "config.json");
|
||||||
|
configBackupPath = path.join(aikidoDir, "config.json.backup");
|
||||||
|
|
||||||
|
// Backup existing config if it exists
|
||||||
|
if (fs.existsSync(configPath)) {
|
||||||
|
fs.copyFileSync(configPath, configBackupPath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
// Restore original environment
|
||||||
|
if (originalEnv !== undefined) {
|
||||||
|
process.env.AIKIDO_SCAN_TIMEOUT_MS = originalEnv;
|
||||||
|
} else {
|
||||||
|
delete process.env.AIKIDO_SCAN_TIMEOUT_MS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore original config file
|
||||||
|
if (fs.existsSync(configBackupPath)) {
|
||||||
|
fs.copyFileSync(configBackupPath, configPath);
|
||||||
|
fs.unlinkSync(configBackupPath);
|
||||||
|
} else if (fs.existsSync(configPath)) {
|
||||||
|
fs.unlinkSync(configPath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return default timeout of 10000ms when no config or env var is set", () => {
|
||||||
|
delete process.env.AIKIDO_SCAN_TIMEOUT_MS;
|
||||||
|
if (fs.existsSync(configPath)) {
|
||||||
|
fs.unlinkSync(configPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeout = getScanTimeout();
|
||||||
|
|
||||||
|
assert.strictEqual(timeout, 10000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return timeout from config file when set", () => {
|
||||||
|
delete process.env.AIKIDO_SCAN_TIMEOUT_MS;
|
||||||
|
fs.writeFileSync(configPath, JSON.stringify({ scanTimeout: 5000 }));
|
||||||
|
|
||||||
|
const timeout = getScanTimeout();
|
||||||
|
|
||||||
|
assert.strictEqual(timeout, 5000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should prioritize environment variable over config file", () => {
|
||||||
|
process.env.AIKIDO_SCAN_TIMEOUT_MS = "20000";
|
||||||
|
fs.writeFileSync(configPath, JSON.stringify({ scanTimeout: 5000 }));
|
||||||
|
|
||||||
|
const timeout = getScanTimeout();
|
||||||
|
|
||||||
|
assert.strictEqual(timeout, 20000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle invalid environment variable and fall back to config", () => {
|
||||||
|
process.env.AIKIDO_SCAN_TIMEOUT_MS = "invalid";
|
||||||
|
fs.writeFileSync(configPath, JSON.stringify({ scanTimeout: 7000 }));
|
||||||
|
|
||||||
|
const timeout = getScanTimeout();
|
||||||
|
|
||||||
|
assert.strictEqual(timeout, 7000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should ignore zero and negative values and fall back to default", () => {
|
||||||
|
process.env.AIKIDO_SCAN_TIMEOUT_MS = "0";
|
||||||
|
|
||||||
|
let timeout = getScanTimeout();
|
||||||
|
assert.strictEqual(timeout, 10000);
|
||||||
|
|
||||||
|
process.env.AIKIDO_SCAN_TIMEOUT_MS = "-5000";
|
||||||
|
|
||||||
|
timeout = getScanTimeout();
|
||||||
|
assert.strictEqual(timeout, 10000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should ignore textual non-numeric values in environment variable and fall back to config", () => {
|
||||||
|
process.env.AIKIDO_SCAN_TIMEOUT_MS = "fast";
|
||||||
|
fs.writeFileSync(configPath, JSON.stringify({ scanTimeout: 8000 }));
|
||||||
|
|
||||||
|
const timeout = getScanTimeout();
|
||||||
|
|
||||||
|
assert.strictEqual(timeout, 8000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should ignore textual non-numeric values in config file and fall back to default", () => {
|
||||||
|
delete process.env.AIKIDO_SCAN_TIMEOUT_MS;
|
||||||
|
fs.writeFileSync(configPath, JSON.stringify({ scanTimeout: "slow" }));
|
||||||
|
|
||||||
|
const timeout = getScanTimeout();
|
||||||
|
|
||||||
|
assert.strictEqual(timeout, 10000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should ignore textual non-numeric values in both env and config, fall back to default", () => {
|
||||||
|
process.env.AIKIDO_SCAN_TIMEOUT_MS = "quick";
|
||||||
|
fs.writeFileSync(configPath, JSON.stringify({ scanTimeout: "medium" }));
|
||||||
|
|
||||||
|
const timeout = getScanTimeout();
|
||||||
|
|
||||||
|
assert.strictEqual(timeout, 10000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should ignore mixed alphanumeric strings in environment variable", () => {
|
||||||
|
process.env.AIKIDO_SCAN_TIMEOUT_MS = "5000ms";
|
||||||
|
fs.writeFileSync(configPath, JSON.stringify({ scanTimeout: 6000 }));
|
||||||
|
|
||||||
|
const timeout = getScanTimeout();
|
||||||
|
|
||||||
|
assert.strictEqual(timeout, 6000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should ignore mixed alphanumeric strings in config file", () => {
|
||||||
|
delete process.env.AIKIDO_SCAN_TIMEOUT_MS;
|
||||||
|
fs.writeFileSync(configPath, JSON.stringify({ scanTimeout: "3000ms" }));
|
||||||
|
|
||||||
|
const timeout = getScanTimeout();
|
||||||
|
|
||||||
|
assert.strictEqual(timeout, 10000);
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue