Vulnerability bigquery rest traversal - #9188
Conversation
…b.com/googleapis/google-cloud-node into vulnerability-bigquery-rest-traversal # Conflicts: # handwritten/bigquery/test/dataset.ts
There was a problem hiding this comment.
Code Review
This pull request introduces URI encoding and path traversal validation utilities to secure service requests against directory traversal attacks. However, the security review identified critical vulnerabilities where percent-encoded sequences (e.g., %2e%2e) can bypass the validation checks in validateUriPathSegment and validateUriPath. Additionally, encodeURIPath may double-encode already percent-encoded segments. To address these issues, it is recommended to decode path segments using decodeURIComponent before performing validation and encoding.
| export function validateUriPathSegment(propertyName: string, value: string): void { | ||
| if (value === '.' || value === '..') { | ||
| throw new Error(`Invalid value ${value} for ${propertyName}`); | ||
| } | ||
| } |
There was a problem hiding this comment.
validateUriPathSegment does not decode the input segment before checking if it is . or ... If a user passes a percent-encoded path traversal sequence (e.g., %2e%2e), this check will be bypassed. To prevent path traversal bypasses, the segment should be decoded using decodeURIComponent before validation.
export function validateUriPathSegment(propertyName: string, value: string): void {
try {
const decoded = decodeURIComponent(value);
if (decoded === '.' || decoded === '..') {
throw new Error(`Invalid value ${value} for ${propertyName}`);
}
} catch {
if (value === '.' || value === '..') {
throw new Error(`Invalid value ${value} for ${propertyName}`);
}
}
}| export function validateUriPath(propertyName: string, value: string): void { | ||
| if (value) { | ||
| // Split by slash and check for exact segment matches of '.' or '..' rather | ||
| // than using a simple string.includes('.') check. This avoids rejecting | ||
| // valid domain-scoped resource segments (e.g. projects/example.com:project-id). | ||
| const segments = value.split('/'); | ||
| if (segments.some(segment => segment === '.' || segment === '..')) { | ||
| throw new Error( | ||
| `Value for ${propertyName} must not contain segments that are exactly . or ..`, | ||
| ); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
validateUriPath does not decode the path segments before checking for . or ... A user could bypass this check by using percent-encoded characters (e.g., %2e%2e). To ensure robust protection against path traversal, each segment should be decoded using decodeURIComponent before checking.
export function validateUriPath(propertyName: string, value: string): void {
if (value) {
const segments = value.split('/');
const hasTraversal = segments.some(segment => {
try {
const decoded = decodeURIComponent(segment);
return decoded === '.' || decoded === '..';
} catch {
return segment === '.' || segment === '..';
}
});
if (hasTraversal) {
throw new Error(
`Value for ${propertyName} must not contain segments that are exactly . or ..`,
);
}
}
}| export function encodeURIPath(uri: string): string { | ||
| const parts = uri.split('/'); | ||
| return parts | ||
| .map(part => { | ||
| if (part === '') { | ||
| return ''; | ||
| } | ||
| if (part.includes(':')) { | ||
| const subparts = part.split(':'); | ||
| return subparts | ||
| .map(subpart => { | ||
| if (subpart === '') { | ||
| return ''; | ||
| } | ||
| validateUriPathSegment('path segment', subpart); | ||
| return encodeWithSlashes(subpart); | ||
| }) | ||
| .join(':'); | ||
| } | ||
| validateUriPathSegment('path segment', part); | ||
| return encodeWithSlashes(part); | ||
| }) | ||
| .join('/'); | ||
| } |
There was a problem hiding this comment.
encodeURIPath currently double-encodes any path segments that are already percent-encoded (e.g., %20 becomes %2520). This will break legitimate requests that contain pre-encoded characters (such as spaces or non-ASCII characters in table/dataset IDs). Additionally, if the input is already percent-encoded, the path traversal checks in validateUriPathSegment can be bypassed.
To fix both issues, each segment/subpart should be decoded using decodeURIComponent before validation and encoding.
export function encodeURIPath(uri: string): string {
const parts = uri.split('/');
return parts
.map(part => {
if (part === '') {
return '';
}
if (part.includes(':')) {
const subparts = part.split(':');
return subparts
.map(subpart => {
if (subpart === '') {
return '';
}
let decoded = subpart;
try {
decoded = decodeURIComponent(subpart);
} catch {
// Fallback to raw subpart if decoding fails
}
validateUriPathSegment('path segment', decoded);
return encodeWithSlashes(decoded);
})
.join(':');
}
let decoded = part;
try {
decoded = decodeURIComponent(part);
} catch {
// Fallback to raw part if decoding fails
}
validateUriPathSegment('path segment', decoded);
return encodeWithSlashes(decoded);
})
.join('/');
}…b.com/googleapis/google-cloud-node into vulnerability-bigquery-rest-traversal
Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly:
Fixes #<issue_number_goes_here> 🦕