Skip to content

Commit cfa6610

Browse files
committed
improve exception handling for _extract_and_validate_wildcards
1 parent 3ad5690 commit cfa6610

1 file changed

Lines changed: 37 additions & 32 deletions

File tree

packages/google-api-core/google/api_core/path_template.py

Lines changed: 37 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@
6363
_MULTI_SEGMENT_PATTERN = r"(.+)"
6464

6565

66+
class _PathValidationFailed(Exception):
67+
"""Internal exception used when an invalid path is encountered."""
68+
69+
6670
def _validate_multi_segment_value(val: str) -> bool:
6771
"""Validate a multi-segment wildcard value.
6872
@@ -209,39 +213,40 @@ def _extract_and_validate_wildcards(
209213
Raises:
210214
ValueError: If a wildcard within a structurally valid value violates path traversal rules.
211215
"""
212-
err = ValueError(
213-
f"Invalid value {val} for {property_name or 'positional variable'}."
214-
)
215-
216-
if template_str is None or template_str == "*":
217-
# Single-segment templates (None or "*") cannot match exactly "." or ".."
218-
# and cannot have multi-segment paths resolving to 0 segments.
219-
#
220-
# Empty strings pose no traversal security risk here.
221-
if val and not _validate_multi_segment_value(val):
222-
raise err
223-
elif template_str == "**":
224-
# Multi-segment templates ("**") must represent at least one valid,
225-
# non-escaped segment.
226-
if not _validate_multi_segment_value(val):
227-
raise err
228-
else:
229-
# Compile the sub-template into a regex capture pattern
230-
# to isolate and validate individual wildcard values.
231-
pattern, wildcard_types = _build_capture_pattern(template_str)
232-
233-
m = pattern.fullmatch(val)
234-
if m is not None:
235-
# Validate each wildcard value within its matched boundaries,
236-
# preventing traversals from escaping their structural positions.
237-
for captured_val in m.groups():
238-
if not _validate_multi_segment_value(captured_val):
239-
raise err
240-
else:
241-
# For values that don't match the pattern, ensure the value doesn't
242-
# resolve to 0 segments (e.g. "projects/..").
216+
try:
217+
if template_str is None or template_str == "*":
218+
# Single-segment templates (None or "*") cannot match exactly "." or ".."
219+
# and cannot have multi-segment paths resolving to 0 segments.
220+
#
221+
# Empty strings pose no traversal security risk here.
243222
if val and not _validate_multi_segment_value(val):
244-
raise err
223+
raise _PathValidationFailed()
224+
elif template_str == "**":
225+
# Multi-segment templates ("**") must represent at least one valid,
226+
# non-escaped segment.
227+
if not _validate_multi_segment_value(val):
228+
raise _PathValidationFailed()
229+
else:
230+
# Compile the sub-template into a regex capture pattern
231+
# to isolate and validate individual wildcard values.
232+
pattern, wildcard_types = _build_capture_pattern(template_str)
233+
234+
m = pattern.fullmatch(val)
235+
if m is not None:
236+
# Validate each wildcard value within its matched boundaries,
237+
# preventing traversals from escaping their structural positions.
238+
for captured_val in m.groups():
239+
if not _validate_multi_segment_value(captured_val):
240+
raise _PathValidationFailed()
241+
else:
242+
# For values that don't match the pattern, ensure the value doesn't
243+
# resolve to 0 segments (e.g. "projects/..").
244+
if val and not _validate_multi_segment_value(val):
245+
raise _PathValidationFailed()
246+
except _PathValidationFailed:
247+
raise ValueError(
248+
f"Invalid value {val} for {property_name or 'positional variable'}."
249+
)
245250

246251

247252
def _expand_variable_match(positional_vars, named_vars, match):

0 commit comments

Comments
 (0)