Skip to content

Commit 961f3e8

Browse files
jazhang00copybara-github
authored andcommitted
fix: rollback of path segment validation in InMemoryArtifactService and GcsArtifactService
Co-authored-by: Jason Zhang <jasoncz@google.com> PiperOrigin-RevId: 944123180
1 parent 3fa993b commit 961f3e8

6 files changed

Lines changed: 67 additions & 357 deletions

File tree

src/google/adk/artifacts/artifact_util.py

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -134,32 +134,3 @@ def validate_artifact_reference_scope(
134134
"Session-scoped artifact references must stay within the same"
135135
" session scope."
136136
)
137-
138-
139-
def validate_path_segment(value: str, field_name: str) -> None:
140-
"""Rejects values that could alter the constructed path.
141-
142-
Args:
143-
value: The caller-supplied identifier (e.g. user_id or session_id).
144-
field_name: Human-readable name used in the error message.
145-
146-
Raises:
147-
InputValidationError: If the value contains path separators, traversal
148-
segments, or null bytes.
149-
"""
150-
if not value:
151-
raise input_validation_error.InputValidationError(
152-
f"{field_name} must not be empty."
153-
)
154-
if "\x00" in value:
155-
raise input_validation_error.InputValidationError(
156-
f"{field_name} must not contain null bytes."
157-
)
158-
if "/" in value or "\\" in value:
159-
raise input_validation_error.InputValidationError(
160-
f"{field_name} {value!r} must not contain path separators."
161-
)
162-
if value in (".", "..") or ".." in value.split("/"):
163-
raise input_validation_error.InputValidationError(
164-
f"{field_name} {value!r} must not contain traversal segments."
165-
)

src/google/adk/artifacts/file_artifact_service.py

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
from pydantic import ValidationError
3535
from typing_extensions import override
3636

37-
from . import artifact_util
3837
from ..errors.input_validation_error import InputValidationError
3938
from .base_artifact_service import ArtifactVersion
4039
from .base_artifact_service import BaseArtifactService
@@ -143,14 +142,39 @@ def _is_user_scoped(session_id: Optional[str], filename: str) -> bool:
143142
return session_id is None or _file_has_user_namespace(filename)
144143

145144

145+
def _validate_path_segment(value: str, field_name: str) -> None:
146+
"""Rejects values that could alter the constructed filesystem path.
147+
148+
Args:
149+
value: The caller-supplied identifier (e.g. user_id or session_id).
150+
field_name: Human-readable name used in the error message.
151+
152+
Raises:
153+
InputValidationError: If the value contains path separators, traversal
154+
segments, or null bytes.
155+
"""
156+
if not value:
157+
raise InputValidationError(f"{field_name} must not be empty.")
158+
if "\x00" in value:
159+
raise InputValidationError(f"{field_name} must not contain null bytes.")
160+
if "/" in value or "\\" in value:
161+
raise InputValidationError(
162+
f"{field_name} {value!r} must not contain path separators."
163+
)
164+
if value in (".", "..") or ".." in value.split("/"):
165+
raise InputValidationError(
166+
f"{field_name} {value!r} must not contain traversal segments."
167+
)
168+
169+
146170
def _user_artifacts_dir(base_root: Path) -> Path:
147171
"""Returns the path that stores user-scoped artifacts."""
148172
return base_root / "artifacts"
149173

150174

151175
def _session_artifacts_dir(base_root: Path, session_id: str) -> Path:
152176
"""Returns the path that stores session-scoped artifacts."""
153-
artifact_util.validate_path_segment(session_id, "session_id")
177+
_validate_path_segment(session_id, "session_id")
154178
return base_root / "sessions" / session_id / "artifacts"
155179

156180

@@ -232,7 +256,7 @@ def __init__(self, root_dir: Path | str):
232256

233257
def _base_root(self, user_id: str, /) -> Path:
234258
"""Returns the artifacts root directory for a user."""
235-
artifact_util.validate_path_segment(user_id, "user_id")
259+
_validate_path_segment(user_id, "user_id")
236260
return self.root_dir / "users" / user_id
237261

238262
def _scope_root(
@@ -245,7 +269,7 @@ def _scope_root(
245269
base = self._base_root(user_id)
246270
if _is_user_scoped(session_id, filename):
247271
return _user_artifacts_dir(base)
248-
if session_id is None:
272+
if not session_id:
249273
raise InputValidationError(
250274
"Session ID must be provided for session-scoped artifacts."
251275
)
@@ -519,7 +543,7 @@ def _list_artifact_keys_sync(
519543

520544
base_root = self._base_root(user_id)
521545

522-
if session_id is not None:
546+
if session_id:
523547
session_root = _session_artifacts_dir(base_root, session_id)
524548
for artifact_dir in _iter_artifact_dirs(session_root):
525549
metadata = self._latest_metadata(artifact_dir)

src/google/adk/artifacts/gcs_artifact_service.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -167,16 +167,13 @@ def _get_blob_prefix(
167167
session_id: Optional[str] = None,
168168
) -> str:
169169
"""Constructs the blob name prefix in GCS for a given artifact."""
170-
artifact_util.validate_path_segment(app_name, "app_name")
171-
artifact_util.validate_path_segment(user_id, "user_id")
172170
if self._file_has_user_namespace(filename):
173171
return f"{app_name}/{user_id}/user/{filename}"
174172

175173
if session_id is None:
176174
raise InputValidationError(
177175
"Session ID must be provided for session-scoped artifacts."
178176
)
179-
artifact_util.validate_path_segment(session_id, "session_id")
180177
return f"{app_name}/{user_id}/{session_id}/{filename}"
181178

182179
def _get_blob_name(
@@ -371,10 +368,6 @@ def _load_artifact(
371368
def _list_artifact_keys(
372369
self, app_name: str, user_id: str, session_id: Optional[str]
373370
) -> list[str]:
374-
artifact_util.validate_path_segment(app_name, "app_name")
375-
artifact_util.validate_path_segment(user_id, "user_id")
376-
if session_id is not None:
377-
artifact_util.validate_path_segment(session_id, "session_id")
378371
filenames = set()
379372

380373
if session_id:

src/google/adk/artifacts/in_memory_artifact_service.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,16 +85,13 @@ def _artifact_path(
8585
Returns:
8686
The constructed artifact path.
8787
"""
88-
artifact_util.validate_path_segment(app_name, "app_name")
89-
artifact_util.validate_path_segment(user_id, "user_id")
9088
if self._file_has_user_namespace(filename):
9189
return f"{app_name}/{user_id}/user/{filename}"
9290

9391
if session_id is None:
9492
raise InputValidationError(
9593
"Session ID must be provided for session-scoped artifacts."
9694
)
97-
artifact_util.validate_path_segment(session_id, "session_id")
9895
return f"{app_name}/{user_id}/{session_id}/{filename}"
9996

10097
@override
@@ -218,10 +215,6 @@ async def load_artifact(
218215
async def list_artifact_keys(
219216
self, *, app_name: str, user_id: str, session_id: Optional[str] = None
220217
) -> list[str]:
221-
artifact_util.validate_path_segment(app_name, "app_name")
222-
artifact_util.validate_path_segment(user_id, "user_id")
223-
if session_id is not None:
224-
artifact_util.validate_path_segment(session_id, "session_id")
225218
usernamespace_prefix = f"{app_name}/{user_id}/user/"
226219
session_prefix = (
227220
f"{app_name}/{user_id}/{session_id}/" if session_id else None

0 commit comments

Comments
 (0)