@sagentlab/navarch-runtime 0.1.117 → 0.1.118

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.
@@ -173,12 +173,80 @@ function expandHome(value) {
173
173
  return value;
174
174
  }
175
175
 
176
+ /**
177
+ * Previous character on the same line, ignoring blanks; "" at a true line
178
+ * start. A line that ends in an unescaped backslash continues onto the next
179
+ * line in the shell, so that backslash is returned instead.
180
+ */
181
+ function previousSignificantChar(command, index) {
182
+ for (let i = index - 1; i >= 0; i--) {
183
+ const ch = command[i];
184
+ if (ch === "\n") {
185
+ let j = i - 1;
186
+ while (j >= 0 && (command[j] === "\r" || command[j] === " " || command[j] === "\t")) j--;
187
+ let slashes = 0;
188
+ while (j >= 0 && command[j] === "\\") {
189
+ slashes++;
190
+ j--;
191
+ }
192
+ return slashes % 2 === 1 ? "\\" : "";
193
+ }
194
+ if (ch !== " " && ch !== "\t") return ch;
195
+ }
196
+ return "";
197
+ }
198
+
199
+ /**
200
+ * True when the token sits directly after a quote that opens a word
201
+ * (`"/** x *\/"`). A closing quote (`"" /*`, `"x"/*`) leaves the token as a
202
+ * bare shell argument, so it does not count.
203
+ */
204
+ function isInsideOpeningQuote(command, start) {
205
+ const quote = command[start - 1];
206
+ // Backticks run a command, so never use them to exempt a path token.
207
+ if (quote !== '"' && quote !== "'") return false;
208
+ // Only the command's first quote can prove an opening quoted word without
209
+ // parsing shell comments, heredocs and nested substitutions. Earlier quote
210
+ // characters make this ambiguous: keep screening rather than risk treating
211
+ // a closing quote after a continuation as an opening one.
212
+ if (/["'`]/.test(command.slice(0, start - 1))) return false;
213
+ const beforeQuote = command[start - 2];
214
+ return beforeQuote === undefined || /[\s=(<>;|&]/.test(beforeQuote);
215
+ }
216
+
217
+ /**
218
+ * True when a "/"-leading token is comment or markup text rather than a path:
219
+ * a closing tag (`</p>`), a block-comment opener (`/**`, `/*`), or a line
220
+ * comment (`// note`). Each rule is deliberately narrow so real escapes stay
221
+ * screened: tag names that exist at the filesystem root (`</home>`) are still
222
+ * checked, `/*` and `//` only count at a comment position (line start, right
223
+ * after an opening quote, or after `;`) so `rm -rf /*`, `find // -name x`
224
+ * and `rm -rf "" /*` stay blocked, and a block comment needs a closing
225
+ * `*` + `/` later in the text.
226
+ */
227
+ function isCommentOrMarkupToken(command, start, token) {
228
+ const end = start + token.length;
229
+ const next = command[end];
230
+
231
+ if (command[start - 1] === "<" && next === ">" && /^\/[A-Za-z][\w:.-]*$/.test(token)) {
232
+ return !fs.existsSync(token);
233
+ }
234
+
235
+ const atCommentPosition =
236
+ isInsideOpeningQuote(command, start) ||
237
+ ["", ";"].includes(previousSignificantChar(command, start));
238
+ if (!atCommentPosition || next === undefined || !/\s/.test(next)) return false;
239
+ if (token === "/*" || token === "/**") return command.indexOf("*/", end) !== -1;
240
+ return token === "//" && /\S/.test(command.slice(end));
241
+ }
242
+
176
243
  /**
177
244
  * Pulls path-like references out of a shell command: absolute paths,
178
245
  * home-anchored paths (`~...`, `$HOME/...`), and relative `..` traversals.
179
246
  * Lexical by design — see the file header for why this is a guardrail, not
180
247
  * a parser. URLs survive untouched (the char before `//` in `https://` is
181
- * `:`, which is not a path delimiter here).
248
+ * `:`, which is not a path delimiter here). Comment and markup text that
249
+ * merely starts with "/" (`/** doc *\/`, `// note`, `</p>`) is not a path.
182
250
  */
183
251
  function extractBashPathCandidates(command) {
184
252
  const candidates = [];
@@ -193,7 +261,11 @@ function extractBashPathCandidates(command) {
193
261
  const absoluteRe = /(?:^|[\s'"`=(<>;|&])(\/[^\s'"`<>;|&)]*)/g;
194
262
  const homeRe = /(?:^|[\s'"`=(<>;|&])((?:~|\$\{?HOME\}?)[^\s'"`<>;|&)]*)/g;
195
263
  let match;
196
- while ((match = absoluteRe.exec(command))) add(match[1]);
264
+ while ((match = absoluteRe.exec(command))) {
265
+ const start = match.index + match[0].length - match[1].length;
266
+ if (isCommentOrMarkupToken(command, start, match[1])) continue;
267
+ add(match[1]);
268
+ }
197
269
  while ((match = homeRe.exec(command))) add(match[1]);
198
270
 
199
271
  for (const token of command.split(/[\s'"`;|&<>()]+/)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sagentlab/navarch-runtime",
3
- "version": "0.1.117",
3
+ "version": "0.1.118",
4
4
  "description": "Navarch machine-side session manager: claims delivery tasks and runs them through local coding agents.",
5
5
  "type": "commonjs",
6
6
  "license": "MIT",