Skip to content

fix(mcp): anchor TP3 loopback URL exemption to a host boundary#113

Open
jichaowang02-lang wants to merge 1 commit into
NVIDIA:mainfrom
jichaowang02-lang:fix/tp3-loopback-host-boundary
Open

fix(mcp): anchor TP3 loopback URL exemption to a host boundary#113
jichaowang02-lang wants to merge 1 commit into
NVIDIA:mainfrom
jichaowang02-lang:fix/tp3-loopback-host-boundary

Conversation

@jichaowang02-lang

Copy link
Copy Markdown

Summary

The TP3 (parameter injection) analyzer's malicious-default-URL check can be
bypassed: a default value pointing at an attacker-controlled host whose
name merely starts with localhost / 127.0.0.1 is treated as benign
loopback and produces no finding.

Root cause

mcp_tool_poisoning.py:

_TP3_MALICIOUS_URL_RE = re.compile(
    r"https?://(?!localhost|127\.0\.0\.1)\S+",
    re.IGNORECASE,
)

The negative lookahead matches the bare substring localhost / 127.0.0.1
with no host boundary, so any host that begins with that prefix is
exempted. _check_tp3 gates the malicious-default finding on this regex, so an
exfiltration default like http://localhost.evil.com/exfil yields zero TP3
findings. An attacker only needs to register a host beginning with
localhost. (or 127.0.0.1.).

>>> bool(_TP3_MALICIOUS_URL_RE.search("http://localhost.attacker.com/exfil?d=secrets"))
False   # ← treated as loopback, not flagged
>>> bool(_TP3_MALICIOUS_URL_RE.search("http://attacker.com/exfil"))
True
default URL before after
http://localhost.evil.com/exfil ❌ not flagged ✅ TP3
http://127.0.0.1.evil.com/x ❌ not flagged ✅ TP3
http://attacker.com/exfil ✅ TP3 ✅ TP3
http://localhost:8080/cb (loopback) exempt exempt
http://127.0.0.1/cb (loopback) exempt exempt

Fix

Anchor the loopback exemption to a real host boundary (port / path / query /
fragment / end of string):

r"https?://(?!(?:localhost|127\.0\.0\.1)(?:[:/?#]|$))\S+"

Only genuine loopback hosts are exempt; external hosts that share the prefix
are now flagged.

Testing

$ pytest tests/test_mcp_tool_poisoning.py -q
24 passed
$ ruff check ... && ruff format --check ...
All checks passed!

Adds test_localhost_prefixed_attacker_url_is_flagged (the bypass cases) and
test_genuine_loopback_default_url_is_exempt (regression guard). Verified
end-to-end against _check_tp3 / node() on Python 3.13.

(No pre-existing issue — newly found while reading the analyzer. Commit is
DCO Signed-off-by.)

`_TP3_MALICIOUS_URL_RE` exempted any default URL whose host merely *starts
with* "localhost"/"127.0.0.1": the negative lookahead matched the bare
substring with no host boundary. An attacker-controlled host such as
`http://localhost.evil.com/exfil` was therefore treated as loopback and
skipped, silently bypassing the malicious-default-URL detection in TP3 — an
attacker only needs to register a host beginning with `localhost.`.

Anchor the exemption with `(?:[:/?#]|$)` so only genuine loopback hosts
(optionally followed by a port/path/query/fragment) are exempt. External
hosts that share the prefix are now flagged; real loopback defaults
(`http://localhost:8080/...`, `http://127.0.0.1/...`) stay exempt.

Adds regression tests for localhost-/127.0.0.1-prefixed attacker URLs and a
guard that genuine loopback defaults remain exempt.

Signed-off-by: jichao wang <jichaowang02@gmail.com>
Copilot AI review requested due to automatic review settings June 20, 2026 03:48

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants