@f5-sales-demo/xcsh 19.98.2 → 19.98.3
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/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@f5-sales-demo/xcsh",
|
|
4
|
-
"version": "19.98.
|
|
4
|
+
"version": "19.98.3",
|
|
5
5
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://github.com/f5-sales-demo/xcsh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -57,13 +57,13 @@
|
|
|
57
57
|
"dependencies": {
|
|
58
58
|
"@agentclientprotocol/sdk": "1.3.0",
|
|
59
59
|
"@mozilla/readability": "^0.6",
|
|
60
|
-
"@f5-sales-demo/xcsh-stats": "19.98.
|
|
61
|
-
"@f5-sales-demo/pi-agent-core": "19.98.
|
|
62
|
-
"@f5-sales-demo/pi-ai": "19.98.
|
|
63
|
-
"@f5-sales-demo/pi-natives": "19.98.
|
|
64
|
-
"@f5-sales-demo/pi-resource-management": "19.98.
|
|
65
|
-
"@f5-sales-demo/pi-tui": "19.98.
|
|
66
|
-
"@f5-sales-demo/pi-utils": "19.98.
|
|
60
|
+
"@f5-sales-demo/xcsh-stats": "19.98.3",
|
|
61
|
+
"@f5-sales-demo/pi-agent-core": "19.98.3",
|
|
62
|
+
"@f5-sales-demo/pi-ai": "19.98.3",
|
|
63
|
+
"@f5-sales-demo/pi-natives": "19.98.3",
|
|
64
|
+
"@f5-sales-demo/pi-resource-management": "19.98.3",
|
|
65
|
+
"@f5-sales-demo/pi-tui": "19.98.3",
|
|
66
|
+
"@f5-sales-demo/pi-utils": "19.98.3",
|
|
67
67
|
"@sinclair/typebox": "^0.34",
|
|
68
68
|
"@xterm/headless": "^6.0",
|
|
69
69
|
"ajv": "^8.20",
|
|
@@ -17,17 +17,17 @@ export interface BuildInfo {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export const BUILD_INFO: BuildInfo = {
|
|
20
|
-
"version": "19.98.
|
|
21
|
-
"commit": "
|
|
22
|
-
"shortCommit": "
|
|
20
|
+
"version": "19.98.3",
|
|
21
|
+
"commit": "4d3b95e9c675ae810cc60b6829fa3541a042d1ac",
|
|
22
|
+
"shortCommit": "4d3b95e",
|
|
23
23
|
"branch": "main",
|
|
24
|
-
"tag": "v19.98.
|
|
25
|
-
"commitDate": "2026-07-
|
|
26
|
-
"buildDate": "2026-07-
|
|
24
|
+
"tag": "v19.98.3",
|
|
25
|
+
"commitDate": "2026-07-28T05:29:35Z",
|
|
26
|
+
"buildDate": "2026-07-28T05:56:14.196Z",
|
|
27
27
|
"dirty": true,
|
|
28
28
|
"prNumber": "",
|
|
29
29
|
"repoUrl": "https://github.com/f5-sales-demo/xcsh",
|
|
30
30
|
"repoSlug": "f5-sales-demo/xcsh",
|
|
31
|
-
"commitUrl": "https://github.com/f5-sales-demo/xcsh/commit/
|
|
32
|
-
"releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v19.98.
|
|
31
|
+
"commitUrl": "https://github.com/f5-sales-demo/xcsh/commit/4d3b95e9c675ae810cc60b6829fa3541a042d1ac",
|
|
32
|
+
"releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v19.98.3"
|
|
33
33
|
};
|
package/src/sandbox/enforce.ts
CHANGED
|
@@ -190,6 +190,28 @@ const REDIRECT_OPERATOR = /[0-9]*(?:&>>|&>|<<<|<<-|<<|>>|>\||<>|>&|<&|>|<)/g;
|
|
|
190
190
|
/** A whitespace token that is only a redirection operator, so the next token is its operand. */
|
|
191
191
|
const BARE_REDIRECT = /^[0-9]*(?:&>>|&>|<<<|<<-|<<|>>|>\||<>|>&|<&|>|<)$/;
|
|
192
192
|
|
|
193
|
+
/**
|
|
194
|
+
* A path glued to its option, as one shell word (#2524).
|
|
195
|
+
*
|
|
196
|
+
* `path.isAbsolute("if=/work/custB/secret")` is false, so the floor's whole-token test
|
|
197
|
+
* never saw these — a single space was the difference between the blocked form and the
|
|
198
|
+
* allowed one.
|
|
199
|
+
*
|
|
200
|
+
* Only the option's VALUE is captured, never an arbitrary `/`-containing substring. That
|
|
201
|
+
* distinction is what keeps #2470 shut: scanning any slash-bearing fragment would read
|
|
202
|
+
* `sed -n '/a/p'` as a path again, which is the false positive #2479 exists to remove.
|
|
203
|
+
* The captured value still has to satisfy `looksLikePath`, so `--output=./out` stays
|
|
204
|
+
* allowed while `--output=/work/custB/x` does not.
|
|
205
|
+
*
|
|
206
|
+
* The short-option form additionally requires its value to begin with `/` or `~`. Without
|
|
207
|
+
* that, `-la` and friends would be read as an option carrying a relative path.
|
|
208
|
+
*/
|
|
209
|
+
const OPTION_VALUE_FORMS: readonly RegExp[] = [
|
|
210
|
+
/^-{1,2}[A-Za-z0-9][^=]*=(.+)$/, // --output=/p, -o=/p
|
|
211
|
+
/^[A-Za-z_][A-Za-z0-9_]*=(.+)$/, // if=/p, of=/p (operand style)
|
|
212
|
+
/^-[A-Za-z0-9]+([/~].*)$/, // -o/p, -C/p (no separator)
|
|
213
|
+
];
|
|
214
|
+
|
|
193
215
|
/**
|
|
194
216
|
* Path-like tokens in a command/code string: bare (whitespace-split) and quoted.
|
|
195
217
|
*
|
|
@@ -231,6 +253,24 @@ function codePathOccurrences(command: string): PathOccurrence[] {
|
|
|
231
253
|
const from = operator.index + operator[0].length;
|
|
232
254
|
add(stripped.slice(from).replace(/^["']|["']$/g, ""), match.index + openingQuote + from, access);
|
|
233
255
|
}
|
|
256
|
+
|
|
257
|
+
// An option glued to its value is one word too, and the lexer cannot help: it
|
|
258
|
+
// correctly reports `if=/work/custB/secret` as a single word, because that is what
|
|
259
|
+
// it is. Which options introduce a filename is command-specific knowledge the floor
|
|
260
|
+
// deliberately does not have, so scan the value and let `looksLikePath` decide.
|
|
261
|
+
//
|
|
262
|
+
// Skipped after a here-string or heredoc delimiter for the same reason the whole
|
|
263
|
+
// token is: that operand is literal data the shell never opens, so `cat <<<if=/tmp/f`
|
|
264
|
+
// prints the text rather than reading the file.
|
|
265
|
+
if (operand !== "skip") {
|
|
266
|
+
for (const form of OPTION_VALUE_FORMS) {
|
|
267
|
+
const optionValue = stripped.match(form);
|
|
268
|
+
if (!optionValue?.[1]) continue;
|
|
269
|
+
const from = stripped.length - optionValue[1].length;
|
|
270
|
+
add(optionValue[1].replace(/^["']|["']$/g, ""), match.index + openingQuote + from, operand);
|
|
271
|
+
break; // the forms overlap; the first that matches has already captured the value
|
|
272
|
+
}
|
|
273
|
+
}
|
|
234
274
|
}
|
|
235
275
|
for (const match of command.matchAll(/["']([^"']+)["']/g)) add(match[1], match.index + 1);
|
|
236
276
|
return found;
|