From cf0920d7ab49d8052bf366ff6b7603e88e458309 Mon Sep 17 00:00:00 2001 From: ly-wang19 Date: Wed, 24 Jun 2026 20:42:31 +0800 Subject: [PATCH] fix(link-understanding): strip markdown links whose label contains brackets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit stripMarkdownLinks used /\[[^\]]*]\(.../ for the link text, which cannot match a label containing ']' (e.g. "[my notes [v2]](https://...)"). Such markdown links survived stripping and their URL was then extracted by BARE_LINK_RE as a bare link — including a stray trailing ')'. This turned a display-only citation into a fetched link. Allow ']' in the label as long as it is not the closing '](' boundary so the markdown link is stripped and its URL is suppressed. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/link-understanding/detect.test.ts | 9 +++++++++ src/link-understanding/detect.ts | 5 ++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/link-understanding/detect.test.ts b/src/link-understanding/detect.test.ts index 3b1737e507e0..20e540deee76 100644 --- a/src/link-understanding/detect.test.ts +++ b/src/link-understanding/detect.test.ts @@ -20,6 +20,15 @@ describe("extractLinksFromMessage", () => { expect(links).toEqual(["https://bare.example"]); }); + it("ignores markdown links whose label contains brackets", () => { + // The closing "]" inside the label must not break markdown stripping, otherwise + // the citation URL leaks out as a bare link (with a stray trailing ")"). + const links = extractLinksFromMessage( + "Check [my notes [v2]](https://internal.example/doc) for details", + ); + expect(links).toStrictEqual([]); + }); + it("blocks 127.0.0.1", () => { const links = extractLinksFromMessage("http://127.0.0.1/test https://ok.test"); expect(links).toEqual(["https://ok.test"]); diff --git a/src/link-understanding/detect.ts b/src/link-understanding/detect.ts index 480fd7c3992a..6118dd5aa646 100644 --- a/src/link-understanding/detect.ts +++ b/src/link-understanding/detect.ts @@ -3,7 +3,10 @@ import { isBlockedHostnameOrIp } from "../infra/net/ssrf.js"; import { DEFAULT_MAX_LINKS } from "./defaults.js"; // Remove markdown link syntax so only bare URLs are considered. -const MARKDOWN_LINK_RE = /\[[^\]]*]\((https?:\/\/\S+?)\)/gi; +// The link-text portion allows "]" that is not the closing "](" boundary so +// markdown links whose label contains brackets (e.g. "[my notes [v2]](...)") +// are still stripped instead of leaking their URL to BARE_LINK_RE. +const MARKDOWN_LINK_RE = /\[(?:[^\]]|](?!\())*]\((https?:\/\/\S+?)\)/gi; const BARE_LINK_RE = /https?:\/\/\S+/gi; function stripMarkdownLinks(message: string): string {