feat(cli): add standalone CLI wrapper — npx llm-security scan without Claude Code
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
8ec320f40c
commit
52d26ddb0b
3 changed files with 189 additions and 1 deletions
77
plugins/llm-security/bin/llm-security.mjs
Executable file
77
plugins/llm-security/bin/llm-security.mjs
Executable file
|
|
@ -0,0 +1,77 @@
|
|||
#!/usr/bin/env node
|
||||
// llm-security CLI — standalone entry point for llm-security scanners.
|
||||
// Usage: llm-security <subcommand> [args]
|
||||
// Works without Claude Code. Zero dependencies.
|
||||
|
||||
import { spawn } from 'node:child_process';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { dirname, resolve } from 'node:path';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const ROOT = resolve(__dirname, '..');
|
||||
const PKG = JSON.parse(readFileSync(resolve(ROOT, 'package.json'), 'utf8'));
|
||||
|
||||
const USAGE = `llm-security v${PKG.version} — AI security scanning for Claude Code projects
|
||||
|
||||
Usage: llm-security <command> [options]
|
||||
|
||||
Commands:
|
||||
scan <target> [--format sarif] [--baseline] [--save-baseline]
|
||||
Run deterministic deep-scan (10 scanners)
|
||||
deep-scan <target> [--format sarif] [--baseline] [--save-baseline]
|
||||
Alias for scan
|
||||
posture <target>
|
||||
Quick security posture assessment (16 categories)
|
||||
audit-bom <target> [--output-file <path>]
|
||||
Generate AI Bill of Materials (CycloneDX 1.6)
|
||||
benchmark [--adaptive] [--category <name>]
|
||||
Run attack simulation benchmark
|
||||
|
||||
Options:
|
||||
--help Show this help
|
||||
--version Show version
|
||||
`;
|
||||
|
||||
const [subcommand, ...rest] = process.argv.slice(2);
|
||||
|
||||
if (!subcommand || subcommand === '--help' || subcommand === '-h') {
|
||||
process.stdout.write(USAGE);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (subcommand === '--version' || subcommand === '-v') {
|
||||
process.stdout.write(`${PKG.version}\n`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Map subcommands to scanner scripts and their arguments
|
||||
const COMMANDS = {
|
||||
scan: { script: 'scanners/scan-orchestrator.mjs' },
|
||||
'deep-scan': { script: 'scanners/scan-orchestrator.mjs' },
|
||||
posture: { script: 'scanners/posture-scanner.mjs' },
|
||||
'audit-bom': { script: 'scanners/ai-bom-generator.mjs' },
|
||||
benchmark: { script: 'scanners/attack-simulator.mjs', prependArgs: ['--benchmark', '--json'] },
|
||||
};
|
||||
|
||||
const cmd = COMMANDS[subcommand];
|
||||
if (!cmd) {
|
||||
process.stderr.write(`Unknown command: ${subcommand}\n\n`);
|
||||
process.stderr.write(USAGE);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const scriptPath = resolve(ROOT, cmd.script);
|
||||
const args = [...(cmd.prependArgs || []), ...rest];
|
||||
|
||||
const child = spawn('node', [scriptPath, ...args], {
|
||||
cwd: process.cwd(),
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
});
|
||||
|
||||
child.stdout.pipe(process.stdout);
|
||||
child.stderr.pipe(process.stderr);
|
||||
|
||||
child.on('close', (code) => {
|
||||
process.exitCode = code ?? 1;
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue