safeuploads 2.0.0__tar.gz → 2.1.0__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.
- {safeuploads-2.0.0 → safeuploads-2.1.0}/.agents/skills/safeuploads-library/SKILL.md +1 -1
- {safeuploads-2.0.0 → safeuploads-2.1.0}/AGENTS.md +1 -1
- {safeuploads-2.0.0 → safeuploads-2.1.0}/CHANGELOG.md +16 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/PKG-INFO +15 -2
- {safeuploads-2.0.0 → safeuploads-2.1.0}/README.md +14 -1
- {safeuploads-2.0.0 → safeuploads-2.1.0}/docs/index.md +14 -1
- {safeuploads-2.0.0 → safeuploads-2.1.0}/docs/security/architecture.md +4 -2
- {safeuploads-2.0.0 → safeuploads-2.1.0}/pyproject.toml +2 -2
- {safeuploads-2.0.0 → safeuploads-2.1.0}/safeuploads/file_validator.py +631 -209
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/test_file_validator.py +34 -0
- safeuploads-2.1.0/tests/test_sync_validation.py +817 -0
- safeuploads-2.1.0/uv.lock +1452 -0
- safeuploads-2.0.0/uv.lock +0 -1339
- {safeuploads-2.0.0 → safeuploads-2.1.0}/.agents/skills/conventional-commits/SKILL.md +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/.agents/skills/conventional-commits/references/pull-requests.md +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/.agents/skills/debugging/SKILL.md +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/.agents/skills/safeuploads-library/references/code-standards.md +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/.agents/skills/safeuploads-library/references/security.md +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/.agents/skills/safeuploads-library/references/testing.md +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/.agents/skills/skill-authoring/SKILL.md +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/.github/CODEOWNERS +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/.github/FUNDING.yml +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/.github/copilot-instructions.md +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/.github/dependabot.yml +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/.github/scripts/check_conventional_commits.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/.github/scripts/smoke_import.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/.github/workflows/audit-safeuploads.yml +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/.github/workflows/conventional-commits.yml +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/.github/workflows/docs.yml +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/.github/workflows/fuzz.yml +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/.github/workflows/lint-and-test.yml +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/.github/workflows/mirror.yml +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/.github/workflows/publish-safeuploads.yml +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/.github/workflows/test-matrix-safeuploads.yml +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/.gitignore +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/CODE_OF_CONDUCT.md +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/CONTRIBUTING.md +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/LICENSE.md +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/SECURITY.md +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/docs/api.md +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/docs/rate-limiting.md +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/docs/security/integration-checklist.md +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/docs/security/threat-model.md +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/examples/README.md +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/examples/fastapi_example.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/mkdocs.yml +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/safeuploads/__init__.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/safeuploads/audit.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/safeuploads/config.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/safeuploads/enums.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/safeuploads/exceptions.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/safeuploads/inspectors/__init__.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/safeuploads/inspectors/base.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/safeuploads/inspectors/content_inspector.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/safeuploads/inspectors/gzip_inspector.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/safeuploads/inspectors/zip_inspector.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/safeuploads/protocols.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/safeuploads/py.typed +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/safeuploads/utils.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/safeuploads/validators/__init__.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/safeuploads/validators/base.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/safeuploads/validators/compression_validator.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/safeuploads/validators/extension_validator.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/safeuploads/validators/filename_validator.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/safeuploads/validators/fit_validator.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/safeuploads/validators/mime_validator.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/safeuploads/validators/signature_validator.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/safeuploads/validators/unicode_validator.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/safeuploads/validators/windows_validator.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/safeuploads/validators/xml_validator.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/__init__.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/conftest.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/fuzz/__init__.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/fuzz/helpers.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/fuzz/test_fuzz_config.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/fuzz/test_fuzz_filenames.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/fuzz/test_fuzz_images.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/fuzz/test_fuzz_zip.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/inspectors/__init__.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/inspectors/test_content_inspector.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/inspectors/test_gzip_inspector.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/inspectors/test_zip_inspector.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/test_audit.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/test_config.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/test_config_validation.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/test_enums.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/test_exceptions.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/test_fastapi_integration.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/test_performance.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/test_utils.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/test_validate_activity_file.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/test_validate_gzip_file.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/validators/__init__.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/validators/test_compression_validator.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/validators/test_extension_validator.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/validators/test_filename_validator.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/validators/test_fit_validator.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/validators/test_mime_validator.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/validators/test_signature_validator.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/validators/test_unicode_validator.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/validators/test_windows_validator.py +0 -0
- {safeuploads-2.0.0 → safeuploads-2.1.0}/tests/validators/test_xml_validator.py +0 -0
|
@@ -16,7 +16,7 @@ Where this skill and any of them disagree, they win.
|
|
|
16
16
|
- Never loosen a default (limit, allow-list, block-list, ratio, depth) to make a test or one caller pass; ask first.
|
|
17
17
|
- Keep the filename check order: Unicode sanitization, then Windows reserved names, then the remaining normalization.
|
|
18
18
|
- Raise a `safeuploads.exceptions` subclass with its `ErrorCode`; the message is safe to show a caller and never contains file contents or filesystem paths.
|
|
19
|
-
- Blocking work (archive inspection, hashing, magic) stays off the event loop
|
|
19
|
+
- Blocking work (archive inspection, hashing, magic) stays off the event loop: async methods use the existing worker-thread offload and `executor` option; `*_sync` methods run it in the calling thread and keep the event-loop guard.
|
|
20
20
|
- A released `ErrorCode` never changes meaning; a new failure mode gets a new code.
|
|
21
21
|
- A new public name goes in `safeuploads/__init__.py` (`__all__`), `docs/api.md`, the README example if it changes usage, and `CHANGELOG.md`.
|
|
22
22
|
- A rename, removal, signature change, default change that accepts or rejects different input, or `ErrorCode` change is breaking: mark it `!` and say so in the changelog.
|
|
@@ -92,7 +92,7 @@ uv run pytest
|
|
|
92
92
|
- Configuration changes go through `FileSecurityConfig` and `SecurityLimits`, and a new field updates their cross-field validation.
|
|
93
93
|
- Failures raise the exceptions in `safeuploads/exceptions.py` (with a stable `ErrorCode`), never a bare `ValueError` or `Exception`.
|
|
94
94
|
- The order of filename checks is significant: Unicode sanitization first, Windows reserved names before other normalization.
|
|
95
|
-
- Blocking inspection never runs on the event loop
|
|
95
|
+
- Blocking inspection never runs on the event loop: the async `validate_*_file` methods offload it to a worker thread, and the `*_sync` methods run it in the calling thread and refuse to run on an event-loop thread.
|
|
96
96
|
- XML is parsed only through `defusedxml` (XXE-safe); archives are inspected without extracting to disk and under size, ratio, depth and entry limits.
|
|
97
97
|
- Never build a filesystem path from an uploaded filename.
|
|
98
98
|
- Logging uses `logging.getLogger(__name__)` and the `SecurityAuditLogger`; never log file contents.
|
|
@@ -4,6 +4,21 @@ All notable changes to this project are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [2.1.0] - 2026-11-05
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `FileValidator.validate_image_file_sync`, `validate_zip_file_sync`, `validate_activity_file_sync` and `validate_gzip_file_sync`: synchronous counterparts of the async `validate_*_file` methods, each taking `(file: SeekableFile, filename: str)` and returning the sanitized filename as `str`. They run the same checks, order, exceptions, error codes and audit events and block the calling thread without the worker-thread offload (the `executor` option applies only to the async methods); called on a thread with a running asyncio event loop they raise `RuntimeError` before reading the file.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- Shortened the uv `exclude-newer` cooldown from 30 to 7 days.
|
|
16
|
+
- Updated all dependency groups to their latest versions.
|
|
17
|
+
|
|
18
|
+
### Security
|
|
19
|
+
|
|
20
|
+
- `validate_image_file` reads the 8 KB header and the content-analysis sample until the requested size or end of file. An upload object whose `read(n)` returned fewer than `n` bytes before its end left the rest of the sample unscanned, so a polyglot signature past the first short read was accepted.
|
|
21
|
+
|
|
7
22
|
## [2.0.0] - 2026-11-01
|
|
8
23
|
|
|
9
24
|
### Added
|
|
@@ -192,6 +207,7 @@ Initial release.
|
|
|
192
207
|
- **MIME type verification:** magic-number validation for common file types.
|
|
193
208
|
- Framework-agnostic async validation, a rich exception hierarchy with machine-readable error codes, secure defaults, and full type hints.
|
|
194
209
|
|
|
210
|
+
[2.1.0]: https://github.com/endurain-project/safeuploads/compare/v2.0.0...v2.1.0
|
|
195
211
|
[2.0.0]: https://github.com/endurain-project/safeuploads/compare/v1.1.3...v2.0.0
|
|
196
212
|
[1.1.3]: https://github.com/endurain-project/safeuploads/compare/v1.1.2...v1.1.3
|
|
197
213
|
[1.1.2]: https://github.com/endurain-project/safeuploads/compare/v1.1.1...v1.1.2
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: safeuploads
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Summary: A comprehensive file security system for validating uploads and preventing attacks
|
|
5
5
|
Author-email: João Vitória Silva <joao@endurain.com>
|
|
6
6
|
License: MIT
|
|
@@ -26,7 +26,7 @@ Secure file upload validation for Python 3.13+ applications. Catches dangerous f
|
|
|
26
26
|
|
|
27
27
|
## Features
|
|
28
28
|
|
|
29
|
-
- **Framework-agnostic** async validation (FastAPI, generic)
|
|
29
|
+
- **Framework-agnostic** async and sync validation (FastAPI, generic)
|
|
30
30
|
- Filename sanitization and Unicode security checks
|
|
31
31
|
- Extension validation with configurable allow/block lists
|
|
32
32
|
- ZIP bomb detection, nested archive inspection, and recursive structure protection
|
|
@@ -71,6 +71,19 @@ async def upload_image(file: UploadFile):
|
|
|
71
71
|
return {"status": "success", "filename": file.filename}
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
+
For synchronous code or worker threads, use the `*_sync` methods. They block the calling thread and return the sanitized filename:
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from safeuploads import FileValidator
|
|
78
|
+
|
|
79
|
+
validator = FileValidator()
|
|
80
|
+
|
|
81
|
+
with open("activity.gpx", "rb") as fh:
|
|
82
|
+
safe_name = validator.validate_activity_file_sync(fh, "activity.gpx")
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Calling a `*_sync` method on a thread running an asyncio event loop raises `RuntimeError`; await the async method there instead.
|
|
86
|
+
|
|
74
87
|
## Configuration
|
|
75
88
|
|
|
76
89
|
```python
|
|
@@ -12,7 +12,7 @@ Secure file upload validation for Python 3.13+ applications. Catches dangerous f
|
|
|
12
12
|
|
|
13
13
|
## Features
|
|
14
14
|
|
|
15
|
-
- **Framework-agnostic** async validation (FastAPI, generic)
|
|
15
|
+
- **Framework-agnostic** async and sync validation (FastAPI, generic)
|
|
16
16
|
- Filename sanitization and Unicode security checks
|
|
17
17
|
- Extension validation with configurable allow/block lists
|
|
18
18
|
- ZIP bomb detection, nested archive inspection, and recursive structure protection
|
|
@@ -57,6 +57,19 @@ async def upload_image(file: UploadFile):
|
|
|
57
57
|
return {"status": "success", "filename": file.filename}
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
+
For synchronous code or worker threads, use the `*_sync` methods. They block the calling thread and return the sanitized filename:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from safeuploads import FileValidator
|
|
64
|
+
|
|
65
|
+
validator = FileValidator()
|
|
66
|
+
|
|
67
|
+
with open("activity.gpx", "rb") as fh:
|
|
68
|
+
safe_name = validator.validate_activity_file_sync(fh, "activity.gpx")
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Calling a `*_sync` method on a thread running an asyncio event loop raises `RuntimeError`; await the async method there instead.
|
|
72
|
+
|
|
60
73
|
## Configuration
|
|
61
74
|
|
|
62
75
|
```python
|
|
@@ -16,7 +16,7 @@ Secure file upload validation for Python 3.13+ applications. Catches dangerous f
|
|
|
16
16
|
|
|
17
17
|
## Features
|
|
18
18
|
|
|
19
|
-
- **Framework-agnostic** async validation (FastAPI, generic)
|
|
19
|
+
- **Framework-agnostic** async and sync validation (FastAPI, generic)
|
|
20
20
|
- Filename sanitization and Unicode security checks
|
|
21
21
|
- Extension validation with configurable allow/block lists
|
|
22
22
|
- ZIP bomb detection, nested archive inspection, and recursive structure protection
|
|
@@ -61,6 +61,19 @@ async def upload_image(file: UploadFile):
|
|
|
61
61
|
return {"status": "success", "filename": file.filename}
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
+
For synchronous code or worker threads, use the `*_sync` methods. They block the calling thread and return the sanitized filename:
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from safeuploads import FileValidator
|
|
68
|
+
|
|
69
|
+
validator = FileValidator()
|
|
70
|
+
|
|
71
|
+
with open("activity.gpx", "rb") as fh:
|
|
72
|
+
safe_name = validator.validate_activity_file_sync(fh, "activity.gpx")
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Calling a `*_sync` method on a thread running an asyncio event loop raises `RuntimeError`; await the async method there instead.
|
|
76
|
+
|
|
64
77
|
## Configuration
|
|
65
78
|
|
|
66
79
|
```python
|
|
@@ -38,7 +38,7 @@ safeuploads/
|
|
|
38
38
|
|
|
39
39
|
| Component | Responsibility |
|
|
40
40
|
|---|---|
|
|
41
|
-
| `FileValidator` | Orchestrates validation for each file type. Manages streaming, resource monitoring
|
|
41
|
+
| `FileValidator` | Orchestrates validation for each file type. Manages streaming, resource monitoring and audit events, and delegates to validators/inspectors. The async methods offload blocking inspection to a worker thread (or a configured `executor`); the `*_sync` methods run the same pipeline in the calling thread and refuse to run on an event-loop thread. Exposes them as `filename_validator`, `signature_validator`, `mime_validator` and the other validator attributes; `magic_mime` and `magic_available` are properties that delegate to `mime_validator`. |
|
|
42
42
|
| `FileSecurityConfig` | Centralizes all configuration: allowed MIME types, extensions, blocked extensions, Unicode characters, and Windows reserved names. |
|
|
43
43
|
| `SecurityLimits` | Holds numeric thresholds: file sizes, compression ratios, timeouts, entry limits, resource caps. |
|
|
44
44
|
| `BaseValidator` | Abstract base class; validators inherit and implement `validate()`. |
|
|
@@ -61,6 +61,8 @@ safeuploads/
|
|
|
61
61
|
|
|
62
62
|
## Validation Pipelines
|
|
63
63
|
|
|
64
|
+
The `validate_*_file_sync` methods run the identical steps listed below in the calling thread, without the worker-thread offload.
|
|
65
|
+
|
|
64
66
|
### Image Validation (`validate_image_file`)
|
|
65
67
|
|
|
66
68
|
```
|
|
@@ -400,7 +402,7 @@ and content threats, are `FileValidationError` subclasses.
|
|
|
400
402
|
## Audit Event Flow
|
|
401
403
|
|
|
402
404
|
```
|
|
403
|
-
FileValidator.validate_*() (
|
|
405
|
+
FileValidator.validate_*() (_validation_run)
|
|
404
406
|
│
|
|
405
407
|
├── bind_correlation_id() # reuses the host's ID, if set
|
|
406
408
|
├── audit.start() # VALIDATION_START
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "safeuploads"
|
|
3
|
-
version = "2.
|
|
3
|
+
version = "2.1.0"
|
|
4
4
|
description = "A comprehensive file security system for validating uploads and preventing attacks"
|
|
5
5
|
authors = [
|
|
6
6
|
{name = "João Vitória Silva",email = "joao@endurain.com"}
|
|
@@ -42,7 +42,7 @@ build-backend = "hatchling.build"
|
|
|
42
42
|
|
|
43
43
|
[tool.uv]
|
|
44
44
|
required-version = "0.13.5"
|
|
45
|
-
exclude-newer = "
|
|
45
|
+
exclude-newer = "7 days"
|
|
46
46
|
|
|
47
47
|
[tool.ruff]
|
|
48
48
|
line-length = 79
|