@remcp/runtime 0.2.189 → 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 CHANGED
@@ -1,5 +1,17 @@
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
+
10
+ ## 0.2.190 — 2026-10-27
11
+
12
+ - Publish the runtime alongside the compatible ReMCP agent; the device HTTPS network probe is agent-owned and does not add runtime MCP tools.
13
+ - Preserve the existing 84-tool runtime contract while verifying public package source and release parity.
14
+
3
15
  ## 0.2.186 — 2026-10-26
4
16
 
5
17
  - Return from same-document CDP navigation promptly when Page.navigate provides no new loaderId instead of awaiting a full load event that never fires.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remcp/runtime",
3
- "version": "0.2.189",
3
+ "version": "0.2.191",
4
4
  "description": "First-party ReMCP local device runtime: filesystem, terminal, computer-use, browser, diagnostics and document tools over MCP.",
5
5
  "type": "module",
6
6
  "license": "MIT",
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
- while (tokens.length && tokens[0].startsWith('-')) tokens.shift();
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
- break;
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, '');