UNPKG

@dawans/promptshield

Version:

Secure your LLM stack with enterprise-grade RulePacks for AI safety scanning

179 lines (178 loc) 8.04 kB
#!/usr/bin/env node "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const commander_1 = require("commander"); const Container_1 = require("../infrastructure/container/Container"); const bootstrap_1 = require("./bootstrap"); const ScanCommand_1 = require("../application/commands/scan/ScanCommand"); const ListCommand_1 = require("../application/commands/list/ListCommand"); const InitCommand_1 = require("../application/commands/init/InitCommand"); const ValidateCommand_1 = require("../application/commands/validate/ValidateCommand"); const Logger_1 = require("../infrastructure/logging/Logger"); const program = new commander_1.Command(); const container = new Container_1.Container(); const logger = Logger_1.LoggerFactory.getLogger(); // Bootstrap the container with all dependencies (0, bootstrap_1.setupContainer)(container); program .name('promptshield') .description('AI safety scanner for LLM outputs - detect prompt injections, PII leaks, and security issues') .version('1.0.0'); // --- Scan Command --- program .command('scan <input>') .description('Scan LLM outputs for safety violations using rulepacks') .option('--rulepack <path>', 'Path to RulePack YAML (default: built-in prompt-injection rules)') .option('-o, --output <format>', 'Output format: json, markdown, csv, table, html, ndjson (default: markdown)') .option('-f, --output-file <file>', 'Write report to file instead of stdout') // Filtering options .option('--severity <levels>', 'Filter by severity: low,medium,high,critical (comma-separated)') .option('--category <categories>', 'Filter by categories: pii,bias,security,compliance (comma-separated)') .option('--max-violations <number>', 'Maximum violations to report', parseInt) .option('--offset <number>', 'Pagination offset', parseInt) .option('--limit <number>', 'Pagination limit', parseInt) // Processing options .option('--fields <fields>', 'Fields to scan (default: prompt,response)') .option('--scan-entire-object', 'Also scan entire object as string') .option('--max-objects <number>', 'Maximum objects to process', parseInt) .option('--max-depth <number>', 'Max nested object depth (default: 4)', parseInt) .option('--schema <schema>', 'JSON schema validation: basic, extended, flexible, or file path') // Performance options .option('--ndjson', 'Force NDJSON mode') .option('--streaming-threshold <number>', 'Threshold for streaming mode (default: 1000)', parseInt) .option('--parallel [workers]', 'Enable parallel processing') .option('--batch-size <number>', 'Batch size for parallel (default: 10)', parseInt) .option('--timeout <seconds>', 'Processing timeout (default: 300)', parseInt) .option('--memory-warning-threshold <number>', 'Memory warning threshold 0-1 (default: 0.8)', parseFloat) // Compression options .option('--compress <type>', 'Output compression: gzip or deflate') .option('--compression-level <level>', 'Compression level 0-9 (default: 6)', parseInt) // Output control .option('-q, --quiet', 'Suppress progress and summary') .option('-v, --verbose', 'Enable verbose output') .option('--debug', 'Enable debug mode') .option('--no-color', 'Disable colored output') .option('--strict', 'Treat warnings as errors') .option('--fail-on <severity>', 'Exit with error on severity: low,medium,high,critical') .action(async (input, options) => { try { // Handle stdin input let actualInput = input; if (input === '-') { // Read from stdin actualInput = await new Promise((resolve, reject) => { let data = ''; process.stdin.setEncoding('utf8'); process.stdin.on('readable', () => { let chunk; while (null !== (chunk = process.stdin.read())) { data += chunk; } }); process.stdin.on('end', () => { resolve(data.trim()); }); process.stdin.on('error', reject); }); } const command = new ScanCommand_1.ScanCommand(actualInput, options); const handler = container.resolve('scanCommandHandler'); const result = await handler.execute(command); if (result.isErr()) { logger.error('Scan failed', result.error); process.exit(1); } } catch (error) { logger.error('Unexpected error', error); console.error('Error:', error.message); process.exit(1); } }); // --- List Command --- program .command('list') .description('List available RulePacks and rules') .option('--rulepack <path>', 'List rules from specific RulePack') .option('--category <category>', 'Filter by category') .option('--severity <severity>', 'Filter by severity') .option('--enabled-only', 'Show only enabled rules') .action(async (options) => { try { const command = new ListCommand_1.ListCommand(options); const handler = container.resolve('listCommandHandler'); const result = await handler.execute(command); if (result.isErr()) { logger.error('List failed', result.error); process.exit(1); } } catch (error) { logger.error('Unexpected error', error); console.error('Error:', error.message); process.exit(1); } }); // --- Init Command --- program .command('init <filename>') .description('Initialize a new RulePack YAML file with templates') .option('-t, --template <template>', 'Template: basic, pii, security, bias, compliance (default: basic)') .option('-d, --description <description>', 'RulePack description') .option('-c, --category <category>', 'RulePack category (default: custom)') .option('--force', 'Overwrite existing file') .option('-v, --verbose', 'Show detailed rule information') .option('-q, --quiet', 'Suppress output messages') .action(async (filename, options) => { try { const command = new InitCommand_1.InitCommand(filename, options); const handler = container.resolve('initCommandHandler'); const result = await handler.execute(command); if (result.isErr()) { logger.error('Init failed', result.error); process.exit(1); } } catch (error) { logger.error('Unexpected error', error); console.error('Error:', error.message); process.exit(1); } }); // --- Validate Command --- program .command('validate <target>') .description('Validate RulePacks and input files') .option('--strict', 'Enable strict validation mode') .option('-v, --verbose', 'Enable verbose output') .option('--skip-warnings', 'Skip warnings in output') .option('--max-errors <number>', 'Maximum errors to report', parseInt) .option('--format <format>', 'Expected file format: json, ndjson, yaml, txt') .option('--output <format>', 'Output format: json, table, summary (default: table)') .option('--batch', 'Enable batch validation mode') .action(async (target, options) => { try { const command = new ValidateCommand_1.ValidateCommand(target, options); const handler = container.resolve('validateCommandHandler'); const result = await handler.execute(command); if (result.isErr()) { logger.error('Validate failed', result.error); process.exit(1); } // Exit with error code if validation failed if (!result.value.isValid) { process.exit(1); } } catch (error) { logger.error('Unexpected error', error); console.error('Error:', error.message); process.exit(1); } }); // Parse command line arguments program.parse(); // Show help if no command provided if (!process.argv.slice(2).length) { program.outputHelp(); }