Skip to content

Commit b3db1b2

Browse files
committed
feat(file): add allowed_path_traversal_elements to file_path (upstream PR #2397)
Ports upstream PR #2397: file_path() gains an optional allowed_path_traversal_elements sequence, whose segments (e.g. '.', '..') may be randomly picked as directory components, to generate test cases for directory traversal detection. Fixes applied on top of the upstream patch: - rewrote paths faker/ -> faker2/ for the renamed package - resolved conflict in faker2/providers/file/__init__.py: the fork renamed the local variable 'seperator' -> 'separator' - tests/providers/test_file.py: the last assertion's regex only matched depth=2 while the call requests depth=3; it now expects four '\\[\w.]+' segments (3 directories + the file name) - tests/providers/test_file.py: removed trailing whitespace (flake8 W293) and reformatted the call for black --line-length 120
1 parent d2d9d78 commit b3db1b2

2 files changed

Lines changed: 24 additions & 1 deletion

File tree

faker2/providers/file/__init__.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ def file_path(
275275
extension: Optional[Union[str, Sequence[str]]] = None,
276276
absolute: Optional[bool] = True,
277277
file_system_rule: Literal["linux", "windows"] = "linux",
278+
allowed_path_traversal_elements: Optional[Sequence[str]] = None,
278279
) -> str:
279280
"""Generate an pathname to a file.
280281
@@ -295,6 +296,11 @@ def file_path(
295296
if ``file_system`` is set (default="linux"), the generated path uses
296297
specified file system path standard, the list of valid file systems include:
297298
``'windows'``, ``'linux'``.
299+
If ``allowed_path_traversal_elements`` is set, it should be a sequence
300+
of path-traversal segments (e.g. ``['.', '..']``) that may be randomly
301+
included as directory components. This is useful for generating test
302+
cases for directory traversal attack detection. Default is ``None``,
303+
meaning no traversal elements are included.
298304
299305
:sample: size=10
300306
:sample: depth=3
@@ -304,6 +310,7 @@ def file_path(
304310
:sample: extension=''
305311
:sample: extension=["a", "bc", "def"]
306312
:sample: depth=5, category='video', extension='abcdef', file_system='windows'
313+
:sample: depth=3, allowed_path_traversal_elements=['.', '..']
307314
"""
308315

309316
if extension is not None and not isinstance(extension, str):
@@ -321,7 +328,11 @@ def file_path(
321328

322329
path: str = self.file_name(category, extension)
323330
for _ in range(0, depth):
324-
path = f"{self.generator.word()}{separator}{path}"
331+
if allowed_path_traversal_elements:
332+
segment = self.random_element(list(allowed_path_traversal_elements) + [self.generator.word()])
333+
else:
334+
segment = self.generator.word()
335+
path = f"{segment}{separator}{path}"
325336

326337
return root + path if absolute else path
327338

tests/providers/test_file.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,18 @@ def test_file_path(self):
5151
file_path = self.fake.file_path(file_system_rule="windows", category="image", absolute=True)
5252
assert re.search(r"^[a-zA-Z]:\\\w+\\\w+\.\w+", file_path)
5353
assert re.search(r"\\\w+\\\w+\.(bmp|gif|jpeg|jpg|png|tiff)$", file_path)
54+
file_path = self.fake.file_path(depth=3, allowed_path_traversal_elements=[".", ".."])
55+
assert re.search(r"\/[\w.]+\/[\w.]+\/[\w.]+\.\w+", file_path)
56+
assert file_path.startswith("/")
57+
file_path = self.fake.file_path(depth=3, allowed_path_traversal_elements=None)
58+
assert re.search(r"\/\w+\/\w+\/\w+\.\w+", file_path)
59+
file_path = self.fake.file_path(depth=3, allowed_path_traversal_elements=[".."])
60+
assert re.search(r"\/[\w.]+\/[\w.]+\/[\w.]+\.\w+", file_path)
61+
assert file_path.startswith("/")
62+
file_path = self.fake.file_path(
63+
depth=3, file_system_rule="windows", allowed_path_traversal_elements=[".", ".."]
64+
)
65+
assert re.search(r"^[a-zA-Z]:\\[\w.]+\\[\w.]+\\[\w.]+\\[\w.]+\.\w+", file_path)
5466

5567
def test_unix_device(self):
5668
reg_device = re.compile(r"^/dev/(vd|sd|xvd)[a-z]$")

0 commit comments

Comments
 (0)