bashbros 0.1.0

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 (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +453 -0
  3. package/dist/audit-MCFNGOIM.js +11 -0
  4. package/dist/audit-MCFNGOIM.js.map +1 -0
  5. package/dist/chunk-43W3RVEL.js +2910 -0
  6. package/dist/chunk-43W3RVEL.js.map +1 -0
  7. package/dist/chunk-4R4GV5V2.js +213 -0
  8. package/dist/chunk-4R4GV5V2.js.map +1 -0
  9. package/dist/chunk-7OCVIDC7.js +12 -0
  10. package/dist/chunk-7OCVIDC7.js.map +1 -0
  11. package/dist/chunk-CSRPOGHY.js +354 -0
  12. package/dist/chunk-CSRPOGHY.js.map +1 -0
  13. package/dist/chunk-DEAF6PYM.js +212 -0
  14. package/dist/chunk-DEAF6PYM.js.map +1 -0
  15. package/dist/chunk-DLP2O6PN.js +273 -0
  16. package/dist/chunk-DLP2O6PN.js.map +1 -0
  17. package/dist/chunk-GD5VNHIN.js +519 -0
  18. package/dist/chunk-GD5VNHIN.js.map +1 -0
  19. package/dist/chunk-ID2O2QTI.js +269 -0
  20. package/dist/chunk-ID2O2QTI.js.map +1 -0
  21. package/dist/chunk-J37RHCFJ.js +357 -0
  22. package/dist/chunk-J37RHCFJ.js.map +1 -0
  23. package/dist/chunk-SB4JS3GU.js +456 -0
  24. package/dist/chunk-SB4JS3GU.js.map +1 -0
  25. package/dist/chunk-SG752FZC.js +200 -0
  26. package/dist/chunk-SG752FZC.js.map +1 -0
  27. package/dist/cli.d.ts +2 -0
  28. package/dist/cli.js +2448 -0
  29. package/dist/cli.js.map +1 -0
  30. package/dist/config-CZMIGNPF.js +13 -0
  31. package/dist/config-CZMIGNPF.js.map +1 -0
  32. package/dist/config-parser-XHE7BC7H.js +13 -0
  33. package/dist/config-parser-XHE7BC7H.js.map +1 -0
  34. package/dist/db-EHQDB5OL.js +11 -0
  35. package/dist/db-EHQDB5OL.js.map +1 -0
  36. package/dist/display-IN4NRJJS.js +18 -0
  37. package/dist/display-IN4NRJJS.js.map +1 -0
  38. package/dist/engine-PKLXW6OF.js +9 -0
  39. package/dist/engine-PKLXW6OF.js.map +1 -0
  40. package/dist/index.d.ts +1498 -0
  41. package/dist/index.js +552 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/moltbot-DXZFVK3X.js +11 -0
  44. package/dist/moltbot-DXZFVK3X.js.map +1 -0
  45. package/dist/ollama-HY35OHW4.js +9 -0
  46. package/dist/ollama-HY35OHW4.js.map +1 -0
  47. package/dist/risk-scorer-Y6KF2XCZ.js +9 -0
  48. package/dist/risk-scorer-Y6KF2XCZ.js.map +1 -0
  49. package/dist/static/index.html +410 -0
  50. package/package.json +68 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/moltbot.ts"],"sourcesContent":["/**\r\n * Moltbot Hook Integration\r\n * Seamlessly integrate BashBros with Moltbot (formerly clawd.bot)\r\n */\r\n\r\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs'\r\nimport { join } from 'path'\r\nimport { homedir } from 'os'\r\nimport { execFileSync } from 'child_process'\r\nimport type {\r\n MoltbotGatewayInfo,\r\n MoltbotSecurityAuditResult,\r\n MoltbotSecurityFinding\r\n} from '../types.js'\r\n\r\nexport interface MoltbotSettings {\r\n hooks?: {\r\n preBash?: MoltbotHookEntry[]\r\n postBash?: MoltbotHookEntry[]\r\n sessionEnd?: MoltbotHookEntry[]\r\n }\r\n gateway?: {\r\n port?: number\r\n host?: string\r\n auth?: unknown\r\n }\r\n agents?: {\r\n defaults?: {\r\n sandbox?: {\r\n mode?: string\r\n }\r\n }\r\n }\r\n [key: string]: unknown\r\n}\r\n\r\ninterface MoltbotHookEntry {\r\n command: string\r\n [key: string]: unknown\r\n}\r\n\r\nexport interface MoltbotStatus {\r\n moltbotInstalled: boolean\r\n clawdbotInstalled: boolean\r\n hooksInstalled: boolean\r\n hooks: string[]\r\n configPath: string | null\r\n gatewayRunning: boolean\r\n sandboxMode: string | null\r\n}\r\n\r\nexport interface MoltbotGatewayStatus {\r\n running: boolean\r\n port: number\r\n host: string\r\n sandboxMode: boolean\r\n error?: string\r\n}\r\n\r\n// Config paths to check (in order of preference)\r\nconst CONFIG_PATHS = [\r\n join(homedir(), '.moltbot', 'config.json'),\r\n join(homedir(), '.clawdbot', 'moltbot.json'),\r\n join(homedir(), '.config', 'moltbot', 'config.json')\r\n]\r\n\r\nconst MOLTBOT_DIR = join(homedir(), '.moltbot')\r\nconst CLAWDBOT_DIR = join(homedir(), '.clawdbot')\r\nconst BASHBROS_HOOK_MARKER = '# bashbros-managed'\r\nconst DEFAULT_GATEWAY_PORT = 18789\r\n\r\nexport class MoltbotHooks {\r\n /**\r\n * Check if moltbot command is installed\r\n */\r\n static isMoltbotInstalled(): boolean {\r\n try {\r\n const cmd = process.platform === 'win32' ? 'where' : 'which'\r\n execFileSync(cmd, ['moltbot'], { stdio: 'pipe', timeout: 3000 })\r\n return true\r\n } catch {\r\n return false\r\n }\r\n }\r\n\r\n /**\r\n * Check if clawdbot command is installed (legacy)\r\n */\r\n static isClawdbotInstalled(): boolean {\r\n try {\r\n const cmd = process.platform === 'win32' ? 'where' : 'which'\r\n execFileSync(cmd, ['clawdbot'], { stdio: 'pipe', timeout: 3000 })\r\n return true\r\n } catch {\r\n return false\r\n }\r\n }\r\n\r\n /**\r\n * Find the config file path\r\n */\r\n static findConfigPath(): string | null {\r\n // Check environment variable first\r\n const envPath = process.env.CLAWDBOT_CONFIG_PATH\r\n if (envPath && existsSync(envPath)) {\r\n return envPath\r\n }\r\n\r\n // Check standard paths\r\n for (const configPath of CONFIG_PATHS) {\r\n if (existsSync(configPath)) {\r\n return configPath\r\n }\r\n }\r\n\r\n return null\r\n }\r\n\r\n /**\r\n * Get the config directory (creating if needed)\r\n */\r\n static getConfigDir(): string {\r\n // Prefer .moltbot if moltbot is installed, otherwise .clawdbot\r\n if (this.isMoltbotInstalled() || existsSync(MOLTBOT_DIR)) {\r\n return MOLTBOT_DIR\r\n }\r\n return CLAWDBOT_DIR\r\n }\r\n\r\n /**\r\n * Load current moltbot settings\r\n */\r\n static loadSettings(): MoltbotSettings {\r\n const configPath = this.findConfigPath()\r\n if (!configPath) {\r\n return {}\r\n }\r\n\r\n try {\r\n const content = readFileSync(configPath, 'utf-8')\r\n return JSON.parse(content)\r\n } catch {\r\n return {}\r\n }\r\n }\r\n\r\n /**\r\n * Save moltbot settings\r\n */\r\n static saveSettings(settings: MoltbotSettings): void {\r\n const configDir = this.getConfigDir()\r\n if (!existsSync(configDir)) {\r\n mkdirSync(configDir, { recursive: true })\r\n }\r\n\r\n const configPath = join(configDir, this.isMoltbotInstalled() ? 'config.json' : 'moltbot.json')\r\n writeFileSync(configPath, JSON.stringify(settings, null, 2), 'utf-8')\r\n }\r\n\r\n /**\r\n * Install BashBros hooks into moltbot\r\n */\r\n static install(): { success: boolean; message: string } {\r\n if (!this.isMoltbotInstalled() && !this.isClawdbotInstalled()) {\r\n return {\r\n success: false,\r\n message: 'Neither moltbot nor clawdbot found. Install moltbot first.'\r\n }\r\n }\r\n\r\n const settings = this.loadSettings()\r\n\r\n // Check if already installed\r\n if (this.isInstalled(settings)) {\r\n return {\r\n success: true,\r\n message: 'BashBros hooks already installed.'\r\n }\r\n }\r\n\r\n // Initialize hooks if not present\r\n if (!settings.hooks) {\r\n settings.hooks = {}\r\n }\r\n\r\n // Add preBash hook for command gating\r\n const preBashHook: MoltbotHookEntry = {\r\n command: `bashbros gate \"$COMMAND\" ${BASHBROS_HOOK_MARKER}`\r\n }\r\n\r\n // Add postBash hook for recording\r\n const postBashHook: MoltbotHookEntry = {\r\n command: `bashbros record \"$COMMAND\" ${BASHBROS_HOOK_MARKER}`\r\n }\r\n\r\n // Add sessionEnd hook for reports\r\n const sessionEndHook: MoltbotHookEntry = {\r\n command: `bashbros session-end ${BASHBROS_HOOK_MARKER}`\r\n }\r\n\r\n // Merge with existing hooks\r\n settings.hooks.preBash = [\r\n ...(settings.hooks.preBash || []),\r\n preBashHook\r\n ]\r\n\r\n settings.hooks.postBash = [\r\n ...(settings.hooks.postBash || []),\r\n postBashHook\r\n ]\r\n\r\n settings.hooks.sessionEnd = [\r\n ...(settings.hooks.sessionEnd || []),\r\n sessionEndHook\r\n ]\r\n\r\n this.saveSettings(settings)\r\n\r\n return {\r\n success: true,\r\n message: 'BashBros hooks installed successfully.'\r\n }\r\n }\r\n\r\n /**\r\n * Uninstall BashBros hooks from moltbot\r\n */\r\n static uninstall(): { success: boolean; message: string } {\r\n const configPath = this.findConfigPath()\r\n if (!configPath) {\r\n return {\r\n success: true,\r\n message: 'No moltbot config found. Nothing to uninstall.'\r\n }\r\n }\r\n\r\n const settings = this.loadSettings()\r\n\r\n if (!settings.hooks) {\r\n return {\r\n success: true,\r\n message: 'No hooks to uninstall.'\r\n }\r\n }\r\n\r\n // Remove BashBros hooks\r\n const filterHooks = (hooks: MoltbotHookEntry[] | undefined): MoltbotHookEntry[] => {\r\n if (!hooks) return []\r\n return hooks.filter(h => !h.command.includes(BASHBROS_HOOK_MARKER))\r\n }\r\n\r\n settings.hooks.preBash = filterHooks(settings.hooks.preBash)\r\n settings.hooks.postBash = filterHooks(settings.hooks.postBash)\r\n settings.hooks.sessionEnd = filterHooks(settings.hooks.sessionEnd)\r\n\r\n // Clean up empty arrays\r\n if (settings.hooks.preBash?.length === 0) delete settings.hooks.preBash\r\n if (settings.hooks.postBash?.length === 0) delete settings.hooks.postBash\r\n if (settings.hooks.sessionEnd?.length === 0) delete settings.hooks.sessionEnd\r\n if (Object.keys(settings.hooks).length === 0) delete settings.hooks\r\n\r\n this.saveSettings(settings)\r\n\r\n return {\r\n success: true,\r\n message: 'BashBros hooks uninstalled successfully.'\r\n }\r\n }\r\n\r\n /**\r\n * Check if BashBros hooks are installed\r\n */\r\n static isInstalled(settings?: MoltbotSettings): boolean {\r\n const s = settings || this.loadSettings()\r\n\r\n if (!s.hooks) return false\r\n\r\n const hasMarker = (hooks: MoltbotHookEntry[] | undefined): boolean => {\r\n if (!hooks) return false\r\n return hooks.some(h => h.command.includes(BASHBROS_HOOK_MARKER))\r\n }\r\n\r\n return hasMarker(s.hooks.preBash) ||\r\n hasMarker(s.hooks.postBash) ||\r\n hasMarker(s.hooks.sessionEnd)\r\n }\r\n\r\n /**\r\n * Check if moltbot gateway is running\r\n */\r\n static async isGatewayRunning(): Promise<boolean> {\r\n const settings = this.loadSettings()\r\n const port = settings.gateway?.port || DEFAULT_GATEWAY_PORT\r\n const host = settings.gateway?.host || 'localhost'\r\n\r\n try {\r\n // Try to connect to the gateway\r\n const controller = new AbortController()\r\n const timeout = setTimeout(() => controller.abort(), 2000)\r\n\r\n const response = await fetch(`http://${host}:${port}/health`, {\r\n method: 'GET',\r\n signal: controller.signal\r\n })\r\n\r\n clearTimeout(timeout)\r\n return response.ok\r\n } catch {\r\n return false\r\n }\r\n }\r\n\r\n /**\r\n * Get gateway status\r\n */\r\n static async getGatewayStatus(): Promise<MoltbotGatewayStatus> {\r\n const settings = this.loadSettings()\r\n const port = settings.gateway?.port || DEFAULT_GATEWAY_PORT\r\n const host = settings.gateway?.host || 'localhost'\r\n const sandboxMode = settings.agents?.defaults?.sandbox?.mode === 'strict'\r\n\r\n const running = await this.isGatewayRunning()\r\n\r\n return {\r\n running,\r\n port,\r\n host,\r\n sandboxMode,\r\n error: running ? undefined : 'Gateway not responding'\r\n }\r\n }\r\n\r\n /**\r\n * Get gateway info (for type system)\r\n */\r\n static async getGatewayInfo(): Promise<MoltbotGatewayInfo | null> {\r\n const status = await this.getGatewayStatus()\r\n if (!status.running) return null\r\n\r\n return {\r\n port: status.port,\r\n host: status.host,\r\n sandboxMode: status.sandboxMode,\r\n authToken: !!this.loadSettings().gateway?.auth\r\n }\r\n }\r\n\r\n /**\r\n * Run security audit using moltbot CLI\r\n */\r\n static async runSecurityAudit(): Promise<MoltbotSecurityAuditResult> {\r\n const findings: MoltbotSecurityFinding[] = []\r\n const timestamp = new Date()\r\n\r\n // Check if moltbot is installed\r\n if (!this.isMoltbotInstalled() && !this.isClawdbotInstalled()) {\r\n return {\r\n passed: false,\r\n findings: [{\r\n severity: 'critical',\r\n category: 'installation',\r\n message: 'Moltbot/clawdbot not installed',\r\n recommendation: 'Install moltbot to enable security auditing'\r\n }],\r\n timestamp\r\n }\r\n }\r\n\r\n // Try to run moltbot security audit\r\n try {\r\n const cmd = this.isMoltbotInstalled() ? 'moltbot' : 'clawdbot'\r\n const output = execFileSync(cmd, ['security', 'audit', '--deep', '--json'], {\r\n encoding: 'utf-8',\r\n timeout: 30000,\r\n stdio: ['pipe', 'pipe', 'pipe']\r\n })\r\n\r\n // Parse moltbot audit output\r\n const auditResult = JSON.parse(output)\r\n\r\n if (auditResult.findings && Array.isArray(auditResult.findings)) {\r\n for (const finding of auditResult.findings) {\r\n findings.push({\r\n severity: finding.severity || 'info',\r\n category: finding.category || 'general',\r\n message: finding.message || 'Unknown finding',\r\n recommendation: finding.recommendation\r\n })\r\n }\r\n }\r\n\r\n return {\r\n passed: findings.filter(f => f.severity === 'critical').length === 0,\r\n findings,\r\n timestamp\r\n }\r\n } catch (error) {\r\n // moltbot security audit not available or failed\r\n // Fall back to basic checks\r\n return this.runBasicSecurityChecks()\r\n }\r\n }\r\n\r\n /**\r\n * Run basic security checks when moltbot audit is not available\r\n */\r\n private static runBasicSecurityChecks(): MoltbotSecurityAuditResult {\r\n const findings: MoltbotSecurityFinding[] = []\r\n const settings = this.loadSettings()\r\n\r\n // Check if hooks are installed\r\n if (!this.isInstalled(settings)) {\r\n findings.push({\r\n severity: 'warning',\r\n category: 'hooks',\r\n message: 'BashBros hooks not installed',\r\n recommendation: 'Run \"bashbros moltbot install\" to enable command gating'\r\n })\r\n }\r\n\r\n // Check sandbox mode\r\n const sandboxMode = settings.agents?.defaults?.sandbox?.mode\r\n if (!sandboxMode || sandboxMode === 'off') {\r\n findings.push({\r\n severity: 'warning',\r\n category: 'sandbox',\r\n message: 'Sandbox mode is disabled',\r\n recommendation: 'Enable sandbox mode in moltbot config for additional protection'\r\n })\r\n }\r\n\r\n // Check gateway configuration\r\n if (settings.gateway) {\r\n if (!settings.gateway.auth) {\r\n findings.push({\r\n severity: 'info',\r\n category: 'gateway',\r\n message: 'Gateway authentication not configured',\r\n recommendation: 'Consider enabling gateway authentication for multi-user environments'\r\n })\r\n }\r\n }\r\n\r\n return {\r\n passed: findings.filter(f => f.severity === 'critical').length === 0,\r\n findings,\r\n timestamp: new Date()\r\n }\r\n }\r\n\r\n /**\r\n * Get comprehensive hook status\r\n */\r\n static getStatus(): MoltbotStatus {\r\n const moltbotInstalled = this.isMoltbotInstalled()\r\n const clawdbotInstalled = this.isClawdbotInstalled()\r\n const configPath = this.findConfigPath()\r\n const settings = this.loadSettings()\r\n const hooksInstalled = this.isInstalled(settings)\r\n\r\n const hooks: string[] = []\r\n if (settings.hooks?.preBash) hooks.push('preBash (gate)')\r\n if (settings.hooks?.postBash) hooks.push('postBash (record)')\r\n if (settings.hooks?.sessionEnd) hooks.push('sessionEnd (report)')\r\n\r\n const sandboxMode = settings.agents?.defaults?.sandbox?.mode || null\r\n\r\n // Note: gatewayRunning is sync approximation; use getGatewayStatus() for accurate check\r\n let gatewayRunning = false\r\n // We can't do async here, but we can check if gateway config exists\r\n if (settings.gateway?.port) {\r\n gatewayRunning = true // Assume configured means intended to run\r\n }\r\n\r\n return {\r\n moltbotInstalled,\r\n clawdbotInstalled,\r\n hooksInstalled,\r\n hooks,\r\n configPath,\r\n gatewayRunning,\r\n sandboxMode\r\n }\r\n }\r\n}\r\n\r\n// Export helper for backward compatibility\r\nexport function getMoltbotHooks(): typeof MoltbotHooks {\r\n return MoltbotHooks\r\n}\r\n"],"mappings":"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","names":[]}
@@ -0,0 +1,456 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/config.ts
4
+ import { readFileSync, existsSync, statSync } from "fs";
5
+ import { parse } from "yaml";
6
+ import { join } from "path";
7
+ import { homedir } from "os";
8
+ var CONFIG_FILENAME = ".bashbros.yml";
9
+ var CONFIG_LIMITS = {
10
+ maxPerMinute: { min: 1, max: 1e4 },
11
+ maxPerHour: { min: 1, max: 1e5 },
12
+ maxPatterns: 100,
13
+ maxPathLength: 1e3
14
+ };
15
+ function findConfig() {
16
+ if (existsSync(CONFIG_FILENAME)) {
17
+ return CONFIG_FILENAME;
18
+ }
19
+ const homeConfig = join(homedir(), CONFIG_FILENAME);
20
+ if (existsSync(homeConfig)) {
21
+ return homeConfig;
22
+ }
23
+ const dotConfig = join(homedir(), ".bashbros", "config.yml");
24
+ if (existsSync(dotConfig)) {
25
+ return dotConfig;
26
+ }
27
+ return null;
28
+ }
29
+ function validateConfigPermissions(configPath) {
30
+ try {
31
+ const stats = statSync(configPath);
32
+ if (process.platform !== "win32") {
33
+ const mode = stats.mode;
34
+ const worldWritable = (mode & 2) !== 0;
35
+ const groupWritable = (mode & 16) !== 0;
36
+ if (worldWritable || groupWritable) {
37
+ console.warn(`\u26A0\uFE0F Warning: Config file ${configPath} has insecure permissions`);
38
+ console.warn(" Run: chmod 600 " + configPath);
39
+ }
40
+ }
41
+ } catch {
42
+ }
43
+ }
44
+ function loadConfig(path) {
45
+ const configPath = path || findConfig();
46
+ if (!configPath) {
47
+ return getDefaultConfig();
48
+ }
49
+ validateConfigPermissions(configPath);
50
+ const content = readFileSync(configPath, "utf-8");
51
+ let parsed;
52
+ try {
53
+ parsed = parse(content, { strict: true });
54
+ } catch (error) {
55
+ console.error("Failed to parse config file:", error);
56
+ return getDefaultConfig();
57
+ }
58
+ const validated = validateConfig(parsed);
59
+ return mergeWithDefaults(validated);
60
+ }
61
+ function validateConfig(parsed) {
62
+ if (!parsed || typeof parsed !== "object") {
63
+ return {};
64
+ }
65
+ const config = parsed;
66
+ const validated = {};
67
+ const validAgents = ["claude-code", "clawdbot", "gemini-cli", "aider", "opencode", "custom"];
68
+ if (typeof config.agent === "string" && validAgents.includes(config.agent)) {
69
+ validated.agent = config.agent;
70
+ }
71
+ const validProfiles = ["balanced", "strict", "permissive", "custom"];
72
+ if (typeof config.profile === "string" && validProfiles.includes(config.profile)) {
73
+ validated.profile = config.profile;
74
+ }
75
+ if (config.commands && typeof config.commands === "object") {
76
+ const cmds = config.commands;
77
+ validated.commands = {
78
+ allow: validateStringArray(cmds.allow, CONFIG_LIMITS.maxPatterns),
79
+ block: validateStringArray(cmds.block, CONFIG_LIMITS.maxPatterns)
80
+ };
81
+ }
82
+ if (config.paths && typeof config.paths === "object") {
83
+ const paths = config.paths;
84
+ validated.paths = {
85
+ allow: validatePathArray(paths.allow),
86
+ block: validatePathArray(paths.block)
87
+ };
88
+ }
89
+ if (config.secrets && typeof config.secrets === "object") {
90
+ const secrets = config.secrets;
91
+ validated.secrets = {
92
+ enabled: typeof secrets.enabled === "boolean" ? secrets.enabled : true,
93
+ mode: secrets.mode === "audit" ? "audit" : "block",
94
+ patterns: validateStringArray(secrets.patterns, CONFIG_LIMITS.maxPatterns)
95
+ };
96
+ }
97
+ if (config.audit && typeof config.audit === "object") {
98
+ const audit = config.audit;
99
+ validated.audit = {
100
+ enabled: typeof audit.enabled === "boolean" ? audit.enabled : true,
101
+ destination: validateAuditDestination(audit.destination),
102
+ remotePath: validateRemotePath(audit.remotePath)
103
+ };
104
+ }
105
+ if (config.rateLimit && typeof config.rateLimit === "object") {
106
+ const rl = config.rateLimit;
107
+ const maxPerMinute = validateNumber(rl.maxPerMinute, CONFIG_LIMITS.maxPerMinute);
108
+ const maxPerHour = validateNumber(rl.maxPerHour, CONFIG_LIMITS.maxPerHour);
109
+ validated.rateLimit = {
110
+ enabled: typeof rl.enabled === "boolean" ? rl.enabled : true,
111
+ maxPerMinute,
112
+ maxPerHour: Math.max(maxPerHour, maxPerMinute)
113
+ };
114
+ }
115
+ if (config.riskScoring && typeof config.riskScoring === "object") {
116
+ const rs = config.riskScoring;
117
+ validated.riskScoring = {
118
+ enabled: typeof rs.enabled === "boolean" ? rs.enabled : true,
119
+ blockThreshold: validateNumber(rs.blockThreshold, { min: 1, max: 10 }),
120
+ warnThreshold: validateNumber(rs.warnThreshold, { min: 1, max: 10 }),
121
+ customPatterns: validateRiskPatterns(rs.customPatterns)
122
+ };
123
+ }
124
+ if (config.loopDetection && typeof config.loopDetection === "object") {
125
+ const ld = config.loopDetection;
126
+ validated.loopDetection = {
127
+ enabled: typeof ld.enabled === "boolean" ? ld.enabled : true,
128
+ maxRepeats: validateNumber(ld.maxRepeats, { min: 1, max: 100 }),
129
+ maxTurns: validateNumber(ld.maxTurns, { min: 10, max: 1e4 }),
130
+ similarityThreshold: validateNumber(ld.similarityThreshold, { min: 0, max: 1 }) / 1,
131
+ // Keep as float
132
+ cooldownMs: validateNumber(ld.cooldownMs, { min: 0, max: 6e4 }),
133
+ windowSize: validateNumber(ld.windowSize, { min: 5, max: 100 }),
134
+ action: ld.action === "block" ? "block" : "warn"
135
+ };
136
+ }
137
+ if (config.anomalyDetection && typeof config.anomalyDetection === "object") {
138
+ const ad = config.anomalyDetection;
139
+ validated.anomalyDetection = {
140
+ enabled: typeof ad.enabled === "boolean" ? ad.enabled : true,
141
+ workingHours: validateWorkingHours(ad.workingHours),
142
+ typicalCommandsPerMinute: validateNumber(ad.typicalCommandsPerMinute, { min: 1, max: 1e3 }),
143
+ learningCommands: validateNumber(ad.learningCommands, { min: 10, max: 500 }),
144
+ suspiciousPatterns: validateStringArray(ad.suspiciousPatterns, 50),
145
+ action: ad.action === "block" ? "block" : "warn"
146
+ };
147
+ }
148
+ if (config.outputScanning && typeof config.outputScanning === "object") {
149
+ const os = config.outputScanning;
150
+ validated.outputScanning = {
151
+ enabled: typeof os.enabled === "boolean" ? os.enabled : true,
152
+ scanForSecrets: typeof os.scanForSecrets === "boolean" ? os.scanForSecrets : true,
153
+ scanForErrors: typeof os.scanForErrors === "boolean" ? os.scanForErrors : true,
154
+ maxOutputLength: validateNumber(os.maxOutputLength, { min: 1e3, max: 1e7 }),
155
+ redactPatterns: validateStringArray(os.redactPatterns, 50)
156
+ };
157
+ }
158
+ if (config.undo && typeof config.undo === "object") {
159
+ const undo = config.undo;
160
+ validated.undo = {
161
+ enabled: typeof undo.enabled === "boolean" ? undo.enabled : true,
162
+ maxStackSize: validateNumber(undo.maxStackSize, { min: 10, max: 1e3 }),
163
+ maxFileSize: validateNumber(undo.maxFileSize, { min: 1024, max: 100 * 1024 * 1024 }),
164
+ ttlMinutes: validateNumber(undo.ttlMinutes, { min: 5, max: 1440 }),
165
+ backupPath: typeof undo.backupPath === "string" ? undo.backupPath.slice(0, 500) : "~/.bashbros/undo"
166
+ };
167
+ }
168
+ return validated;
169
+ }
170
+ function validateRiskPatterns(value) {
171
+ if (!Array.isArray(value)) return [];
172
+ return value.filter(
173
+ (item) => item && typeof item === "object" && typeof item.pattern === "string" && typeof item.score === "number" && typeof item.factor === "string"
174
+ ).slice(0, 50).map((item) => ({
175
+ pattern: String(item.pattern).slice(0, 500),
176
+ score: Math.max(1, Math.min(10, Math.floor(Number(item.score)))),
177
+ factor: String(item.factor).slice(0, 200)
178
+ }));
179
+ }
180
+ function validateWorkingHours(value) {
181
+ if (!Array.isArray(value) || value.length !== 2) {
182
+ return [6, 22];
183
+ }
184
+ const start = Math.max(0, Math.min(23, Math.floor(Number(value[0]) || 0)));
185
+ const end = Math.max(0, Math.min(24, Math.floor(Number(value[1]) || 24)));
186
+ return [start, end];
187
+ }
188
+ function validateStringArray(value, maxItems) {
189
+ if (!Array.isArray(value)) return [];
190
+ return value.filter((item) => typeof item === "string").slice(0, maxItems).map((s) => s.slice(0, 500));
191
+ }
192
+ function validatePathArray(value) {
193
+ if (!Array.isArray(value)) return [];
194
+ return value.filter((item) => typeof item === "string").slice(0, CONFIG_LIMITS.maxPatterns).map((s) => s.slice(0, CONFIG_LIMITS.maxPathLength)).filter((s) => !s.includes("\0"));
195
+ }
196
+ function validateNumber(value, limits) {
197
+ if (typeof value !== "number" || !Number.isFinite(value)) {
198
+ return limits.min;
199
+ }
200
+ return Math.max(limits.min, Math.min(limits.max, Math.floor(value)));
201
+ }
202
+ function validateAuditDestination(value) {
203
+ if (value === "remote" || value === "both") {
204
+ return value;
205
+ }
206
+ return "local";
207
+ }
208
+ function validateRemotePath(value) {
209
+ if (typeof value !== "string") {
210
+ return void 0;
211
+ }
212
+ try {
213
+ const url = new URL(value);
214
+ if (url.protocol !== "https:") {
215
+ console.warn("\u26A0\uFE0F Warning: Remote audit path must use HTTPS. Ignoring:", value);
216
+ return void 0;
217
+ }
218
+ const hostname = url.hostname.toLowerCase();
219
+ if (hostname === "localhost" || hostname === "127.0.0.1" || hostname.startsWith("192.168.") || hostname.startsWith("10.")) {
220
+ console.warn("\u26A0\uFE0F Warning: Remote audit path points to local address");
221
+ }
222
+ return value;
223
+ } catch {
224
+ console.warn("\u26A0\uFE0F Warning: Invalid remote audit URL:", value);
225
+ return void 0;
226
+ }
227
+ }
228
+ function getDefaultConfig() {
229
+ return {
230
+ agent: "claude-code",
231
+ profile: "balanced",
232
+ commands: getDefaultCommands("balanced"),
233
+ paths: getDefaultPaths("balanced"),
234
+ secrets: {
235
+ enabled: true,
236
+ mode: "block",
237
+ patterns: [
238
+ ".env*",
239
+ "*.pem",
240
+ "*.key",
241
+ "*credentials*",
242
+ "*secret*",
243
+ ".aws/*",
244
+ ".ssh/*"
245
+ ]
246
+ },
247
+ audit: {
248
+ enabled: true,
249
+ destination: "local"
250
+ },
251
+ rateLimit: {
252
+ enabled: true,
253
+ maxPerMinute: 100,
254
+ maxPerHour: 1e3
255
+ },
256
+ riskScoring: getDefaultRiskScoring("balanced"),
257
+ loopDetection: getDefaultLoopDetection("balanced"),
258
+ anomalyDetection: getDefaultAnomalyDetection("balanced"),
259
+ outputScanning: getDefaultOutputScanning("balanced"),
260
+ undo: getDefaultUndo(),
261
+ ward: getDefaultWard(),
262
+ dashboard: getDefaultDashboard()
263
+ };
264
+ }
265
+ function getDefaultRiskScoring(profile) {
266
+ const thresholds = {
267
+ strict: { block: 6, warn: 3 },
268
+ balanced: { block: 9, warn: 6 },
269
+ permissive: { block: 10, warn: 8 }
270
+ };
271
+ const t = thresholds[profile] || thresholds.balanced;
272
+ return {
273
+ enabled: true,
274
+ blockThreshold: t.block,
275
+ warnThreshold: t.warn,
276
+ customPatterns: []
277
+ };
278
+ }
279
+ function getDefaultLoopDetection(profile) {
280
+ const settings = {
281
+ strict: { maxRepeats: 2, maxTurns: 50, action: "block" },
282
+ balanced: { maxRepeats: 3, maxTurns: 100, action: "warn" },
283
+ permissive: { maxRepeats: 5, maxTurns: 200, action: "warn" }
284
+ };
285
+ const s = settings[profile] || settings.balanced;
286
+ return {
287
+ enabled: true,
288
+ maxRepeats: s.maxRepeats,
289
+ maxTurns: s.maxTurns,
290
+ similarityThreshold: 0.85,
291
+ cooldownMs: 1e3,
292
+ windowSize: 20,
293
+ action: s.action
294
+ };
295
+ }
296
+ function getDefaultAnomalyDetection(profile) {
297
+ return {
298
+ enabled: profile !== "permissive",
299
+ workingHours: [6, 22],
300
+ typicalCommandsPerMinute: 30,
301
+ learningCommands: 50,
302
+ suspiciousPatterns: [],
303
+ action: profile === "strict" ? "block" : "warn"
304
+ };
305
+ }
306
+ function getDefaultOutputScanning(profile) {
307
+ return {
308
+ enabled: true,
309
+ scanForSecrets: true,
310
+ scanForErrors: true,
311
+ maxOutputLength: 1e5,
312
+ redactPatterns: [
313
+ "password\\s*[=:]\\s*\\S+",
314
+ "api[_-]?key\\s*[=:]\\s*\\S+",
315
+ "secret\\s*[=:]\\s*\\S+",
316
+ "token\\s*[=:]\\s*\\S+",
317
+ "Bearer\\s+[A-Za-z0-9\\-._~+/]+=*",
318
+ "sk-[A-Za-z0-9]{20,}",
319
+ "ghp_[A-Za-z0-9]{36}",
320
+ "glpat-[A-Za-z0-9\\-]{20,}"
321
+ ]
322
+ };
323
+ }
324
+ function getDefaultUndo() {
325
+ return {
326
+ enabled: true,
327
+ maxStackSize: 100,
328
+ maxFileSize: 10 * 1024 * 1024,
329
+ // 10MB
330
+ ttlMinutes: 60,
331
+ // 1 hour
332
+ backupPath: "~/.bashbros/undo"
333
+ };
334
+ }
335
+ function getDefaultWard() {
336
+ return {
337
+ enabled: true,
338
+ exposure: {
339
+ scanInterval: 3e4,
340
+ // 30 seconds
341
+ externalProbe: false,
342
+ severityActions: {
343
+ low: "alert",
344
+ medium: "alert",
345
+ high: "block",
346
+ critical: "block_and_kill"
347
+ }
348
+ },
349
+ connectors: {
350
+ proxyAllMcp: false,
351
+ telemetryRetention: "7d"
352
+ },
353
+ egress: {
354
+ defaultAction: "block"
355
+ }
356
+ };
357
+ }
358
+ function getDefaultDashboard() {
359
+ return {
360
+ enabled: true,
361
+ port: 7890,
362
+ bind: "127.0.0.1"
363
+ };
364
+ }
365
+ function getDefaultCommands(profile) {
366
+ const dangerousCommands = [
367
+ "rm -rf /",
368
+ "rm -rf ~",
369
+ "rm -rf /*",
370
+ ":(){:|:&};:",
371
+ "mkfs",
372
+ "dd if=/dev/zero",
373
+ "> /dev/sda",
374
+ "chmod -R 777 /",
375
+ "curl * | bash",
376
+ "wget * | bash",
377
+ "curl * | sh",
378
+ "wget * | sh"
379
+ ];
380
+ const commonAllowed = [
381
+ "ls *",
382
+ "cat *",
383
+ "head *",
384
+ "tail *",
385
+ "grep *",
386
+ "git *",
387
+ "npm *",
388
+ "npx *",
389
+ "pnpm *",
390
+ "yarn *",
391
+ "node *",
392
+ "python *",
393
+ "pip *",
394
+ "mkdir *",
395
+ "touch *",
396
+ "cp *",
397
+ "mv *",
398
+ "cd *",
399
+ "pwd",
400
+ "echo *",
401
+ "which *",
402
+ "code *",
403
+ "vim *",
404
+ "nano *"
405
+ ];
406
+ if (profile === "strict") {
407
+ return { allow: [], block: dangerousCommands };
408
+ }
409
+ if (profile === "permissive") {
410
+ return { allow: ["*"], block: dangerousCommands };
411
+ }
412
+ return { allow: commonAllowed, block: dangerousCommands };
413
+ }
414
+ function getDefaultPaths(profile) {
415
+ const dangerousPaths = [
416
+ "~/.ssh",
417
+ "~/.aws",
418
+ "~/.gnupg",
419
+ "~/.config/gh",
420
+ "/etc/passwd",
421
+ "/etc/shadow"
422
+ ];
423
+ if (profile === "strict") {
424
+ return { allow: ["."], block: dangerousPaths };
425
+ }
426
+ if (profile === "permissive") {
427
+ return { allow: ["*"], block: dangerousPaths };
428
+ }
429
+ return { allow: [".", "~"], block: dangerousPaths };
430
+ }
431
+ function mergeWithDefaults(parsed) {
432
+ const defaults = getDefaultConfig();
433
+ return {
434
+ ...defaults,
435
+ ...parsed,
436
+ commands: { ...defaults.commands, ...parsed.commands },
437
+ paths: { ...defaults.paths, ...parsed.paths },
438
+ secrets: { ...defaults.secrets, ...parsed.secrets },
439
+ audit: { ...defaults.audit, ...parsed.audit },
440
+ rateLimit: { ...defaults.rateLimit, ...parsed.rateLimit },
441
+ riskScoring: { ...defaults.riskScoring, ...parsed.riskScoring },
442
+ loopDetection: { ...defaults.loopDetection, ...parsed.loopDetection },
443
+ anomalyDetection: { ...defaults.anomalyDetection, ...parsed.anomalyDetection },
444
+ outputScanning: { ...defaults.outputScanning, ...parsed.outputScanning },
445
+ undo: { ...defaults.undo, ...parsed.undo },
446
+ ward: { ...defaults.ward, ...parsed.ward },
447
+ dashboard: { ...defaults.dashboard, ...parsed.dashboard }
448
+ };
449
+ }
450
+
451
+ export {
452
+ findConfig,
453
+ loadConfig,
454
+ getDefaultConfig
455
+ };
456
+ //# sourceMappingURL=chunk-SB4JS3GU.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: 'balanced',\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 'rm -rf /',\n 'rm -rf ~',\n 'rm -rf /*',\n ':(){:|:&};:',\n 'mkfs',\n 'dd if=/dev/zero',\n '> /dev/sda',\n 'chmod -R 777 /',\n 'curl * | bash',\n 'wget * | bash',\n 'curl * | sh',\n 'wget * | sh'\n ]\n\n const commonAllowed = [\n 'ls *', 'cat *', 'head *', 'tail *', 'grep *',\n 'git *', 'npm *', 'npx *', 'pnpm *', 'yarn *',\n 'node *', 'python *', 'pip *',\n 'mkdir *', 'touch *', 'cp *', 'mv *',\n 'cd *', 'pwd', 'echo *', 'which *',\n 'code *', 'vim *', 'nano *'\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":[]}