Skip to content

fix: close the residual gaps from the v1 backport review - #6890

Open
GWeale wants to merge 1 commit into
v1from
fix-v1-review-residuals
Open

fix: close the residual gaps from the v1 backport review#6890
GWeale wants to merge 1 commit into
v1from
fix-v1-review-residuals

Conversation

@GWeale

@GWeale GWeale commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

Closes the three code findings from the review of the v1 security backport batch (#6788#6809) that were not regressions from those PRs and so were left for a follow-up.

Deleting an artifact no longer deletes the artifacts nested under it. Ports 08d21cc35 and a5864a0ed, which are a pair. Filenames may contain /, so doc and doc/nested are two distinct artifacts, and a nested artifact is stored beneath its parent. Deletion removed the artifact's whole directory, so deleting doc destroyed doc/nested as well, and doc/nested never appeared in list_artifact_keys. Deletion now removes only the artifact's own versions/ directory and then prunes whatever it leaves empty, bounded by the scope root. Listing keeps walking past an artifact directory, skipping only that artifact's versions.

The GCS half of 08d21cc35 is carried too, because the same conflation is present here: version scans matched every blob under the prefix, so list_versions("doc") reported versions belonging to doc/nested. Loading and saving resolve max(versions), so a read without an explicit version could resolve to a version the artifact does not have and silently return None, and the next write skipped version numbers. A blob now counts only when its name is the prefix followed by a single decimal segment, which also replaces an unguarded int() that raised on any object in the bucket not written by ADK.

A skill name may no longer carry a trailing newline. _SNAKE_OR_KEBAB_NAME_PATTERN ends in $, which outside re.MULTILINE also matches just before a trailing newline, so match let "my-skill\n" through into the resource name. fullmatch does not. Traversal was already blocked either way, so this only closes the newline. Upstream covers the same gap with a second layer, quote(name, safe=''), at the point where it interpolates the name into a URL; v1 calls the SDK with a resource name rather than building a URL, so percent-encoding would be wrong here and the validator is the right place.

A failing temp-file cleanup no longer masks the error that caused it. The cleanup in add_session_to_memory runs in a finally, so an exception raised there displaces the one already propagating, including CancelledError. It caught only FileNotFoundError. A cancelled asyncio.to_thread returns while the worker thread may still hold the file, and on Windows the remove then fails with PermissionError. Now any OSError is logged and swallowed, which is what a best-effort cleanup should do. I could not reproduce the Windows-specific failure here, since POSIX unlink succeeds on an open file, but the displacement is a property of finally rather than of one platform.

Each change is covered by a test that fails when only that change is reverted. Artifact coverage is upstream's, parametrized across the in-memory, GCS and file backends; two assertions compare artifact text rather than Part equality because v1's GCS backend round-trips a text part as inline_data, which is a pre-existing difference from main and not touched here.

Not included, from the same review: the ten defects that are byte-identical on main, which belong upstream, and the release-note items for the artifact storage layout and the API registry credential scoping.

Three findings from the review of the v1 security backport batch that
were not caused by those PRs.

Deleting an artifact removed its whole directory, and a nested artifact
lives beneath its parent, so deleting doc destroyed doc/nested; listing
never reported it either. Deletion now takes only the artifact's own
versions directory and prunes what it leaves empty. The GCS backend had
the same conflation in its version scan, where a nested artifact's
versions were reported as the parent's.

A skill name could carry a trailing newline into the resource name,
because the pattern ends in $ and was applied with match rather than
fullmatch.

The RAG temp-file cleanup caught only FileNotFoundError. It runs in a
finally, so any other OSError displaced the exception already
propagating, including CancelledError.
@GWeale
GWeale force-pushed the fix-v1-review-residuals branch from f456cef to 326279a Compare August 24, 2026 23:06
os.remove(temp_file_path)
except FileNotFoundError:
pass
except OSError:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked the other three against upstream and they line up: _prune_empty_dirs, _iter_artifact_dirs and _delete_artifact_sync are byte-identical to origin/main, as is the GCS _parse_version. Good catch on the list_versions side of the GCS conflation -- a read resolving max(versions) to a version the artifact does not own was not something I had traced.

Also, thank you for correcting the skill-name suggestion. I offered .fullmatch() or quote() as equivalent options and they are not: v1 hands a resource name to self._client.skills.get(name=...) where main builds a URL string, so percent-encoding here would double-encode. fullmatch is the right call and the comment you added says why.

This one is the exception to the pattern, and it is the only thing I would raise. It is not a port -- origin/main:283-288 is still except FileNotFoundError: pass, so this leaves v1 ahead of main and the next backport touching this file has a divergence to reconcile by hand. The description already makes the argument that would justify fixing main too: "the displacement is a property of finally rather than of one platform". The to_thread wrapper makes it easier to hit on v1, but a read-only mount or a permissions problem displaces the propagating exception on main just the same.

Non-blocking, and I would not hold this PR for it -- would it be worth sending the same three lines upstream so the two converge rather than drift?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants