micro-models-agent 0.28.17 → 0.29.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.
- package/dist/cli/commands.js +3 -116
- package/dist/cli/main.js +8 -35
- package/dist/cli/repl.js +611 -110
- package/dist/cli/setup.js +12 -32
- package/dist/config/config.js +30 -46
- package/dist/config/defaults.js +1 -10
- package/dist/config/security.js +8 -15
- package/dist/core/agent-moe.js +12 -24
- package/dist/core/agent.js +47 -281
- package/dist/core/bootstrap.js +36 -52
- package/dist/core/session-logger.js +2 -35
- package/dist/i18n/en.json +15 -79
- package/dist/i18n/index.js +9 -12
- package/dist/i18n/ru.json +15 -79
- package/dist/index.js +13 -13
- package/dist/llm/openai-compat.js +10 -39
- package/dist/logger/app-logger.js +16 -83
- package/dist/main.js +625 -243
- package/dist/modules/browser/session.js +60 -108
- package/dist/modules/context/history.js +15 -0
- package/dist/modules/context/manager.js +10 -119
- package/dist/modules/execution/auditor.js +39 -33
- package/dist/modules/execution/index.js +6 -8
- package/dist/modules/execution/module.js +32 -474
- package/dist/modules/execution/moe-executor.js +40 -97
- package/dist/modules/execution/planner.js +13 -63
- package/dist/modules/execution/stuck-detector.js +39 -252
- package/dist/modules/execution/tracker.js +7 -21
- package/dist/modules/execution/verifier.js +17 -46
- package/dist/modules/hallucination/confidence.js +2 -7
- package/dist/modules/hallucination/consistency.js +42 -8
- package/dist/modules/hallucination/detector.js +21 -26
- package/dist/modules/hallucination/factual.js +150 -170
- package/dist/modules/hallucination/index.js +4 -5
- package/dist/modules/index.js +5 -5
- package/dist/modules/mcp/client.js +2 -8
- package/dist/modules/memory/store.js +0 -4
- package/dist/modules/plugins/builtin/lint-on-write.js +38 -143
- package/dist/modules/processes/detect.js +34 -0
- package/dist/modules/processes/index.js +2 -1
- package/dist/modules/processes/registry.js +35 -125
- package/dist/modules/processes/runner.js +110 -9
- package/dist/modules/security/audit-log.js +10 -30
- package/dist/modules/security/command-validator.js +16 -42
- package/dist/modules/security/content-scanner.js +8 -9
- package/dist/modules/security/network-validator.js +2 -2
- package/dist/modules/security/path-validator.js +10 -64
- package/dist/modules/security/security-policies.js +67 -221
- package/dist/modules/security/session-encryption.js +25 -42
- package/dist/modules/session/manager.js +10 -15
- package/dist/modules/session/store.js +8 -62
- package/dist/modules/skills/index.js +3 -2
- package/dist/modules/skills/matcher.js +27 -0
- package/dist/modules/skills/module.js +23 -10
- package/dist/tools/bash.js +90 -287
- package/dist/tools/create-dir.js +1 -0
- package/dist/tools/delete-file.js +1 -0
- package/dist/tools/edit-file.js +8 -10
- package/dist/tools/executor.js +7 -57
- package/dist/tools/grep-tool.js +29 -51
- package/dist/tools/index.js +40 -55
- package/dist/tools/load-skill.js +18 -14
- package/dist/tools/move-file.js +2 -3
- package/dist/tools/pipeline-run.js +1 -1
- package/dist/tools/read-file.js +5 -15
- package/dist/tools/search-history.js +22 -42
- package/dist/tools/subagent.js +12 -21
- package/dist/tools/web-browse.js +25 -54
- package/dist/tools/web-fetch.js +34 -60
- package/dist/tools/web-search.js +20 -39
- package/dist/tools/write-file.js +10 -13
- package/dist/ui/diff.js +16 -9
- package/dist/ui/renderer.js +6 -69
- package/package.json +1 -1
- package/dist/cli/repl-commands.js +0 -633
- package/dist/core/workspace.js +0 -76
- package/dist/logger/file-log.js +0 -151
- package/dist/modules/certification/cli.js +0 -176
- package/dist/modules/certification/fact-checker.js +0 -84
- package/dist/modules/certification/loader.js +0 -111
- package/dist/modules/certification/manifest.js +0 -50
- package/dist/modules/certification/runner.js +0 -162
- package/dist/modules/certification/scenarios.js +0 -124
- package/dist/modules/certification/types.js +0 -1
- package/dist/modules/execution/plan-coverage.js +0 -68
- package/dist/modules/execution/plan-persister.js +0 -46
- package/dist/modules/execution/plan-store.js +0 -159
- package/dist/modules/hallucination/js-identifiers.js +0 -72
- package/dist/modules/hallucination/llm-judge.js +0 -103
- package/dist/modules/lsp/client.js +0 -235
- package/dist/modules/lsp/config.js +0 -81
- package/dist/modules/lsp/index.js +0 -3
- package/dist/modules/lsp/module.js +0 -68
- package/dist/modules/lsp/types.js +0 -1
|
@@ -1,48 +1,28 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, appendFileSync } from "fs";
|
|
2
|
-
import { resolve
|
|
2
|
+
import { resolve } from "path";
|
|
3
3
|
import { homedir } from "os";
|
|
4
4
|
import { globalAuditNotifier } from "./audit-notifier";
|
|
5
|
-
let _globalAuditDir = resolve(homedir(), ".mma", "logs");
|
|
6
|
-
let _sessionAuditDir = null;
|
|
7
|
-
function getAuditDir() {
|
|
8
|
-
return _sessionAuditDir ?? _globalAuditDir;
|
|
9
|
-
}
|
|
10
5
|
/**
|
|
11
|
-
*
|
|
12
|
-
* are written to the session-specific directory instead of the global one.
|
|
6
|
+
* Directory and file path for audit logs
|
|
13
7
|
*/
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if (!existsSync(dir)) {
|
|
17
|
-
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
18
|
-
}
|
|
19
|
-
}
|
|
8
|
+
const AUDIT_LOG_DIR = resolve(homedir(), ".mma", "logs");
|
|
9
|
+
const AUDIT_LOG_PATH = resolve(AUDIT_LOG_DIR, "audit.jsonl");
|
|
20
10
|
/**
|
|
21
|
-
*
|
|
11
|
+
* Ensure audit log directory exists
|
|
22
12
|
*/
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Set the global audit log directory (used when no session dir is active).
|
|
28
|
-
*/
|
|
29
|
-
export function setGlobalAuditDir(dir) {
|
|
30
|
-
_globalAuditDir = dir;
|
|
31
|
-
if (!existsSync(dir)) {
|
|
32
|
-
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
13
|
+
function ensureAuditLogDir() {
|
|
14
|
+
if (!existsSync(AUDIT_LOG_DIR)) {
|
|
15
|
+
mkdirSync(AUDIT_LOG_DIR, { recursive: true, mode: 0o700 });
|
|
33
16
|
}
|
|
34
17
|
}
|
|
35
18
|
/**
|
|
36
19
|
* Write an audit log entry
|
|
37
20
|
*/
|
|
38
21
|
export function logAudit(entry) {
|
|
39
|
-
|
|
40
|
-
if (!existsSync(dir)) {
|
|
41
|
-
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
42
|
-
}
|
|
22
|
+
ensureAuditLogDir();
|
|
43
23
|
try {
|
|
44
24
|
const logEntry = JSON.stringify(entry);
|
|
45
|
-
appendFileSync(
|
|
25
|
+
appendFileSync(AUDIT_LOG_PATH, logEntry + "\n", "utf8");
|
|
46
26
|
}
|
|
47
27
|
catch {
|
|
48
28
|
// Silently fail if we can't write to audit log
|
|
@@ -1,25 +1,11 @@
|
|
|
1
1
|
import { DEFAULT_SECURITY_CONFIG } from "../../config/security";
|
|
2
2
|
// Fallback in case DEFAULT_SECURITY_CONFIG is not available
|
|
3
3
|
const FALLBACK_BASH_CONFIG = {
|
|
4
|
-
|
|
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
|
-
],
|
|
4
|
+
blacklist: ['rm', 'dd', 'chmod', 'wget', 'curl', 'scp', 'ssh', 'nc', 'netcat', 'powershell', 'pwsh', 'cmd'],
|
|
19
5
|
whitelist: [],
|
|
20
6
|
blockDangerousFlags: false,
|
|
21
|
-
dangerousFlags: [
|
|
22
|
-
dangerousOperators: [
|
|
7
|
+
dangerousFlags: ['--force', '-rf', '--no-preserve-root'],
|
|
8
|
+
dangerousOperators: ['>', '>>', '2>', '2>>', '|', '&&', '||', ';', '`'],
|
|
23
9
|
logCommands: true,
|
|
24
10
|
};
|
|
25
11
|
export const DEFAULT_BASH_CONFIG = DEFAULT_SECURITY_CONFIG?.bash || FALLBACK_BASH_CONFIG;
|
|
@@ -46,11 +32,7 @@ function extractBaseCommand(trimmed) {
|
|
|
46
32
|
i++;
|
|
47
33
|
continue;
|
|
48
34
|
}
|
|
49
|
-
if (tok === "sudo" ||
|
|
50
|
-
tok === "env" ||
|
|
51
|
-
tok === "command" ||
|
|
52
|
-
tok === "exec" ||
|
|
53
|
-
tok === "nohup") {
|
|
35
|
+
if (tok === "sudo" || tok === "env" || tok === "command" || tok === "exec" || tok === "nohup") {
|
|
54
36
|
i++;
|
|
55
37
|
continue;
|
|
56
38
|
}
|
|
@@ -63,7 +45,7 @@ function extractBaseCommand(trimmed) {
|
|
|
63
45
|
const parts = raw.split(/[\\/]/);
|
|
64
46
|
const basename = parts[parts.length - 1] || raw;
|
|
65
47
|
// Strip .exe/.cmd/.bat extensions for blacklist matching (e.g. powershell.exe → powershell)
|
|
66
|
-
return basename.replace(/\.(exe|cmd|bat)$/i,
|
|
48
|
+
return basename.replace(/\.(exe|cmd|bat)$/i, '') || basename;
|
|
67
49
|
}
|
|
68
50
|
/**
|
|
69
51
|
* Check if a shell operator appears as a standalone token in the command.
|
|
@@ -74,16 +56,16 @@ function extractBaseCommand(trimmed) {
|
|
|
74
56
|
* "2>" does NOT match "2>>" (append stderr)
|
|
75
57
|
*/
|
|
76
58
|
function containsOperator(command, op) {
|
|
77
|
-
const escaped = op.replace(/[.*+?^${}()|[\]\\]/g,
|
|
59
|
+
const escaped = op.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
78
60
|
// For single-char operators that are prefixes of multi-char ones,
|
|
79
61
|
// ensure they don't match when part of a longer sequence.
|
|
80
62
|
// Note: "&" is no longer in dangerousOperators (&& is legitimate),
|
|
81
63
|
// but keep the special case for defense-in-depth if manually added.
|
|
82
|
-
if (op ===
|
|
64
|
+
if (op === '&')
|
|
83
65
|
return /(?<!&)&(?!&)/.test(command);
|
|
84
|
-
if (op ===
|
|
66
|
+
if (op === '|')
|
|
85
67
|
return /(?<!\|)\|(?!\|)/.test(command);
|
|
86
|
-
if (op ===
|
|
68
|
+
if (op === '>')
|
|
87
69
|
return /(?<!>)>(?!>)/.test(command);
|
|
88
70
|
// For everything else (including multi-char like ">>", "2>", "2>>", ";", "`"),
|
|
89
71
|
// match literally.
|
|
@@ -93,17 +75,11 @@ function containsOperator(command, op) {
|
|
|
93
75
|
* Check if a command is allowed based on security configuration
|
|
94
76
|
*/
|
|
95
77
|
export function isCommandAllowed(command, securityConfig) {
|
|
96
|
-
// If security
|
|
97
|
-
|
|
98
|
-
return { allowed: true, sanitizedCommand: command };
|
|
99
|
-
}
|
|
78
|
+
// If no security config, allow everything (backward compatibility)
|
|
79
|
+
let config = securityConfig || DEFAULT_BASH_CONFIG;
|
|
100
80
|
// Ensure config has all required fields
|
|
101
|
-
let config = securityConfig;
|
|
102
81
|
if (!config.blacklist || !Array.isArray(config.blacklist)) {
|
|
103
|
-
config =
|
|
104
|
-
...FALLBACK_BASH_CONFIG,
|
|
105
|
-
enabled: config.enabled ?? FALLBACK_BASH_CONFIG.enabled,
|
|
106
|
-
};
|
|
82
|
+
config = FALLBACK_BASH_CONFIG;
|
|
107
83
|
}
|
|
108
84
|
const trimmedCommand = command.trim();
|
|
109
85
|
if (!trimmedCommand) {
|
|
@@ -111,7 +87,7 @@ export function isCommandAllowed(command, securityConfig) {
|
|
|
111
87
|
}
|
|
112
88
|
// Shell wrappers are always blocked — they bypass all command validation.
|
|
113
89
|
// The agent should use the bash tool directly, not shell interpreters.
|
|
114
|
-
const ALWAYS_BLOCKED = [
|
|
90
|
+
const ALWAYS_BLOCKED = ['powershell', 'pwsh', 'cmd'];
|
|
115
91
|
const baseForShellCheck = extractBaseCommand(trimmedCommand);
|
|
116
92
|
if (ALWAYS_BLOCKED.includes(baseForShellCheck)) {
|
|
117
93
|
return {
|
|
@@ -150,9 +126,7 @@ export function isCommandAllowed(command, securityConfig) {
|
|
|
150
126
|
};
|
|
151
127
|
}
|
|
152
128
|
// Also check the raw first token in case it's a simple name
|
|
153
|
-
const rawFirst = trimmedCommand
|
|
154
|
-
.split(/\s+/)[0]
|
|
155
|
-
.replace(/\.(exe|cmd|bat)$/i, "");
|
|
129
|
+
const rawFirst = trimmedCommand.split(/\s+/)[0].replace(/\.(exe|cmd|bat)$/i, '');
|
|
156
130
|
if (rawFirst !== baseCommand && config.blacklist.includes(rawFirst)) {
|
|
157
131
|
return {
|
|
158
132
|
allowed: false,
|
|
@@ -196,7 +170,7 @@ export function sanitizeCommandForLog(command) {
|
|
|
196
170
|
];
|
|
197
171
|
let sanitized = command;
|
|
198
172
|
for (const pattern of patterns) {
|
|
199
|
-
if (pattern.toString().includes(
|
|
173
|
+
if (pattern.toString().includes('($1')) {
|
|
200
174
|
// For patterns with capture groups, use the group in replacement
|
|
201
175
|
sanitized = sanitized.replace(pattern, (match, p1) => {
|
|
202
176
|
return `${p1}=[REDACTED]`;
|
|
@@ -204,7 +178,7 @@ export function sanitizeCommandForLog(command) {
|
|
|
204
178
|
}
|
|
205
179
|
else {
|
|
206
180
|
// For simple patterns, just replace with [REDACTED]
|
|
207
|
-
sanitized = sanitized.replace(pattern,
|
|
181
|
+
sanitized = sanitized.replace(pattern, '[REDACTED]');
|
|
208
182
|
}
|
|
209
183
|
}
|
|
210
184
|
return sanitized;
|
|
@@ -1,9 +1,5 @@
|
|
|
1
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).
|
|
2
|
+
* Default dangerous patterns to scan for in file content
|
|
7
3
|
*/
|
|
8
4
|
const DEFAULT_DANGEROUS_PATTERNS = [
|
|
9
5
|
/eval\(/gi,
|
|
@@ -15,6 +11,9 @@ const DEFAULT_DANGEROUS_PATTERNS = [
|
|
|
15
11
|
/require\('net'\).createServer/gi,
|
|
16
12
|
/require\('fs'\).writeFileSync/gi,
|
|
17
13
|
/require\('fs'\).unlinkSync/gi,
|
|
14
|
+
/process\.exit\(/gi,
|
|
15
|
+
/setInterval\(/gi,
|
|
16
|
+
/setTimeout\(/gi,
|
|
18
17
|
/while\(true\)/gi,
|
|
19
18
|
/for\(;;\)/gi,
|
|
20
19
|
/rm -rf/gi,
|
|
@@ -22,6 +21,8 @@ const DEFAULT_DANGEROUS_PATTERNS = [
|
|
|
22
21
|
/chmod 777/gi,
|
|
23
22
|
/wget.*\|.*sh/gi,
|
|
24
23
|
/curl.*\|.*sh/gi,
|
|
24
|
+
/\$\{/gi,
|
|
25
|
+
/`[^`]+`/gi,
|
|
25
26
|
];
|
|
26
27
|
/**
|
|
27
28
|
* Scan file content for dangerous patterns
|
|
@@ -32,9 +33,7 @@ export function scanContent(content, filePath, config) {
|
|
|
32
33
|
return { allowed: true };
|
|
33
34
|
}
|
|
34
35
|
// Use default patterns if none provided
|
|
35
|
-
const patterns = config.dangerousPatterns?.length
|
|
36
|
-
? config.dangerousPatterns
|
|
37
|
-
: DEFAULT_DANGEROUS_PATTERNS;
|
|
36
|
+
const patterns = config.dangerousPatterns?.length ? config.dangerousPatterns : DEFAULT_DANGEROUS_PATTERNS;
|
|
38
37
|
const matches = [];
|
|
39
38
|
for (const pattern of patterns) {
|
|
40
39
|
const match = content.match(pattern);
|
|
@@ -45,7 +44,7 @@ export function scanContent(content, filePath, config) {
|
|
|
45
44
|
if (matches.length > 0) {
|
|
46
45
|
return {
|
|
47
46
|
allowed: false,
|
|
48
|
-
reason: `Content contains dangerous patterns
|
|
47
|
+
reason: `Content contains dangerous patterns: ${matches.slice(0, 3).join(", ")}`,
|
|
49
48
|
matches,
|
|
50
49
|
};
|
|
51
50
|
}
|
|
@@ -3,8 +3,8 @@ import { sanitizeLogMessage } from "./data-sanitizer";
|
|
|
3
3
|
* Check if a URL is allowed based on security configuration
|
|
4
4
|
*/
|
|
5
5
|
export function isUrlAllowed(url, securityConfig) {
|
|
6
|
-
// If security
|
|
7
|
-
if (!securityConfig
|
|
6
|
+
// If no security config, allow everything (backward compatibility)
|
|
7
|
+
if (!securityConfig) {
|
|
8
8
|
return { allowed: true };
|
|
9
9
|
}
|
|
10
10
|
try {
|
|
@@ -8,36 +8,13 @@ export function isPathInScope(baseDir, targetPath, scope, securityConfig) {
|
|
|
8
8
|
const targetResolved = resolve(baseDir, normalize(targetPath));
|
|
9
9
|
// First, check that path is within baseDir
|
|
10
10
|
if (!targetResolved.startsWith(baseResolved)) {
|
|
11
|
-
return {
|
|
12
|
-
allowed: false,
|
|
13
|
-
reason: "Path is outside the base working directory",
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
// If security is disabled, skip pattern validation
|
|
17
|
-
if (!securityConfig?.enabled) {
|
|
18
|
-
// Still enforce subagent scope if present
|
|
19
|
-
if (scope) {
|
|
20
|
-
const isReadScope = scope.read_only_files.some((f) => targetResolved.startsWith(resolve(baseDir, f)));
|
|
21
|
-
const isWriteScope = scope.allowed_files.some((f) => targetResolved.startsWith(resolve(baseDir, f)));
|
|
22
|
-
if (isWriteScope)
|
|
23
|
-
return { allowed: true };
|
|
24
|
-
if (isReadScope)
|
|
25
|
-
return { allowed: true, reason: "Read-only file" };
|
|
26
|
-
return {
|
|
27
|
-
allowed: false,
|
|
28
|
-
reason: "Path is not within the allowed scope for this sub-agent",
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
return { allowed: true };
|
|
11
|
+
return { allowed: false, reason: "Path is outside the base working directory" };
|
|
32
12
|
}
|
|
33
13
|
// Check global denied patterns
|
|
34
14
|
if (securityConfig?.denied?.length) {
|
|
35
15
|
for (const pattern of securityConfig.denied) {
|
|
36
16
|
if (matchesGlobPattern(targetResolved, baseDir, pattern)) {
|
|
37
|
-
return {
|
|
38
|
-
allowed: false,
|
|
39
|
-
reason: `Path matches denied pattern: ${pattern}`,
|
|
40
|
-
};
|
|
17
|
+
return { allowed: false, reason: `Path matches denied pattern: ${pattern}` };
|
|
41
18
|
}
|
|
42
19
|
}
|
|
43
20
|
}
|
|
@@ -51,10 +28,7 @@ export function isPathInScope(baseDir, targetPath, scope, securityConfig) {
|
|
|
51
28
|
}
|
|
52
29
|
}
|
|
53
30
|
if (!isAllowed) {
|
|
54
|
-
return {
|
|
55
|
-
allowed: false,
|
|
56
|
-
reason: "Path does not match any allowed pattern",
|
|
57
|
-
};
|
|
31
|
+
return { allowed: false, reason: "Path does not match any allowed pattern" };
|
|
58
32
|
}
|
|
59
33
|
}
|
|
60
34
|
// Check tool-specific scope (for subagents)
|
|
@@ -80,33 +54,13 @@ export function isPathWritable(baseDir, targetPath, scope, securityConfig) {
|
|
|
80
54
|
const targetResolved = resolve(baseDir, normalize(targetPath));
|
|
81
55
|
// First, check that path is within baseDir
|
|
82
56
|
if (!targetResolved.startsWith(baseResolved)) {
|
|
83
|
-
return {
|
|
84
|
-
allowed: false,
|
|
85
|
-
reason: "Path is outside the base working directory",
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
// If security is disabled, skip pattern validation
|
|
89
|
-
if (!securityConfig?.enabled) {
|
|
90
|
-
// Still enforce subagent scope if present
|
|
91
|
-
if (scope) {
|
|
92
|
-
const isWriteScope = scope.allowed_files.some((f) => targetResolved.startsWith(resolve(baseDir, f)));
|
|
93
|
-
if (isWriteScope)
|
|
94
|
-
return { allowed: true };
|
|
95
|
-
return {
|
|
96
|
-
allowed: false,
|
|
97
|
-
reason: "Path is not within the allowed scope for this sub-agent",
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
return { allowed: true };
|
|
57
|
+
return { allowed: false, reason: "Path is outside the base working directory" };
|
|
101
58
|
}
|
|
102
59
|
// Check global denied patterns
|
|
103
60
|
if (securityConfig?.denied?.length) {
|
|
104
61
|
for (const pattern of securityConfig.denied) {
|
|
105
62
|
if (matchesGlobPattern(targetResolved, baseDir, pattern)) {
|
|
106
|
-
return {
|
|
107
|
-
allowed: false,
|
|
108
|
-
reason: `Path matches denied pattern: ${pattern}`,
|
|
109
|
-
};
|
|
63
|
+
return { allowed: false, reason: `Path matches denied pattern: ${pattern}` };
|
|
110
64
|
}
|
|
111
65
|
}
|
|
112
66
|
}
|
|
@@ -120,10 +74,7 @@ export function isPathWritable(baseDir, targetPath, scope, securityConfig) {
|
|
|
120
74
|
}
|
|
121
75
|
}
|
|
122
76
|
if (!isAllowed) {
|
|
123
|
-
return {
|
|
124
|
-
allowed: false,
|
|
125
|
-
reason: "Path does not match any allowed pattern",
|
|
126
|
-
};
|
|
77
|
+
return { allowed: false, reason: "Path does not match any allowed pattern" };
|
|
127
78
|
}
|
|
128
79
|
}
|
|
129
80
|
// Check tool-specific scope (for subagents)
|
|
@@ -154,8 +105,7 @@ function matchesGlobPattern(targetPath, baseDir, pattern) {
|
|
|
154
105
|
return true;
|
|
155
106
|
}
|
|
156
107
|
// Check if target is inside the directory
|
|
157
|
-
if (resolvedTargetPath.startsWith(resolvedDirPattern + "/") ||
|
|
158
|
-
resolvedTargetPath.startsWith(resolvedDirPattern + "\\")) {
|
|
108
|
+
if (resolvedTargetPath.startsWith(resolvedDirPattern + "/") || resolvedTargetPath.startsWith(resolvedDirPattern + "\\")) {
|
|
159
109
|
return true;
|
|
160
110
|
}
|
|
161
111
|
return false;
|
|
@@ -166,8 +116,7 @@ function matchesGlobPattern(targetPath, baseDir, pattern) {
|
|
|
166
116
|
const prefix = parts[0];
|
|
167
117
|
const suffix = parts[1];
|
|
168
118
|
const resolvedPrefix = resolve(baseDir, prefix);
|
|
169
|
-
if (!resolvedTargetPath.startsWith(resolvedPrefix + "/") &&
|
|
170
|
-
!resolvedTargetPath.startsWith(resolvedPrefix + "\\")) {
|
|
119
|
+
if (!resolvedTargetPath.startsWith(resolvedPrefix + "/") && !resolvedTargetPath.startsWith(resolvedPrefix + "\\")) {
|
|
171
120
|
return false;
|
|
172
121
|
}
|
|
173
122
|
const remainingPath = resolvedTargetPath.slice(resolvedPrefix.length);
|
|
@@ -183,8 +132,7 @@ function matchesGlobPattern(targetPath, baseDir, pattern) {
|
|
|
183
132
|
const prefix = parts[0];
|
|
184
133
|
const suffix = parts[1];
|
|
185
134
|
const resolvedPrefix = resolve(baseDir, prefix);
|
|
186
|
-
if (!resolvedTargetPath.startsWith(resolvedPrefix + "/") &&
|
|
187
|
-
!resolvedTargetPath.startsWith(resolvedPrefix + "\\")) {
|
|
135
|
+
if (!resolvedTargetPath.startsWith(resolvedPrefix + "/") && !resolvedTargetPath.startsWith(resolvedPrefix + "\\")) {
|
|
188
136
|
return false;
|
|
189
137
|
}
|
|
190
138
|
if (!suffix) {
|
|
@@ -199,9 +147,7 @@ function matchesGlobPattern(targetPath, baseDir, pattern) {
|
|
|
199
147
|
.replace(/\?/g, ".")
|
|
200
148
|
.replace(/\./g, "\\.");
|
|
201
149
|
const regex = new RegExp(`^${regexPattern}$`);
|
|
202
|
-
const relativePath = resolvedTargetPath
|
|
203
|
-
.slice(resolvedBaseDir.length + 1)
|
|
204
|
-
.replace(/\\/g, "/");
|
|
150
|
+
const relativePath = resolvedTargetPath.slice(resolvedBaseDir.length + 1).replace(/\\/g, "/");
|
|
205
151
|
return regex.test(relativePath);
|
|
206
152
|
}
|
|
207
153
|
// Exact match
|