micro-models-agent 0.59.0 → 0.60.1

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 (233) hide show
  1. package/CHANGELOG.md +584 -564
  2. package/README.md +358 -358
  3. package/dist/certification/certifications.json +493 -493
  4. package/dist/i18n/en.json +746 -718
  5. package/dist/i18n/ru.json +746 -718
  6. package/dist/main.js +566 -70
  7. package/dist/modules/browser/bridge-server.mjs +219 -219
  8. package/dist/skills/builtin/git.md +36 -36
  9. package/dist/skills/builtin/typescript.md +35 -35
  10. package/package.json +51 -51
  11. package/dist/cli/commands.js +0 -447
  12. package/dist/cli/completer.js +0 -167
  13. package/dist/cli/index.js +0 -2
  14. package/dist/cli/main.js +0 -153
  15. package/dist/cli/plugin-commands.js +0 -36
  16. package/dist/cli/repl-commands.js +0 -761
  17. package/dist/cli/repl.js +0 -702
  18. package/dist/cli/run-result.js +0 -33
  19. package/dist/cli/security-commands.js +0 -164
  20. package/dist/cli/setup.js +0 -237
  21. package/dist/config/config.js +0 -276
  22. package/dist/config/defaults.js +0 -141
  23. package/dist/config/domains.js +0 -179
  24. package/dist/config/experts.js +0 -15
  25. package/dist/config/index.js +0 -4
  26. package/dist/config/security.js +0 -213
  27. package/dist/config/types.js +0 -1
  28. package/dist/core/agent-moe.js +0 -102
  29. package/dist/core/agent.js +0 -1018
  30. package/dist/core/bootstrap.js +0 -481
  31. package/dist/core/crash-handler.js +0 -51
  32. package/dist/core/environment.js +0 -199
  33. package/dist/core/index.js +0 -2
  34. package/dist/core/prompt-builder.js +0 -76
  35. package/dist/core/session-logger.js +0 -251
  36. package/dist/core/types.js +0 -1
  37. package/dist/core/version.js +0 -26
  38. package/dist/core/workspace.js +0 -76
  39. package/dist/i18n/index.js +0 -46
  40. package/dist/index.js +0 -22
  41. package/dist/llm/image-utils.js +0 -143
  42. package/dist/llm/index.js +0 -4
  43. package/dist/llm/model-loader.js +0 -78
  44. package/dist/llm/openai-compat.js +0 -497
  45. package/dist/llm/orchestrator.js +0 -200
  46. package/dist/llm/provider.js +0 -10
  47. package/dist/llm/response.js +0 -39
  48. package/dist/llm/token-counter.js +0 -39
  49. package/dist/llm/types.js +0 -1
  50. package/dist/logger/app-logger.js +0 -189
  51. package/dist/logger/file-log.js +0 -151
  52. package/dist/logger/index.js +0 -1
  53. package/dist/migration/backup.js +0 -45
  54. package/dist/migration/detect.js +0 -50
  55. package/dist/migration/index.js +0 -2
  56. package/dist/modules/artifacts/store.js +0 -61
  57. package/dist/modules/browser/actions.js +0 -76
  58. package/dist/modules/browser/bridge-client.js +0 -199
  59. package/dist/modules/browser/bridge-path.js +0 -10
  60. package/dist/modules/browser/cookie-store.js +0 -24
  61. package/dist/modules/browser/driver.js +0 -136
  62. package/dist/modules/browser/index.js +0 -7
  63. package/dist/modules/browser/module.js +0 -29
  64. package/dist/modules/browser/session.js +0 -342
  65. package/dist/modules/browser/snapshot.js +0 -148
  66. package/dist/modules/browser/types.js +0 -12
  67. package/dist/modules/certification/cli.js +0 -213
  68. package/dist/modules/certification/fact-checker.js +0 -82
  69. package/dist/modules/certification/loader.js +0 -106
  70. package/dist/modules/certification/manifest.js +0 -58
  71. package/dist/modules/certification/runner.js +0 -245
  72. package/dist/modules/certification/scenarios.js +0 -407
  73. package/dist/modules/certification/types.js +0 -1
  74. package/dist/modules/context/chunk-query.js +0 -100
  75. package/dist/modules/context/fact-extractor.js +0 -168
  76. package/dist/modules/context/history.js +0 -15
  77. package/dist/modules/context/index.js +0 -1
  78. package/dist/modules/context/manager.js +0 -440
  79. package/dist/modules/execution/audit-runners.js +0 -206
  80. package/dist/modules/execution/auditor.js +0 -218
  81. package/dist/modules/execution/execution-plugin.js +0 -431
  82. package/dist/modules/execution/index.js +0 -8
  83. package/dist/modules/execution/module.js +0 -625
  84. package/dist/modules/execution/moe-executor.js +0 -304
  85. package/dist/modules/execution/plan-coverage.js +0 -68
  86. package/dist/modules/execution/plan-persister.js +0 -46
  87. package/dist/modules/execution/plan-store.js +0 -196
  88. package/dist/modules/execution/plan-tool.js +0 -677
  89. package/dist/modules/execution/plan-validator.js +0 -153
  90. package/dist/modules/execution/planner.js +0 -94
  91. package/dist/modules/execution/stuck-detector.js +0 -746
  92. package/dist/modules/execution/tracker.js +0 -69
  93. package/dist/modules/execution/types.js +0 -1
  94. package/dist/modules/execution/verifier.js +0 -235
  95. package/dist/modules/execution/windows-commands.js +0 -41
  96. package/dist/modules/hallucination/confidence.js +0 -66
  97. package/dist/modules/hallucination/consistency.js +0 -26
  98. package/dist/modules/hallucination/detector.js +0 -47
  99. package/dist/modules/hallucination/factual.js +0 -169
  100. package/dist/modules/hallucination/index.js +0 -5
  101. package/dist/modules/hallucination/js-identifiers.js +0 -262
  102. package/dist/modules/hallucination/llm-judge.js +0 -101
  103. package/dist/modules/index.js +0 -5
  104. package/dist/modules/indexer/cache.js +0 -40
  105. package/dist/modules/indexer/index.js +0 -3
  106. package/dist/modules/indexer/module.js +0 -246
  107. package/dist/modules/indexer/project-profile.js +0 -183
  108. package/dist/modules/indexer/walker.js +0 -101
  109. package/dist/modules/lsp/check-tool.js +0 -58
  110. package/dist/modules/lsp/client.js +0 -389
  111. package/dist/modules/lsp/command.js +0 -60
  112. package/dist/modules/lsp/config.js +0 -135
  113. package/dist/modules/lsp/index.js +0 -3
  114. package/dist/modules/lsp/module.js +0 -260
  115. package/dist/modules/lsp/probe.js +0 -86
  116. package/dist/modules/lsp/project-root.js +0 -32
  117. package/dist/modules/lsp/startup-check.js +0 -144
  118. package/dist/modules/lsp/types.js +0 -1
  119. package/dist/modules/mcp/client.js +0 -399
  120. package/dist/modules/mcp/index.js +0 -3
  121. package/dist/modules/mcp/module.js +0 -142
  122. package/dist/modules/mcp/registry.js +0 -15
  123. package/dist/modules/memory/index.js +0 -1
  124. package/dist/modules/memory/module.js +0 -96
  125. package/dist/modules/memory/search.js +0 -42
  126. package/dist/modules/memory/store.js +0 -69
  127. package/dist/modules/pipelines/engine.js +0 -60
  128. package/dist/modules/pipelines/index.js +0 -3
  129. package/dist/modules/pipelines/parser.js +0 -56
  130. package/dist/modules/pipelines/template.js +0 -14
  131. package/dist/modules/plugins/builtin/lint-on-write.js +0 -334
  132. package/dist/modules/plugins/builtin/notify.js +0 -9
  133. package/dist/modules/plugins/index.js +0 -1
  134. package/dist/modules/plugins/loader.js +0 -70
  135. package/dist/modules/plugins/manager.js +0 -261
  136. package/dist/modules/plugins/types.js +0 -1
  137. package/dist/modules/pricing/index.js +0 -61
  138. package/dist/modules/pricing/prices.js +0 -129
  139. package/dist/modules/processes/detect.js +0 -34
  140. package/dist/modules/processes/index.js +0 -2
  141. package/dist/modules/processes/registry.js +0 -327
  142. package/dist/modules/processes/runner.js +0 -23
  143. package/dist/modules/providers/create.js +0 -22
  144. package/dist/modules/providers/fallback.js +0 -79
  145. package/dist/modules/providers/health.js +0 -46
  146. package/dist/modules/providers/index.js +0 -5
  147. package/dist/modules/providers/manager.js +0 -161
  148. package/dist/modules/providers/presets.js +0 -128
  149. package/dist/modules/providers/registry.js +0 -22
  150. package/dist/modules/providers/types.js +0 -1
  151. package/dist/modules/registry.js +0 -48
  152. package/dist/modules/security/audit-log.js +0 -136
  153. package/dist/modules/security/audit-notifier.js +0 -292
  154. package/dist/modules/security/command-validator.js +0 -219
  155. package/dist/modules/security/content-scanner.js +0 -53
  156. package/dist/modules/security/data-sanitizer.js +0 -89
  157. package/dist/modules/security/encryption.js +0 -242
  158. package/dist/modules/security/index.js +0 -14
  159. package/dist/modules/security/network-validator.js +0 -88
  160. package/dist/modules/security/path-validator.js +0 -203
  161. package/dist/modules/security/rate-limiter.js +0 -119
  162. package/dist/modules/security/security-policies.js +0 -531
  163. package/dist/modules/security/session-encryption.js +0 -210
  164. package/dist/modules/security/session-isolation.js +0 -95
  165. package/dist/modules/session/index.js +0 -3
  166. package/dist/modules/session/manager.js +0 -172
  167. package/dist/modules/session/module.js +0 -24
  168. package/dist/modules/session/store.js +0 -222
  169. package/dist/modules/session/types.js +0 -1
  170. package/dist/modules/skills/index.js +0 -2
  171. package/dist/modules/skills/loader.js +0 -72
  172. package/dist/modules/skills/matcher.js +0 -27
  173. package/dist/modules/skills/module.js +0 -129
  174. package/dist/modules/types.js +0 -1
  175. package/dist/modules/updater/checker.js +0 -96
  176. package/dist/modules/updater/index.js +0 -2
  177. package/dist/modules/updater/module.js +0 -116
  178. package/dist/modules/user-profile/compressor.js +0 -16
  179. package/dist/modules/user-profile/index.js +0 -1
  180. package/dist/modules/user-profile/profile.js +0 -68
  181. package/dist/tools/approve.js +0 -33
  182. package/dist/tools/attach-image.js +0 -101
  183. package/dist/tools/bash.js +0 -519
  184. package/dist/tools/browser.js +0 -115
  185. package/dist/tools/chunk-query.js +0 -100
  186. package/dist/tools/create-dir.js +0 -56
  187. package/dist/tools/delete-file.js +0 -63
  188. package/dist/tools/download-file.js +0 -117
  189. package/dist/tools/edit-file.js +0 -80
  190. package/dist/tools/enable-tools.js +0 -59
  191. package/dist/tools/executor.js +0 -154
  192. package/dist/tools/file-info.js +0 -47
  193. package/dist/tools/filter-tools.js +0 -17
  194. package/dist/tools/glob-tool.js +0 -27
  195. package/dist/tools/grep-tool.js +0 -125
  196. package/dist/tools/hidden-tools-block.js +0 -37
  197. package/dist/tools/index.js +0 -78
  198. package/dist/tools/list-dir.js +0 -49
  199. package/dist/tools/load-skill.js +0 -43
  200. package/dist/tools/mcp-call.js +0 -69
  201. package/dist/tools/move-file.js +0 -86
  202. package/dist/tools/path-utils.js +0 -101
  203. package/dist/tools/pipeline-run.js +0 -145
  204. package/dist/tools/preview.js +0 -2
  205. package/dist/tools/process-kill.js +0 -40
  206. package/dist/tools/process-list.js +0 -37
  207. package/dist/tools/process-log.js +0 -54
  208. package/dist/tools/question.js +0 -141
  209. package/dist/tools/read-file.js +0 -179
  210. package/dist/tools/recall.js +0 -118
  211. package/dist/tools/registry.js +0 -47
  212. package/dist/tools/remember.js +0 -68
  213. package/dist/tools/scope-check.js +0 -32
  214. package/dist/tools/search-history.js +0 -85
  215. package/dist/tools/subagent.js +0 -196
  216. package/dist/tools/types.js +0 -1
  217. package/dist/tools/user-input.js +0 -123
  218. package/dist/tools/web-browse.js +0 -87
  219. package/dist/tools/web-fetch.js +0 -119
  220. package/dist/tools/web-search.js +0 -105
  221. package/dist/tools/write-file.js +0 -82
  222. package/dist/ui/box.js +0 -77
  223. package/dist/ui/colors.js +0 -4
  224. package/dist/ui/diff.js +0 -178
  225. package/dist/ui/index.js +0 -6
  226. package/dist/ui/line-editor.js +0 -822
  227. package/dist/ui/line-math.js +0 -73
  228. package/dist/ui/md-formatter.js +0 -212
  229. package/dist/ui/output.js +0 -13
  230. package/dist/ui/plan-view.js +0 -103
  231. package/dist/ui/renderer.js +0 -259
  232. package/dist/ui/spinner.js +0 -70
  233. package/dist/ui/table.js +0 -144
@@ -1,219 +0,0 @@
1
- import { DEFAULT_SECURITY_CONFIG } from "../../config/security";
2
- // Fallback in case DEFAULT_SECURITY_CONFIG is not available
3
- const FALLBACK_BASH_CONFIG = {
4
- enabled: false,
5
- blacklist: [
6
- "rm",
7
- "dd",
8
- "chmod",
9
- "wget",
10
- "curl",
11
- "scp",
12
- "ssh",
13
- "nc",
14
- "netcat",
15
- "powershell",
16
- "pwsh",
17
- "cmd",
18
- ],
19
- whitelist: [],
20
- blockDangerousFlags: false,
21
- dangerousFlags: ["--force", "-rf", "--no-preserve-root"],
22
- dangerousOperators: [">", ">>", "2>", "2>>", "|", "&&", "||", ";", "`"],
23
- logCommands: true,
24
- };
25
- export const DEFAULT_BASH_CONFIG = DEFAULT_SECURITY_CONFIG?.bash || FALLBACK_BASH_CONFIG;
26
- /**
27
- * Extract the actual base command from a shell command string.
28
- * Handles path prefixes, env assignments, and sudo.
29
- *
30
- * Examples:
31
- * "/usr/bin/rm -rf /" → "rm"
32
- * "sudo /usr/bin/rm file" → "rm"
33
- * "NODE_ENV=prod node app.js" → "node"
34
- * "env PATH=/x rm -rf /" → "rm"
35
- * "cmd=rm; $cmd -rf /" → "cmd" (variable indirection — not expanded)
36
- * "powershell Remove-Item x" → "Remove-Item"
37
- * "cmd /c del /s /q" → "del"
38
- */
39
- function extractBaseCommand(trimmed) {
40
- const tokens = trimmed.split(/\s+/);
41
- let i = 0;
42
- // Skip env assignments (KEY=val, KEY="val", etc.) and prefixed commands
43
- while (i < tokens.length) {
44
- const tok = tokens[i];
45
- if (/^[a-zA-Z_][a-zA-Z0-9_]*=/.test(tok)) {
46
- i++;
47
- continue;
48
- }
49
- if (tok === "sudo" || tok === "env" || tok === "command" || tok === "exec" || tok === "nohup") {
50
- i++;
51
- continue;
52
- }
53
- break;
54
- }
55
- if (i >= tokens.length)
56
- return tokens[0] ?? "";
57
- // Extract basename from path (e.g. /usr/bin/rm → rm)
58
- const raw = tokens[i];
59
- const parts = raw.split(/[\\/]/);
60
- const basename = parts[parts.length - 1] || raw;
61
- // Strip .exe/.cmd/.bat extensions for blacklist matching (e.g. powershell.exe → powershell)
62
- return basename.replace(/\.(exe|cmd|bat)$/i, "") || basename;
63
- }
64
- /**
65
- * Check if a shell operator appears as a standalone token in the command.
66
- * Uses regex with lookbehind/lookahead so that:
67
- * "&" does NOT match "&&" (AND operator)
68
- * "|" does NOT match "||" (OR operator)
69
- * ">" does NOT match ">>" (append)
70
- * "2>" does NOT match "2>>" (append stderr)
71
- */
72
- function containsOperator(command, op) {
73
- const escaped = op.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
74
- // For single-char operators that are prefixes of multi-char ones,
75
- // ensure they don't match when part of a longer sequence.
76
- // Note: "&" is no longer in dangerousOperators (&& is legitimate),
77
- // but keep the special case for defense-in-depth if manually added.
78
- if (op === "&")
79
- return /(?<!&)&(?!&)/.test(command);
80
- if (op === "|")
81
- return /(?<!\|)\|(?!\|)/.test(command);
82
- if (op === ">")
83
- return /(?<!>)>(?!>)/.test(command);
84
- // For everything else (including multi-char like ">>", "2>", "2>>", ";", "`"),
85
- // match literally.
86
- return new RegExp(escaped).test(command);
87
- }
88
- /**
89
- * Check if a command is allowed based on security configuration
90
- */
91
- export function isCommandAllowed(command, securityConfig) {
92
- // If security is disabled, allow everything
93
- if (!securityConfig?.enabled) {
94
- return { allowed: true, sanitizedCommand: command };
95
- }
96
- // Ensure config has all required fields
97
- let config = securityConfig;
98
- if (!config.blacklist || !Array.isArray(config.blacklist)) {
99
- config = {
100
- ...FALLBACK_BASH_CONFIG,
101
- enabled: config.enabled ?? FALLBACK_BASH_CONFIG.enabled,
102
- };
103
- }
104
- const trimmedCommand = command.trim();
105
- if (!trimmedCommand) {
106
- return { allowed: false, reason: "Empty command" };
107
- }
108
- // Shell wrappers are always blocked — they bypass all command validation.
109
- // The agent should use the bash tool directly, not shell interpreters.
110
- const ALWAYS_BLOCKED = ["powershell", "pwsh", "cmd"];
111
- const baseForShellCheck = extractBaseCommand(trimmedCommand);
112
- if (ALWAYS_BLOCKED.includes(baseForShellCheck)) {
113
- return {
114
- allowed: false,
115
- reason: `Shell wrapper "${baseForShellCheck}" is blocked — use the bash tool directly`,
116
- };
117
- }
118
- // Interpreters with -c/-e execute arbitrary code and bypass the blacklist.
119
- const INTERPRETER_FLAGS = /(?:^|\s)(?:bash|sh|zsh|dash|python|python3|node|bun|perl|ruby)\s+(?:-c|-e|--command|--eval)\b/;
120
- if (INTERPRETER_FLAGS.test(trimmedCommand)) {
121
- return { allowed: false, reason: "Interpreter -c/-e invocation is blocked" };
122
- }
123
- // Multi-line commands can hide blacklisted commands after a newline.
124
- if (/\r?\n/.test(trimmedCommand)) {
125
- return { allowed: false, reason: "Multi-line commands are blocked" };
126
- }
127
- // Chained commands bypass the base-command check (e.g. "echo hi; rm -rf /").
128
- if (/[;&|]/.test(trimmedCommand) &&
129
- /(^|[;&|\s])(rm|del|rd|dd|chmod|chown|wget|curl|nc|netcat|shutdown|reboot|mkfs|fdisk)\b/.test(trimmedCommand)) {
130
- return { allowed: false, reason: "Command chaining to a dangerous command is blocked" };
131
- }
132
- // Check dangerous operators FIRST — before parsing the base command.
133
- // e.g. "curl http://x | bash" must be caught even though curl isn't blacklisted.
134
- if (config.blockDangerousFlags) {
135
- for (const op of config.dangerousOperators || []) {
136
- if (containsOperator(trimmedCommand, op)) {
137
- return {
138
- allowed: false,
139
- reason: `Operator "${op}" is not allowed`,
140
- };
141
- }
142
- }
143
- }
144
- // Extract actual base command (handles paths, env vars, sudo)
145
- const baseCommand = extractBaseCommand(trimmedCommand);
146
- // Check whitelist first (if non-empty, only whitelisted commands are allowed)
147
- if (config.whitelist.length > 0) {
148
- if (!config.whitelist.includes(baseCommand)) {
149
- return {
150
- allowed: false,
151
- reason: `Command "${baseCommand}" is not in the whitelist`,
152
- };
153
- }
154
- }
155
- // Check blacklist (against the extracted basename, not the raw token)
156
- if (config.blacklist.includes(baseCommand)) {
157
- return {
158
- allowed: false,
159
- reason: `Command "${baseCommand}" is blacklisted`,
160
- };
161
- }
162
- // Also check the raw first token in case it's a simple name
163
- const rawFirst = trimmedCommand.split(/\s+/)[0].replace(/\.(exe|cmd|bat)$/i, "");
164
- if (rawFirst !== baseCommand && config.blacklist.includes(rawFirst)) {
165
- return {
166
- allowed: false,
167
- reason: `Command "${rawFirst}" is blacklisted`,
168
- };
169
- }
170
- // Check for dangerous flags
171
- if (config.blockDangerousFlags) {
172
- for (const flag of config.dangerousFlags || []) {
173
- // Check for flag as whole word or with equals
174
- const flagPattern = new RegExp(`(?:^|\\s)${flag}(?:\\s|$|=)`);
175
- if (flagPattern.test(trimmedCommand)) {
176
- return {
177
- allowed: false,
178
- reason: `Dangerous flag "${flag}" is not allowed`,
179
- };
180
- }
181
- }
182
- }
183
- // Command is allowed
184
- return { allowed: true, sanitizedCommand: command };
185
- }
186
- /**
187
- * Sanitize command for logging (remove sensitive information)
188
- * Uses the same patterns as sanitizeLogMessage for consistency
189
- */
190
- export function sanitizeCommandForLog(command) {
191
- // Import patterns from data-sanitizer to maintain consistency
192
- const patterns = [
193
- /(api[_-]?key|apikey|api_key)\s*[=:\s]\s*["']?([a-zA-Z0-9_-]{10,})["']?/gi,
194
- /(token|access[_-]?token|auth[_-]?token)\s*[=:\s]\s*["']?([a-zA-Z0-9_-]{10,})["']?/gi,
195
- /(password|passwd|pwd|pass)\s*[=:\s]\s*["']?([^\s"'&]+)["']?/gi,
196
- /(secret|private[_-]?key|secret[_-]?key)\s*[=:\s]\s*["']?([^\s"'&]+)["']?/gi,
197
- /\bsk-[a-zA-Z0-9_-]{20,}\b/gi,
198
- /\bctx7sk-[a-zA-Z0-9_-]{10,}\b/gi,
199
- /\bghp_[a-zA-Z0-9]{30,}\b/gi,
200
- /\bgithub_pat_[a-zA-Z0-9]{20,}\b/gi,
201
- /\bAKIA[0-9A-Z]{15,}\b/gi,
202
- /Bearer\s+[a-zA-Z0-9_-]{20,}/gi,
203
- /\b([a-zA-Z0-9_-]{30,})\b/g,
204
- ];
205
- let sanitized = command;
206
- for (const pattern of patterns) {
207
- if (pattern.toString().includes("($1")) {
208
- // For patterns with capture groups, use the group in replacement
209
- sanitized = sanitized.replace(pattern, (match, p1) => {
210
- return `${p1}=[REDACTED]`;
211
- });
212
- }
213
- else {
214
- // For simple patterns, just replace with [REDACTED]
215
- sanitized = sanitized.replace(pattern, "[REDACTED]");
216
- }
217
- }
218
- return sanitized;
219
- }
@@ -1,53 +0,0 @@
1
- /**
2
- * Default dangerous patterns to scan for in file content.
3
- * NOTE: only genuinely dangerous constructs are listed. Legitimate runtime
4
- * APIs (process.exit, setTimeout/setInterval, template literals `${}`,
5
- * backtick strings) are NOT scanned — they are valid JS/TS and blocking
6
- * them breaks ordinary CLI code (false positives).
7
- */
8
- const DEFAULT_DANGEROUS_PATTERNS = [
9
- /eval\(/gi,
10
- /new Function\(/gi,
11
- /child_process\.exec\(/gi,
12
- /child_process\.spawn\(/gi,
13
- /require\('child_process'\)/gi,
14
- /require\('http'\).createServer/gi,
15
- /require\('net'\).createServer/gi,
16
- /require\('fs'\).writeFileSync/gi,
17
- /require\('fs'\).unlinkSync/gi,
18
- /while\(true\)/gi,
19
- /for\(;;\)/gi,
20
- /rm -rf/gi,
21
- /dd if=/gi,
22
- /chmod 777/gi,
23
- /wget.*\|.*sh/gi,
24
- /curl.*\|.*sh/gi,
25
- ];
26
- /**
27
- * Scan file content for dangerous patterns
28
- */
29
- export function scanContent(content, filePath, config) {
30
- // If scanning is disabled, allow everything
31
- if (!config?.enabled) {
32
- return { allowed: true };
33
- }
34
- // Use default patterns if none provided
35
- const patterns = config.dangerousPatterns?.length
36
- ? config.dangerousPatterns
37
- : DEFAULT_DANGEROUS_PATTERNS;
38
- const matches = [];
39
- for (const pattern of patterns) {
40
- const match = content.match(pattern);
41
- if (match) {
42
- matches.push(...match);
43
- }
44
- }
45
- if (matches.length > 0) {
46
- return {
47
- allowed: false,
48
- reason: `Content contains dangerous patterns in ${filePath}: ${matches.slice(0, 3).join(", ")}`,
49
- matches,
50
- };
51
- }
52
- return { allowed: true };
53
- }
@@ -1,89 +0,0 @@
1
- /**
2
- * Patterns for sensitive data that should be redacted from logs
3
- */
4
- const SENSITIVE_PATTERNS = [
5
- // API keys and tokens in key=value format
6
- {
7
- pattern: /(api[_-]?key|apikey|api_key)\s*[=:\s]\s*["']?([a-zA-Z0-9_-]{10,})["']?/gi,
8
- replacement: "$1=[REDACTED]",
9
- },
10
- {
11
- pattern: /(token|access[_-]?token|auth[_-]?token)\s*[=:\s]\s*["']?([a-zA-Z0-9_-]{10,})["']?/gi,
12
- replacement: "$1=[REDACTED]",
13
- },
14
- {
15
- pattern: /(password|passwd|pwd|pass)\s*[=:\s]\s*["']?([^\s"'&]+)["']?/gi,
16
- replacement: "$1=[REDACTED]",
17
- },
18
- {
19
- pattern: /(secret|private[_-]?key|secret[_-]?key)\s*[=:\s]\s*["']?([^\s"'&]+)["']?/gi,
20
- replacement: "$1=[REDACTED]",
21
- },
22
- // Standalone API keys (not in key=value format)
23
- {
24
- pattern: /\bsk-[a-zA-Z0-9_-]{20,}\b/gi,
25
- replacement: "sk-[REDACTED]",
26
- },
27
- {
28
- pattern: /\bctx7sk-[a-zA-Z0-9_-]{10,}\b/gi,
29
- replacement: "ctx7sk-[REDACTED]",
30
- },
31
- {
32
- pattern: /\bghp_[a-zA-Z0-9]{30,}\b/gi,
33
- replacement: "ghp_[REDACTED]",
34
- },
35
- {
36
- pattern: /\bgithub_pat_[a-zA-Z0-9]{20,}\b/gi,
37
- replacement: "github_pat_[REDACTED]",
38
- },
39
- {
40
- pattern: /\bAKIA[0-9A-Z]{15,}\b/gi,
41
- replacement: "AKIA[REDACTED]",
42
- },
43
- // Bearer tokens in headers
44
- {
45
- pattern: /Bearer\s+[a-zA-Z0-9_-]{20,}/gi,
46
- replacement: "Bearer [REDACTED]",
47
- },
48
- // Generic high-entropy strings that look like secrets (30+ chars, mixed case and numbers)
49
- {
50
- pattern: /\b([a-zA-Z0-9_-]{30,})\b/g,
51
- replacement: "[REDACTED]",
52
- },
53
- ];
54
- /**
55
- * Sanitize a log message by removing sensitive information
56
- */
57
- export function sanitizeLogMessage(message) {
58
- let sanitized = message;
59
- for (const { pattern, replacement } of SENSITIVE_PATTERNS) {
60
- sanitized = sanitized.replace(pattern, replacement);
61
- }
62
- return sanitized;
63
- }
64
- /**
65
- * Sanitize a command string by removing sensitive information
66
- */
67
- export function sanitizeCommand(command) {
68
- return sanitizeLogMessage(command);
69
- }
70
- /**
71
- * Sanitize a URL by removing sensitive query parameters
72
- */
73
- export function sanitizeUrl(url) {
74
- try {
75
- const urlObj = new URL(url);
76
- const sensitiveKeys = ["api_key", "token", "password", "secret", "access_token", "auth", "key"];
77
- const sanitizedParams = new URLSearchParams(urlObj.search);
78
- for (const key of sensitiveKeys) {
79
- if (sanitizedParams.has(key)) {
80
- sanitizedParams.set(key, "[REDACTED]");
81
- }
82
- }
83
- urlObj.search = sanitizedParams.toString();
84
- return urlObj.toString();
85
- }
86
- catch {
87
- return sanitizeLogMessage(url);
88
- }
89
- }
@@ -1,242 +0,0 @@
1
- import { createCipheriv, createDecipheriv, randomBytes, scryptSync, timingSafeEqual } from "node:crypto";
2
- import { homedir } from "os";
3
- import { join } from "path";
4
- import { readFileSync, writeFileSync, existsSync, copyFileSync } from "fs";
5
- const DEFAULT_CONFIG = {
6
- keyPath: join(homedir(), ".mma", ".encryption-key"),
7
- algorithm: "aes-256-gcm",
8
- };
9
- const IV_LENGTH = 16;
10
- const SALT_LENGTH = 16;
11
- const KEY_LENGTH = 32;
12
- const AUTH_TAG_LENGTH = 16;
13
- /**
14
- * Generate a new encryption key
15
- */
16
- export function generateEncryptionKey() {
17
- return randomBytes(KEY_LENGTH);
18
- }
19
- /**
20
- * Get or create encryption key from file
21
- */
22
- export function getOrCreateEncryptionKey(config) {
23
- const { keyPath } = { ...DEFAULT_CONFIG, ...config };
24
- // Ensure directory exists
25
- const dir = join(keyPath, "..");
26
- if (!existsSync(dir)) {
27
- // Note: We can't use mkdirSync here because we're in a pure function
28
- // This will be handled by the caller
29
- }
30
- if (!existsSync(keyPath)) {
31
- const key = generateEncryptionKey();
32
- try {
33
- writeFileSync(keyPath, key.toString("base64"), { mode: 0o600 });
34
- return key;
35
- }
36
- catch (e) {
37
- // If we can't persist the key, encrypted data would be unrecoverable
38
- // on the next run — fail loudly instead of silently degrading.
39
- throw new Error(`Cannot write encryption key to ${keyPath}: ${e instanceof Error ? e.message : String(e)}`);
40
- }
41
- }
42
- try {
43
- const keyBase64 = readFileSync(keyPath, "utf8");
44
- return Buffer.from(keyBase64, "base64");
45
- }
46
- catch {
47
- // Backup the corrupted key file before throwing
48
- try {
49
- const backupPath = `${keyPath}.corrupted.${Date.now()}`;
50
- copyFileSync(keyPath, backupPath);
51
- }
52
- catch {
53
- /* backup failed, continue */
54
- }
55
- throw new Error(`Encryption key file is corrupted or unreadable: ${keyPath}. ` +
56
- `All previously encrypted data will be unrecoverable. ` +
57
- `Delete the key file to generate a new one.`);
58
- }
59
- }
60
- /**
61
- * Encrypt a string using AES-256-GCM
62
- * Note: Bun's crypto module doesn't support getAuthTag/setAuthTag directly,
63
- * so we use a simpler approach with AES-256-CBC + HMAC for authentication
64
- */
65
- export function encryptString(plaintext, key) {
66
- const iv = randomBytes(IV_LENGTH);
67
- const salt = randomBytes(SALT_LENGTH);
68
- // Derive encryption key and HMAC key from the main key
69
- const encKey = scryptSync(key, salt, KEY_LENGTH);
70
- const hmacSalt = Buffer.concat([salt, Buffer.from([1, 2, 3, 4])]);
71
- const hmacKey = scryptSync(key, hmacSalt, KEY_LENGTH);
72
- // Encrypt with AES-256-CBC
73
- const cipher = createCipheriv("aes-256-cbc", encKey, iv);
74
- let encrypted = cipher.update(plaintext, "utf8", "base64");
75
- encrypted += cipher.final("base64");
76
- // Create HMAC for authentication
77
- const hmac = createHmac("sha256", hmacKey);
78
- hmac.update(iv);
79
- hmac.update(Buffer.from(encrypted, "base64"));
80
- const authTag = hmac.digest();
81
- // Format: salt:iv:authTag:encrypted
82
- return `${salt.toString("base64")}:${iv.toString("base64")}:${authTag.toString("base64")}:${encrypted}`;
83
- }
84
- /**
85
- * Decrypt a string using AES-256-CBC + HMAC
86
- */
87
- export function decryptString(encryptedText, key) {
88
- const parts = encryptedText.split(":");
89
- if (parts.length !== 4) {
90
- throw new Error("Invalid encrypted format");
91
- }
92
- const salt = Buffer.from(parts[0], "base64");
93
- const iv = Buffer.from(parts[1], "base64");
94
- const authTag = Buffer.from(parts[2], "base64");
95
- const encrypted = parts[3];
96
- // Derive encryption key and HMAC key from the main key
97
- const encKey = scryptSync(key, salt, KEY_LENGTH);
98
- const hmacSalt = Buffer.concat([salt, Buffer.from([1, 2, 3, 4])]);
99
- const hmacKey = scryptSync(key, hmacSalt, KEY_LENGTH);
100
- // Verify HMAC
101
- const hmac = createHmac("sha256", hmacKey);
102
- hmac.update(iv);
103
- hmac.update(Buffer.from(encrypted, "base64"));
104
- const computedTag = hmac.digest();
105
- if (computedTag.length !== authTag.length || !timingSafeEqual(computedTag, authTag)) {
106
- throw new Error("Authentication failed: invalid tag");
107
- }
108
- // Decrypt with AES-256-CBC
109
- const decipher = createDecipheriv("aes-256-cbc", encKey, iv);
110
- let decrypted = decipher.update(encrypted, "base64", "utf8");
111
- decrypted += decipher.final("utf8");
112
- return decrypted;
113
- }
114
- import { createHmac } from "node:crypto";
115
- /**
116
- * Check if a string looks like it's encrypted
117
- */
118
- export function isEncryptedString(value) {
119
- // Encrypted strings have the format: salt:iv:authTag:encrypted
120
- // Each part is base64 encoded and separated by colons
121
- const parts = value.split(":");
122
- if (parts.length !== 4)
123
- return false;
124
- // Check if all parts are valid base64
125
- try {
126
- Buffer.from(parts[0], "base64");
127
- Buffer.from(parts[1], "base64");
128
- Buffer.from(parts[2], "base64");
129
- Buffer.from(parts[3], "base64");
130
- return true;
131
- }
132
- catch {
133
- return false;
134
- }
135
- }
136
- /**
137
- * Sensitive field names that should be encrypted
138
- */
139
- export const SENSITIVE_FIELD_NAMES = [
140
- "api_key",
141
- "apikey",
142
- "apiKey",
143
- "token",
144
- "access_token",
145
- "accessToken",
146
- "password",
147
- "passwd",
148
- "pwd",
149
- "secret",
150
- "private_key",
151
- "privateKey",
152
- "auth",
153
- "authorization",
154
- "key",
155
- ];
156
- /**
157
- * Check if a field name indicates sensitive data
158
- */
159
- export function isSensitiveFieldName(fieldName) {
160
- const lowerFieldName = fieldName.toLowerCase();
161
- return SENSITIVE_FIELD_NAMES.some((name) => lowerFieldName.includes(name));
162
- }
163
- /**
164
- * Encrypt sensitive fields in an object
165
- */
166
- export function encryptSensitiveFields(obj, key) {
167
- const result = {};
168
- for (const [fieldName, value] of Object.entries(obj)) {
169
- if (isSensitiveFieldName(fieldName) && typeof value === "string" && !isEncryptedString(value)) {
170
- result[fieldName] = encryptString(value, key);
171
- }
172
- else if (typeof value === "object" && value !== null) {
173
- if (Array.isArray(value)) {
174
- result[fieldName] = value.map((item) => typeof item === "object" && item !== null && !(item instanceof RegExp)
175
- ? encryptSensitiveFields(item, key)
176
- : item);
177
- }
178
- else if (value instanceof RegExp) {
179
- result[fieldName] = value;
180
- }
181
- else {
182
- result[fieldName] = encryptSensitiveFields(value, key);
183
- }
184
- }
185
- else {
186
- result[fieldName] = value;
187
- }
188
- }
189
- return result;
190
- }
191
- /**
192
- * Decrypt sensitive fields in an object
193
- */
194
- export function decryptSensitiveFields(obj, key) {
195
- const result = {};
196
- for (const [fieldName, value] of Object.entries(obj)) {
197
- if (isSensitiveFieldName(fieldName) && typeof value === "string" && isEncryptedString(value)) {
198
- try {
199
- result[fieldName] = decryptString(value, key);
200
- }
201
- catch {
202
- // If decryption fails, keep the encrypted value
203
- result[fieldName] = value;
204
- }
205
- }
206
- else if (typeof value === "object" && value !== null) {
207
- if (Array.isArray(value)) {
208
- result[fieldName] = value.map((item) => typeof item === "object" && item !== null && !(item instanceof RegExp)
209
- ? decryptSensitiveFields(item, key)
210
- : item);
211
- }
212
- else if (value instanceof RegExp) {
213
- result[fieldName] = value;
214
- }
215
- else {
216
- result[fieldName] = decryptSensitiveFields(value, key);
217
- }
218
- }
219
- else {
220
- result[fieldName] = value;
221
- }
222
- }
223
- return result;
224
- }
225
- /**
226
- * Create a wrapper for encrypting/decrypting config values
227
- */
228
- export class ConfigEncryptor {
229
- key;
230
- constructor(config) {
231
- this.key = getOrCreateEncryptionKey(config);
232
- }
233
- encrypt(config) {
234
- return encryptSensitiveFields(config, this.key);
235
- }
236
- decrypt(config) {
237
- return decryptSensitiveFields(config, this.key);
238
- }
239
- getKey() {
240
- return this.key;
241
- }
242
- }
@@ -1,14 +0,0 @@
1
- export * from "./command-validator";
2
- export * from "./path-validator";
3
- export * from "./network-validator";
4
- export * from "./content-scanner";
5
- export * from "./data-sanitizer";
6
- export * from "./audit-log";
7
- export * from "./rate-limiter";
8
- export * from "./encryption";
9
- export * from "./session-isolation";
10
- export * from "./session-encryption";
11
- export * from "./audit-notifier";
12
- export * from "./security-policies";
13
- // Re-export sanitizeUrl from network-validator to avoid conflicts
14
- export { sanitizeUrl } from "./network-validator";