Repository navigation
Avoid dotted ID collisions in VertexAiRagMemoryService memory lookups #5294
Description
Summary
VertexAiRagMemoryService stores Vertex RAG file display_name values as a dot-delimited string:
f"{session.app_name}.{session.user_id}.{session.id}"search_memory() then accepts a retrieved context when its source_display_name starts with:
f"{app_name}.{user_id}."This makes the client-side scope check ambiguous when IDs contain dots. For example, a memory stored for app_name="demo" and user_id="alice.smith" has a legacy display name beginning with demo.alice.smith.. A search for app_name="demo" and user_id="alice" also checks for the prefix demo.alice., so it can accept the other user's memory.
Impact
This can expose Vertex RAG-backed memory across users in the same app when one user ID is a dot-prefix of another user ID, such as alice and alice.smith. If an agent uses preload memory, the returned memory can be inserted into the next model request for the wrong user.
This appears to be a follow-up to the existing user scoping fix from #1019: the current code is intended to filter by app_name and user_id, but the dot-delimited representation is not unambiguous.
Expected behavior
Memory lookup should compare the stored app/user scope exactly. Dots in app_name, user_id, or session_id should stay inside their own component instead of changing how the display name is parsed.
Proposed fix
The prepared PR changes new Vertex RAG uploads to use an encoded v1 display-name format for app_name, user_id, and session_id. Search parsing then compares the decoded components exactly. For old data, it keeps compatibility only for the unambiguous legacy three-part app.user.session form and ignores ambiguous legacy names.
Validation
The prepared PR adds unit coverage that:
- rejects a legacy collision such as
demo.alice.smith.session_secretwhen searching asdemo/alice - still accepts new encoded display names containing dotted app/user/session IDs
- keeps unambiguous legacy
demo.alice.legacy_sessionbehavior
Local validation:
PYTHONPATH=src python -m pytest tests/unittests/memory -q
42 passed, 2 warnings
python -m isort --check-only src/google/adk/memory/vertex_ai_rag_memory_service.py tests/unittests/memory/test_vertex_ai_rag_memory_service.py
passed
python -m pyink --check --config pyproject.toml src/google/adk/memory/vertex_ai_rag_memory_service.py tests/unittests/memory/test_vertex_ai_rag_memory_service.py
passed
git diff --check -- src/google/adk/memory/vertex_ai_rag_memory_service.py tests/unittests/memory/test_vertex_ai_rag_memory_service.py
passed
Activity
surajksharma07 commentedon Apr 16, 2026
@petrmarinec the startswith(f"{app_name}.{user_id}.") check in search_memory can't distinguish a field separator from a dot inside a user ID, which is exactly how alice silently picks up alice.smith's entries from the same corpus.
The encoded v1 display-name approach in PR #5295 is the right call.
The one area worth careful testing is the backward-compat path — specifically that unambiguous legacy entries (exactly 3 dot-segments, no in-field dots) still surface correctly, and that ambiguous ones are silently skipped rather than leaked.
Could you do a quick smoke-test against a real corpus with mixed old-format and new-format entries before the review pass, just to confirm the legacy-compat logic holds up end-to-end?
petrmarinec commentedon Apr 16, 2026
Hi, I confirmed this with a real Vertex RAG corpus before the review pass.
I ran a mixed old-format/new-format smoke test against a disposable Vertex RAG corpus.
The corpus contained:
app_name=demo,user_id=alicedemo.alice.legacy-...demo.alice.smith.secret-...Raw RAG retrieval returned all three uploaded entries.
Then I queried through the actual
VertexAiRagMemoryService.search_memory(app_name="demo", user_id="alice", ...)path.The filtered result included the new encoded v1 Alice entry and the unambiguous legacy
demo.alice.legacy-...entry. It did not include the ambiguous legacydemo.alice.smith.secret-...entry.surajksharma07 commentedon Apr 19, 2026
@petrmarinec that's exactly the validation we needed — raw retrieval surfacing all three entries is the important detail here, because it confirms the attack surface is real (the corpus hands back alice.smith's entry without any server-side scoping) and the client-side filter in _parse_display_name is the only gate.
The fact that the ambiguous legacy name was silently dropped rather than passed through proves the compat logic is correct under real-corpus conditions, not just in unit tests — that's the result we were looking for.
PR #5295 looks good — the three-case split (v1 accept, unambiguous-legacy accept, ambiguous-legacy drop) is holding up end-to-end.
Team is looking more into it.
surajksharma07 commentedon Apr 19, 2026
@DeanChensj Please have a look into it once.
fix: Scope Vertex RAG memory display names