python-multipart 0.0.21__tar.gz → 0.0.22__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. {python_multipart-0.0.21 → python_multipart-0.0.22}/CHANGELOG.md +4 -0
  2. {python_multipart-0.0.21 → python_multipart-0.0.22}/PKG-INFO +1 -1
  3. {python_multipart-0.0.21 → python_multipart-0.0.22}/python_multipart/__init__.py +1 -1
  4. {python_multipart-0.0.21 → python_multipart-0.0.22}/python_multipart/multipart.py +3 -1
  5. python_multipart-0.0.22/tests/test_file.py +26 -0
  6. {python_multipart-0.0.21 → python_multipart-0.0.22}/.gitignore +0 -0
  7. {python_multipart-0.0.21 → python_multipart-0.0.22}/LICENSE.txt +0 -0
  8. {python_multipart-0.0.21 → python_multipart-0.0.22}/README.md +0 -0
  9. {python_multipart-0.0.21 → python_multipart-0.0.22}/multipart/__init__.py +0 -0
  10. {python_multipart-0.0.21 → python_multipart-0.0.22}/multipart/decoders.py +0 -0
  11. {python_multipart-0.0.21 → python_multipart-0.0.22}/multipart/exceptions.py +0 -0
  12. {python_multipart-0.0.21 → python_multipart-0.0.22}/multipart/multipart.py +0 -0
  13. {python_multipart-0.0.21 → python_multipart-0.0.22}/pyproject.toml +0 -0
  14. {python_multipart-0.0.21 → python_multipart-0.0.22}/python_multipart/decoders.py +0 -0
  15. {python_multipart-0.0.21 → python_multipart-0.0.22}/python_multipart/exceptions.py +0 -0
  16. {python_multipart-0.0.21 → python_multipart-0.0.22}/python_multipart/py.typed +0 -0
  17. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/__init__.py +0 -0
  18. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/compat.py +0 -0
  19. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/CRLF_in_header.http +0 -0
  20. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/CRLF_in_header.yaml +0 -0
  21. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/CR_in_header.http +0 -0
  22. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/CR_in_header.yaml +0 -0
  23. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/CR_in_header_value.http +0 -0
  24. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/CR_in_header_value.yaml +0 -0
  25. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/almost_match_boundary.http +0 -0
  26. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/almost_match_boundary.yaml +0 -0
  27. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/almost_match_boundary_without_CR.http +0 -0
  28. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/almost_match_boundary_without_CR.yaml +0 -0
  29. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/almost_match_boundary_without_LF.http +0 -0
  30. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/almost_match_boundary_without_LF.yaml +0 -0
  31. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/almost_match_boundary_without_final_hyphen.http +0 -0
  32. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/almost_match_boundary_without_final_hyphen.yaml +0 -0
  33. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/bad_end_of_headers.http +0 -0
  34. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/bad_end_of_headers.yaml +0 -0
  35. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/bad_header_char.http +0 -0
  36. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/bad_header_char.yaml +0 -0
  37. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/bad_initial_boundary.http +0 -0
  38. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/bad_initial_boundary.yaml +0 -0
  39. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/base64_encoding.http +0 -0
  40. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/base64_encoding.yaml +0 -0
  41. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/empty_header.http +0 -0
  42. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/empty_header.yaml +0 -0
  43. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/empty_message.http +0 -0
  44. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/empty_message.yaml +0 -0
  45. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/empty_message_with_bad_end.http +0 -0
  46. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/empty_message_with_bad_end.yaml +0 -0
  47. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/header_with_number.http +0 -0
  48. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/header_with_number.yaml +0 -0
  49. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/mixed_plain_and_base64_encoding.http +0 -0
  50. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/mixed_plain_and_base64_encoding.yaml +0 -0
  51. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/multiple_fields.http +0 -0
  52. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/multiple_fields.yaml +0 -0
  53. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/multiple_files.http +0 -0
  54. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/multiple_files.yaml +0 -0
  55. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/quoted_printable_encoding.http +0 -0
  56. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/quoted_printable_encoding.yaml +0 -0
  57. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/single_field.http +0 -0
  58. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/single_field.yaml +0 -0
  59. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/single_field_blocks.http +0 -0
  60. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/single_field_blocks.yaml +0 -0
  61. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/single_field_longer.http +0 -0
  62. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/single_field_longer.yaml +0 -0
  63. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/single_field_single_file.http +0 -0
  64. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/single_field_single_file.yaml +0 -0
  65. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/single_field_with_leading_newlines.http +0 -0
  66. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/single_field_with_leading_newlines.yaml +0 -0
  67. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/single_file.http +0 -0
  68. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/single_file.yaml +0 -0
  69. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/utf8_filename.http +0 -0
  70. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_data/http/utf8_filename.yaml +0 -0
  71. {python_multipart-0.0.21 → python_multipart-0.0.22}/tests/test_multipart.py +0 -0
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.0.22 (2026-01-25)
4
+
5
+ * Drop directory path from filename in `File` [9433f4b](https://github.com/Kludex/python-multipart/commit/9433f4bbc9652bdde82bbe380984e32f8cfc89c4).
6
+
3
7
  ## 0.0.21 (2025-12-17)
4
8
 
5
9
  * Add support for Python 3.14 and drop EOL 3.8 and 3.9 [#216](https://github.com/Kludex/python-multipart/pull/216).
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-multipart
3
- Version: 0.0.21
3
+ Version: 0.0.22
4
4
  Summary: A streaming multipart parser for Python
5
5
  Project-URL: Homepage, https://github.com/Kludex/python-multipart
6
6
  Project-URL: Documentation, https://kludex.github.io/python-multipart/
@@ -2,7 +2,7 @@
2
2
  __author__ = "Andrew Dunham"
3
3
  __license__ = "Apache"
4
4
  __copyright__ = "Copyright (c) 2012-2013, Andrew Dunham"
5
- __version__ = "0.0.21"
5
+ __version__ = "0.0.22"
6
6
 
7
7
  from .multipart import (
8
8
  BaseParser,
@@ -375,7 +375,9 @@ class File:
375
375
 
376
376
  # Split the extension from the filename.
377
377
  if file_name is not None:
378
- base, ext = os.path.splitext(file_name)
378
+ # Extract just the basename to avoid directory traversal
379
+ basename = os.path.basename(file_name)
380
+ base, ext = os.path.splitext(basename)
379
381
  self._file_base = base
380
382
  self._ext = ext
381
383
 
@@ -0,0 +1,26 @@
1
+ from pathlib import Path
2
+
3
+ from python_multipart.multipart import File
4
+
5
+
6
+ def test_upload_dir_with_leading_slash_in_filename(tmp_path: Path) -> None:
7
+ upload_dir = tmp_path / "upload"
8
+ upload_dir.mkdir()
9
+
10
+ # When the file_name provided has a leading slash, we should only use the basename.
11
+ # This is to avoid directory traversal.
12
+ to_upload = tmp_path / "foo.txt"
13
+
14
+ file = File(
15
+ bytes(to_upload),
16
+ config={
17
+ "UPLOAD_DIR": bytes(upload_dir),
18
+ "UPLOAD_KEEP_FILENAME": True,
19
+ "UPLOAD_KEEP_EXTENSIONS": True,
20
+ "MAX_MEMORY_FILE_SIZE": 10,
21
+ },
22
+ )
23
+ file.write(b"123456789012")
24
+ assert not file.in_memory
25
+ assert Path(upload_dir / "foo.txt").exists()
26
+ assert Path(upload_dir / "foo.txt").read_bytes() == b"123456789012"