Skip to content

feat(storage): Implement robust path validation and structured skip reporting#7546

Open
thiyaguk09 wants to merge 13 commits intogoogleapis:mainfrom
thiyaguk09:fix/download-directory-path-traversal
Open

feat(storage): Implement robust path validation and structured skip reporting#7546
thiyaguk09 wants to merge 13 commits intogoogleapis:mainfrom
thiyaguk09:fix/download-directory-path-traversal

Conversation

@thiyaguk09
Copy link
Contributor

  • Adds protection against path traversal (../) using normalized path resolution.
  • Prevents Windows-style drive letter injection while allowing GCS timestamps.
  • Implements directory jail logic to ensure absolute-style paths are relative to destination.
  • Preserves backward compatibility by returning an augmented DownloadResponse array.
  • Automates recursive directory creation for validated nested files.
  • Adds a comprehensive 13-scenario test suite for edge-case parity.

- Adds protection against path traversal (../) using normalized path
resolution.
- Prevents Windows-style drive letter injection while allowing GCS
timestamps.
- Implements directory jail logic to ensure absolute-style paths are
relative to destination.
- Preserves backward compatibility by returning an augmented
DownloadResponse array.
- Automates recursive directory creation for validated nested files.
- Adds comprehensive 13-scenario test suite for edge-case parity.
@product-auto-label product-auto-label bot added the api: storage Issues related to the Cloud Storage API. label Mar 10, 2026
@thiyaguk09 thiyaguk09 marked this pull request as ready for review March 10, 2026 08:26
@thiyaguk09 thiyaguk09 requested a review from a team as a code owner March 10, 2026 08:26
@quirogas quirogas added the priority: p2 Moderately-important priority. Fix may not be included in next release. label Mar 10, 2026
- Implemented "jail" logic using path.resolve to prevent traversal.
- Neutralized leading slashes in GCS object names.
- Pre-allocated results array to maintain 1:1 input/output index parity.
- Added automatic recursive directory creation for nested local paths.
- Fixed prioritization of destination options in downloadManyFiles.
- Reordered security checks to catch illegal drive letters before path
resolution.
- Fixed SkipReason mismatch (Illegal Character vs Path Traversal) on
Windows.
- Ensured absolute Windows paths are neutralized before traversal
validation.
…fety

Isolated async loop variables to fix path leakage, decoupled prefix
from destination logic, and added cross-platform traversal checks
for both forward and backslashes.
Updated Parity Check tests with platform-conditional logic to handle
OS-specific backslash resolution.
Comment on lines +639 to 641
if (options.skipIfExists && existsSync(resolvedPath)) {
continue;
}

Choose a reason for hiding this comment

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

Can you assign an empty result here as well with a skip reason? If you continue, you'll be leaving a hole in the final result array.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have updated the logic to assign a skipped result with SkipReason.ALREADY_EXISTS to that index before continuing. This ensures the finalResults array remains fully populated and perfectly aligned with the input file array.

* // - "file2.txt" (with the contents from my-bucket.file2.txt)
* const response = await transferManager.downloadManyFiles('test-folder');
* // All files with GCS prefix of 'test-folder' have been downloaded.
* ```

Choose a reason for hiding this comment

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

Can you add more notes here about the change in behavior. Previously we were returning only files which actually get downloaed. Previously the input request files size and returned results need not match but now the return result is exactly equal to input request files size. Also communicate the traversal vulnerability changes using a few comments here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have updated the JSDoc.


export type DownloadResponse = [Buffer];

export type DownloadResponseWithStatus = [Buffer] & {

Choose a reason for hiding this comment

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

QQ. This doesn't cause a breaking change to the existing customers because the return type is being changed? The existing users can still continue using this utility as is?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is not a breaking change. The return type remains an array at its core, so existing code using index-based access or destructuring (e.g., [buffer]) will continue to work. We are simply using a TypeScript intersection to attach additive metadata like skipped and reason to that array object.

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

Labels

api: storage Issues related to the Cloud Storage API. priority: p2 Moderately-important priority. Fix may not be included in next release.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants