pnp / pnp/cli-microsoft365

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

Open Beginner friendly
#7,397 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug help wanted needs research
Dominant language
TypeScript
Stars
1.5k
Forks
413
Avg merge
5d 6h
Merged PRs (30d)
21

Description

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

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at the Command.getJsonOutput entry point and reproduce the pure-JS example from the issue, then inspect how command output is routed through it. Verify that parsed JSON preserves newline-containing string values, including CRLF and bare-LF cases; the issue is done when valid JSON output round-trips without injected characters.

Written by the indexing model from the issue text.

Assessment

Tech stack
node.js, typescript
Domain
cli
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.