mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-05 02:41:56 +00:00
fix(claude-code): allow dot-prefixed segments in git-subdir paths
This commit is contained in:
parent
24123269cc
commit
57aa8113f1
2 changed files with 38 additions and 4 deletions
|
|
@ -126,10 +126,12 @@ async def get_marketplace():
|
|||
|
||||
|
||||
# Allowlist for git-subdir paths: one or more segments separated by '/'.
|
||||
# Each segment must start with an alphanumeric character and contain only
|
||||
# alphanumeric characters, dots, hyphens, and underscores.
|
||||
# This implicitly blocks '..', leading '/', backslashes, and percent-encoded sequences.
|
||||
_VALID_GIT_SUBDIR_PATH_RE = re.compile(r"^[a-zA-Z0-9][a-zA-Z0-9._-]*(/[a-zA-Z0-9][a-zA-Z0-9._-]*)*$")
|
||||
# Each segment contains only alphanumeric characters, dots, hyphens, and
|
||||
# underscores, and may start with a dot (e.g. '.claude/skills/my-skill').
|
||||
# The negative lookahead rejects the '.' and '..' segments so leading '/',
|
||||
# backslashes, percent-encoded sequences, and path traversal are still blocked.
|
||||
_GIT_SUBDIR_SEGMENT = r"(?!\.{1,2}(?:/|$))[a-zA-Z0-9._-]+"
|
||||
_VALID_GIT_SUBDIR_PATH_RE = re.compile(rf"^{_GIT_SUBDIR_SEGMENT}(?:/{_GIT_SUBDIR_SEGMENT})*$")
|
||||
|
||||
|
||||
def _validate_plugin_source(source: Dict[str, Any]) -> None:
|
||||
|
|
|
|||
|
|
@ -170,6 +170,33 @@ async def test_register_plugin_git_subdir_empty_path():
|
|||
assert "path" in exc_info.value.detail["error"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
".claude/skills/my-skill",
|
||||
".github/workflows",
|
||||
".config",
|
||||
"plugins/.hidden/my-plugin",
|
||||
],
|
||||
)
|
||||
async def test_register_plugin_git_subdir_dot_prefixed_path(path):
|
||||
"""git-subdir paths with dot-prefixed segments (e.g. .claude/) register successfully."""
|
||||
request = RegisterPluginRequest(
|
||||
name="dot-prefixed-plugin",
|
||||
source={
|
||||
"source": "git-subdir",
|
||||
"url": "https://github.com/org/monorepo.git",
|
||||
"path": path,
|
||||
},
|
||||
)
|
||||
|
||||
response = await register_plugin(request=request, user_api_key_dict=_USER)
|
||||
|
||||
assert response["status"] == "success"
|
||||
assert response["plugin"]["source"]["path"] == path
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_register_plugin_git_subdir_path_traversal():
|
||||
"""git-subdir with path traversal segments raises HTTP 400."""
|
||||
|
|
@ -181,6 +208,11 @@ async def test_register_plugin_git_subdir_path_traversal():
|
|||
"plugins/%2e%2e/secrets", # percent-encoded traversal
|
||||
"plugins/%2E%2E/secrets", # uppercase percent-encoded traversal
|
||||
"plugins/%252e%252e/secrets", # double-encoded traversal
|
||||
"..", # bare parent segment
|
||||
".", # bare current-dir segment
|
||||
"plugins/..", # trailing parent traversal
|
||||
".claude/../secrets", # dot-prefixed segment followed by traversal
|
||||
"plugins/./secrets", # embedded current-dir segment
|
||||
]:
|
||||
request = RegisterPluginRequest(
|
||||
name="bad-plugin",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue