Skip to content

Bug report: JSON output corrupts string values containing newlines — getJsonOutput injects a literal backslash (regression from #2807 fix) #7397

Description

@arthur-bamboo

Description

Command.getJsonOutput() post-processes JSON.stringify output with a regex intended to escape newlines (comment references #2807):

getJsonOutput(logStatement: any): string {
  return JSON
    .stringify(logStatement, null, 2)
    // replace unescaped newlines with escaped newlines #2807
    .replace(/([^\\])\\n/g, '$1\\\\\\n');
}

JSON.stringify already emits valid JSON (all control characters in string values are escaped), so this regex never has a legitimate match to fix — but its single-character lookbehind cannot see escape context, so it corrupts data: any \n escape inside a string value that is preceded by a non-backslash character gets rewritten, which injects a literal backslash into the parsed value. Since every newline's \n escape is preceded by a normal character (\r\n → the n-escape follows r; bare \n follows the previous data character), this fires on every newline inside every string value of every command's JSON output.

Minimal repro (pure JS, current main logic)

const mangled = JSON.stringify({ b: "line1\r\nline2" })
  .replace(/([^\\])\\n/g, '$1\\\\\\n');
// mangled === '{"b":"line1\\r\\\\\\nline2"}'
JSON.parse(mangled).b;
// → "line1\r\\\nline2"  — a literal backslash injected between CR and LF

Bare-LF values are corrupted the same way ("a\nb""a\<LF>b" after parse).

Real-world impact

Microsoft Graph message bodies (Exchange HTML) are full of CRLF newlines. Reading a message through the CLI returns corrupted content:

m365 request --method post --url "https://graph.microsoft.com/v1.0/me/messages/{id}/createReplyAll"
# → body.content contains a literal "\" before every newline

The same draft fetched with curl and a bearer token comes back clean — on a real reply draft we measured 77 injected backslashes via the CLI vs 0 via curl for byte-identical Graph content. --output json and the default output are equally affected (both route through getJsonOutput).

This is not cosmetic: in a compose flow (createReplyAll → read draft body → PATCH body with quoted history → send), the injected backslashes are sent to recipients — at least one survives Outlook's HTML sanitization as visible \ text in the delivered email.

Steps to reproduce

  1. m365 request --url "https://graph.microsoft.com/v1.0/me/messages/{any-id}?$select=body" for any message whose HTML contains CRLF (virtually all Exchange-generated HTML).
  2. Compare body.content with the same GET via curl/Graph Explorer.

Or run the pure-JS snippet above.

Expected results

JSON output is JSON.stringify's output: parseable, and parsing returns the original data unchanged.

Actual results

Parsing the CLI's JSON output returns string values with a literal \ injected before every newline.

Suggested fix

Remove the .replace(...). The #2807 problem (invalid JSON from _ObjectIdentity_ containing raw control characters) cannot be reproduced through JSON.stringify, which escapes control characters by construction — if some code path emits raw control characters today, the fix belongs there, not in a post-stringify rewrite that corrupts valid output.

Diagnostics

  • CLI for Microsoft 365 version: 11.8.0 (latest at time of filing; logic present in current main)
  • Node.js: v22 (Homebrew), macOS (Darwin 25.5)
  • Shell: zsh

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions