@dawans/promptshield
Version:
Secure your LLM stack with enterprise-grade RulePacks for AI safety scanning
179 lines (178 loc) • 8.04 kB
JavaScript
;
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();
}