Fix: reject whitespace-padded artifact filenames in all three backends - #856
Open
AmaadMartin wants to merge 2 commits into
Open
Fix: reject whitespace-padded artifact filenames in all three backends#856AmaadMartin wants to merge 2 commits into
AmaadMartin wants to merge 2 commits into
Conversation
added 2 commits
August 9, 2026 02:14
…ckends FileArtifactService trimmed a filename before mapping it onto a directory, so ' a.txt' and 'a.txt' addressed one artifact while the in-memory and GCS backends kept them apart. A filename is a storage key, so the backends must agree. A filename becomes a directory name on the filesystem backend, and Windows strips trailing spaces from a path component, so a padded name cannot be stored distinctly there. Every backend now rejects a padded name on save, and every read or delete path treats one as not found.
All three backends now report the same error first for the same input.
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.
Please ensure you have read the contribution guide before creating a pull request.
Link to Issue or Description of Change
No existing issue.
Problem:
FileArtifactService.getArtifactDir()trimmed the filename before it mapped the name onto a directory.' a.txt'and'a.txt'therefore addressed one artifact: a save under the padded name appended a version to the unpadded artifact, and a load under either name returned the other one's content.InMemoryArtifactServiceandGcsArtifactServicekeep the two names apart, so the three backends disagreed on the same key. An artifact filename is a storage key, so this is data corruption on the filesystem backend.Solution: One shared guard,
assertUnpaddedFilename(), rejects a filename whose scope-relative part has leading or trailing whitespace, and all three backends call it insaveArtifact. A filename becomes a directory name on the filesystem backend, and Windows removes trailing spaces from a path component (reference), so a padded name cannot be stored there distinctly from its unpadded twin; CI runs the unit suite onwindows-latest, so "preserve the padding" is not implementable. The guard runs first ingetArtifactDir(), whose existingtry/catchcallers turn a padded name into a miss, so every read and delete now misses instead of aliasing. The.trim()is deleted.Breaking change:
saveArtifactnow rejects a padded filename on all three backends.FileArtifactServicepreviously aliased such a name onto the unpadded artifact; the other two stored it as a distinct key. Reads on GCS stay unguarded, so a padded object written by an older version can still be listed, read and deleted. The one in-repo caller that passes a caller-controlled filename (Runner.saveArtifacts) already catches a save failure and keeps the original part.Deviations from the spec (2):
core/src/artifacts/artifact_filename.ts, not inbase_artifact_service.ts.core/src/common.tsre-exports* from './artifacts/base_artifact_service.js', so putting it there would publish it as@google/adkpublic API, which the spec forbids.Collision check:
gh pr list --repo AmaadMartin/adk-js --state open --limit 100, thengh pr diff --name-onlyon every artifact PR. No open PR implements this fix. Three siblings touch the same files: #760 (host-independent rooting) and #765 (filename edge-case tests) both merge cleanly with this branch (git merge-tree); #855 (nested delete) rewritesgetArtifactDirand conflicts textually, not semantically — whichever lands first, the other rebases.Testing Plan
Please describe the tests that you ran to verify your changes. This is required for all PRs that are not small documentation or typo fixes.
Unit Tests:
[x] I have added or updated unit tests for my change.
[x] All unit tests pass locally.
The new
describe('whitespace-padded filenames')block is added at the end of the shared conformance suiterunArtifactServiceTests(), so it runs against all three backends. No existing test is modified.Coverage of the new module
core/src/artifacts/artifact_filename.ts: 100% lines, 100% branches.Proof the tests can fail. Each mutation was applied to the fixed code, and the suite was re-run:
cleanFilename = cleanFilename.trim(), keep the guardgetArtifactDirand restore the trimFileArtifactService.promise resolved "1" instead of rejecting(the original bug: a version appended toa.txt),expected { text: 'unpadded' } to be undefined.in_memory_artifact_service.tsInMemoryArtifactService:promise resolved "+0" instead of rejecting.gcs_artifact_service.tsGcsArtifactService:promise resolved "+0" instead of rejecting.InMemoryArtifactService.saveArtifactsynchronous again, so the guard throws instead of rejectingArtifact filename " padded.txt" must not have...) rather than rejecting the promise. This pins theasynckeyword.Manual End-to-End (E2E) Tests:
Please provide instructions on how to manually test your changes, including any necessary setup or configuration.
Run the reproduction against the built package.
GcsArtifactServiceneeds a real bucket, so it is covered by the fake-bucket conformance suite instead.Result on both backends:
Before this change,
FileArtifactServicereturned[0,1]and"second".Checklist
[x] I have read the CONTRIBUTING.md document.
[x] I have performed a self-review of my own code.
[x] I have commented my code, particularly in hard-to-understand areas.
[x] I have added tests that prove my fix is effective or that my feature works.
[x] New and existing unit tests pass locally with my changes.