Skills: URI traversal validation skips percent-encoded dot-segments
I maintainer di solito rispondono entro 1 giorno
Valutazione
- Difficoltà
- 2/5
- Tempo stimato
- 1-3 ore
- Idoneità per principianti
- 78/100
Direzione di ricerca
Leggi src/mcp/shared/skills.py, concentrandoti su _validate_resource_uri_in_skill() e validate_directory_result(). Inizia tracciando come ciascuna funzione suddivide e controlla i segmenti del percorso URI, poi aggiungi test di regressione per i segmenti punto con codifica percentuale in entrambi i percorsi di convalida. Il lavoro è completato quando i segmenti . e .. codificati vengono rifiutati, mentre gli URI di risorse validi e i figli diretti continuano a essere accettati.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Initial Checks
- I confirm that I'm using the newest release of my line (the latest 2.x, or the latest 1.x if I'm still on v1)
- I confirm that I searched for my issue in https://github.com/modelcontextprotocol/python-sdk/issues before opening this issue
Release line
2.x (current stable)
Description
In src/mcp/shared/skills.py (added in #3485 for the SEP-2640 Skills extension), _validate_resource_uri_in_skill() and validate_directory_result() check for directory traversal segments against the raw URI path segments:
# src/mcp/shared/skills.py (line 253)
if any(segment in (".", "..") for segment in resource_parts.path.split("/")):
raise ValueError(f"resource URI {resource_uri!r} contains a traversal segment")
Because URI paths are not URL-unquoted prior to validation, percent-encoded traversal segments (e.g. %2e%2e, %2E%2E, or %2e) bypass this check.
Specifically:
- In
_validate_resource_uri_in_skill(), a resource URI such asskill://acme/billing/refunds/%2e%2e/other/secret.txtstarts withroot + "/"(/billing/refunds/), but its segments (["", "billing", "refunds", "%2e%2e", "other", "secret.txt"]) do not literally equal".."or".". As a result, the resource is admitted as a valid member ofskill://acme/billing/refunds/SKILL.md. - In
validate_directory_result(), line 293 checksrelative in (".", ".."). A child URI containing%2e%2eor%2ebypasses this check and is admitted as a valid direct child.
Example Code
from mcp.shared.skills import Skill, SkillResource, ReadDirectoryResult, validate_directory_result
from mcp_types import Resource
root = "skill://git-workflow/SKILL.md"
digest = "sha256:" + "a" * 64
# 1. Percent-encoded traversal bypasses manifest validation:
s = Skill(
uri=root,
frontmatter={"name": "git-workflow", "description": "d"},
resources=[
SkillResource(uri=root, digest=digest, size=10),
SkillResource(uri="skill://git-workflow/%2e%2e/evil.md", digest=digest, size=10)
]
)
# Expected: ValueError / ValidationError for traversal segment
# Observed: Accepted without error
# 2. Percent-encoded dot-segment bypasses directory validation:
res = ReadDirectoryResult(resources=[Resource(uri="skill://pdf/templates/%2e%2e", name="x")])
validate_directory_result("skill://pdf/templates", res)
# Expected: ValueError ("is not a direct child")
# Observed: Accepted without error
Proposed Fix
Normalize path segments with urllib.parse.unquote() before splitting and inspecting dot-segments in both _validate_resource_uri_in_skill and validate_directory_result.
I have a minimal regression test and fix ready and would be happy to submit a PR if this looks good.
- Lingua principale
- Python
- Stelle
- 24.5k
- Fork
- 4k
- Merge medio
- 18h 3m
- PR unite (30g)
- 39
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di modelcontextprotocol/python-sdk
-
2026-07-28 SSE responses share one header list, so in-place middleware appends leak across responsesForse di nuovo libera Una pull request per questa issue è stata chiusa senza essere unita. Apertabug spec-2026-07-28 v2
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
modelcontextprotocol/python-sdk#3701 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
bug v1 v2
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
modelcontextprotocol/python-sdk#3698 ·
I maintainer di solito rispondono entro 1 giorno
-
Streamable HTTP: Content-Type handling is order dependent and accepts invalid valuesForse già presa @jayzuccarelli l’ha presa 4 giorni fa. Apertabug v1 v2
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
modelcontextprotocol/python-sdk#3691 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Type annotation gaps and unhandled FileNotFoundError in src/mcp/cli/cli.pyForse di nuovo libera Una pull request per questa issue è stata chiusa senza essere unita. Apertaenhancement
Difficoltà 2/5 1-3 ore Idoneità per principianti 84/100
modelcontextprotocol/python-sdk#3673 · 4 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
Streamable HTTP server rejects a mixed-case Content-Type with 415Forse già presa @CRYPTONIKAV l’ha presa 6 giorni fa. Apertabug v1 v2
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
modelcontextprotocol/python-sdk#3670 · 4 commenti ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di modelcontextprotocol/python-sdk
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 65/100
noontide-co/mainbranch#1211 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
[1.x Bug] run-operation --sql reports success but rolls back writes on transaction-capable adaptersApertatriage
Difficoltà 2/5 1-3 ore Idoneità per principianti 64/100
dbt-labs/dbt#16784 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
bug needs-triage
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
I maintainer di solito rispondono entro 1 giorno
-
bug settings ui
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
DewaldOosthuizen/py-tray-command-launcher#162 ·
I maintainer di solito rispondono entro 1 giorno
-
run_all sleeps after the final job despite documenting delays between jobsForse già presa @Gambit-Checkmate l’ha presa oggi. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 74/100