UNPKG

dnsweeper

Version:

Advanced CLI tool for DNS record risk analysis and cleanup. Features CSV import for Cloudflare/Route53, automated risk assessment, and parallel DNS validation.

205 lines 6.49 kB
import { promises as fs } from 'fs'; import os from 'os'; import path from 'path'; /** * デフォルト設定 */ const DEFAULT_CONFIG = { dns: { timeout: 5000, retries: 3, concurrent: 10, }, csv: { encoding: 'utf8', delimiter: ',', quote: '"', skipEmptyLines: true, maxRows: 1000000, }, risk: { weights: { unusedDays: 0.4, namingPattern: 0.3, ttl: 0.3, }, thresholds: { high: 70, medium: 40, }, }, output: { format: 'table', colors: true, verbose: false, quiet: false, }, }; /** * 設定ファイルのパスを検索 */ async function findConfigFile() { const configFileNames = ['.dnsweeper.json', '.dnsweeperrc', 'dnsweeper.config.json']; // 現在のディレクトリから上位ディレクトリまで探索 let currentDir = process.cwd(); while (true) { for (const fileName of configFileNames) { const configPath = path.join(currentDir, fileName); try { await fs.access(configPath); return configPath; } catch { // ファイルが存在しない場合は次を試す } } const parentDir = path.dirname(currentDir); if (parentDir === currentDir) { break; // ルートディレクトリに到達 } currentDir = parentDir; } // ホームディレクトリもチェック const homeDir = os.homedir(); for (const fileName of configFileNames) { const configPath = path.join(homeDir, fileName); try { await fs.access(configPath); return configPath; } catch { // ファイルが存在しない場合は次を試す } } return null; } /** * 設定ファイルを読み込む */ export async function loadConfig(configPath) { let config = { ...DEFAULT_CONFIG }; // 設定ファイルパスが指定されていない場合は自動検索 const actualConfigPath = configPath || (await findConfigFile()); if (actualConfigPath) { try { const configContent = await fs.readFile(actualConfigPath, 'utf8'); const fileConfig = JSON.parse(configContent); // 深いマージを実行 config = deepMerge(config, fileConfig); console.log(`設定ファイルを読み込みました: ${actualConfigPath}`); } catch (error) { console.error(`設定ファイルの読み込みエラー: ${actualConfigPath}`, error); throw error; } } // 環境変数から設定を上書き config = mergeEnvironmentVariables(config); return config; } /** * 環境変数から設定をマージ */ function mergeEnvironmentVariables(config) { const env = process.env; // DNS設定 if (env.DNSWEEPER_DNS_TIMEOUT) { config.dns = config.dns || {}; config.dns.timeout = parseInt(env.DNSWEEPER_DNS_TIMEOUT, 10); } if (env.DNSWEEPER_DNS_SERVERS) { config.dns = config.dns || {}; config.dns.servers = env.DNSWEEPER_DNS_SERVERS.split(','); } // API設定 if (env.CLOUDFLARE_API_KEY) { config.api = config.api || {}; config.api.cloudflare = config.api.cloudflare || {}; config.api.cloudflare.apiKey = env.CLOUDFLARE_API_KEY; } if (env.CLOUDFLARE_EMAIL) { config.api = config.api || {}; config.api.cloudflare = config.api.cloudflare || {}; config.api.cloudflare.email = env.CLOUDFLARE_EMAIL; } if (env.AWS_ACCESS_KEY_ID) { config.api = config.api || {}; config.api.route53 = config.api.route53 || {}; config.api.route53.accessKeyId = env.AWS_ACCESS_KEY_ID; } if (env.AWS_SECRET_ACCESS_KEY) { config.api = config.api || {}; config.api.route53 = config.api.route53 || {}; config.api.route53.secretAccessKey = env.AWS_SECRET_ACCESS_KEY; } // 出力設定 if (env.DNSWEEPER_OUTPUT_FORMAT) { config.output = config.output || {}; config.output.format = env.DNSWEEPER_OUTPUT_FORMAT; } if (env.NO_COLOR || env.DNSWEEPER_NO_COLOR) { config.output = config.output || {}; config.output.colors = false; } return config; } /** * オブジェクトの深いマージ */ function deepMerge(target, source) { const result = { ...target }; for (const key in source) { if (source.hasOwnProperty(key)) { if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) { result[key] = deepMerge(result[key] || {}, source[key]); } else { result[key] = source[key]; } } } return result; } /** * 設定の検証 */ export function validateConfig(config) { // DNS設定の検証 if (config.dns?.timeout && config.dns.timeout < 0) { throw new Error('DNS timeout must be positive'); } if (config.dns?.retries && config.dns.retries < 0) { throw new Error('DNS retries must be positive'); } // リスク重みの検証 if (config.risk?.weights) { const weights = config.risk.weights; const total = (weights.unusedDays || 0) + (weights.namingPattern || 0) + (weights.ttl || 0); if (Math.abs(total - 1.0) > 0.01) { throw new Error('Risk weights must sum to 1.0'); } } // しきい値の検証 if (config.risk?.thresholds) { const { high = 70, medium = 40 } = config.risk.thresholds; if (high <= medium) { throw new Error('High risk threshold must be greater than medium threshold'); } } } /** * 設定をファイルに保存 */ export async function saveConfig(config, configPath) { const actualConfigPath = configPath || path.join(process.cwd(), '.dnsweeper.json'); try { const configContent = JSON.stringify(config, null, 2); await fs.writeFile(actualConfigPath, configContent, 'utf8'); console.log(`設定ファイルを保存しました: ${actualConfigPath}`); } catch (error) { console.error(`設定ファイルの保存エラー: ${actualConfigPath}`, error); throw error; } } //# sourceMappingURL=config.js.map