bashbros 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/{chunk-A535VV7N.js → chunk-BW6XCOJH.js} +4 -3
  2. package/dist/chunk-BW6XCOJH.js.map +1 -0
  3. package/dist/chunk-FRMAIRQ2.js +89 -0
  4. package/dist/chunk-FRMAIRQ2.js.map +1 -0
  5. package/dist/{chunk-GD5VNHIN.js → chunk-QWZGB4V3.js} +4 -85
  6. package/dist/chunk-QWZGB4V3.js.map +1 -0
  7. package/dist/{chunk-WPJJZLT6.js → chunk-SQCP6IYB.js} +2 -2
  8. package/dist/{chunk-VVSCAH2B.js → chunk-XCZMQRSX.js} +125 -10
  9. package/dist/chunk-XCZMQRSX.js.map +1 -0
  10. package/dist/chunk-YUMNBQAY.js +766 -0
  11. package/dist/chunk-YUMNBQAY.js.map +1 -0
  12. package/dist/cli.js +577 -30
  13. package/dist/cli.js.map +1 -1
  14. package/dist/{config-43SK6SFI.js → config-JLLOTFLI.js} +2 -2
  15. package/dist/{db-EHQDB5OL.js → db-OBKEXRTP.js} +2 -2
  16. package/dist/{display-HFIFXOOL.js → display-6LZ2HBCU.js} +3 -3
  17. package/dist/engine-EGPAS2EX.js +10 -0
  18. package/dist/index.js +6 -4
  19. package/dist/index.js.map +1 -1
  20. package/dist/session-Y4MICATZ.js +15 -0
  21. package/dist/session-Y4MICATZ.js.map +1 -0
  22. package/dist/static/index.html +1873 -276
  23. package/package.json +1 -1
  24. package/dist/chunk-A535VV7N.js.map +0 -1
  25. package/dist/chunk-CSRPOGHY.js +0 -354
  26. package/dist/chunk-CSRPOGHY.js.map +0 -1
  27. package/dist/chunk-GD5VNHIN.js.map +0 -1
  28. package/dist/chunk-VVSCAH2B.js.map +0 -1
  29. package/dist/engine-PKLXW6OF.js +0 -9
  30. /package/dist/{chunk-WPJJZLT6.js.map → chunk-SQCP6IYB.js.map} +0 -0
  31. /package/dist/{config-43SK6SFI.js.map → config-JLLOTFLI.js.map} +0 -0
  32. /package/dist/{db-EHQDB5OL.js.map → db-OBKEXRTP.js.map} +0 -0
  33. /package/dist/{display-HFIFXOOL.js.map → display-6LZ2HBCU.js.map} +0 -0
  34. /package/dist/{engine-PKLXW6OF.js.map → engine-EGPAS2EX.js.map} +0 -0
@@ -228,7 +228,7 @@ function validateRemotePath(value) {
228
228
  function getDefaultConfig() {
229
229
  return {
230
230
  agent: "claude-code",
231
- profile: "balanced",
231
+ profile: "permissive",
232
232
  commands: getDefaultCommands("balanced"),
233
233
  paths: getDefaultPaths("balanced"),
234
234
  secrets: {
@@ -498,9 +498,10 @@ function getDefaultCommands(profile) {
498
498
  "biome *",
499
499
  "ruff *",
500
500
  "black *",
501
- // AI coding assistants
501
+ // AI coding assistants & security tools
502
502
  "claude *",
503
503
  "aider *",
504
+ "bashbros *",
504
505
  // Editors
505
506
  "code *",
506
507
  "cursor *",
@@ -602,4 +603,4 @@ export {
602
603
  loadConfig,
603
604
  getDefaultConfig
604
605
  };
605
- //# sourceMappingURL=chunk-A535VV7N.js.map
606
+ //# sourceMappingURL=chunk-BW6XCOJH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config.ts"],"sourcesContent":["import { readFileSync, existsSync, statSync } from 'fs'\nimport { parse } from 'yaml'\nimport { join } from 'path'\nimport { homedir } from 'os'\nimport type {\n BashBrosConfig,\n SecurityProfile,\n RiskScoringPolicy,\n LoopDetectionPolicy,\n AnomalyDetectionPolicy,\n OutputScanningPolicy,\n UndoPolicy,\n RiskPattern,\n WardPolicy,\n DashboardPolicy\n} from './types.js'\n\nconst CONFIG_FILENAME = '.bashbros.yml'\n\n// Configuration limits for validation\nconst CONFIG_LIMITS = {\n maxPerMinute: { min: 1, max: 10000 },\n maxPerHour: { min: 1, max: 100000 },\n maxPatterns: 100,\n maxPathLength: 1000\n}\n\nexport function findConfig(): string | null {\n // Check current directory\n if (existsSync(CONFIG_FILENAME)) {\n return CONFIG_FILENAME\n }\n\n // Check home directory\n const homeConfig = join(homedir(), CONFIG_FILENAME)\n if (existsSync(homeConfig)) {\n return homeConfig\n }\n\n // Check ~/.bashbros/config.yml\n const dotConfig = join(homedir(), '.bashbros', 'config.yml')\n if (existsSync(dotConfig)) {\n return dotConfig\n }\n\n return null\n}\n\n/**\n * SECURITY: Validate config file permissions\n */\nfunction validateConfigPermissions(configPath: string): void {\n try {\n const stats = statSync(configPath)\n\n // On Unix, check if file is world-writable (security risk)\n if (process.platform !== 'win32') {\n const mode = stats.mode\n const worldWritable = (mode & 0o002) !== 0\n const groupWritable = (mode & 0o020) !== 0\n\n if (worldWritable || groupWritable) {\n console.warn(`⚠️ Warning: Config file ${configPath} has insecure permissions`)\n console.warn(' Run: chmod 600 ' + configPath)\n }\n }\n } catch {\n // Ignore permission check errors\n }\n}\n\nexport function loadConfig(path?: string): BashBrosConfig {\n const configPath = path || findConfig()\n\n if (!configPath) {\n return getDefaultConfig()\n }\n\n // SECURITY: Check file permissions\n validateConfigPermissions(configPath)\n\n const content = readFileSync(configPath, 'utf-8')\n\n // SECURITY: Use safe YAML parsing (no custom tags)\n let parsed: unknown\n try {\n parsed = parse(content, { strict: true })\n } catch (error) {\n console.error('Failed to parse config file:', error)\n return getDefaultConfig()\n }\n\n // SECURITY: Validate parsed config\n const validated = validateConfig(parsed)\n\n return mergeWithDefaults(validated)\n}\n\n/**\n * SECURITY: Validate and sanitize config values\n */\nfunction validateConfig(parsed: unknown): Partial<BashBrosConfig> {\n if (!parsed || typeof parsed !== 'object') {\n return {}\n }\n\n const config = parsed as Record<string, unknown>\n const validated: Partial<BashBrosConfig> = {}\n\n // Validate agent type\n const validAgents = ['claude-code', 'clawdbot', 'gemini-cli', 'aider', 'opencode', 'custom']\n if (typeof config.agent === 'string' && validAgents.includes(config.agent)) {\n validated.agent = config.agent as BashBrosConfig['agent']\n }\n\n // Validate profile\n const validProfiles = ['balanced', 'strict', 'permissive', 'custom']\n if (typeof config.profile === 'string' && validProfiles.includes(config.profile)) {\n validated.profile = config.profile as SecurityProfile\n }\n\n // Validate commands\n if (config.commands && typeof config.commands === 'object') {\n const cmds = config.commands as Record<string, unknown>\n validated.commands = {\n allow: validateStringArray(cmds.allow, CONFIG_LIMITS.maxPatterns),\n block: validateStringArray(cmds.block, CONFIG_LIMITS.maxPatterns)\n }\n }\n\n // Validate paths\n if (config.paths && typeof config.paths === 'object') {\n const paths = config.paths as Record<string, unknown>\n validated.paths = {\n allow: validatePathArray(paths.allow),\n block: validatePathArray(paths.block)\n }\n }\n\n // Validate secrets\n if (config.secrets && typeof config.secrets === 'object') {\n const secrets = config.secrets as Record<string, unknown>\n validated.secrets = {\n enabled: typeof secrets.enabled === 'boolean' ? secrets.enabled : true,\n mode: secrets.mode === 'audit' ? 'audit' : 'block',\n patterns: validateStringArray(secrets.patterns, CONFIG_LIMITS.maxPatterns)\n }\n }\n\n // Validate audit\n if (config.audit && typeof config.audit === 'object') {\n const audit = config.audit as Record<string, unknown>\n validated.audit = {\n enabled: typeof audit.enabled === 'boolean' ? audit.enabled : true,\n destination: validateAuditDestination(audit.destination),\n remotePath: validateRemotePath(audit.remotePath)\n }\n }\n\n // Validate rate limit\n if (config.rateLimit && typeof config.rateLimit === 'object') {\n const rl = config.rateLimit as Record<string, unknown>\n const maxPerMinute = validateNumber(rl.maxPerMinute, CONFIG_LIMITS.maxPerMinute)\n const maxPerHour = validateNumber(rl.maxPerHour, CONFIG_LIMITS.maxPerHour)\n\n // SECURITY: Ensure hour limit >= minute limit\n validated.rateLimit = {\n enabled: typeof rl.enabled === 'boolean' ? rl.enabled : true,\n maxPerMinute,\n maxPerHour: Math.max(maxPerHour, maxPerMinute)\n }\n }\n\n // Validate risk scoring\n if (config.riskScoring && typeof config.riskScoring === 'object') {\n const rs = config.riskScoring as Record<string, unknown>\n validated.riskScoring = {\n enabled: typeof rs.enabled === 'boolean' ? rs.enabled : true,\n blockThreshold: validateNumber(rs.blockThreshold, { min: 1, max: 10 }),\n warnThreshold: validateNumber(rs.warnThreshold, { min: 1, max: 10 }),\n customPatterns: validateRiskPatterns(rs.customPatterns)\n }\n }\n\n // Validate loop detection\n if (config.loopDetection && typeof config.loopDetection === 'object') {\n const ld = config.loopDetection as Record<string, unknown>\n validated.loopDetection = {\n enabled: typeof ld.enabled === 'boolean' ? ld.enabled : true,\n maxRepeats: validateNumber(ld.maxRepeats, { min: 1, max: 100 }),\n maxTurns: validateNumber(ld.maxTurns, { min: 10, max: 10000 }),\n similarityThreshold: validateNumber(ld.similarityThreshold, { min: 0, max: 1 }) / 1, // Keep as float\n cooldownMs: validateNumber(ld.cooldownMs, { min: 0, max: 60000 }),\n windowSize: validateNumber(ld.windowSize, { min: 5, max: 100 }),\n action: ld.action === 'block' ? 'block' : 'warn'\n }\n }\n\n // Validate anomaly detection\n if (config.anomalyDetection && typeof config.anomalyDetection === 'object') {\n const ad = config.anomalyDetection as Record<string, unknown>\n validated.anomalyDetection = {\n enabled: typeof ad.enabled === 'boolean' ? ad.enabled : true,\n workingHours: validateWorkingHours(ad.workingHours),\n typicalCommandsPerMinute: validateNumber(ad.typicalCommandsPerMinute, { min: 1, max: 1000 }),\n learningCommands: validateNumber(ad.learningCommands, { min: 10, max: 500 }),\n suspiciousPatterns: validateStringArray(ad.suspiciousPatterns, 50),\n action: ad.action === 'block' ? 'block' : 'warn'\n }\n }\n\n // Validate output scanning\n if (config.outputScanning && typeof config.outputScanning === 'object') {\n const os = config.outputScanning as Record<string, unknown>\n validated.outputScanning = {\n enabled: typeof os.enabled === 'boolean' ? os.enabled : true,\n scanForSecrets: typeof os.scanForSecrets === 'boolean' ? os.scanForSecrets : true,\n scanForErrors: typeof os.scanForErrors === 'boolean' ? os.scanForErrors : true,\n maxOutputLength: validateNumber(os.maxOutputLength, { min: 1000, max: 10000000 }),\n redactPatterns: validateStringArray(os.redactPatterns, 50)\n }\n }\n\n // Validate undo\n if (config.undo && typeof config.undo === 'object') {\n const undo = config.undo as Record<string, unknown>\n validated.undo = {\n enabled: typeof undo.enabled === 'boolean' ? undo.enabled : true,\n maxStackSize: validateNumber(undo.maxStackSize, { min: 10, max: 1000 }),\n maxFileSize: validateNumber(undo.maxFileSize, { min: 1024, max: 100 * 1024 * 1024 }),\n ttlMinutes: validateNumber(undo.ttlMinutes, { min: 5, max: 1440 }),\n backupPath: typeof undo.backupPath === 'string' ? undo.backupPath.slice(0, 500) : '~/.bashbros/undo'\n }\n }\n\n return validated\n}\n\nfunction validateRiskPatterns(value: unknown): RiskPattern[] {\n if (!Array.isArray(value)) return []\n\n return value\n .filter((item): item is Record<string, unknown> =>\n item && typeof item === 'object' &&\n typeof item.pattern === 'string' &&\n typeof item.score === 'number' &&\n typeof item.factor === 'string'\n )\n .slice(0, 50)\n .map(item => ({\n pattern: String(item.pattern).slice(0, 500),\n score: Math.max(1, Math.min(10, Math.floor(Number(item.score)))),\n factor: String(item.factor).slice(0, 200)\n }))\n}\n\nfunction validateWorkingHours(value: unknown): [number, number] {\n if (!Array.isArray(value) || value.length !== 2) {\n return [6, 22]\n }\n\n const start = Math.max(0, Math.min(23, Math.floor(Number(value[0]) || 0)))\n const end = Math.max(0, Math.min(24, Math.floor(Number(value[1]) || 24)))\n\n return [start, end]\n}\n\nfunction validateStringArray(value: unknown, maxItems: number): string[] {\n if (!Array.isArray(value)) return []\n\n return value\n .filter((item): item is string => typeof item === 'string')\n .slice(0, maxItems)\n .map(s => s.slice(0, 500)) // Limit string length\n}\n\nfunction validatePathArray(value: unknown): string[] {\n if (!Array.isArray(value)) return []\n\n return value\n .filter((item): item is string => typeof item === 'string')\n .slice(0, CONFIG_LIMITS.maxPatterns)\n .map(s => s.slice(0, CONFIG_LIMITS.maxPathLength))\n .filter(s => !s.includes('\\0')) // Block null bytes\n}\n\nfunction validateNumber(value: unknown, limits: { min: number; max: number }): number {\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n return limits.min\n }\n return Math.max(limits.min, Math.min(limits.max, Math.floor(value)))\n}\n\nfunction validateAuditDestination(value: unknown): 'local' | 'remote' | 'both' {\n if (value === 'remote' || value === 'both') {\n return value\n }\n return 'local'\n}\n\n/**\n * SECURITY: Validate remote audit path (must be HTTPS)\n */\nfunction validateRemotePath(value: unknown): string | undefined {\n if (typeof value !== 'string') {\n return undefined\n }\n\n try {\n const url = new URL(value)\n\n // SECURITY: Only allow HTTPS\n if (url.protocol !== 'https:') {\n console.warn('⚠️ Warning: Remote audit path must use HTTPS. Ignoring:', value)\n return undefined\n }\n\n // Block localhost/private IPs for remote\n const hostname = url.hostname.toLowerCase()\n if (hostname === 'localhost' || hostname === '127.0.0.1' || hostname.startsWith('192.168.') || hostname.startsWith('10.')) {\n // Allow for testing but warn\n console.warn('⚠️ Warning: Remote audit path points to local address')\n }\n\n return value\n } catch {\n console.warn('⚠️ Warning: Invalid remote audit URL:', value)\n return undefined\n }\n}\n\nexport function getDefaultConfig(): BashBrosConfig {\n return {\n agent: 'claude-code',\n profile: 'permissive',\n commands: getDefaultCommands('balanced'),\n paths: getDefaultPaths('balanced'),\n secrets: {\n enabled: true,\n mode: 'block',\n patterns: [\n '.env*',\n '*.pem',\n '*.key',\n '*credentials*',\n '*secret*',\n '.aws/*',\n '.ssh/*'\n ]\n },\n audit: {\n enabled: true,\n destination: 'local'\n },\n rateLimit: {\n enabled: true,\n maxPerMinute: 100,\n maxPerHour: 1000\n },\n riskScoring: getDefaultRiskScoring('balanced'),\n loopDetection: getDefaultLoopDetection('balanced'),\n anomalyDetection: getDefaultAnomalyDetection('balanced'),\n outputScanning: getDefaultOutputScanning('balanced'),\n undo: getDefaultUndo(),\n ward: getDefaultWard(),\n dashboard: getDefaultDashboard()\n }\n}\n\nfunction getDefaultRiskScoring(profile: SecurityProfile): RiskScoringPolicy {\n const thresholds: Record<string, { block: number; warn: number }> = {\n strict: { block: 6, warn: 3 },\n balanced: { block: 9, warn: 6 },\n permissive: { block: 10, warn: 8 }\n }\n const t = thresholds[profile] || thresholds.balanced\n\n return {\n enabled: true,\n blockThreshold: t.block,\n warnThreshold: t.warn,\n customPatterns: []\n }\n}\n\nfunction getDefaultLoopDetection(profile: SecurityProfile): LoopDetectionPolicy {\n const settings: Record<string, { maxRepeats: number; maxTurns: number; action: 'warn' | 'block' }> = {\n strict: { maxRepeats: 2, maxTurns: 50, action: 'block' },\n balanced: { maxRepeats: 3, maxTurns: 100, action: 'warn' },\n permissive: { maxRepeats: 5, maxTurns: 200, action: 'warn' }\n }\n const s = settings[profile] || settings.balanced\n\n return {\n enabled: true,\n maxRepeats: s.maxRepeats,\n maxTurns: s.maxTurns,\n similarityThreshold: 0.85,\n cooldownMs: 1000,\n windowSize: 20,\n action: s.action\n }\n}\n\nfunction getDefaultAnomalyDetection(profile: SecurityProfile): AnomalyDetectionPolicy {\n return {\n enabled: profile !== 'permissive',\n workingHours: [6, 22],\n typicalCommandsPerMinute: 30,\n learningCommands: 50,\n suspiciousPatterns: [],\n action: profile === 'strict' ? 'block' : 'warn'\n }\n}\n\nfunction getDefaultOutputScanning(profile: SecurityProfile): OutputScanningPolicy {\n return {\n enabled: true,\n scanForSecrets: true,\n scanForErrors: true,\n maxOutputLength: 100000,\n redactPatterns: [\n 'password\\\\s*[=:]\\\\s*\\\\S+',\n 'api[_-]?key\\\\s*[=:]\\\\s*\\\\S+',\n 'secret\\\\s*[=:]\\\\s*\\\\S+',\n 'token\\\\s*[=:]\\\\s*\\\\S+',\n 'Bearer\\\\s+[A-Za-z0-9\\\\-._~+/]+=*',\n 'sk-[A-Za-z0-9]{20,}',\n 'ghp_[A-Za-z0-9]{36}',\n 'glpat-[A-Za-z0-9\\\\-]{20,}'\n ]\n }\n}\n\nfunction getDefaultUndo(): UndoPolicy {\n return {\n enabled: true,\n maxStackSize: 100,\n maxFileSize: 10 * 1024 * 1024, // 10MB\n ttlMinutes: 60, // 1 hour\n backupPath: '~/.bashbros/undo'\n }\n}\n\nfunction getDefaultWard(): WardPolicy {\n return {\n enabled: true,\n exposure: {\n scanInterval: 30000, // 30 seconds\n externalProbe: false,\n severityActions: {\n low: 'alert',\n medium: 'alert',\n high: 'block',\n critical: 'block_and_kill'\n }\n },\n connectors: {\n proxyAllMcp: false,\n telemetryRetention: '7d'\n },\n egress: {\n defaultAction: 'block'\n }\n }\n}\n\nfunction getDefaultDashboard(): DashboardPolicy {\n return {\n enabled: true,\n port: 7890,\n bind: '127.0.0.1'\n }\n}\n\nfunction getDefaultCommands(profile: SecurityProfile) {\n const dangerousCommands = [\n // Destructive rm patterns (various flag orders)\n 'rm * /',\n 'rm * ~',\n 'rm * /*',\n 'rm * /home*',\n 'rm * /etc*',\n 'rm * /usr*',\n 'rm * /var*',\n 'rm * /bin*',\n 'rm * /sbin*',\n 'rm * /lib*',\n 'rm * /boot*',\n 'rm * /opt*',\n 'rm * /root*',\n 'rm * /srv*',\n 'rm * /mnt*',\n 'rm * /media*',\n // Windows destructive patterns\n 'rm * C:\\\\*',\n 'rm * C:/*',\n 'Remove-Item * C:\\\\*',\n 'Remove-Item * C:/*',\n 'rd /s *',\n 'rmdir /s *',\n // Fork bomb\n ':(){:|:&};:',\n // Disk destruction\n 'mkfs*',\n 'dd if=/dev/zero*',\n 'dd of=/dev/*',\n '> /dev/sda*',\n '> /dev/nvme*',\n '> /dev/hd*',\n // Dangerous permission changes\n 'chmod -R 777 /*',\n 'chmod -R 777 /',\n 'chmod * 777 /*',\n 'chown -R * /*',\n // Pipe to shell (code execution)\n 'curl * | bash*',\n 'curl * | sh*',\n 'wget * | bash*',\n 'wget * | sh*',\n 'curl * | sudo*',\n 'wget * | sudo*',\n // History/log destruction\n 'history -c*',\n 'shred *',\n // Network attacks\n ':(){ :|:& };:',\n // Dangerous redirects\n '> /etc/passwd*',\n '> /etc/shadow*'\n ]\n\n const commonAllowed = [\n // File operations\n 'ls *', 'dir *', 'cat *', 'head *', 'tail *', 'less *', 'more *',\n 'grep *', 'find *', 'rg *', 'fd *',\n 'mkdir *', 'touch *', 'cp *', 'mv *', 'rm *',\n 'cd *', 'pwd', 'echo *', 'printf *', 'which *', 'where *', 'type *',\n 'tar *', 'zip *', 'unzip *', 'gzip *', 'gunzip *',\n\n // Text processing\n 'sed *', 'awk *', 'sort *', 'uniq *', 'wc *', 'diff *', 'tr *',\n\n // Version control\n 'git *', 'gh *',\n\n // Package managers & runtimes\n 'npm *', 'npx *', 'pnpm *', 'yarn *', 'bun *',\n 'node *', 'deno *', 'tsx *', 'ts-node *',\n 'python *', 'python3 *', 'pip *', 'pip3 *', 'uv *', 'pipx *',\n 'cargo *', 'rustc *', 'rustup *',\n 'go *',\n\n // Build tools\n 'tsc *', 'esbuild *', 'vite *', 'webpack *', 'rollup *', 'tsup *',\n 'make *', 'cmake *',\n\n // Testing & linting\n 'jest *', 'vitest *', 'pytest *', 'mocha *',\n 'eslint *', 'prettier *', 'biome *', 'ruff *', 'black *',\n\n // AI coding assistants & security tools\n 'claude *', 'aider *', 'bashbros *',\n\n // Editors\n 'code *', 'cursor *', 'vim *', 'nvim *', 'nano *', 'emacs *',\n\n // Docker & containers\n 'docker *', 'docker-compose *', 'podman *',\n\n // Network (safe operations)\n 'curl *', 'wget *', 'ping *', 'ssh *',\n\n // System info\n 'env', 'env *', 'printenv *', 'whoami', 'hostname', 'uname *', 'date', 'uptime',\n 'ps *', 'top', 'htop', 'btop',\n\n // Shell basics\n 'clear', 'cls', 'history', 'alias *', 'export *', 'source *', 'exit',\n 'true', 'false', 'test *', 'man *', 'help *',\n\n // PowerShell (Windows)\n 'Get-*', 'Set-*', 'New-*', 'Remove-*', 'Select-*', 'Where-*', 'ForEach-*'\n ]\n\n if (profile === 'strict') {\n return { allow: [], block: dangerousCommands }\n }\n\n if (profile === 'permissive') {\n return { allow: ['*'], block: dangerousCommands }\n }\n\n // balanced\n return { allow: commonAllowed, block: dangerousCommands }\n}\n\nfunction getDefaultPaths(profile: SecurityProfile) {\n const dangerousPaths = [\n '~/.ssh',\n '~/.aws',\n '~/.gnupg',\n '~/.config/gh',\n '/etc/passwd',\n '/etc/shadow'\n ]\n\n if (profile === 'strict') {\n return { allow: ['.'], block: dangerousPaths }\n }\n\n if (profile === 'permissive') {\n return { allow: ['*'], block: dangerousPaths }\n }\n\n // balanced\n return { allow: ['.', '~'], block: dangerousPaths }\n}\n\nfunction mergeWithDefaults(parsed: Partial<BashBrosConfig>): BashBrosConfig {\n const defaults = getDefaultConfig()\n return {\n ...defaults,\n ...parsed,\n commands: { ...defaults.commands, ...parsed.commands },\n paths: { ...defaults.paths, ...parsed.paths },\n secrets: { ...defaults.secrets, ...parsed.secrets },\n audit: { ...defaults.audit, ...parsed.audit },\n rateLimit: { ...defaults.rateLimit, ...parsed.rateLimit },\n riskScoring: { ...defaults.riskScoring, ...parsed.riskScoring },\n loopDetection: { ...defaults.loopDetection, ...parsed.loopDetection },\n anomalyDetection: { ...defaults.anomalyDetection, ...parsed.anomalyDetection },\n outputScanning: { ...defaults.outputScanning, ...parsed.outputScanning },\n undo: { ...defaults.undo, ...parsed.undo },\n ward: { ...defaults.ward, ...parsed.ward },\n dashboard: { ...defaults.dashboard, ...parsed.dashboard }\n }\n}\n\nexport { BashBrosConfig }\n"],"mappings":"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","names":[]}
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/session.ts
4
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
5
+ import { join } from "path";
6
+ import { homedir } from "os";
7
+ var SESSION_FILE = join(homedir(), ".bashbros", "session-allow.json");
8
+ function ensureDir() {
9
+ const dir = join(homedir(), ".bashbros");
10
+ if (!existsSync(dir)) {
11
+ mkdirSync(dir, { recursive: true, mode: 448 });
12
+ }
13
+ }
14
+ function loadSession() {
15
+ try {
16
+ if (!existsSync(SESSION_FILE)) {
17
+ return null;
18
+ }
19
+ const data = JSON.parse(readFileSync(SESSION_FILE, "utf-8"));
20
+ if (data.pid !== process.pid) {
21
+ const age = Date.now() - data.startTime;
22
+ if (age > 24 * 60 * 60 * 1e3) {
23
+ return null;
24
+ }
25
+ }
26
+ return data;
27
+ } catch {
28
+ return null;
29
+ }
30
+ }
31
+ function saveSession(data) {
32
+ ensureDir();
33
+ writeFileSync(SESSION_FILE, JSON.stringify(data, null, 2), { mode: 384 });
34
+ }
35
+ function getOrCreateSession() {
36
+ const existing = loadSession();
37
+ if (existing) {
38
+ return existing;
39
+ }
40
+ const newSession = {
41
+ pid: process.pid,
42
+ startTime: Date.now(),
43
+ allowedCommands: []
44
+ };
45
+ saveSession(newSession);
46
+ return newSession;
47
+ }
48
+ function allowForSession(command) {
49
+ const session = getOrCreateSession();
50
+ if (!session.allowedCommands.includes(command)) {
51
+ session.allowedCommands.push(command);
52
+ saveSession(session);
53
+ }
54
+ }
55
+ function isAllowedForSession(command) {
56
+ const session = loadSession();
57
+ if (!session) {
58
+ return false;
59
+ }
60
+ if (session.allowedCommands.includes(command)) {
61
+ return true;
62
+ }
63
+ for (const allowed of session.allowedCommands) {
64
+ if (allowed.endsWith("*")) {
65
+ const prefix = allowed.slice(0, -1);
66
+ if (command.startsWith(prefix)) {
67
+ return true;
68
+ }
69
+ }
70
+ }
71
+ return false;
72
+ }
73
+ function getSessionAllowlist() {
74
+ const session = loadSession();
75
+ return session?.allowedCommands || [];
76
+ }
77
+ function clearSessionAllowlist() {
78
+ const session = getOrCreateSession();
79
+ session.allowedCommands = [];
80
+ saveSession(session);
81
+ }
82
+
83
+ export {
84
+ allowForSession,
85
+ isAllowedForSession,
86
+ getSessionAllowlist,
87
+ clearSessionAllowlist
88
+ };
89
+ //# sourceMappingURL=chunk-FRMAIRQ2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/session.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs'\r\nimport { join } from 'path'\r\nimport { homedir } from 'os'\r\n\r\n/**\r\n * Session-based allowlist for temporary command permissions.\r\n * Stored in a temp file that gets cleared on restart.\r\n */\r\n\r\nconst SESSION_FILE = join(homedir(), '.bashbros', 'session-allow.json')\r\n\r\ninterface SessionData {\r\n pid: number\r\n startTime: number\r\n allowedCommands: string[]\r\n}\r\n\r\nfunction ensureDir(): void {\r\n const dir = join(homedir(), '.bashbros')\r\n if (!existsSync(dir)) {\r\n mkdirSync(dir, { recursive: true, mode: 0o700 })\r\n }\r\n}\r\n\r\nfunction loadSession(): SessionData | null {\r\n try {\r\n if (!existsSync(SESSION_FILE)) {\r\n return null\r\n }\r\n\r\n const data = JSON.parse(readFileSync(SESSION_FILE, 'utf-8'))\r\n\r\n // Check if session is from current process\r\n if (data.pid !== process.pid) {\r\n // Different process - check if it's stale (older than 24 hours)\r\n const age = Date.now() - data.startTime\r\n if (age > 24 * 60 * 60 * 1000) {\r\n return null\r\n }\r\n }\r\n\r\n return data\r\n } catch {\r\n return null\r\n }\r\n}\r\n\r\nfunction saveSession(data: SessionData): void {\r\n ensureDir()\r\n writeFileSync(SESSION_FILE, JSON.stringify(data, null, 2), { mode: 0o600 })\r\n}\r\n\r\nfunction getOrCreateSession(): SessionData {\r\n const existing = loadSession()\r\n\r\n if (existing) {\r\n return existing\r\n }\r\n\r\n const newSession: SessionData = {\r\n pid: process.pid,\r\n startTime: Date.now(),\r\n allowedCommands: []\r\n }\r\n\r\n saveSession(newSession)\r\n return newSession\r\n}\r\n\r\n/**\r\n * Add a command to the session allowlist\r\n */\r\nexport function allowForSession(command: string): void {\r\n const session = getOrCreateSession()\r\n\r\n if (!session.allowedCommands.includes(command)) {\r\n session.allowedCommands.push(command)\r\n saveSession(session)\r\n }\r\n}\r\n\r\n/**\r\n * Check if a command is allowed for this session\r\n */\r\nexport function isAllowedForSession(command: string): boolean {\r\n const session = loadSession()\r\n\r\n if (!session) {\r\n return false\r\n }\r\n\r\n // Check exact match\r\n if (session.allowedCommands.includes(command)) {\r\n return true\r\n }\r\n\r\n // Check pattern match (command starts with allowed pattern)\r\n for (const allowed of session.allowedCommands) {\r\n if (allowed.endsWith('*')) {\r\n const prefix = allowed.slice(0, -1)\r\n if (command.startsWith(prefix)) {\r\n return true\r\n }\r\n }\r\n }\r\n\r\n return false\r\n}\r\n\r\n/**\r\n * Get all commands allowed for this session\r\n */\r\nexport function getSessionAllowlist(): string[] {\r\n const session = loadSession()\r\n return session?.allowedCommands || []\r\n}\r\n\r\n/**\r\n * Clear the session allowlist\r\n */\r\nexport function clearSessionAllowlist(): void {\r\n const session = getOrCreateSession()\r\n session.allowedCommands = []\r\n saveSession(session)\r\n}\r\n"],"mappings":";;;AAAA,SAAS,YAAY,cAAc,eAAe,iBAAiB;AACnE,SAAS,YAAY;AACrB,SAAS,eAAe;AAOxB,IAAM,eAAe,KAAK,QAAQ,GAAG,aAAa,oBAAoB;AAQtE,SAAS,YAAkB;AACzB,QAAM,MAAM,KAAK,QAAQ,GAAG,WAAW;AACvC,MAAI,CAAC,WAAW,GAAG,GAAG;AACpB,cAAU,KAAK,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAAA,EACjD;AACF;AAEA,SAAS,cAAkC;AACzC,MAAI;AACF,QAAI,CAAC,WAAW,YAAY,GAAG;AAC7B,aAAO;AAAA,IACT;AAEA,UAAM,OAAO,KAAK,MAAM,aAAa,cAAc,OAAO,CAAC;AAG3D,QAAI,KAAK,QAAQ,QAAQ,KAAK;AAE5B,YAAM,MAAM,KAAK,IAAI,IAAI,KAAK;AAC9B,UAAI,MAAM,KAAK,KAAK,KAAK,KAAM;AAC7B,eAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,YAAY,MAAyB;AAC5C,YAAU;AACV,gBAAc,cAAc,KAAK,UAAU,MAAM,MAAM,CAAC,GAAG,EAAE,MAAM,IAAM,CAAC;AAC5E;AAEA,SAAS,qBAAkC;AACzC,QAAM,WAAW,YAAY;AAE7B,MAAI,UAAU;AACZ,WAAO;AAAA,EACT;AAEA,QAAM,aAA0B;AAAA,IAC9B,KAAK,QAAQ;AAAA,IACb,WAAW,KAAK,IAAI;AAAA,IACpB,iBAAiB,CAAC;AAAA,EACpB;AAEA,cAAY,UAAU;AACtB,SAAO;AACT;AAKO,SAAS,gBAAgB,SAAuB;AACrD,QAAM,UAAU,mBAAmB;AAEnC,MAAI,CAAC,QAAQ,gBAAgB,SAAS,OAAO,GAAG;AAC9C,YAAQ,gBAAgB,KAAK,OAAO;AACpC,gBAAY,OAAO;AAAA,EACrB;AACF;AAKO,SAAS,oBAAoB,SAA0B;AAC5D,QAAM,UAAU,YAAY;AAE5B,MAAI,CAAC,SAAS;AACZ,WAAO;AAAA,EACT;AAGA,MAAI,QAAQ,gBAAgB,SAAS,OAAO,GAAG;AAC7C,WAAO;AAAA,EACT;AAGA,aAAW,WAAW,QAAQ,iBAAiB;AAC7C,QAAI,QAAQ,SAAS,GAAG,GAAG;AACzB,YAAM,SAAS,QAAQ,MAAM,GAAG,EAAE;AAClC,UAAI,QAAQ,WAAW,MAAM,GAAG;AAC9B,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAKO,SAAS,sBAAgC;AAC9C,QAAM,UAAU,YAAY;AAC5B,SAAO,SAAS,mBAAmB,CAAC;AACtC;AAKO,SAAS,wBAA8B;AAC5C,QAAM,UAAU,mBAAmB;AACnC,UAAQ,kBAAkB,CAAC;AAC3B,cAAY,OAAO;AACrB;","names":[]}
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ isAllowedForSession
4
+ } from "./chunk-FRMAIRQ2.js";
2
5
 
3
6
  // src/policy/command-filter.ts
4
7
  var CommandFilter = class {
@@ -356,86 +359,6 @@ var RateLimiter = class {
356
359
  }
357
360
  };
358
361
 
359
- // src/session.ts
360
- import { existsSync as existsSync2, readFileSync, writeFileSync, mkdirSync } from "fs";
361
- import { join } from "path";
362
- import { homedir as homedir2 } from "os";
363
- var SESSION_FILE = join(homedir2(), ".bashbros", "session-allow.json");
364
- function ensureDir() {
365
- const dir = join(homedir2(), ".bashbros");
366
- if (!existsSync2(dir)) {
367
- mkdirSync(dir, { recursive: true, mode: 448 });
368
- }
369
- }
370
- function loadSession() {
371
- try {
372
- if (!existsSync2(SESSION_FILE)) {
373
- return null;
374
- }
375
- const data = JSON.parse(readFileSync(SESSION_FILE, "utf-8"));
376
- if (data.pid !== process.pid) {
377
- const age = Date.now() - data.startTime;
378
- if (age > 24 * 60 * 60 * 1e3) {
379
- return null;
380
- }
381
- }
382
- return data;
383
- } catch {
384
- return null;
385
- }
386
- }
387
- function saveSession(data) {
388
- ensureDir();
389
- writeFileSync(SESSION_FILE, JSON.stringify(data, null, 2), { mode: 384 });
390
- }
391
- function getOrCreateSession() {
392
- const existing = loadSession();
393
- if (existing) {
394
- return existing;
395
- }
396
- const newSession = {
397
- pid: process.pid,
398
- startTime: Date.now(),
399
- allowedCommands: []
400
- };
401
- saveSession(newSession);
402
- return newSession;
403
- }
404
- function allowForSession(command) {
405
- const session = getOrCreateSession();
406
- if (!session.allowedCommands.includes(command)) {
407
- session.allowedCommands.push(command);
408
- saveSession(session);
409
- }
410
- }
411
- function isAllowedForSession(command) {
412
- const session = loadSession();
413
- if (!session) {
414
- return false;
415
- }
416
- if (session.allowedCommands.includes(command)) {
417
- return true;
418
- }
419
- for (const allowed of session.allowedCommands) {
420
- if (allowed.endsWith("*")) {
421
- const prefix = allowed.slice(0, -1);
422
- if (command.startsWith(prefix)) {
423
- return true;
424
- }
425
- }
426
- }
427
- return false;
428
- }
429
- function getSessionAllowlist() {
430
- const session = loadSession();
431
- return session?.allowedCommands || [];
432
- }
433
- function clearSessionAllowlist() {
434
- const session = getOrCreateSession();
435
- session.allowedCommands = [];
436
- saveSession(session);
437
- }
438
-
439
362
  // src/policy/engine.ts
440
363
  var PolicyEngine = class {
441
364
  constructor(config) {
@@ -510,10 +433,6 @@ export {
510
433
  PathSandbox,
511
434
  SecretsGuard,
512
435
  RateLimiter,
513
- allowForSession,
514
- isAllowedForSession,
515
- getSessionAllowlist,
516
- clearSessionAllowlist,
517
436
  PolicyEngine
518
437
  };
519
- //# sourceMappingURL=chunk-GD5VNHIN.js.map
438
+ //# sourceMappingURL=chunk-QWZGB4V3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/policy/command-filter.ts","../src/policy/path-sandbox.ts","../src/policy/secrets-guard.ts","../src/policy/rate-limiter.ts","../src/policy/engine.ts"],"sourcesContent":["import type { CommandPolicy, PolicyViolation } from '../types.js'\r\n\r\nexport class CommandFilter {\r\n private allowPatterns: RegExp[]\r\n private blockPatterns: RegExp[]\r\n\r\n constructor(private policy: CommandPolicy) {\r\n this.allowPatterns = policy.allow.map(p => this.globToRegex(p))\r\n this.blockPatterns = policy.block.map(p => this.globToRegex(p))\r\n }\r\n\r\n check(command: string): PolicyViolation | null {\r\n // Check block list first (higher priority)\r\n for (let i = 0; i < this.blockPatterns.length; i++) {\r\n if (this.blockPatterns[i].test(command)) {\r\n return {\r\n type: 'command',\r\n rule: `block[${i}]: ${this.policy.block[i]}`,\r\n message: `Command matches blocked pattern: ${this.policy.block[i]}`\r\n }\r\n }\r\n }\r\n\r\n // If allow list is empty or contains '*', allow by default\r\n if (this.policy.allow.length === 0 || this.policy.allow.includes('*')) {\r\n return null\r\n }\r\n\r\n // Check if command matches any allow pattern\r\n const allowed = this.allowPatterns.some(pattern => pattern.test(command))\r\n\r\n if (!allowed) {\r\n return {\r\n type: 'command',\r\n rule: 'allow (no match)',\r\n message: 'Command not in allowlist'\r\n }\r\n }\r\n\r\n return null\r\n }\r\n\r\n private globToRegex(glob: string): RegExp {\r\n // Escape special regex chars except *\r\n const escaped = glob\r\n .replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&')\r\n .replace(/\\*/g, '.*')\r\n\r\n return new RegExp(`^${escaped}$`, 'i')\r\n }\r\n}\r\n","import { resolve } from 'path'\nimport { homedir } from 'os'\nimport { realpathSync, lstatSync, existsSync } from 'fs'\nimport type { PathPolicy, PolicyViolation } from '../types.js'\n\nexport class PathSandbox {\n private allowedPaths: string[]\n private blockedPaths: string[]\n\n constructor(private policy: PathPolicy) {\n this.allowedPaths = policy.allow.map(p => this.normalizePath(p))\n this.blockedPaths = policy.block.map(p => this.normalizePath(p))\n }\n\n check(path: string): PolicyViolation | null {\n // SECURITY: Resolve symlinks to get real path\n const { realPath, isSymlink } = this.resolvePath(path)\n\n // Check for symlink attacks\n if (isSymlink) {\n const originalNormalized = this.normalizePath(path)\n // If symlink points outside of where it appears to be, block it\n if (!realPath.startsWith(originalNormalized.split('/')[0])) {\n return {\n type: 'path',\n rule: 'symlink_escape',\n message: `Symlink escape detected: ${path} -> ${realPath}`\n }\n }\n }\n\n // Check block list first (use real path)\n for (const blocked of this.blockedPaths) {\n if (realPath.startsWith(blocked) || realPath === blocked) {\n return {\n type: 'path',\n rule: `block: ${blocked}`,\n message: `Access to path is blocked: ${path}`\n }\n }\n }\n\n // If allow list contains '*', allow anything not blocked\n if (this.policy.allow.includes('*')) {\n return null\n }\n\n // Check if real path is within allowed directories\n const allowed = this.allowedPaths.some(\n allowedPath =>\n realPath.startsWith(allowedPath) || realPath === allowedPath\n )\n\n if (!allowed) {\n return {\n type: 'path',\n rule: 'allow (outside sandbox)',\n message: `Path is outside allowed directories: ${path}`\n }\n }\n\n return null\n }\n\n /**\n * SECURITY FIX: Resolve symlinks to detect escape attempts\n */\n private resolvePath(path: string): { realPath: string; isSymlink: boolean } {\n const normalizedPath = this.normalizePath(path)\n\n try {\n // Check if path exists and is a symlink\n if (existsSync(normalizedPath)) {\n const stats = lstatSync(normalizedPath)\n const isSymlink = stats.isSymbolicLink()\n\n // Get real path (follows symlinks)\n const realPath = realpathSync(normalizedPath)\n\n return { realPath, isSymlink }\n }\n } catch {\n // Path doesn't exist yet or can't be accessed\n }\n\n return { realPath: normalizedPath, isSymlink: false }\n }\n\n private normalizePath(path: string): string {\n // Expand ~ to home directory\n if (path.startsWith('~')) {\n path = path.replace('~', homedir())\n }\n\n // Handle . as current directory\n if (path === '.') {\n return process.cwd()\n }\n\n return resolve(path)\n }\n\n /**\n * Check if a path would escape the sandbox via symlink\n */\n isSymlinkEscape(path: string): boolean {\n const { realPath, isSymlink } = this.resolvePath(path)\n\n if (!isSymlink) return false\n\n // Check if real path is in blocked list\n for (const blocked of this.blockedPaths) {\n if (realPath.startsWith(blocked)) {\n return true\n }\n }\n\n // Check if real path escapes allowed directories\n if (!this.policy.allow.includes('*')) {\n const inAllowed = this.allowedPaths.some(\n allowedPath => realPath.startsWith(allowedPath)\n )\n if (!inAllowed) {\n return true\n }\n }\n\n return false\n }\n}\n","import type { SecretsPolicy, PolicyViolation } from '../types.js'\n\nexport class SecretsGuard {\n private patterns: RegExp[]\n\n constructor(private policy: SecretsPolicy) {\n this.patterns = policy.patterns.map(p => this.globToRegex(p))\n }\n\n check(command: string, paths: string[]): PolicyViolation | null {\n if (!this.policy.enabled) {\n return null\n }\n\n // Check command for secret file access\n for (const path of paths) {\n if (this.isSecretPath(path)) {\n return {\n type: 'secrets',\n rule: `pattern match: ${path}`,\n message: `Attempted access to sensitive file: ${path}`\n }\n }\n }\n\n // Check for common secret-leaking patterns in commands\n // SECURITY FIX: Enhanced patterns to catch bypass attempts\n const dangerousPatterns = [\n // Direct file access (multiple readers)\n /(cat|head|tail|less|more|bat)\\s+.*\\.env/i,\n /(cat|head|tail|less|more|bat)\\s+.*\\.pem/i,\n /(cat|head|tail|less|more|bat)\\s+.*\\.key/i,\n /(cat|head|tail|less|more|bat)\\s+.*credentials/i,\n /(cat|head|tail|less|more|bat)\\s+.*secret/i,\n /(cat|head|tail|less|more|bat)\\s+.*password/i,\n /(cat|head|tail|less|more|bat)\\s+.*token/i,\n\n // Python/Perl/Ruby file readers\n /python.*open\\s*\\(.*\\.(env|pem|key)/i,\n /python.*-c.*open/i,\n /perl.*-[pne].*\\.(env|pem|key)/i,\n /ruby.*-e.*File\\.(read|open)/i,\n\n // Environment variable exposure\n /echo\\s+\\$\\w*(KEY|SECRET|TOKEN|PASSWORD|CREDENTIAL|API)/i,\n /printenv.*(KEY|SECRET|TOKEN|PASSWORD)/i,\n /env\\s*\\|\\s*grep.*(KEY|SECRET|TOKEN|PASSWORD)/i,\n\n // Curl/wget with secrets\n /curl.*-d.*\\$\\w*(KEY|SECRET|TOKEN)/i,\n /curl.*-H.*Authorization/i,\n /wget.*--header.*Authorization/i,\n\n // Base64 encoding (obfuscation attempt)\n /base64.*\\.env/i,\n /base64.*\\.pem/i,\n /base64.*\\.key/i,\n /base64\\s+-d/i, // Decoding could reveal secrets\n\n // SECURITY FIX: Command substitution bypass attempts\n /cat\\s+\\$\\(/i, // cat $(...)\n /cat\\s+`/i, // cat `...`\n /cat\\s+\\$\\{/i, // cat ${...}\n\n // SECURITY FIX: Variable indirection\n /\\w+=.*\\.env.*;\\s*cat\\s+\\$/i, // VAR=.env; cat $VAR\n /\\w+=.*secret.*;\\s*cat\\s+\\$/i,\n\n // SECURITY FIX: Glob expansion bypass\n /cat\\s+\\*env/i, // cat *env\n /cat\\s+\\.\\*env/i, // cat .*env\n /cat\\s+\\?\\?env/i, // cat ??env\n\n // SECURITY FIX: Printf/echo tricks\n /printf\\s+.*\\\\x/i, // Hex encoding\n /echo\\s+-e.*\\\\x/i, // Echo with hex\n /echo\\s+-e.*\\\\[0-7]/i, // Octal encoding\n\n // SECURITY FIX: Here-doc/here-string\n /cat\\s*<<.*\\.env/i,\n /cat\\s*<<<.*secret/i,\n\n // Process substitution\n /cat\\s+<\\(/i, // cat <(...)\n\n // History/log access\n /cat.*\\.bash_history/i,\n /cat.*\\.zsh_history/i,\n /cat.*history/i,\n\n // AWS/cloud credentials\n /cat.*\\.aws\\/credentials/i,\n /cat.*\\.aws\\/config/i,\n /cat.*\\.kube\\/config/i,\n /cat.*\\.docker\\/config/i,\n\n // SSH keys\n /cat.*id_rsa/i,\n /cat.*id_ed25519/i,\n /cat.*id_ecdsa/i,\n /cat.*known_hosts/i,\n /cat.*authorized_keys/i,\n\n // GPG\n /cat.*\\.gnupg/i,\n /gpg.*--export-secret/i,\n\n // Git credentials\n /cat.*\\.git-credentials/i,\n /cat.*\\.netrc/i,\n\n // Database files\n /cat.*\\.pgpass/i,\n /cat.*\\.my\\.cnf/i,\n ]\n\n for (const pattern of dangerousPatterns) {\n if (pattern.test(command)) {\n return {\n type: 'secrets',\n rule: 'dangerous pattern',\n message: 'Command may expose secrets'\n }\n }\n }\n\n // SECURITY FIX: Check for encoded commands\n if (this.containsEncodedSecretAccess(command)) {\n return {\n type: 'secrets',\n rule: 'encoded command',\n message: 'Command contains encoded secret access attempt'\n }\n }\n\n return null\n }\n\n /**\n * SECURITY FIX: Detect base64/hex encoded secret access\n */\n private containsEncodedSecretAccess(command: string): boolean {\n // Check for base64 encoded sensitive paths\n const sensitiveBase64 = [\n 'LmVudg==', // .env\n 'LnBlbQ==', // .pem\n 'LmtleQ==', // .key\n 'aWRfcnNh', // id_rsa\n 'Y3JlZGVudGlhbHM=', // credentials\n 'c2VjcmV0', // secret\n ]\n\n for (const encoded of sensitiveBase64) {\n if (command.includes(encoded)) {\n return true\n }\n }\n\n // Check for hex encoded paths\n const sensitiveHex = [\n '2e656e76', // .env\n '2e70656d', // .pem\n '2e6b6579', // .key\n '69645f727361', // id_rsa\n ]\n\n for (const hex of sensitiveHex) {\n if (command.toLowerCase().includes(hex)) {\n return true\n }\n }\n\n return false\n }\n\n private isSecretPath(path: string): boolean {\n const lowerPath = path.toLowerCase()\n\n return this.patterns.some(pattern => pattern.test(lowerPath))\n }\n\n private globToRegex(glob: string): RegExp {\n const escaped = glob\n .replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/\\*/g, '.*')\n\n return new RegExp(escaped, 'i')\n }\n}\n","import type { RateLimitPolicy, PolicyViolation } from '../types.js'\r\n\r\nexport class RateLimiter {\r\n private minuteWindow: number[] = []\r\n private hourWindow: number[] = []\r\n\r\n constructor(private policy: RateLimitPolicy) {}\r\n\r\n check(): PolicyViolation | null {\r\n if (!this.policy.enabled) {\r\n return null\r\n }\r\n\r\n const now = Date.now()\r\n this.cleanup(now)\r\n\r\n // Check per-minute limit\r\n if (this.minuteWindow.length >= this.policy.maxPerMinute) {\r\n return {\r\n type: 'rate_limit',\r\n rule: `maxPerMinute: ${this.policy.maxPerMinute}`,\r\n message: `Rate limit exceeded: ${this.minuteWindow.length}/${this.policy.maxPerMinute} commands per minute`\r\n }\r\n }\r\n\r\n // Check per-hour limit\r\n if (this.hourWindow.length >= this.policy.maxPerHour) {\r\n return {\r\n type: 'rate_limit',\r\n rule: `maxPerHour: ${this.policy.maxPerHour}`,\r\n message: `Rate limit exceeded: ${this.hourWindow.length}/${this.policy.maxPerHour} commands per hour`\r\n }\r\n }\r\n\r\n return null\r\n }\r\n\r\n record(): void {\r\n const now = Date.now()\r\n this.minuteWindow.push(now)\r\n this.hourWindow.push(now)\r\n }\r\n\r\n private cleanup(now: number): void {\r\n const oneMinuteAgo = now - 60 * 1000\r\n const oneHourAgo = now - 60 * 60 * 1000\r\n\r\n this.minuteWindow = this.minuteWindow.filter(t => t > oneMinuteAgo)\r\n this.hourWindow = this.hourWindow.filter(t => t > oneHourAgo)\r\n }\r\n\r\n getStats(): { minute: number; hour: number } {\r\n const now = Date.now()\r\n this.cleanup(now)\r\n\r\n return {\r\n minute: this.minuteWindow.length,\r\n hour: this.hourWindow.length\r\n }\r\n }\r\n}\r\n","import type { BashBrosConfig, PolicyViolation } from '../types.js'\r\nimport { CommandFilter } from './command-filter.js'\r\nimport { PathSandbox } from './path-sandbox.js'\r\nimport { SecretsGuard } from './secrets-guard.js'\r\nimport { RateLimiter } from './rate-limiter.js'\r\nimport { isAllowedForSession } from '../session.js'\r\n\r\nexport class PolicyEngine {\r\n private commandFilter: CommandFilter\r\n private pathSandbox: PathSandbox\r\n private secretsGuard: SecretsGuard\r\n private rateLimiter: RateLimiter\r\n\r\n constructor(private config: BashBrosConfig) {\r\n this.commandFilter = new CommandFilter(config.commands)\r\n this.pathSandbox = new PathSandbox(config.paths)\r\n this.secretsGuard = new SecretsGuard(config.secrets)\r\n this.rateLimiter = new RateLimiter(config.rateLimit)\r\n }\r\n\r\n validate(command: string): PolicyViolation[] {\r\n const violations: PolicyViolation[] = []\r\n\r\n // Check rate limit first\r\n const rateViolation = this.rateLimiter.check()\r\n if (rateViolation) {\r\n violations.push(rateViolation)\r\n return violations // Early exit on rate limit\r\n }\r\n\r\n // Check session allowlist first (temporary permissions)\r\n if (isAllowedForSession(command)) {\r\n this.rateLimiter.record()\r\n return [] // Allowed for this session\r\n }\r\n\r\n // Check command against allow/block lists\r\n const commandViolation = this.commandFilter.check(command)\r\n if (commandViolation) {\r\n violations.push(commandViolation)\r\n }\r\n\r\n // Extract paths from command and check sandbox\r\n const paths = this.extractPaths(command)\r\n for (const path of paths) {\r\n const pathViolation = this.pathSandbox.check(path)\r\n if (pathViolation) {\r\n violations.push(pathViolation)\r\n }\r\n }\r\n\r\n // Check for secrets access\r\n if (this.config.secrets.enabled) {\r\n const secretsViolation = this.secretsGuard.check(command, paths)\r\n if (secretsViolation) {\r\n violations.push(secretsViolation)\r\n }\r\n }\r\n\r\n // Record for rate limiting\r\n this.rateLimiter.record()\r\n\r\n return violations\r\n }\r\n\r\n private extractPaths(command: string): string[] {\r\n const paths: string[] = []\r\n\r\n // Remove quotes for analysis but preserve content\r\n const unquoted = command.replace(/[\"']/g, ' ')\r\n\r\n // Simple path extraction - look for file-like arguments\r\n const tokens = unquoted.split(/\\s+/)\r\n\r\n for (const token of tokens) {\r\n // Skip flags and empty tokens\r\n if (token.startsWith('-') || !token) continue\r\n\r\n // Check if it looks like a path\r\n if (\r\n token.startsWith('/') ||\r\n token.startsWith('./') ||\r\n token.startsWith('../') ||\r\n token.startsWith('~/') ||\r\n token.startsWith('$HOME') ||\r\n token.startsWith('${HOME}') ||\r\n token.includes('.env') ||\r\n token.includes('.pem') ||\r\n token.includes('.key') ||\r\n token.includes('.ssh') ||\r\n token.includes('.aws') ||\r\n token.includes('.gnupg') ||\r\n token.includes('.kube') ||\r\n token.includes('credentials') ||\r\n token.includes('secret') ||\r\n token.includes('password') ||\r\n token.includes('id_rsa') ||\r\n token.includes('id_ed25519') ||\r\n // Files with extensions that might be sensitive\r\n /\\.(env|pem|key|crt|pfx|p12|jks|keystore)$/i.test(token)\r\n ) {\r\n paths.push(token)\r\n }\r\n }\r\n\r\n // Also extract paths from variable assignments\r\n const varAssignments = command.match(/\\w+=[^\\s;]+/g) || []\r\n for (const assignment of varAssignments) {\r\n const value = assignment.split('=')[1]\r\n if (value && (value.includes('/') || value.includes('.'))) {\r\n paths.push(value)\r\n }\r\n }\r\n\r\n return paths\r\n }\r\n\r\n isAllowed(command: string): boolean {\r\n return this.validate(command).length === 0\r\n }\r\n}\r\n"],"mappings":"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","names":[]}
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  loadConfig
4
- } from "./chunk-A535VV7N.js";
4
+ } from "./chunk-BW6XCOJH.js";
5
5
 
6
6
  // src/transparency/display.ts
7
7
  import chalk from "chalk";
@@ -210,4 +210,4 @@ export {
210
210
  formatPermissionsTable,
211
211
  formatAgentSummary
212
212
  };
213
- //# sourceMappingURL=chunk-WPJJZLT6.js.map
213
+ //# sourceMappingURL=chunk-SQCP6IYB.js.map
@@ -7,10 +7,10 @@ import {
7
7
  } from "./chunk-DLP2O6PN.js";
8
8
  import {
9
9
  loadConfig
10
- } from "./chunk-A535VV7N.js";
10
+ } from "./chunk-BW6XCOJH.js";
11
11
  import {
12
12
  PolicyEngine
13
- } from "./chunk-GD5VNHIN.js";
13
+ } from "./chunk-QWZGB4V3.js";
14
14
  import {
15
15
  __require
16
16
  } from "./chunk-7OCVIDC7.js";
@@ -582,9 +582,9 @@ var ClaudeCodeHooks = class {
582
582
  }
583
583
  };
584
584
  async function gateCommand(command) {
585
- const { PolicyEngine: PolicyEngine2 } = await import("./engine-PKLXW6OF.js");
585
+ const { PolicyEngine: PolicyEngine2 } = await import("./engine-EGPAS2EX.js");
586
586
  const { RiskScorer } = await import("./risk-scorer-Y6KF2XCZ.js");
587
- const { loadConfig: loadConfig2 } = await import("./config-43SK6SFI.js");
587
+ const { loadConfig: loadConfig2 } = await import("./config-JLLOTFLI.js");
588
588
  const config = loadConfig2();
589
589
  const engine = new PolicyEngine2(config);
590
590
  const scorer = new RiskScorer();
@@ -1767,7 +1767,29 @@ var BashBro = class extends EventEmitter4 {
1767
1767
  if (!this.ollama || !this.ollamaAvailable) {
1768
1768
  return null;
1769
1769
  }
1770
- return this.ollama.suggestCommand(context);
1770
+ const startTime = Date.now();
1771
+ try {
1772
+ const result = await this.ollama.suggestCommand(context);
1773
+ const latency = Date.now() - startTime;
1774
+ this.emit("bro:suggestion", {
1775
+ input: context,
1776
+ output: result ?? "",
1777
+ model: this.ollama.getModel(),
1778
+ latencyMs: latency,
1779
+ success: result !== null
1780
+ });
1781
+ return result;
1782
+ } catch (error) {
1783
+ const latency = Date.now() - startTime;
1784
+ this.emit("bro:suggestion", {
1785
+ input: context,
1786
+ output: "",
1787
+ model: this.ollama?.getModel() ?? "unknown",
1788
+ latencyMs: latency,
1789
+ success: false
1790
+ });
1791
+ return null;
1792
+ }
1771
1793
  }
1772
1794
  /**
1773
1795
  * Ask Bash Bro to explain a command
@@ -1776,7 +1798,29 @@ var BashBro = class extends EventEmitter4 {
1776
1798
  if (!this.ollama || !this.ollamaAvailable) {
1777
1799
  return "Ollama not available for explanations.";
1778
1800
  }
1779
- return this.ollama.explainCommand(command);
1801
+ const startTime = Date.now();
1802
+ try {
1803
+ const result = await this.ollama.explainCommand(command);
1804
+ const latency = Date.now() - startTime;
1805
+ this.emit("bro:explanation", {
1806
+ input: command,
1807
+ output: result,
1808
+ model: this.ollama.getModel(),
1809
+ latencyMs: latency,
1810
+ success: true
1811
+ });
1812
+ return result;
1813
+ } catch (error) {
1814
+ const latency = Date.now() - startTime;
1815
+ this.emit("bro:explanation", {
1816
+ input: command,
1817
+ output: "Could not explain command.",
1818
+ model: this.ollama?.getModel() ?? "unknown",
1819
+ latencyMs: latency,
1820
+ success: false
1821
+ });
1822
+ return "Could not explain command.";
1823
+ }
1780
1824
  }
1781
1825
  /**
1782
1826
  * Ask Bash Bro to fix a failed command
@@ -1785,7 +1829,29 @@ var BashBro = class extends EventEmitter4 {
1785
1829
  if (!this.ollama || !this.ollamaAvailable) {
1786
1830
  return null;
1787
1831
  }
1788
- return this.ollama.fixCommand(command, error);
1832
+ const startTime = Date.now();
1833
+ try {
1834
+ const result = await this.ollama.fixCommand(command, error);
1835
+ const latency = Date.now() - startTime;
1836
+ this.emit("bro:fix", {
1837
+ input: `${command} | Error: ${error}`,
1838
+ output: result ?? "",
1839
+ model: this.ollama.getModel(),
1840
+ latencyMs: latency,
1841
+ success: result !== null
1842
+ });
1843
+ return result;
1844
+ } catch (err) {
1845
+ const latency = Date.now() - startTime;
1846
+ this.emit("bro:fix", {
1847
+ input: `${command} | Error: ${error}`,
1848
+ output: "",
1849
+ model: this.ollama?.getModel() ?? "unknown",
1850
+ latencyMs: latency,
1851
+ success: false
1852
+ });
1853
+ return null;
1854
+ }
1789
1855
  }
1790
1856
  /**
1791
1857
  * Set the Ollama model to use
@@ -1803,7 +1869,29 @@ var BashBro = class extends EventEmitter4 {
1803
1869
  return null;
1804
1870
  }
1805
1871
  const shell = this.profile?.shell || "bash";
1806
- return this.ollama.generateScript(description, shell);
1872
+ const startTime = Date.now();
1873
+ try {
1874
+ const result = await this.ollama.generateScript(description, shell);
1875
+ const latency = Date.now() - startTime;
1876
+ this.emit("bro:script", {
1877
+ input: description,
1878
+ output: result ?? "",
1879
+ model: this.ollama.getModel(),
1880
+ latencyMs: latency,
1881
+ success: result !== null
1882
+ });
1883
+ return result;
1884
+ } catch (error) {
1885
+ const latency = Date.now() - startTime;
1886
+ this.emit("bro:script", {
1887
+ input: description,
1888
+ output: "",
1889
+ model: this.ollama?.getModel() ?? "unknown",
1890
+ latencyMs: latency,
1891
+ success: false
1892
+ });
1893
+ return null;
1894
+ }
1807
1895
  }
1808
1896
  /**
1809
1897
  * Analyze command for security risks using AI
@@ -1817,7 +1905,34 @@ var BashBro = class extends EventEmitter4 {
1817
1905
  suggestions: []
1818
1906
  };
1819
1907
  }
1820
- return this.ollama.analyzeCommandSafety(command);
1908
+ const startTime = Date.now();
1909
+ try {
1910
+ const result = await this.ollama.analyzeCommandSafety(command);
1911
+ const latency = Date.now() - startTime;
1912
+ this.emit("bro:safety", {
1913
+ input: command,
1914
+ output: `Risk: ${result.risk} - ${result.explanation}`,
1915
+ model: this.ollama.getModel(),
1916
+ latencyMs: latency,
1917
+ success: true
1918
+ });
1919
+ return result;
1920
+ } catch (error) {
1921
+ const latency = Date.now() - startTime;
1922
+ this.emit("bro:safety", {
1923
+ input: command,
1924
+ output: "Analysis failed",
1925
+ model: this.ollama?.getModel() ?? "unknown",
1926
+ latencyMs: latency,
1927
+ success: false
1928
+ });
1929
+ return {
1930
+ safe: true,
1931
+ risk: "low",
1932
+ explanation: "Analysis unavailable.",
1933
+ suggestions: []
1934
+ };
1935
+ }
1821
1936
  }
1822
1937
  /**
1823
1938
  * Summarize a terminal session
@@ -2907,4 +3022,4 @@ export {
2907
3022
  UndoStack,
2908
3023
  LoopDetector
2909
3024
  };
2910
- //# sourceMappingURL=chunk-VVSCAH2B.js.map
3025
+ //# sourceMappingURL=chunk-XCZMQRSX.js.map