Repository navigation
Fix literal \n in CLI-mode safeoutputs string args not becoming newlines
#47330
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -778,6 +778,40 @@ function resolveSchemaPropertyKey(key, schemaProperties, normalizedSchemaKeyMap, | |
| return normalizedSchemaKeyMap.get(normalized) || key; | ||
| } | ||
|
|
||
| /** | ||
| * Unescape standard escape sequences in a CLI string argument. | ||
| * | ||
| * Converts the same escape sequences that JSON string parsing recognises so | ||
| * that agents can write `--body "Hello\nWorld"` and get an actual newline, | ||
| * matching the behaviour of JSON stdin mode where `JSON.parse` handles `\n`. | ||
| * | ||
| * Supported sequences: | ||
| * `\n` → newline | ||
| * `\t` → tab | ||
| * `\r` → carriage return | ||
| * `\\` → single backslash | ||
| * Any other `\X` is left unchanged (the backslash is preserved). | ||
| * | ||
| * @param {string} str - Raw CLI string argument | ||
| * @returns {string} String with escape sequences replaced by their literal characters | ||
| */ | ||
| function unescapeCliStringArg(str) { | ||
| return str.replace(/\\([\s\S])/g, (match, char) => { | ||
| switch (char) { | ||
| case "n": | ||
| return "\n"; | ||
| case "t": | ||
| return "\t"; | ||
| case "r": | ||
| return "\r"; | ||
| case "\\": | ||
| return "\\"; | ||
| default: | ||
| return match; | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * Parse and coerce a CLI argument value based on the MCP tool schema property type. | ||
| * | ||
|
|
@@ -857,6 +891,14 @@ function coerceToolArgValue(key, rawValue, schemaProperty, existingValue, allowN | |
| } | ||
| } | ||
|
|
||
| // Unescape standard escape sequences in string-typed values so that agents | ||
| // can write `--body "Hello\nWorld"` and get an actual newline. This mirrors | ||
| // the behaviour of JSON stdin mode where JSON.parse interprets `\n` as a | ||
| // newline character. | ||
| if (types.includes("string")) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Silent data corruption on Windows paths and regex patterns: 💡 Details and suggested fixThe shell strips one level of quoting but leaves backslashes intact in the argv array. When a user passes: safeoutputs some_tool --path "C:\\Users\\alice\\docs"Argv delivers safeoutputs some_tool --path "C:\temp\new_file"
# \t → tab, \n → newline — path becomes "C:<TAB>emp<LF>ew_file"The transformation is irreversible and undetectable by callers. Suggested approach: apply unescaping only when the flag value was explicitly quoted/escaped by the agent in a context where
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unescaping is skipped for untyped/schema-less fields, creating inconsistent behavior: when 💡 DetailsThe code path after the new block: // When schema metadata is unavailable, apply conservative numeric coercion fallback
if (allowNumericFallback && types.length === 0) {
// ...
}
return rawValue; // unescaped rawValue returned for typeless fieldsIf the stated goal is parity with JSON stdin mode for all string arguments, the fix is incomplete for typeless schemas. At minimum, document why typeless strings are intentionally excluded.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated in
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [/diagnosing-bugs] 💡 Suggested mitigationThe existing Copilot review comment on this line already flags this concern. Possible options:
Option 3 is lowest risk short-term but option 2 is safer. @copilot please address this.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in |
||
| return unescapeCliStringArg(rawValue); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed in |
||
| } | ||
|
|
||
| // When schema metadata is unavailable (e.g. empty tools cache), apply | ||
| // conservative numeric coercion fallback for CLI ergonomics. | ||
| if (allowNumericFallback && types.length === 0) { | ||
|
|
@@ -1376,6 +1418,7 @@ if (require.main === module) { | |
| module.exports = { | ||
| parseToolArgs, | ||
| coerceToolArgValue, | ||
| unescapeCliStringArg, | ||
| extractJSONRPCMessages, | ||
| renderProgressMessages, | ||
| formatResponse, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[/diagnosing-bugs] The PR claims parity with
JSON.parse, but\b(backspace),\f(form feed), and\uXXXX(Unicode) are valid JSON escape sequences thatJSON.parsehandles yetunescapeCliStringArgleaves unchanged.💡 Example gap
If agents ever pass Unicode escapes or
\b/\fvia CLI flags, those will silently differ from JSON stdin mode. Consider adding\b,\f, and a\uXXXXhandler, or documenting the known gap in the JSDoc.@copilot please address this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in
da0a061:unescapeCliStringArgnow also handles\b,\f, and\uXXXX, and the tests cover those escape sequences.