fix(gateway): deliver MEDIA: tags wrapped in Markdown emphasis

Models routinely present a file to the user with the delivery tag wrapped in
Markdown emphasis — `**MEDIA:/path.pptx**`, `*MEDIA:/path*`, `_MEDIA:/path_`.
MEDIA_TAG_CLEANUP_RE only tolerated a single leading/trailing quote or backtick
(`[`"']?`), and its closing lookahead set excluded `*` and `_`, so an
emphasis-wrapped tag never matched. The file was then silently never delivered
and the literal `MEDIA:/path` text leaked into the chat instead — the user sees
a path, not the attachment.

Allow a short run of emphasis/quote markers (`[`"'*_]{0,3}`) on both sides of
the tag and add `*`/`_` to the closing lookahead. Code-block, inline-code and
blockquote contexts are still neutralised earlier by `_mask_protected_spans`
(#35695), so documentation/example tags remain non-deliverable; the
absolute-path anchor still rejects relative paths; `_` inside a filename is
unaffected.

Adds regression coverage in TestExtractMedia for bold/italic/underscore
wrapping, mid-prose bold, emphasis-wrapped .html, underscore-in-filename, and
emphasis-wrapped relative-path rejection.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Julien Talbot 2026-06-13 22:45:38 +04:00 committed by Teknium
parent b58b1fa962
commit aaddc6c93a
2 changed files with 58 additions and 4 deletions

View file

@ -1510,11 +1510,20 @@ _MEDIA_EXT_ALTERNATION = "|".join(
# consumer so both behave identically.
# Path anchors: ``~/`` (Unix home-relative), ``/`` (Unix absolute),
# ``X:\\`` or ``X:/`` (Windows drive-letter absolute — #34632).
# Emphasis tolerance: models routinely wrap the tag in Markdown emphasis
# (``**MEDIA:/x.pdf**``, ``*MEDIA:/x.pdf*``, ``_MEDIA:/x.pdf_``) when they
# present a file to the user. The old single-quote anchor (``[`"']?``) and the
# closing lookahead (which lacked ``*``/``_``) failed to match such tags, so the
# file was silently never delivered and the literal ``MEDIA:`` text leaked into
# the chat. Allow a short run of emphasis/quote markers on both sides so the tag
# is recognised regardless of cosmetic Markdown. Code-block / inline-code /
# blockquote contexts are still neutralised earlier by ``_mask_protected_spans``
# (#35695), so example tags remain non-deliverable.
MEDIA_TAG_CLEANUP_RE = re.compile(
r'''[`"']?MEDIA:\s*'''
r'''[`"'*_]{0,3}MEDIA:\s*'''
r'''(?P<path>`[^`\n]+`|"[^"\n]+"|'[^'\n]+'|'''
r'''(?:~/|/|[A-Za-z]:[/\\])\S+(?:[^\S\n]+\S+)*?\.(?:''' + _MEDIA_EXT_ALTERNATION + r'''))'''
r'''(?=[\s`"',;:)\]}\[]|$)[`"']?''',
r'''(?=[\s`"'*_,;:)\]}\[]|$)[`"'*_]{0,3}''',
re.IGNORECASE,
)
@ -1529,10 +1538,10 @@ MEDIA_TAG_CLEANUP_RE = re.compile(
# prompt-injection paths that do not validate are left visible instead of
# silently dropped.
MEDIA_EXTENSIONLESS_TAG_RE = re.compile(
r'''[`"']?MEDIA:\s*'''
r'''[`"'*_]{0,3}MEDIA:\s*'''
r'''(?P<path>`[^`\n]+`|"[^"\n]+"|'[^'\n]+'|'''
r'''(?:~/|/|[A-Za-z]:[/\\])[^\s\n`"']+)'''
r'''[`"']?\s*''',
r'''[`"'*_]{0,3}\s*''',
re.IGNORECASE,
)

View file

@ -529,6 +529,51 @@ class TestExtractMedia:
assert [p for p, _ in media] == ["/r/a.png"]
assert "`MEDIA:/ex/b.png`" in cleaned
# --- Markdown emphasis wrapping tolerance ---
# Models routinely present a file as **MEDIA:/path** / *MEDIA:/path* /
# _MEDIA:/path_. The old pattern only tolerated a single quote/backtick, so
# the emphasis prevented the match and the file was silently never
# delivered (the literal MEDIA: text leaked into the chat instead).
def test_media_bold_wrapped_extracted(self):
media, cleaned = BasePlatformAdapter.extract_media(
"**MEDIA:/home/u/report.pptx**"
)
assert media == [("/home/u/report.pptx", False)]
assert "MEDIA:" not in cleaned
def test_media_italic_asterisk_extracted(self):
media, _ = BasePlatformAdapter.extract_media("*MEDIA:/home/u/report.pdf*")
assert media == [("/home/u/report.pdf", False)]
def test_media_italic_underscore_extracted(self):
media, _ = BasePlatformAdapter.extract_media("_MEDIA:/home/u/report.pdf_")
assert media == [("/home/u/report.pdf", False)]
def test_media_bold_mid_prose_extracted_and_stripped(self):
media, cleaned = BasePlatformAdapter.extract_media(
"Voici votre fichier **MEDIA:/tmp/r.pdf** bonne lecture"
)
assert media == [("/tmp/r.pdf", False)]
assert "MEDIA:" not in cleaned
assert "bonne lecture" in cleaned
def test_media_bold_wrapped_html_extracted(self):
# .html is a recognised extension; emphasis was the only blocker.
media, _ = BasePlatformAdapter.extract_media("**MEDIA:/srv/page.html**")
assert media == [("/srv/page.html", False)]
def test_media_underscore_in_filename_unaffected(self):
# Emphasis tolerance must not eat a legitimate '_' inside the path.
media, _ = BasePlatformAdapter.extract_media("MEDIA:/tmp/my_report_v2.pptx")
assert media == [("/tmp/my_report_v2.pptx", False)]
def test_media_bold_relative_path_still_ignored(self):
# The absolute-path anchor must still reject relative paths even when
# wrapped in emphasis.
media, _ = BasePlatformAdapter.extract_media("**MEDIA:report.html**")
assert media == []
class TestMediaInsideSerializedJson:
"""Regression coverage for #34375 — MEDIA: embedded in serialized JSON