@eddacraft/anvil-runtime 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.
- package/LICENSE +14 -0
- package/dist/cache/cache-key.d.ts +45 -0
- package/dist/cache/cache-key.d.ts.map +1 -0
- package/dist/cache/cache-key.js +135 -0
- package/dist/cache/index.d.ts +27 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +38 -0
- package/dist/cache/providers/file-cache.d.ts +63 -0
- package/dist/cache/providers/file-cache.d.ts.map +1 -0
- package/dist/cache/providers/file-cache.js +369 -0
- package/dist/cache/providers/memory-cache.d.ts +52 -0
- package/dist/cache/providers/memory-cache.d.ts.map +1 -0
- package/dist/cache/providers/memory-cache.js +197 -0
- package/dist/cache/providers/null-cache.d.ts +26 -0
- package/dist/cache/providers/null-cache.d.ts.map +1 -0
- package/dist/cache/providers/null-cache.js +50 -0
- package/dist/cache/types.d.ts +114 -0
- package/dist/cache/types.d.ts.map +1 -0
- package/dist/cache/types.js +4 -0
- package/dist/concurrency/agent.d.ts +137 -0
- package/dist/concurrency/agent.d.ts.map +1 -0
- package/dist/concurrency/agent.js +440 -0
- package/dist/concurrency/atomic.d.ts +93 -0
- package/dist/concurrency/atomic.d.ts.map +1 -0
- package/dist/concurrency/atomic.js +281 -0
- package/dist/concurrency/git-agent.d.ts +114 -0
- package/dist/concurrency/git-agent.d.ts.map +1 -0
- package/dist/concurrency/git-agent.js +313 -0
- package/dist/concurrency/index.d.ts +95 -0
- package/dist/concurrency/index.d.ts.map +1 -0
- package/dist/concurrency/index.js +127 -0
- package/dist/concurrency/lock-manager.d.ts +170 -0
- package/dist/concurrency/lock-manager.d.ts.map +1 -0
- package/dist/concurrency/lock-manager.js +525 -0
- package/dist/concurrency/queue-manager.d.ts +166 -0
- package/dist/concurrency/queue-manager.d.ts.map +1 -0
- package/dist/concurrency/queue-manager.js +442 -0
- package/dist/concurrency/types.d.ts +382 -0
- package/dist/concurrency/types.d.ts.map +1 -0
- package/dist/concurrency/types.js +204 -0
- package/dist/export/constraint-collector.d.ts +175 -0
- package/dist/export/constraint-collector.d.ts.map +1 -0
- package/dist/export/constraint-collector.js +203 -0
- package/dist/export/formatters/llms-txt-formatter.d.ts +89 -0
- package/dist/export/formatters/llms-txt-formatter.d.ts.map +1 -0
- package/dist/export/formatters/llms-txt-formatter.js +249 -0
- package/dist/export/formatters/mcp-resource-formatter.d.ts +186 -0
- package/dist/export/formatters/mcp-resource-formatter.d.ts.map +1 -0
- package/dist/export/formatters/mcp-resource-formatter.js +139 -0
- package/dist/export/formatters/prompt-formatter.d.ts +83 -0
- package/dist/export/formatters/prompt-formatter.d.ts.map +1 -0
- package/dist/export/formatters/prompt-formatter.js +256 -0
- package/dist/export/index.d.ts +10 -0
- package/dist/export/index.d.ts.map +1 -0
- package/dist/export/index.js +9 -0
- package/dist/gate/check.interface.d.ts +15 -0
- package/dist/gate/check.interface.d.ts.map +1 -0
- package/dist/gate/check.interface.js +18 -0
- package/dist/gate/checks/antipattern.check.d.ts +27 -0
- package/dist/gate/checks/antipattern.check.d.ts.map +1 -0
- package/dist/gate/checks/antipattern.check.js +140 -0
- package/dist/gate/checks/architecture/circular-detector.d.ts +33 -0
- package/dist/gate/checks/architecture/circular-detector.d.ts.map +1 -0
- package/dist/gate/checks/architecture/circular-detector.js +71 -0
- package/dist/gate/checks/architecture/dependency-analyzer.d.ts +81 -0
- package/dist/gate/checks/architecture/dependency-analyzer.d.ts.map +1 -0
- package/dist/gate/checks/architecture/dependency-analyzer.js +136 -0
- package/dist/gate/checks/architecture/layer-validator.d.ts +75 -0
- package/dist/gate/checks/architecture/layer-validator.d.ts.map +1 -0
- package/dist/gate/checks/architecture/layer-validator.js +193 -0
- package/dist/gate/checks/architecture.check.d.ts +56 -0
- package/dist/gate/checks/architecture.check.d.ts.map +1 -0
- package/dist/gate/checks/architecture.check.js +394 -0
- package/dist/gate/checks/command-safety.check.d.ts +12 -0
- package/dist/gate/checks/command-safety.check.d.ts.map +1 -0
- package/dist/gate/checks/command-safety.check.js +230 -0
- package/dist/gate/checks/coverage.check.d.ts +9 -0
- package/dist/gate/checks/coverage.check.d.ts.map +1 -0
- package/dist/gate/checks/coverage.check.js +81 -0
- package/dist/gate/checks/dependency.check.d.ts +17 -0
- package/dist/gate/checks/dependency.check.d.ts.map +1 -0
- package/dist/gate/checks/dependency.check.js +342 -0
- package/dist/gate/checks/eslint.check.d.ts +14 -0
- package/dist/gate/checks/eslint.check.d.ts.map +1 -0
- package/dist/gate/checks/eslint.check.js +79 -0
- package/dist/gate/checks/policy.check.d.ts +78 -0
- package/dist/gate/checks/policy.check.d.ts.map +1 -0
- package/dist/gate/checks/policy.check.js +457 -0
- package/dist/gate/checks/secret/entropy-detector.d.ts +44 -0
- package/dist/gate/checks/secret/entropy-detector.d.ts.map +1 -0
- package/dist/gate/checks/secret/entropy-detector.js +76 -0
- package/dist/gate/checks/secret/git-scanner.d.ts +36 -0
- package/dist/gate/checks/secret/git-scanner.d.ts.map +1 -0
- package/dist/gate/checks/secret/git-scanner.js +90 -0
- package/dist/gate/checks/secret/secret-patterns.d.ts +42 -0
- package/dist/gate/checks/secret/secret-patterns.d.ts.map +1 -0
- package/dist/gate/checks/secret/secret-patterns.js +137 -0
- package/dist/gate/checks/secret.check.d.ts +56 -0
- package/dist/gate/checks/secret.check.d.ts.map +1 -0
- package/dist/gate/checks/secret.check.js +245 -0
- package/dist/gate/config/command-safety-config.d.ts +5 -0
- package/dist/gate/config/command-safety-config.d.ts.map +1 -0
- package/dist/gate/config/command-safety-config.js +69 -0
- package/dist/gate/config/index.d.ts +2 -0
- package/dist/gate/config/index.d.ts.map +1 -0
- package/dist/gate/config/index.js +1 -0
- package/dist/gate/formatters/command-safety-formatter.d.ts +10 -0
- package/dist/gate/formatters/command-safety-formatter.d.ts.map +1 -0
- package/dist/gate/formatters/command-safety-formatter.js +64 -0
- package/dist/gate/formatters/index.d.ts +2 -0
- package/dist/gate/formatters/index.d.ts.map +1 -0
- package/dist/gate/formatters/index.js +1 -0
- package/dist/gate/gate-config.d.ts +44 -0
- package/dist/gate/gate-config.d.ts.map +1 -0
- package/dist/gate/gate-config.js +334 -0
- package/dist/gate/gate-runner.d.ts +160 -0
- package/dist/gate/gate-runner.d.ts.map +1 -0
- package/dist/gate/gate-runner.js +531 -0
- package/dist/gate/index.d.ts +20 -0
- package/dist/gate/index.d.ts.map +1 -0
- package/dist/gate/index.js +14 -0
- package/dist/gate/parsers/command-parser.d.ts +18 -0
- package/dist/gate/parsers/command-parser.d.ts.map +1 -0
- package/dist/gate/parsers/command-parser.js +363 -0
- package/dist/gate/parsers/index.d.ts +2 -0
- package/dist/gate/parsers/index.d.ts.map +1 -0
- package/dist/gate/parsers/index.js +1 -0
- package/dist/gate/policy/index.d.ts +12 -0
- package/dist/gate/policy/index.d.ts.map +1 -0
- package/dist/gate/policy/index.js +10 -0
- package/dist/gate/rules/default-filesystem-rules.d.ts +3 -0
- package/dist/gate/rules/default-filesystem-rules.d.ts.map +1 -0
- package/dist/gate/rules/default-filesystem-rules.js +201 -0
- package/dist/gate/rules/default-git-rules.d.ts +3 -0
- package/dist/gate/rules/default-git-rules.d.ts.map +1 -0
- package/dist/gate/rules/default-git-rules.js +192 -0
- package/dist/gate/rules/index.d.ts +5 -0
- package/dist/gate/rules/index.d.ts.map +1 -0
- package/dist/gate/rules/index.js +3 -0
- package/dist/gate/rules/rule-matcher.d.ts +27 -0
- package/dist/gate/rules/rule-matcher.d.ts.map +1 -0
- package/dist/gate/rules/rule-matcher.js +228 -0
- package/dist/gate/rules/types.d.ts +250 -0
- package/dist/gate/rules/types.d.ts.map +1 -0
- package/dist/gate/rules/types.js +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/types/gate.types.d.ts +42 -0
- package/dist/types/gate.types.d.ts.map +1 -0
- package/dist/types/gate.types.js +94 -0
- package/dist/watch/debouncer.d.ts +90 -0
- package/dist/watch/debouncer.d.ts.map +1 -0
- package/dist/watch/debouncer.js +135 -0
- package/dist/watch/file-watcher.d.ts +73 -0
- package/dist/watch/file-watcher.d.ts.map +1 -0
- package/dist/watch/file-watcher.js +121 -0
- package/dist/watch/git-status.d.ts +98 -0
- package/dist/watch/git-status.d.ts.map +1 -0
- package/dist/watch/git-status.js +266 -0
- package/dist/watch/index.d.ts +16 -0
- package/dist/watch/index.d.ts.map +1 -0
- package/dist/watch/index.js +15 -0
- package/dist/watch/orchestrator.d.ts +113 -0
- package/dist/watch/orchestrator.d.ts.map +1 -0
- package/dist/watch/orchestrator.js +409 -0
- package/dist/watch/types.d.ts +190 -0
- package/dist/watch/types.d.ts.map +1 -0
- package/dist/watch/types.js +76 -0
- package/package.json +60 -0
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
import { parse as shellParse } from 'shell-quote';
|
|
2
|
+
import { createDebugger } from '@eddacraft/anvil-core';
|
|
3
|
+
const debug = createDebugger('gate');
|
|
4
|
+
const MAX_UNWRAP_DEPTH = 5;
|
|
5
|
+
const SHELL_WRAPPERS = ['bash', 'sh', 'zsh', 'dash'];
|
|
6
|
+
const PRIVILEGED_WRAPPERS = ['sudo', 'doas'];
|
|
7
|
+
const ENV_WRAPPERS = ['env', 'command', 'nohup', 'nice', 'time', 'strace'];
|
|
8
|
+
const INTERPRETER_COMMANDS = ['python', 'python3', 'node', 'ruby', 'perl', 'php'];
|
|
9
|
+
const COMMAND_OPERATORS = ['and', 'or', ';', '|', '||', '&&'];
|
|
10
|
+
function isShellWrapper(cmd) {
|
|
11
|
+
return SHELL_WRAPPERS.includes(cmd);
|
|
12
|
+
}
|
|
13
|
+
function isPrivilegedWrapper(cmd) {
|
|
14
|
+
return PRIVILEGED_WRAPPERS.includes(cmd);
|
|
15
|
+
}
|
|
16
|
+
function isEnvWrapper(cmd) {
|
|
17
|
+
return ENV_WRAPPERS.includes(cmd);
|
|
18
|
+
}
|
|
19
|
+
function isInterpreter(cmd) {
|
|
20
|
+
return INTERPRETER_COMMANDS.includes(cmd);
|
|
21
|
+
}
|
|
22
|
+
function isGlobToken(token) {
|
|
23
|
+
return (typeof token === 'object' &&
|
|
24
|
+
token !== null &&
|
|
25
|
+
'op' in token &&
|
|
26
|
+
token.op === 'glob' &&
|
|
27
|
+
'pattern' in token);
|
|
28
|
+
}
|
|
29
|
+
function isOperatorToken(token) {
|
|
30
|
+
return (typeof token === 'object' &&
|
|
31
|
+
token !== null &&
|
|
32
|
+
'op' in token &&
|
|
33
|
+
COMMAND_OPERATORS.includes(token.op));
|
|
34
|
+
}
|
|
35
|
+
function splitByOperators(tokens) {
|
|
36
|
+
const commands = [];
|
|
37
|
+
let currentTokens = [];
|
|
38
|
+
let lastOperator;
|
|
39
|
+
for (const token of tokens) {
|
|
40
|
+
if (isOperatorToken(token)) {
|
|
41
|
+
if (currentTokens.length > 0) {
|
|
42
|
+
commands.push({ tokens: currentTokens, operator: lastOperator });
|
|
43
|
+
currentTokens = [];
|
|
44
|
+
}
|
|
45
|
+
lastOperator = token.op;
|
|
46
|
+
}
|
|
47
|
+
else if (typeof token === 'string') {
|
|
48
|
+
currentTokens.push(token);
|
|
49
|
+
}
|
|
50
|
+
else if (isGlobToken(token)) {
|
|
51
|
+
currentTokens.push(token.pattern);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (currentTokens.length > 0) {
|
|
55
|
+
commands.push({ tokens: currentTokens, operator: lastOperator });
|
|
56
|
+
}
|
|
57
|
+
return commands;
|
|
58
|
+
}
|
|
59
|
+
function tokenise(cmd) {
|
|
60
|
+
const parsed = shellParse(cmd);
|
|
61
|
+
return parsed
|
|
62
|
+
.map((token) => {
|
|
63
|
+
if (typeof token === 'string') {
|
|
64
|
+
return token;
|
|
65
|
+
}
|
|
66
|
+
if (isGlobToken(token)) {
|
|
67
|
+
return token.pattern;
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
})
|
|
71
|
+
.filter((token) => token !== null && token.length > 0);
|
|
72
|
+
}
|
|
73
|
+
function tokeniseWithOperators(cmd) {
|
|
74
|
+
const parsed = shellParse(cmd);
|
|
75
|
+
const hasOperator = parsed.some(isOperatorToken);
|
|
76
|
+
if (!hasOperator) {
|
|
77
|
+
const tokens = parsed
|
|
78
|
+
.map((token) => {
|
|
79
|
+
if (typeof token === 'string')
|
|
80
|
+
return token;
|
|
81
|
+
if (isGlobToken(token))
|
|
82
|
+
return token.pattern;
|
|
83
|
+
return null;
|
|
84
|
+
})
|
|
85
|
+
.filter((token) => token !== null && token.length > 0);
|
|
86
|
+
return { tokens, isCompound: false, subCommands: [{ tokens }] };
|
|
87
|
+
}
|
|
88
|
+
const subCommands = splitByOperators(parsed);
|
|
89
|
+
const allTokens = subCommands.flatMap((sc) => sc.tokens);
|
|
90
|
+
return { tokens: allTokens, isCompound: true, subCommands };
|
|
91
|
+
}
|
|
92
|
+
function extractShellWrapperArg(tokens) {
|
|
93
|
+
const cmdIndex = tokens.findIndex((t) => t === '-c');
|
|
94
|
+
if (cmdIndex !== -1 && cmdIndex + 1 < tokens.length) {
|
|
95
|
+
return tokens[cmdIndex + 1];
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
function extractEnvCommand(tokens) {
|
|
100
|
+
let startIndex = 1;
|
|
101
|
+
while (startIndex < tokens.length) {
|
|
102
|
+
const token = tokens[startIndex];
|
|
103
|
+
if (token.includes('=') || token.startsWith('-')) {
|
|
104
|
+
startIndex++;
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (startIndex < tokens.length) {
|
|
111
|
+
return tokens.slice(startIndex);
|
|
112
|
+
}
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
function extractInterpreterCommand(tokens) {
|
|
116
|
+
const cIndex = tokens.findIndex((t) => t === '-c' || t === '-e');
|
|
117
|
+
if (cIndex !== -1 && cIndex + 1 < tokens.length) {
|
|
118
|
+
const script = tokens[cIndex + 1];
|
|
119
|
+
const execPatterns = [
|
|
120
|
+
/os\.system\s*\(\s*['"](.*?)['"]\s*\)/,
|
|
121
|
+
/subprocess\.(?:run|call|Popen)\s*\(\s*['"](.*?)['"]/,
|
|
122
|
+
/exec\s*\(\s*['"](.*?)['"]\s*\)/,
|
|
123
|
+
/execSync\s*\(\s*['"](.*?)['"]\s*\)/,
|
|
124
|
+
/`([^`]+)`/,
|
|
125
|
+
/system\s*\(\s*['"](.*?)['"]\s*\)/,
|
|
126
|
+
];
|
|
127
|
+
for (const pattern of execPatterns) {
|
|
128
|
+
const match = script.match(pattern);
|
|
129
|
+
if (match?.[1]) {
|
|
130
|
+
return match[1];
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
function unwrapCommand(cmd, depth = 0) {
|
|
137
|
+
if (depth >= MAX_UNWRAP_DEPTH) {
|
|
138
|
+
return { unwrapped: cmd, wrappers: [] };
|
|
139
|
+
}
|
|
140
|
+
const trimmed = cmd.trim();
|
|
141
|
+
if (!trimmed) {
|
|
142
|
+
return { unwrapped: cmd, wrappers: [] };
|
|
143
|
+
}
|
|
144
|
+
const tokens = tokenise(trimmed);
|
|
145
|
+
if (tokens.length === 0) {
|
|
146
|
+
return { unwrapped: cmd, wrappers: [] };
|
|
147
|
+
}
|
|
148
|
+
const firstToken = tokens[0];
|
|
149
|
+
if (isShellWrapper(firstToken)) {
|
|
150
|
+
const innerCmd = extractShellWrapperArg(tokens);
|
|
151
|
+
if (innerCmd) {
|
|
152
|
+
const inner = unwrapCommand(innerCmd, depth + 1);
|
|
153
|
+
return {
|
|
154
|
+
unwrapped: inner.unwrapped,
|
|
155
|
+
wrappers: [firstToken, ...inner.wrappers],
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (isPrivilegedWrapper(firstToken)) {
|
|
160
|
+
const sudoFlagsWithArgs = ['-u', '-g', '-H', '-C', '-h', '-p', '-r', '-t', '-T', '-U'];
|
|
161
|
+
let startIndex = 1;
|
|
162
|
+
while (startIndex < tokens.length) {
|
|
163
|
+
const token = tokens[startIndex];
|
|
164
|
+
if (token.startsWith('-')) {
|
|
165
|
+
if (sudoFlagsWithArgs.includes(token) && startIndex + 1 < tokens.length) {
|
|
166
|
+
startIndex += 2;
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
startIndex++;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (startIndex < tokens.length) {
|
|
177
|
+
const remaining = tokens.slice(startIndex).join(' ');
|
|
178
|
+
const inner = unwrapCommand(remaining, depth + 1);
|
|
179
|
+
return {
|
|
180
|
+
unwrapped: inner.unwrapped,
|
|
181
|
+
wrappers: [firstToken, ...inner.wrappers],
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (isEnvWrapper(firstToken)) {
|
|
186
|
+
const remaining = extractEnvCommand(tokens);
|
|
187
|
+
if (remaining) {
|
|
188
|
+
const inner = unwrapCommand(remaining.join(' '), depth + 1);
|
|
189
|
+
return {
|
|
190
|
+
unwrapped: inner.unwrapped,
|
|
191
|
+
wrappers: [firstToken, ...inner.wrappers],
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (isInterpreter(firstToken)) {
|
|
196
|
+
const innerCmd = extractInterpreterCommand(tokens);
|
|
197
|
+
if (innerCmd) {
|
|
198
|
+
const inner = unwrapCommand(innerCmd, depth + 1);
|
|
199
|
+
return {
|
|
200
|
+
unwrapped: inner.unwrapped,
|
|
201
|
+
wrappers: [firstToken, ...inner.wrappers],
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return { unwrapped: trimmed, wrappers: [] };
|
|
206
|
+
}
|
|
207
|
+
function expandCombinedFlags(flags) {
|
|
208
|
+
const expanded = [];
|
|
209
|
+
for (const flag of flags) {
|
|
210
|
+
if (flag.startsWith('--')) {
|
|
211
|
+
expanded.push(flag);
|
|
212
|
+
}
|
|
213
|
+
else if (flag.startsWith('-') && flag.length > 2) {
|
|
214
|
+
for (let i = 1; i < flag.length; i++) {
|
|
215
|
+
expanded.push(`-${flag[i]}`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
expanded.push(flag);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return expanded;
|
|
223
|
+
}
|
|
224
|
+
function extractSubcommand(command, args) {
|
|
225
|
+
const commandsWithSubcommands = [
|
|
226
|
+
'git',
|
|
227
|
+
'npm',
|
|
228
|
+
'yarn',
|
|
229
|
+
'pnpm',
|
|
230
|
+
'docker',
|
|
231
|
+
'kubectl',
|
|
232
|
+
'cargo',
|
|
233
|
+
'go',
|
|
234
|
+
];
|
|
235
|
+
if (commandsWithSubcommands.includes(command) && args.length > 0) {
|
|
236
|
+
const firstArg = args[0];
|
|
237
|
+
if (!firstArg.startsWith('-') && !firstArg.includes('/') && !firstArg.includes('=')) {
|
|
238
|
+
return firstArg;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return undefined;
|
|
242
|
+
}
|
|
243
|
+
function parseFromTokens(tokens, rawCmd, wrappers) {
|
|
244
|
+
if (tokens.length === 0) {
|
|
245
|
+
return {
|
|
246
|
+
raw: rawCmd,
|
|
247
|
+
command: '',
|
|
248
|
+
subcommand: undefined,
|
|
249
|
+
flags: [],
|
|
250
|
+
args: [],
|
|
251
|
+
unwrapped: rawCmd,
|
|
252
|
+
wrapperChain: wrappers,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
const [command, ...rest] = tokens;
|
|
256
|
+
const rawFlags = rest.filter((t) => t.startsWith('-'));
|
|
257
|
+
const flags = expandCombinedFlags(rawFlags);
|
|
258
|
+
const args = rest.filter((t) => !t.startsWith('-'));
|
|
259
|
+
const subcommand = extractSubcommand(command, args);
|
|
260
|
+
const remainingArgs = subcommand ? args.slice(1) : args;
|
|
261
|
+
return {
|
|
262
|
+
raw: rawCmd,
|
|
263
|
+
command,
|
|
264
|
+
subcommand,
|
|
265
|
+
flags,
|
|
266
|
+
args: remainingArgs,
|
|
267
|
+
unwrapped: tokens.join(' '),
|
|
268
|
+
wrapperChain: wrappers,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
export function parseCommand(cmd) {
|
|
272
|
+
debug(`parseCommand: raw=${cmd}`);
|
|
273
|
+
const { unwrapped, wrappers } = unwrapCommand(cmd);
|
|
274
|
+
if (wrappers.length > 0) {
|
|
275
|
+
debug('parseCommand: unwrapped through', { wrappers });
|
|
276
|
+
}
|
|
277
|
+
const tokens = tokenise(unwrapped);
|
|
278
|
+
if (tokens.length === 0) {
|
|
279
|
+
debug('parseCommand: empty command after tokenisation');
|
|
280
|
+
return {
|
|
281
|
+
raw: cmd,
|
|
282
|
+
command: '',
|
|
283
|
+
subcommand: undefined,
|
|
284
|
+
flags: [],
|
|
285
|
+
args: [],
|
|
286
|
+
unwrapped,
|
|
287
|
+
wrapperChain: wrappers,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
const [command, ...rest] = tokens;
|
|
291
|
+
const rawFlags = rest.filter((t) => t.startsWith('-'));
|
|
292
|
+
const flags = expandCombinedFlags(rawFlags);
|
|
293
|
+
const args = rest.filter((t) => !t.startsWith('-'));
|
|
294
|
+
const subcommand = extractSubcommand(command, args);
|
|
295
|
+
const remainingArgs = subcommand ? args.slice(1) : args;
|
|
296
|
+
debug('parseCommand result', { command, subcommand, flags });
|
|
297
|
+
return {
|
|
298
|
+
raw: cmd,
|
|
299
|
+
command,
|
|
300
|
+
subcommand,
|
|
301
|
+
flags,
|
|
302
|
+
args: remainingArgs,
|
|
303
|
+
unwrapped,
|
|
304
|
+
wrapperChain: wrappers,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
export function parseCompoundCommand(cmd) {
|
|
308
|
+
debug(`parseCompoundCommand: raw=${cmd}`);
|
|
309
|
+
const { unwrapped, wrappers } = unwrapCommand(cmd);
|
|
310
|
+
const { isCompound, subCommands } = tokeniseWithOperators(unwrapped);
|
|
311
|
+
if (!isCompound || subCommands.length <= 1) {
|
|
312
|
+
debug('parseCompoundCommand: single command');
|
|
313
|
+
return {
|
|
314
|
+
isCompound: false,
|
|
315
|
+
commands: [parseCommand(cmd)],
|
|
316
|
+
operators: [],
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
debug(`parseCompoundCommand: compound with ${subCommands.length} sub-commands`);
|
|
320
|
+
const commands = [];
|
|
321
|
+
const operators = [];
|
|
322
|
+
for (const subCmd of subCommands) {
|
|
323
|
+
if (subCmd.tokens.length > 0) {
|
|
324
|
+
const parsed = parseFromTokens(subCmd.tokens, subCmd.tokens.join(' '), wrappers);
|
|
325
|
+
commands.push(parsed);
|
|
326
|
+
}
|
|
327
|
+
if (subCmd.operator) {
|
|
328
|
+
operators.push(subCmd.operator);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return {
|
|
332
|
+
isCompound: true,
|
|
333
|
+
commands,
|
|
334
|
+
operators,
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
export class CommandParser {
|
|
338
|
+
parse(cmd) {
|
|
339
|
+
return parseCommand(cmd);
|
|
340
|
+
}
|
|
341
|
+
parseCompound(cmd) {
|
|
342
|
+
return parseCompoundCommand(cmd);
|
|
343
|
+
}
|
|
344
|
+
parseMultiple(commands) {
|
|
345
|
+
return commands.map((cmd) => this.parse(cmd));
|
|
346
|
+
}
|
|
347
|
+
parseAllCommands(cmd) {
|
|
348
|
+
const result = parseCompoundCommand(cmd);
|
|
349
|
+
return result.commands;
|
|
350
|
+
}
|
|
351
|
+
isWrapped(cmd) {
|
|
352
|
+
const result = unwrapCommand(cmd);
|
|
353
|
+
return result.wrappers.length > 0;
|
|
354
|
+
}
|
|
355
|
+
isCompound(cmd) {
|
|
356
|
+
const result = parseCompoundCommand(cmd);
|
|
357
|
+
return result.isCompound;
|
|
358
|
+
}
|
|
359
|
+
getWrappers(cmd) {
|
|
360
|
+
const result = unwrapCommand(cmd);
|
|
361
|
+
return result.wrappers;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/gate/parsers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { parseCommand, CommandParser } from './command-parser.js';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Policy module exports
|
|
3
|
+
*
|
|
4
|
+
* Re-exports from @eddacraft/anvil-policy to avoid code duplication.
|
|
5
|
+
* OPA integration for flexible policy evaluation using Rego language.
|
|
6
|
+
*
|
|
7
|
+
* Note: SignatureAlgorithm is intentionally not re-exported here as it is
|
|
8
|
+
* already exported from @eddacraft/anvil-contracts via gate.types.
|
|
9
|
+
*/
|
|
10
|
+
export { OPABinaryManager, getOPABinaryManager, PolicyLoader, OPAExecutor, BundleManager, getBundleManager, BundleVerifier, loadKeyFromFile, } from '@eddacraft/anvil-policy';
|
|
11
|
+
export type { OPABinaryConfig, BinaryInfo, LoadedPolicy, PolicyDiscoveryResult, PolicyLoaderConfig, OPAInput, PolicyViolation, OPAEvaluationResult, OPAExecutorConfig, ViolationCategory, BundleConfig, BundleAuthConfig, BundleCacheEntry, BundleManagerConfig, BundleSyncResult, VerificationResult, PublicKeyConfig, BundleVerifierConfig, SignatureManifest, } from '@eddacraft/anvil-policy';
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/gate/policy/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,YAAY,EACZ,WAAW,EACX,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,eAAe,GAChB,MAAM,yBAAyB,CAAC;AAEjC,YAAY,EACV,eAAe,EACf,UAAU,EACV,YAAY,EACZ,qBAAqB,EACrB,kBAAkB,EAClB,QAAQ,EACR,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,yBAAyB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Policy module exports
|
|
3
|
+
*
|
|
4
|
+
* Re-exports from @eddacraft/anvil-policy to avoid code duplication.
|
|
5
|
+
* OPA integration for flexible policy evaluation using Rego language.
|
|
6
|
+
*
|
|
7
|
+
* Note: SignatureAlgorithm is intentionally not re-exported here as it is
|
|
8
|
+
* already exported from @eddacraft/anvil-contracts via gate.types.
|
|
9
|
+
*/
|
|
10
|
+
export { OPABinaryManager, getOPABinaryManager, PolicyLoader, OPAExecutor, BundleManager, getBundleManager, BundleVerifier, loadKeyFromFile, } from '@eddacraft/anvil-policy';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"default-filesystem-rules.d.ts","sourceRoot":"","sources":["../../../src/gate/rules/default-filesystem-rules.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,eAAO,MAAM,wBAAwB,EAAE,WAAW,EA2NjD,CAAC"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
export const DEFAULT_FILESYSTEM_RULES = [
|
|
2
|
+
{
|
|
3
|
+
id: 'rm-rf-root',
|
|
4
|
+
category: 'filesystem',
|
|
5
|
+
command: 'rm',
|
|
6
|
+
flags: { dangerous: ['-r', '-f', '--recursive', '--force'] },
|
|
7
|
+
args: { pattern: /^\/$/ },
|
|
8
|
+
action: 'block',
|
|
9
|
+
severity: 'error',
|
|
10
|
+
reason: 'rm -rf / would delete the entire filesystem',
|
|
11
|
+
suggestion: 'NEVER delete the root filesystem. Review the target path carefully.',
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
id: 'rm-rf-home',
|
|
15
|
+
category: 'filesystem',
|
|
16
|
+
command: 'rm',
|
|
17
|
+
flags: { dangerous: ['-r', '-f'] },
|
|
18
|
+
args: { pattern: /^(~|~\/.*|\$HOME|\$\{HOME\}|\$HOME\/.*)$/ },
|
|
19
|
+
action: 'block',
|
|
20
|
+
severity: 'error',
|
|
21
|
+
reason: 'rm -rf on home directory is extremely dangerous',
|
|
22
|
+
suggestion: 'NEVER delete your home directory. Specify exact subdirectories if needed.',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: 'rm-rf-current-dir',
|
|
26
|
+
category: 'filesystem',
|
|
27
|
+
command: 'rm',
|
|
28
|
+
flags: { dangerous: ['-r', '-f'] },
|
|
29
|
+
args: { pattern: /^\.$/ },
|
|
30
|
+
action: 'block',
|
|
31
|
+
severity: 'error',
|
|
32
|
+
reason: 'rm -rf . deletes the entire current directory',
|
|
33
|
+
suggestion: 'Specify exact subdirectories or files instead',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: 'rm-rf-parent-traversal',
|
|
37
|
+
category: 'filesystem',
|
|
38
|
+
command: 'rm',
|
|
39
|
+
flags: { dangerous: ['-r', '-f'] },
|
|
40
|
+
args: { pattern: /\.\./ },
|
|
41
|
+
action: 'block',
|
|
42
|
+
severity: 'error',
|
|
43
|
+
reason: 'rm -rf with parent directory traversal (..) can escape current directory',
|
|
44
|
+
suggestion: 'Use absolute paths or paths within the current directory only',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: 'rm-rf-system-dirs',
|
|
48
|
+
category: 'filesystem',
|
|
49
|
+
command: 'rm',
|
|
50
|
+
flags: { dangerous: ['-r', '-f'] },
|
|
51
|
+
args: { pattern: /^\/(bin|boot|dev|etc|lib|lib64|opt|proc|root|sbin|sys|usr|var)$/ },
|
|
52
|
+
action: 'block',
|
|
53
|
+
severity: 'error',
|
|
54
|
+
reason: 'rm -rf on system directories would break the operating system',
|
|
55
|
+
suggestion: 'NEVER delete system directories.',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: 'rm-rf-tmp-dir',
|
|
59
|
+
category: 'filesystem',
|
|
60
|
+
command: 'rm',
|
|
61
|
+
flags: { dangerous: ['-r', '-f'] },
|
|
62
|
+
args: { pattern: /^(\/tmp|\/var\/tmp|\$TMPDIR|\$\{TMPDIR\})(\/.*)?$/ },
|
|
63
|
+
action: 'allow',
|
|
64
|
+
severity: 'info',
|
|
65
|
+
reason: 'Temporary directory deletion is safe',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: 'rm-rf-build-dirs',
|
|
69
|
+
category: 'filesystem',
|
|
70
|
+
command: 'rm',
|
|
71
|
+
flags: { dangerous: ['-r', '-f'] },
|
|
72
|
+
args: {
|
|
73
|
+
pattern: /^(\.\/)?(\.)?(node_modules|dist|build|target|\.next|\.cache|\.nuxt|\.output|coverage|__pycache__|\.pytest_cache|\.mypy_cache|\.tox|\.venv|venv)$/,
|
|
74
|
+
},
|
|
75
|
+
action: 'allow',
|
|
76
|
+
severity: 'info',
|
|
77
|
+
reason: 'Common build/cache directory deletion is safe (reproducible artifacts)',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: 'rm-rf-with-recursive',
|
|
81
|
+
category: 'filesystem',
|
|
82
|
+
command: 'rm',
|
|
83
|
+
flags: { dangerous: ['-r', '--recursive'] },
|
|
84
|
+
action: 'warn',
|
|
85
|
+
severity: 'warning',
|
|
86
|
+
reason: 'rm -r recursively deletes directories - verify the target path',
|
|
87
|
+
suggestion: 'List files first with "ls -la", review carefully before deleting',
|
|
88
|
+
conditions: { strictModeOnly: true },
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
id: 'rmdir-force',
|
|
92
|
+
category: 'filesystem',
|
|
93
|
+
command: 'rmdir',
|
|
94
|
+
flags: { dangerous: ['-p', '--parents'] },
|
|
95
|
+
action: 'warn',
|
|
96
|
+
severity: 'warning',
|
|
97
|
+
reason: 'rmdir -p removes parent directories',
|
|
98
|
+
suggestion: 'Verify the full directory path before removing',
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
id: 'mv-overwrite',
|
|
102
|
+
category: 'filesystem',
|
|
103
|
+
command: 'mv',
|
|
104
|
+
flags: { dangerous: ['-f', '--force'] },
|
|
105
|
+
args: { pattern: /^(\/|~|\$HOME)/ },
|
|
106
|
+
action: 'warn',
|
|
107
|
+
severity: 'warning',
|
|
108
|
+
reason: 'mv -f to root/home paths can overwrite important files',
|
|
109
|
+
suggestion: 'Use mv without -f to get overwrite prompts',
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
id: 'chmod-recursive-777',
|
|
113
|
+
category: 'filesystem',
|
|
114
|
+
command: 'chmod',
|
|
115
|
+
flags: { dangerous: ['-R', '--recursive'] },
|
|
116
|
+
args: { pattern: /^777$/ },
|
|
117
|
+
action: 'warn',
|
|
118
|
+
severity: 'warning',
|
|
119
|
+
reason: 'chmod -R 777 makes all files world-writable (security risk)',
|
|
120
|
+
suggestion: 'Use more restrictive permissions like 755 for directories, 644 for files',
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
id: 'chown-recursive-root',
|
|
124
|
+
category: 'filesystem',
|
|
125
|
+
command: 'chown',
|
|
126
|
+
flags: { dangerous: ['-R', '--recursive'] },
|
|
127
|
+
args: { pattern: /^root/ },
|
|
128
|
+
action: 'warn',
|
|
129
|
+
severity: 'warning',
|
|
130
|
+
reason: 'chown -R root can cause permission issues',
|
|
131
|
+
suggestion: 'Verify you want to change ownership recursively to root',
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
id: 'rm-rf-root-glob',
|
|
135
|
+
category: 'filesystem',
|
|
136
|
+
command: 'rm',
|
|
137
|
+
flags: { dangerous: ['-r', '-f', '--recursive', '--force'] },
|
|
138
|
+
args: { pattern: /^\/\*$/ },
|
|
139
|
+
action: 'block',
|
|
140
|
+
severity: 'error',
|
|
141
|
+
reason: 'rm -rf /* would delete the entire filesystem contents',
|
|
142
|
+
suggestion: 'NEVER delete filesystem root. Review the target path carefully.',
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
id: 'chmod-777-sensitive',
|
|
146
|
+
category: 'filesystem',
|
|
147
|
+
command: 'chmod',
|
|
148
|
+
args: { pattern: /^\/(etc|root|var|boot|lib|bin|sbin|usr)/ },
|
|
149
|
+
action: 'block',
|
|
150
|
+
severity: 'error',
|
|
151
|
+
reason: 'chmod on system paths can compromise system security',
|
|
152
|
+
suggestion: 'System files should not be modified. Use proper configuration management.',
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
id: 'dd-block-device',
|
|
156
|
+
category: 'filesystem',
|
|
157
|
+
command: 'dd',
|
|
158
|
+
args: { pattern: /of=\/dev\/(sd[a-z]|hd[a-z]|nvme\d+n\d+|vd[a-z]|xvd[a-z])/ },
|
|
159
|
+
action: 'block',
|
|
160
|
+
severity: 'error',
|
|
161
|
+
reason: 'dd writing to block devices can destroy entire disks',
|
|
162
|
+
suggestion: 'Verify the target device carefully. This will overwrite all data.',
|
|
163
|
+
references: ['https://wiki.archlinux.org/title/Dd'],
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
id: 'mkfs-any',
|
|
167
|
+
category: 'filesystem',
|
|
168
|
+
command: 'mkfs',
|
|
169
|
+
action: 'block',
|
|
170
|
+
severity: 'error',
|
|
171
|
+
reason: 'mkfs formats and destroys all data on the target device',
|
|
172
|
+
suggestion: 'Ensure the target device is correct. All existing data will be lost.',
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
id: 'mkfs-ext4',
|
|
176
|
+
category: 'filesystem',
|
|
177
|
+
command: 'mkfs.ext4',
|
|
178
|
+
action: 'block',
|
|
179
|
+
severity: 'error',
|
|
180
|
+
reason: 'mkfs.ext4 formats and destroys all data on the target device',
|
|
181
|
+
suggestion: 'Ensure the target device is correct. All existing data will be lost.',
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
id: 'mkfs-xfs',
|
|
185
|
+
category: 'filesystem',
|
|
186
|
+
command: 'mkfs.xfs',
|
|
187
|
+
action: 'block',
|
|
188
|
+
severity: 'error',
|
|
189
|
+
reason: 'mkfs.xfs formats and destroys all data on the target device',
|
|
190
|
+
suggestion: 'Ensure the target device is correct. All existing data will be lost.',
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
id: 'mkfs-btrfs',
|
|
194
|
+
category: 'filesystem',
|
|
195
|
+
command: 'mkfs.btrfs',
|
|
196
|
+
action: 'block',
|
|
197
|
+
severity: 'error',
|
|
198
|
+
reason: 'mkfs.btrfs formats and destroys all data on the target device',
|
|
199
|
+
suggestion: 'Ensure the target device is correct. All existing data will be lost.',
|
|
200
|
+
},
|
|
201
|
+
];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"default-git-rules.d.ts","sourceRoot":"","sources":["../../../src/gate/rules/default-git-rules.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,eAAO,MAAM,iBAAiB,EAAE,WAAW,EAiN1C,CAAC"}
|