diff --git a/src/telegram/format.ts b/src/telegram/format.ts
index cdeb3002a888..7feab62aeadf 100644
--- a/src/telegram/format.ts
+++ b/src/telegram/format.ts
@@ -187,13 +187,13 @@ export function wrapFileReferencesInHtml(html: string): string {
return `${prefix}${escapeHtml(filename)}`;
});
- // Update tag depth
+ // Update tag depth (clamp at 0 for malformed HTML with stray closing tags)
if (tagName === "code") {
- codeDepth += isClosing ? -1 : 1;
+ codeDepth = isClosing ? Math.max(0, codeDepth - 1) : codeDepth + 1;
} else if (tagName === "pre") {
- preDepth += isClosing ? -1 : 1;
+ preDepth = isClosing ? Math.max(0, preDepth - 1) : preDepth + 1;
} else if (tagName === "a") {
- anchorDepth += isClosing ? -1 : 1;
+ anchorDepth = isClosing ? Math.max(0, anchorDepth - 1) : anchorDepth + 1;
}
// Add the tag itself
@@ -236,14 +236,16 @@ export function wrapFileReferencesInHtml(html: string): string {
if (lastOpen > lastClose) {
return m; // Inside a tag attribute
}
- // Skip if inside code/pre tags (count opens vs closes before offset)
+ // Skip if inside code/pre/anchor tags (count opens vs closes before offset)
const textBefore = snapshot.slice(0, offset);
const codeOpens = (textBefore.match(/ codeCloses || preOpens > preCloses) {
- return m; // Inside code/pre content
+ const anchorOpens = (textBefore.match(/]/gi) || []).length;
+ const anchorCloses = (textBefore.match(/<\/a/gi) || []).length;
+ if (codeOpens > codeCloses || preOpens > preCloses || anchorOpens > anchorCloses) {
+ return m; // Inside code/pre/anchor content
}
return `${prefix}${escapeHtml(tld)}`;
});
diff --git a/src/telegram/format.wrap-md.test.ts b/src/telegram/format.wrap-md.test.ts
index 36e59970a2e6..e882ef837b70 100644
--- a/src/telegram/format.wrap-md.test.ts
+++ b/src/telegram/format.wrap-md.test.ts
@@ -336,6 +336,27 @@ describe("edge cases", () => {
expect(result).not.toContain("");
});
+ it("does not wrap orphaned TLD inside anchor link text", () => {
+ // R&D.md inside anchor text should NOT have D.md wrapped
+ const input = 'R&D.md';
+ const result = wrapFileReferencesInHtml(input);
+ expect(result).toBe(input);
+ expect(result).not.toContain("D.md");
+ });
+
+ it("handles malformed HTML with stray closing tags (negative depth)", () => {
+ // Stray before content shouldn't break protection logic
+ // (depth should clamp at 0, not go negative)
+ const input = "README.mdinside after.md";
+ const result = wrapFileReferencesInHtml(input);
+ // README.md should be wrapped (codeDepth = 0 after clamping stray close)
+ expect(result).toContain("README.md");
+ // after.md should be wrapped (codeDepth = 0 after proper close)
+ expect(result).toContain("after.md");
+ // Should not have nested code tags
+ expect(result).not.toContain("");
+ });
+
it("does not wrap orphaned TLD inside href attributes", () => {
// D.md inside href should NOT be wrapped
const input = 'link';