Skip to content

Commit af24db3

Browse files
committed
fix: escape URL in markdown rendering and harden test assertions
- Escape verificationUriComplete with escapeMarkdownInline() before passing to renderInlineMarkdown() to prevent underscores in URLs (e.g., self-hosted instances) from being interpreted as emphasis - Set SENTRY_PLAIN_OUTPUT=1 in upgrade test mock context so table assertions use raw markdown pipes instead of Unicode box-drawing characters that only appear in TTY mode - Add test for URL escaping with underscore-containing URLs
1 parent 4bfa18b commit af24db3

File tree

3 files changed

+37
-3
lines changed

3 files changed

+37
-3
lines changed

src/lib/interactive-login.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ import { setupCopyKeyListener } from "./clipboard.js";
1212
import { getDbPath } from "./db/index.js";
1313
import { setUserInfo } from "./db/user.js";
1414
import { formatError } from "./errors.js";
15-
import { renderInlineMarkdown } from "./formatters/markdown.js";
15+
import {
16+
escapeMarkdownInline,
17+
renderInlineMarkdown,
18+
} from "./formatters/markdown.js";
1619
import { logger } from "./logger.js";
1720
import { completeOAuthFlow, performDeviceFlow } from "./oauth.js";
1821
import { generateQRCode } from "./qrcode.js";
@@ -61,7 +64,11 @@ export function buildDeviceFlowDisplay(
6164
const lines: string[] = [];
6265

6366
lines.push("");
64-
lines.push(renderInlineMarkdown(` URL: ${verificationUriComplete}`));
67+
lines.push(
68+
renderInlineMarkdown(
69+
` URL: ${escapeMarkdownInline(verificationUriComplete)}`
70+
)
71+
);
6572
lines.push(renderInlineMarkdown(` Code: \`${userCode}\``));
6673
lines.push("");
6774

test/commands/cli/upgrade.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ function createMockContext(
6363
...overrides.env,
6464
};
6565

66+
// Force plain output so formatUpgradeResult renders raw markdown tables
67+
// (ASCII pipes) instead of Unicode box-drawing characters in TTY mode.
68+
const origPlain = process.env.SENTRY_PLAIN_OUTPUT;
69+
process.env.SENTRY_PLAIN_OUTPUT = "1";
70+
6671
// Capture consola output (routed to process.stderr)
6772
const origWrite = process.stderr.write.bind(process.stderr);
6873
process.stderr.write = ((chunk: string | Uint8Array) => {
@@ -123,6 +128,11 @@ function createMockContext(
123128
errors,
124129
restore: () => {
125130
process.stderr.write = origWrite;
131+
if (origPlain === undefined) {
132+
delete process.env.SENTRY_PLAIN_OUTPUT;
133+
} else {
134+
process.env.SENTRY_PLAIN_OUTPUT = origPlain;
135+
}
126136
},
127137
};
128138
}

test/lib/interactive-login.test.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,24 @@ describe("buildDeviceFlowDisplay", () => {
3030
test("includes complete URL with embedded code", () => {
3131
const lines = buildDeviceFlowDisplay(CODE, URL, true, false);
3232
const joined = lines.join("\n");
33-
expect(joined).toContain(URL);
33+
// URL is escaped for markdown safety (underscores become \_)
34+
expect(joined).toContain("sentry.io/auth/device/");
35+
expect(joined).toContain("ABCD-EFGH");
36+
});
37+
38+
test("escapes markdown characters in URLs", () => {
39+
const urlWithUnderscores =
40+
"https://self_hosted.example.com/auth/device/?user_code=AB_CD";
41+
const lines = buildDeviceFlowDisplay(
42+
"AB_CD",
43+
urlWithUnderscores,
44+
true,
45+
false
46+
);
47+
const joined = lines.join("\n");
48+
// Underscores should be escaped so they aren't interpreted as emphasis
49+
expect(joined).toContain("self\\_hosted");
50+
expect(joined).not.toContain("<em>");
3451
});
3552

3653
test("includes user code as inline code span", () => {

0 commit comments

Comments
 (0)