Skills: URI traversal validation skips percent-encoded dot-segments
Los mantenedores suelen responder en 1 día
@Varshith-Kali ya está trabajando en esto.
Desde el 13/10/2026.
- #3703 de @chauvuusvn — cerrado sin fusionar
Evaluación
- Dificultad
- 2/5
- Tiempo estimado
- 1-3 horas
- Aptitud para principiantes
- 78/100
Línea de trabajo
Lee src/mcp/shared/skills.py y céntrate en _validate_resource_uri_in_skill() y validate_directory_result(). Empieza por rastrear cómo cada función divide y comprueba los segmentos de la ruta URI; después, añade pruebas de regresión para los segmentos de punto codificados con porcentajes en ambas rutas de validación. La tarea estará completa cuando se rechacen los segmentos codificados . y .., mientras que los URI de recursos válidos y los elementos secundarios directos sigan pasando.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
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.
- Lenguaje dominante
- Python
- Estrellas
- 24.5k
- Forks
- 4k
- Merge medio
- 18 h 31 min
- PR fusionados (30 d)
- 38
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de modelcontextprotocol/python-sdk
-
2026-07-28 SSE responses share one header list, so in-place middleware appends leak across responsesQuizá libre de nuevo Un pull request para esta issue se cerró sin fusionarse. Abiertobug spec-2026-07-28 v2
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
modelcontextprotocol/python-sdk#3701 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
bug v1 v2
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
modelcontextprotocol/python-sdk#3698 ·
Los mantenedores suelen responder en 1 día
-
Streamable HTTP: Content-Type handling is order dependent and accepts invalid valuesPosiblemente ocupada @jayzuccarelli la tomó hace 2 días. Abiertobug v1 v2
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
modelcontextprotocol/python-sdk#3691 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Type annotation gaps and unhandled FileNotFoundError in src/mcp/cli/cli.pyQuizá libre de nuevo Un pull request para esta issue se cerró sin fusionarse. Abiertoenhancement
Dificultad 2/5 1-3 horas Aptitud para principiantes 84/100
modelcontextprotocol/python-sdk#3673 · 4 comentarios ·
Los mantenedores suelen responder en 1 día
-
Streamable HTTP server rejects a mixed-case Content-Type with 415Posiblemente ocupada @CRYPTONIKAV la tomó hace 4 días. Abiertobug v1 v2
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
modelcontextprotocol/python-sdk#3670 · 4 comentarios ·
Los mantenedores suelen responder en 1 día
Todos los issues de modelcontextprotocol/python-sdk
Issues similares
-
Agentic
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
Imbad0202/academic-research-skills#979 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
reflex-dev/reflex#7561 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
prometheus-community/ansible#906 · 2 comentarios ·