From 0d61629fee810b9c4b44240d51fab01821057d42 Mon Sep 17 00:00:00 2001 From: ghost <49853598+JSONbored@users.noreply.github.com> Date: Wed, 8 Jul 2026 23:25:29 -0700 Subject: [PATCH] fix(review): prevent generated test markdown breakout --- src/review/e2e-test-gen-render.ts | 11 +++++++++-- test/unit/e2e-test-gen-render.test.ts | 21 +++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/review/e2e-test-gen-render.ts b/src/review/e2e-test-gen-render.ts index a2757aacce..f3760c0e93 100644 --- a/src/review/e2e-test-gen-render.ts +++ b/src/review/e2e-test-gen-render.ts @@ -31,6 +31,12 @@ export type E2eTestGenCommentInput = { commit?: E2eTestGenCommitOutcome | undefined; }; +function markdownFenceFor(source: string): string { + const backtickRunLengths = Array.from(source.matchAll(/`+/g), (match) => match[0]!.length); + const longestBacktickRun = Math.max(0, ...backtickRunLengths); + return "`".repeat(Math.max(3, longestBacktickRun + 1)); +} + /** * Build the PR-comment body for a `@gittensory generate-tests` result. A null `testSource` renders a * clear "nothing usable" note rather than silently posting no comment at all — the maintainer who invoked @@ -74,6 +80,7 @@ export function buildE2eTestGenCommentBody(input: E2eTestGenCommentInput): strin "", ] : []; + const fence = markdownFenceFor(input.testSource); return [ AGENT_COMMAND_COMMENT_MARKER, "", @@ -82,9 +89,9 @@ export function buildE2eTestGenCommentBody(input: E2eTestGenCommentInput): strin "> This is a suggestion, not a guarantee — review it like any other test before merging.", ...declineNote, "", - "```typescript", + `${fence}typescript`, input.testSource, - "```", + fence, "", "---", gittensoryFooter(), diff --git a/test/unit/e2e-test-gen-render.test.ts b/test/unit/e2e-test-gen-render.test.ts index 50fa6530ee..e782845ade 100644 --- a/test/unit/e2e-test-gen-render.test.ts +++ b/test/unit/e2e-test-gen-render.test.ts @@ -10,6 +10,27 @@ describe("buildE2eTestGenCommentBody", () => { expect(body).toContain("```typescript\ntest('x', () => {});\n```"); }); + it("uses a longer markdown fence when generated source contains backtick fences", () => { + const source = [ + "import { test, expect } from '@playwright/test';", + "", + "test('injected markdown stays inside the code block', async ({ page }) => {", + " const attackerMarkdown = `", + "```", + "# rendered outside the fence before the fix", + "```", + " `;", + " expect(attackerMarkdown).toContain('# rendered outside');", + "});", + ].join("\n"); + + const body = buildE2eTestGenCommentBody({ actor: "maintainer", testSource: source }); + + expect(body).toContain("````typescript\n"); + expect(body).toContain(source + "\n````"); + expect(body.split("\n")).not.toContain("```typescript"); + }); + it("uses a custom framework name when provided", () => { const body = buildE2eTestGenCommentBody({ actor: "maintainer", testSource: "it('x', () => {});", framework: "Cypress" }); expect(body).toContain("AI-generated Cypress test for @maintainer");