feat: initial open marketplace with llm-security, config-audit, ultraplan-local
This commit is contained in:
commit
f93d6abdae
380 changed files with 65935 additions and 0 deletions
130
plugins/config-audit/scanners/drift-cli.mjs
Normal file
130
plugins/config-audit/scanners/drift-cli.mjs
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Config-Audit Drift CLI
|
||||
* Compare current configuration against a saved baseline.
|
||||
* Usage:
|
||||
* node drift-cli.mjs <path> --save [--name my-baseline]
|
||||
* node drift-cli.mjs <path> [--baseline my-baseline] [--json]
|
||||
* node drift-cli.mjs --list
|
||||
* Zero external dependencies.
|
||||
*/
|
||||
|
||||
import { resolve } from 'node:path';
|
||||
import { runAllScanners } from './scan-orchestrator.mjs';
|
||||
import { diffEnvelopes, formatDiffReport } from './lib/diff-engine.mjs';
|
||||
import { saveBaseline, loadBaseline, listBaselines } from './lib/baseline.mjs';
|
||||
|
||||
async function main() {
|
||||
const args = process.argv.slice(2);
|
||||
let targetPath = '.';
|
||||
let baselineName = 'default';
|
||||
let save = false;
|
||||
let list = false;
|
||||
let jsonMode = false;
|
||||
let includeGlobal = false;
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] === '--save') {
|
||||
save = true;
|
||||
} else if (args[i] === '--name' && args[i + 1]) {
|
||||
baselineName = args[++i];
|
||||
} else if (args[i] === '--baseline' && args[i + 1]) {
|
||||
baselineName = args[++i];
|
||||
} else if (args[i] === '--list') {
|
||||
list = true;
|
||||
} else if (args[i] === '--json') {
|
||||
jsonMode = true;
|
||||
} else if (args[i] === '--global') {
|
||||
includeGlobal = true;
|
||||
} else if (!args[i].startsWith('-')) {
|
||||
targetPath = args[i];
|
||||
}
|
||||
}
|
||||
|
||||
// --- List mode ---
|
||||
if (list) {
|
||||
const result = await listBaselines();
|
||||
if (jsonMode) {
|
||||
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
||||
} else {
|
||||
if (result.baselines.length === 0) {
|
||||
process.stderr.write('No baselines saved.\n');
|
||||
process.stderr.write('Save one with: node drift-cli.mjs <path> --save\n');
|
||||
} else {
|
||||
process.stderr.write('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
||||
process.stderr.write(' Saved Baselines\n');
|
||||
process.stderr.write('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n');
|
||||
for (const b of result.baselines) {
|
||||
process.stderr.write(` ${b.name.padEnd(20)} ${b.findingCount} findings ${b.savedAt}\n`);
|
||||
}
|
||||
process.stderr.write('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
||||
}
|
||||
}
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// --- Save mode ---
|
||||
if (save) {
|
||||
if (!jsonMode) {
|
||||
process.stderr.write(`Config-Audit Drift CLI v2.1.0\n`);
|
||||
process.stderr.write(`Saving baseline "${baselineName}" for ${resolve(targetPath)}\n\n`);
|
||||
}
|
||||
|
||||
const envelope = await runAllScanners(targetPath, { includeGlobal });
|
||||
const result = await saveBaseline(envelope, baselineName);
|
||||
|
||||
if (jsonMode) {
|
||||
process.stdout.write(JSON.stringify({ saved: true, name: result.name, path: result.path }, null, 2) + '\n');
|
||||
} else {
|
||||
process.stderr.write(`\nBaseline "${result.name}" saved to ${result.path}\n`);
|
||||
process.stderr.write(`Findings: ${envelope.aggregate.total_findings}\n`);
|
||||
}
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// --- Drift mode (default) ---
|
||||
if (!jsonMode) {
|
||||
process.stderr.write(`Config-Audit Drift CLI v2.1.0\n`);
|
||||
process.stderr.write(`Target: ${resolve(targetPath)}\n`);
|
||||
process.stderr.write(`Baseline: ${baselineName}\n\n`);
|
||||
}
|
||||
|
||||
// Load baseline
|
||||
const baseline = await loadBaseline(baselineName);
|
||||
if (!baseline) {
|
||||
if (jsonMode) {
|
||||
process.stdout.write(JSON.stringify({ error: `Baseline "${baselineName}" not found. Save one with --save.` }, null, 2) + '\n');
|
||||
} else {
|
||||
process.stderr.write(`Baseline "${baselineName}" not found.\n`);
|
||||
process.stderr.write(`Save one first: node drift-cli.mjs <path> --save\n`);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Run current scan
|
||||
const current = await runAllScanners(targetPath, { includeGlobal });
|
||||
|
||||
// Diff
|
||||
const diff = diffEnvelopes(baseline, current);
|
||||
|
||||
if (jsonMode) {
|
||||
process.stdout.write(JSON.stringify(diff, null, 2) + '\n');
|
||||
} else {
|
||||
const report = formatDiffReport(diff);
|
||||
process.stderr.write('\n' + report + '\n');
|
||||
}
|
||||
|
||||
// Exit code: 0=stable/improving, 1=degrading
|
||||
if (diff.summary.trend === 'degrading') process.exit(1);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Only run CLI if invoked directly
|
||||
const isDirectRun = process.argv[1] && resolve(process.argv[1]) === resolve(new URL(import.meta.url).pathname);
|
||||
if (isDirectRun) {
|
||||
main().catch(err => {
|
||||
process.stderr.write(`Fatal: ${err.message}\n`);
|
||||
process.exit(3);
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue