fix(paths): reject trailing newlines in scoped IDs (#5911)

Co-authored-by: Zhangchenhao-111 <285911435+Zhangchenhao-111@users.noreply.github.com>
This commit is contained in:
haochen 2026-09-27 10:13:05 +08:00 committed by GitHub
parent 231335d309
commit 5d3972791d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 19 additions and 3 deletions

View File

@ -31,14 +31,14 @@ def _validate_thread_id(thread_id: str) -> str:
def _validate_user_id(user_id: str) -> str:
"""Validate a user ID before using it in filesystem paths."""
if not _SAFE_USER_ID_RE.match(user_id):
if not _SAFE_USER_ID_RE.fullmatch(user_id):
raise ValueError(f"Invalid user_id {user_id!r}: only alphanumeric characters, hyphens, and underscores are allowed.")
return user_id
def _validate_integration_id(integration_id: str) -> str:
"""Validate an integration ID before using it in filesystem paths."""
if not _SAFE_INTEGRATION_ID_RE.match(integration_id):
if not _SAFE_INTEGRATION_ID_RE.fullmatch(integration_id):
raise ValueError(f"Invalid integration_id {integration_id!r}: only alphanumeric characters, dots, hyphens, and underscores are allowed.")
# The charset allows dots for names like ``some.integration``; reject the
# bare ``.``/``..`` path components so a future caller cannot escape the
@ -50,7 +50,7 @@ def _validate_integration_id(integration_id: str) -> str:
def _validate_project_id(project_id: str) -> str:
"""Validate a project ID before using it in filesystem paths."""
if not _SAFE_USER_ID_RE.match(project_id):
if not _SAFE_USER_ID_RE.fullmatch(project_id):
raise ValueError(f"Invalid project_id {project_id!r}: only alphanumeric characters, hyphens, and underscores are allowed.")
return project_id

View File

@ -29,6 +29,10 @@ class TestValidateUserId:
with pytest.raises(ValueError, match="Invalid user_id"):
paths.user_dir("")
def test_rejects_trailing_newline(self, paths: Paths):
with pytest.raises(ValueError, match="Invalid user_id"):
paths.user_dir("alice\n")
class TestMakeSafeUserId:
def test_already_safe_id_is_unchanged(self):
@ -80,6 +84,12 @@ class TestValidateIntegrationId:
with pytest.raises(ValueError, match="Invalid integration_id"):
_validate_integration_id(integration_id)
def test_rejects_trailing_newline(self):
from deerflow.config.paths import _validate_integration_id
with pytest.raises(ValueError, match="Invalid integration_id"):
_validate_integration_id("lark-cli\n")
@pytest.mark.parametrize("integration_id", [".", ".."])
def test_host_integration_config_dir_rejects_dot_traversal(self, paths: Paths, integration_id):
with pytest.raises(ValueError, match="Invalid integration_id"):
@ -91,6 +101,12 @@ class TestValidateIntegrationId:
paths.host_user_integration_data_dir("alice", integration_id)
class TestValidateProjectId:
def test_rejects_trailing_newline(self, paths: Paths):
with pytest.raises(ValueError, match="Invalid project_id"):
paths.user_project_dir("alice", "project\n")
class TestUserDir:
def test_user_dir(self, paths: Paths):
assert paths.user_dir("alice") == paths.base_dir / "users" / "alice"