fix(artifacts): reject interior path separators in validate_path_segment - #6975
Open
chelsealong wants to merge 1 commit into
Open
fix(artifacts): reject interior path separators in validate_path_segment#6975chelsealong wants to merge 1 commit into
chelsealong wants to merge 1 commit into
Conversation
artifact_util.validate_path_segment only rejected a leading '/' or '\',
not one appearing anywhere in the value. FileArtifactService builds its
user-scoped and session-scoped directories so that they differ by an
infix ("sessions/<id>"), so a user_id containing that infix collapses
onto another caller's session-scoped directory, letting artifacts saved
under one scope be read back under the other.
Restores the full separator check that existed before the validator was
consolidated into artifact_util (and inadvertently weakened) in 45a77dc.
Fixes google#6973
chelsealong
force-pushed
the
fix/artifact-path-segment-separator
branch
from
September 3, 2026 00:33
830d69d to
ba7ec04
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Link to Issue or Description of Change
Problem:
artifact_util.validate_path_segmentonly rejects a value that startswith
/or\, not one that contains a separator anywhere.FileArtifactServicebuilds its user-scoped and session-scoped directories so that they differ by
an infix (
.../users/<user_id>/artifacts/...vs..../users/<user_id>/sessions/<session_id>/artifacts/...), so auser_idcontaining
/sessions/<id>now resolves to the same directory as anotheruser's session scope. Concretely: a user-scoped save with
user_id="victim/sessions/s1"lands in exactly the directory thatuser_id="victim", session_id="s1"uses for its session-scoped artifacts,so one caller's artifact overwrites/reads another's.
This is a regression from
45a77dc5("fix: Validate path segments inGcsArtifactService and InMemoryArtifactService to prevent cross-user
artifact access"), which consolidated per-service path validation into a
single
artifact_util.validate_path_segmenthelper but weakened the checkfrom "must not contain a separator" to "must not start with a separator" in
the process.
Solution:
Restore the full separator check (
"/" in value or "\\" in value), matchingthe check
FileArtifactServicehad before the consolidation. This subsumesthe old "must not start with a slash" branch, since any leading separator is
also an interior one.
One existing test,
test_save_and_load_namespaced_user_id_succeeds, assertedthat a
user_idlike"group/user123"round-trips successfully across allthree backends. That test was added in the same commit that introduced the
regression, and is itself a symptom of the same weakened check rather than an
independently designed feature — permitting arbitrary separators in
user_idis exactly what makes the
FileArtifactServicecollision possible, so itcan't be preserved without leaving the vulnerability open. I converted it into
a negative test (
test_save_artifact_rejects_slash_in_user_id) asserting theseparator is now rejected, and added a new test,
test_file_rejects_user_id_that_collides_with_session_scope, that reproducesthe exact scope-collision scenario from the issue end-to-end against
FileArtifactService.Testing Plan
Unit Tests:
Added/updated:
tests/unittests/artifacts/test_artifact_util.py: moved"group/user123","has/slash","back\\slash"from the valid-segment cases to theinvalid-segment cases, and added
"victim/sessions/s1"as an invalid case.tests/unittests/artifacts/test_artifact_service.py: addedtest_file_rejects_user_id_that_collides_with_session_scope, whichreproduces the issue's exact repro (save a session-scoped artifact for
user_id="victim", then confirm a user-scoped save withuser_id="victim/sessions/s1"is rejected instead of overwriting it, thenconfirms the original artifact still reads back intact); converted
test_save_and_load_namespaced_user_id_succeedsintotest_save_artifact_rejects_slash_in_user_id; updated the expected errormessages in
INVALID_PATH_SEGMENT_CASESto match the restored check.I confirmed the new/updated tests fail without the fix by reverting just
src/google/adk/artifacts/artifact_util.pyto themainversion(
git checkout HEAD~1 -- src/google/adk/artifacts/artifact_util.py) andre-running:
With the fix restored, the full artifact suite passes:
Also ran
pyink --check,ruff check,isort --check, andcodespellonthe three changed files — clean, aside from one pre-existing, unrelated
rufffinding (SimpleNamespaceimported but unused intest_artifact_service.py) that is already present onmainand untouchedby this change.
Manual End-to-End (E2E) Tests:
Not applicable — this is a pure library fix to a synchronous validation
helper with no I/O or model/network involvement; the added unit test exercises
the real
FileArtifactServiceagainst a temp directory end-to-end (save,rejected collision attempt, load), which is the scenario from the issue.
Checklist
Additional context
GcsArtifactServiceandInMemoryArtifactServiceshare the same validatorand are also covered by the new/updated tests, even though the issue's
analysis is that their
{app}/{user}/user/{file}layout doesn't have thisparticular collision — the shared helper doesn't distinguish between
backends, so the fix (and the regression test) applies uniformly across all
three.
This change was developed with AI assistance (Claude Code). The bug was
verified against the current
mainbranch, the fix and tests were writtenand reviewed by me, and all test output quoted above was produced by running
the suite in this checkout.