@remcp/runtime 0.2.190 → 0.2.191
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/CHANGELOG.md +7 -0
- package/package.json +1 -1
- package/src/policy.mjs +35 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.191 — 2026-10-28
|
|
4
|
+
|
|
5
|
+
- Correct the catastrophic-command heuristic for shell wrappers that consume option values, including sudo, nice, ionice, and env split-string.
|
|
6
|
+
- Add no-side-effect guardrail regression cases; no new MCP tool or expanded device permission.
|
|
7
|
+
- Make the short-process terminal assertion wait for the real exit event after buffered output without changing runtime I/O behavior.
|
|
8
|
+
- This matcher is not a shell sandbox and cannot protect computers running with unrestricted God Mode; deployment still requires OS least-privilege mitigation.
|
|
9
|
+
|
|
3
10
|
## 0.2.190 — 2026-10-27
|
|
4
11
|
|
|
5
12
|
- Publish the runtime alongside the compatible ReMCP agent; the device HTTPS network probe is agent-owned and does not add runtime MCP tools.
|
package/package.json
CHANGED
package/src/policy.mjs
CHANGED
|
@@ -60,19 +60,49 @@ function segments(command) {
|
|
|
60
60
|
.filter(Boolean);
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
// A wrapper option may consume the following token. Dropping the option but
|
|
64
|
+
// not its value makes the guardrail examine "root"/"10" instead of "rm".
|
|
65
|
+
const WRAPPER_VALUE_FLAGS = Object.freeze({
|
|
66
|
+
sudo: new Set(['-u', '--user', '-g', '--group', '-h', '--host', '-p', '--prompt', '-C', '--close-from', '-D', '--chdir', '-R', '--chroot', '-T', '--command-timeout']),
|
|
67
|
+
doas: new Set(['-u']),
|
|
68
|
+
env: new Set(['-u', '--unset', '-C', '--chdir', '-S', '--split-string']),
|
|
69
|
+
nice: new Set(['-n', '--adjustment']),
|
|
70
|
+
ionice: new Set(['-c', '--class', '-n', '--classdata', '-p', '--pid', '-P', '--pgid', '-u', '--uid']),
|
|
71
|
+
stdbuf: new Set(['-i', '--input', '-o', '--output', '-e', '--error']),
|
|
72
|
+
timeout: new Set(['-s', '--signal', '-k', '--kill-after']),
|
|
73
|
+
});
|
|
74
|
+
|
|
63
75
|
function commandWord(segment) {
|
|
64
76
|
const tokens = matchingText(segment).split(' ').filter(Boolean);
|
|
65
77
|
while (tokens.length) {
|
|
66
78
|
const token = tokens[0];
|
|
67
79
|
if (/^[A-Za-z_][A-Za-z0-9_]*=/.test(token)) { tokens.shift(); continue; }
|
|
68
80
|
const tokenName = path.basename(token).replace(/\.(exe|cmd|bat)$/i, '').toLowerCase();
|
|
69
|
-
if (WRAPPERS.has(tokenName))
|
|
81
|
+
if (!WRAPPERS.has(tokenName)) break;
|
|
82
|
+
tokens.shift();
|
|
83
|
+
const valueFlags = WRAPPER_VALUE_FLAGS[tokenName];
|
|
84
|
+
while (tokens.length) {
|
|
85
|
+
const option = tokens[0];
|
|
86
|
+
if (option === '--') { tokens.shift(); break; }
|
|
87
|
+
if (tokenName === 'env' && /^[A-Za-z_][A-Za-z0-9_]*=/.test(option)) {
|
|
88
|
+
tokens.shift();
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
// env -S/--split-string executes its argument as command words; never
|
|
92
|
+
// discard that argument as an ordinary wrapper option value.
|
|
93
|
+
if (tokenName === 'env' && (option === '-S' || option === '--split-string')) {
|
|
94
|
+
tokens.shift();
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
if (tokenName === 'env' && (option.startsWith('--split-string=') || /^-S.+/.test(option))) {
|
|
98
|
+
tokens[0] = option.startsWith('--split-string=') ? option.slice('--split-string='.length) : option.slice(2);
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
if (!option.startsWith('-') || option === '-') break;
|
|
70
102
|
tokens.shift();
|
|
71
|
-
|
|
72
|
-
if (tokenName === 'timeout' && tokens.length && /^\d+[smhd]?$/.test(tokens[0])) tokens.shift();
|
|
73
|
-
continue;
|
|
103
|
+
if (valueFlags?.has(option) && tokens.length) tokens.shift();
|
|
74
104
|
}
|
|
75
|
-
|
|
105
|
+
if (tokenName === 'timeout' && tokens.length && /^\d+(?:\.\d+)?[smhd]?$/.test(tokens[0])) tokens.shift();
|
|
76
106
|
}
|
|
77
107
|
const word = tokens[0] || '';
|
|
78
108
|
return word.replace(/^.*[\\/]/, '').replace(/\.(exe|cmd|bat)$/i, '');
|