safeuploads 1.0.1__tar.gz → 1.1.1__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-1.1.1/.github/CODEOWNERS +4 -0
- safeuploads-1.1.1/.github/FUNDING.yml +13 -0
- safeuploads-1.1.1/.github/ISSUE_TEMPLATE/bug_report.md +35 -0
- safeuploads-1.1.1/.github/ISSUE_TEMPLATE/config.yml +5 -0
- safeuploads-1.1.1/.github/ISSUE_TEMPLATE/feature_request.md +27 -0
- safeuploads-1.1.1/.github/PULL_REQUEST_TEMPLATE.md +33 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/.github/copilot-instructions.md +1 -1
- safeuploads-1.1.1/.github/dependabot.yml +56 -0
- {safeuploads-1.0.1/.forgejo → safeuploads-1.1.1/.github}/scripts/check_conventional_commits.py +8 -6
- safeuploads-1.1.1/.github/scripts/smoke_import.py +35 -0
- safeuploads-1.1.1/.github/workflows/audit-safeuploads.yml +58 -0
- {safeuploads-1.0.1/.forgejo → safeuploads-1.1.1/.github}/workflows/conventional-commits.yml +12 -13
- safeuploads-1.1.1/.github/workflows/docs.yml +81 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/.github/workflows/fuzz.yml +6 -13
- safeuploads-1.0.1/.github/workflows/lint&test.yml → safeuploads-1.1.1/.github/workflows/lint-and-test.yml +19 -25
- safeuploads-1.1.1/.github/workflows/mirror.yml +30 -0
- safeuploads-1.1.1/.github/workflows/publish-safeuploads.yml +170 -0
- safeuploads-1.1.1/CHANGELOG.md +161 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/CONTRIBUTING.md +2 -2
- {safeuploads-1.0.1 → safeuploads-1.1.1}/PKG-INFO +21 -9
- {safeuploads-1.0.1 → safeuploads-1.1.1}/README.md +20 -8
- {safeuploads-1.0.1 → safeuploads-1.1.1}/docs/index.md +21 -9
- {safeuploads-1.0.1 → safeuploads-1.1.1}/docs/rate-limiting.md +1 -1
- {safeuploads-1.0.1 → safeuploads-1.1.1}/docs/security/architecture.md +11 -11
- {safeuploads-1.0.1 → safeuploads-1.1.1}/docs/security/threat-model.md +21 -2
- {safeuploads-1.0.1 → safeuploads-1.1.1}/examples/README.md +13 -5
- {safeuploads-1.0.1 → safeuploads-1.1.1}/examples/fastapi_example.py +125 -3
- {safeuploads-1.0.1 → safeuploads-1.1.1}/mkdocs.yml +1 -1
- {safeuploads-1.0.1 → safeuploads-1.1.1}/pyproject.toml +51 -1
- {safeuploads-1.0.1 → safeuploads-1.1.1}/safeuploads/__init__.py +6 -3
- {safeuploads-1.0.1 → safeuploads-1.1.1}/safeuploads/audit.py +4 -3
- {safeuploads-1.0.1 → safeuploads-1.1.1}/safeuploads/config.py +467 -310
- {safeuploads-1.0.1 → safeuploads-1.1.1}/safeuploads/enums.py +28 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/safeuploads/exceptions.py +29 -13
- safeuploads-1.1.1/safeuploads/file_validator.py +1212 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/safeuploads/inspectors/__init__.py +4 -2
- safeuploads-1.1.1/safeuploads/inspectors/base.py +34 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/safeuploads/inspectors/content_inspector.py +40 -63
- {safeuploads-1.0.1 → safeuploads-1.1.1}/safeuploads/inspectors/gzip_inspector.py +16 -20
- {safeuploads-1.0.1 → safeuploads-1.1.1}/safeuploads/inspectors/zip_inspector.py +44 -69
- safeuploads-1.1.1/safeuploads/py.typed +0 -0
- safeuploads-1.1.1/safeuploads/utils.py +311 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/safeuploads/validators/base.py +6 -1
- {safeuploads-1.0.1 → safeuploads-1.1.1}/safeuploads/validators/compression_validator.py +67 -28
- {safeuploads-1.0.1 → safeuploads-1.1.1}/safeuploads/validators/extension_validator.py +3 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/safeuploads/validators/unicode_validator.py +5 -1
- {safeuploads-1.0.1 → safeuploads-1.1.1}/safeuploads/validators/windows_validator.py +3 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/safeuploads/validators/xml_validator.py +10 -3
- {safeuploads-1.0.1 → safeuploads-1.1.1}/tests/inspectors/test_zip_inspector.py +36 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/tests/test_config.py +45 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/tests/test_config_validation.py +63 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/tests/test_enums.py +20 -0
- safeuploads-1.1.1/tests/test_fastapi_integration.py +164 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/tests/test_file_validator.py +241 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/tests/test_utils.py +37 -4
- {safeuploads-1.0.1 → safeuploads-1.1.1}/tests/validators/test_compression_validator.py +89 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/tests/validators/test_unicode_validator.py +5 -1
- safeuploads-1.1.1/uv.lock +1120 -0
- safeuploads-1.0.1/.forgejo/workflows/docs.yml +0 -51
- safeuploads-1.0.1/.forgejo/workflows/fuzz.yml +0 -44
- safeuploads-1.0.1/.forgejo/workflows/lint-and-test.yml +0 -86
- safeuploads-1.0.1/.forgejo/workflows/publish.yml +0 -63
- safeuploads-1.0.1/.forgejo/workflows/renovate.yml +0 -37
- safeuploads-1.0.1/.github/workflows/publish.yml +0 -67
- safeuploads-1.0.1/renovate.json +0 -35
- safeuploads-1.0.1/safeuploads/file_validator.py +0 -1028
- safeuploads-1.0.1/safeuploads/utils.py +0 -176
- safeuploads-1.0.1/uv.lock +0 -422
- {safeuploads-1.0.1 → safeuploads-1.1.1}/.github/agents/se-gitops-ci-specialist.agent.md +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/.github/agents/se-security-reviewer.agent.md +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/.github/agents/test-implementor.agent.md +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/.github/agents/test-reviewer.agent.md +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/.github/instructions/agents.instructions.md +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/.github/instructions/github-actions-ci-cd-best-practices.instructions.md +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/.github/instructions/instructions.instructions.md +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/.github/instructions/prompt.instructions.md +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/.github/instructions/python.instructions.md +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/.github/instructions/security-and-owasp.instructions.md +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/.gitignore +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/CODE_OF_CONDUCT.md +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/LICENSE.md +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/SECURITY.md +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/docs/api.md +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/docs/security/integration-checklist.md +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/safeuploads/protocols.py +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/safeuploads/validators/__init__.py +2 -2
- {safeuploads-1.0.1 → safeuploads-1.1.1}/tests/__init__.py +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/tests/conftest.py +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/tests/fuzz/__init__.py +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/tests/fuzz/test_fuzz_config.py +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/tests/fuzz/test_fuzz_filenames.py +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/tests/fuzz/test_fuzz_images.py +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/tests/fuzz/test_fuzz_zip.py +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/tests/inspectors/__init__.py +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/tests/inspectors/test_content_inspector.py +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/tests/inspectors/test_gzip_inspector.py +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/tests/test_audit.py +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/tests/test_exceptions.py +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/tests/test_performance.py +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/tests/test_validate_activity_file.py +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/tests/test_validate_gzip_file.py +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/tests/validators/__init__.py +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/tests/validators/test_extension_validator.py +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/tests/validators/test_windows_validator.py +0 -0
- {safeuploads-1.0.1 → safeuploads-1.1.1}/tests/validators/test_xml_validator.py +0 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# These are supported funding model platforms
|
|
2
|
+
|
|
3
|
+
github: joaovitoriasilva # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
|
4
|
+
patreon: endurain # Replace with a single Patreon username
|
|
5
|
+
#open_collective: # Replace with a single Open Collective username
|
|
6
|
+
#ko_fi: # Replace with a single Ko-fi username
|
|
7
|
+
#tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
|
8
|
+
#community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
|
9
|
+
liberapay: endurain # Replace with a single Liberapay username
|
|
10
|
+
#issuehunt: # Replace with a single IssueHunt username
|
|
11
|
+
#otechie: # Replace with a single Otechie username
|
|
12
|
+
#lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
|
13
|
+
custom: ['https://buymeacoffee.com/endurain'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: 🐞 Bug report
|
|
3
|
+
about: Report a bug
|
|
4
|
+
title: "[BUG]: "
|
|
5
|
+
labels: [bug]
|
|
6
|
+
assignees: ''
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Checklist
|
|
10
|
+
|
|
11
|
+
- [ ] I have searched **existing issues** to make sure this bug hasn't already been reported.
|
|
12
|
+
- [ ] I have checked the **documentation** at [https://safeuploads.endurain.com](https://safeuploads.endurain.com) and didn't find a solution.
|
|
13
|
+
|
|
14
|
+
## Description
|
|
15
|
+
|
|
16
|
+
Describe the bug you encountered.
|
|
17
|
+
|
|
18
|
+
## Steps to Reproduce
|
|
19
|
+
|
|
20
|
+
1. Version
|
|
21
|
+
2. Go to '...'
|
|
22
|
+
3. Click on '...'
|
|
23
|
+
4. See error
|
|
24
|
+
|
|
25
|
+
## Expected Behavior
|
|
26
|
+
|
|
27
|
+
What did you expect to happen?
|
|
28
|
+
|
|
29
|
+
## Screenshots, logs or files
|
|
30
|
+
|
|
31
|
+
If applicable, add screenshots, logs or files to help explain your problem. If you do not wish to upload sensitive information to the issue you can send that info to [joao@endurain.com](joao@endurain.com).
|
|
32
|
+
|
|
33
|
+
## Additional Context
|
|
34
|
+
|
|
35
|
+
Add any other context about the problem here.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: 💡 Feature request
|
|
3
|
+
about: Suggest an idea or enhancement for this project
|
|
4
|
+
title: "[FEATURE]: "
|
|
5
|
+
labels: [enhancement]
|
|
6
|
+
assignees: ''
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Summary
|
|
10
|
+
|
|
11
|
+
Briefly describe the feature you'd like to see.
|
|
12
|
+
|
|
13
|
+
## Motivation
|
|
14
|
+
|
|
15
|
+
Why is this feature important? What problem does it solve?
|
|
16
|
+
|
|
17
|
+
## Proposed Solution
|
|
18
|
+
|
|
19
|
+
How would this feature work? Describe any ideas you have.
|
|
20
|
+
|
|
21
|
+
## Alternatives
|
|
22
|
+
|
|
23
|
+
Have you considered any alternatives?
|
|
24
|
+
|
|
25
|
+
## Additional Context
|
|
26
|
+
|
|
27
|
+
Add screenshots, mockups, or examples if relevant.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
## What does this PR do?
|
|
2
|
+
|
|
3
|
+
A clear description of the change.
|
|
4
|
+
|
|
5
|
+
## Related issue
|
|
6
|
+
|
|
7
|
+
Required for anything beyond a small bug fix or typo.
|
|
8
|
+
Don't forget to reference the issue so it auto-closes works, for example use `Closes #XYZ`
|
|
9
|
+
|
|
10
|
+
## Type of change
|
|
11
|
+
|
|
12
|
+
Check all that apply
|
|
13
|
+
|
|
14
|
+
- [ ] Bug fix
|
|
15
|
+
- [ ] New feature (exception during freeze; link prior discussion/acknowledgment)
|
|
16
|
+
- [ ] Documentation
|
|
17
|
+
- [ ] Refactor / code quality
|
|
18
|
+
- [ ] Translation
|
|
19
|
+
- [ ] Other (describe below)
|
|
20
|
+
|
|
21
|
+
## Checklist
|
|
22
|
+
|
|
23
|
+
- [ ] This PR is focused on a single concern
|
|
24
|
+
- [ ] An issue was opened and acknowledged before writing this code (if not a small fix)
|
|
25
|
+
- [ ] If this PR introduces a new feature during freeze, it was discussed and explicitly acknowledged first
|
|
26
|
+
- [ ] The PR is under ~300 lines changed, excluding lock files, migrations, and generated code
|
|
27
|
+
- [ ] Tests have been added or updated where applicable
|
|
28
|
+
- [ ] Documentation has been updated where applicable
|
|
29
|
+
- [ ] Commit messages follow Conventional Commits format
|
|
30
|
+
|
|
31
|
+
## Notes for the reviewer
|
|
32
|
+
|
|
33
|
+
Anything that would help review this. Tricky areas, alternative approaches considered, screenshots, etc.
|
|
@@ -77,7 +77,7 @@ Committing should use clear messages following [Conventional Commits](https://ww
|
|
|
77
77
|
|
|
78
78
|
**Format:** `<type>(<scope>)!: <description>` — `(<scope>)` and the breaking-change `!` are optional.
|
|
79
79
|
|
|
80
|
-
The following rules are enforced automatically on every PR (against the PR title and every commit subject) by `.
|
|
80
|
+
The following rules are enforced automatically on every PR (against the PR title and every commit subject) by `.github/workflows/conventional-commits.yml`. Validation follows the [Conventional Commits 1.0.0](https://www.conventionalcommits.org/en/v1.0.0/) spec, with a single project-policy addition (the allowed-type whitelist). Generated commit messages must comply:
|
|
81
81
|
|
|
82
82
|
- **Header format:** `<type>(<scope>)!: <description>` — the `(<scope>)` and breaking-change `!` are optional, and a space is required after the colon.
|
|
83
83
|
- **Allowed types (project policy):** `build`, `chore`, `ci`, `docs`, `feat`, `fix`, `perf`, `refactor`, `revert`, `style`, `test`. Types are matched case-insensitively per the spec.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Dependabot keeps dependencies *fresh*; the separate `audit-safeuploads` workflow
|
|
2
|
+
# is what catches an advisory against a version already pinned. Both are needed.
|
|
3
|
+
#
|
|
4
|
+
# Updates are grouped so a week's worth of patch bumps arrives as one reviewable
|
|
5
|
+
# pull request rather than a dozen. `uv.lock` is regenerated by Dependabot's uv
|
|
6
|
+
# support, so the lockfile and `pyproject.toml` stay in step.
|
|
7
|
+
version: 2
|
|
8
|
+
|
|
9
|
+
updates:
|
|
10
|
+
- package-ecosystem: uv
|
|
11
|
+
directory: /
|
|
12
|
+
schedule:
|
|
13
|
+
interval: weekly
|
|
14
|
+
day: monday
|
|
15
|
+
time: "05:00"
|
|
16
|
+
timezone: Etc/UTC
|
|
17
|
+
open-pull-requests-limit: 5
|
|
18
|
+
labels:
|
|
19
|
+
- dependencies
|
|
20
|
+
commit-message:
|
|
21
|
+
# Matches the Conventional Commits check in `conventional-commits.yml`.
|
|
22
|
+
prefix: chore
|
|
23
|
+
include: scope
|
|
24
|
+
groups:
|
|
25
|
+
# The runtime dependency set is small and deliberate, so these are worth
|
|
26
|
+
# reading individually rather than batching.
|
|
27
|
+
production-dependencies:
|
|
28
|
+
dependency-type: production
|
|
29
|
+
update-types:
|
|
30
|
+
- minor
|
|
31
|
+
- patch
|
|
32
|
+
development-dependencies:
|
|
33
|
+
dependency-type: development
|
|
34
|
+
update-types:
|
|
35
|
+
- minor
|
|
36
|
+
- patch
|
|
37
|
+
|
|
38
|
+
# Every action is pinned to a commit SHA, so without this they never move.
|
|
39
|
+
- package-ecosystem: github-actions
|
|
40
|
+
directory: /
|
|
41
|
+
schedule:
|
|
42
|
+
interval: weekly
|
|
43
|
+
day: monday
|
|
44
|
+
time: "05:30"
|
|
45
|
+
timezone: Etc/UTC
|
|
46
|
+
open-pull-requests-limit: 5
|
|
47
|
+
labels:
|
|
48
|
+
- dependencies
|
|
49
|
+
- github-actions
|
|
50
|
+
commit-message:
|
|
51
|
+
prefix: chore
|
|
52
|
+
include: scope
|
|
53
|
+
groups:
|
|
54
|
+
github-actions:
|
|
55
|
+
patterns:
|
|
56
|
+
- "*"
|
{safeuploads-1.0.1/.forgejo → safeuploads-1.1.1/.github}/scripts/check_conventional_commits.py
RENAMED
|
@@ -12,9 +12,9 @@ Usage::
|
|
|
12
12
|
check_conventional_commits.py "feat(api): add endpoint"
|
|
13
13
|
git log --format=%s base..head | check_conventional_commits.py --stdin
|
|
14
14
|
|
|
15
|
-
Designed to run with the Python interpreter already available on the
|
|
16
|
-
runner image. Uses only the standard library so there is no
|
|
17
|
-
supply-chain surface.
|
|
15
|
+
Designed to run with the Python interpreter already available on the GitHub
|
|
16
|
+
Actions runner image. Uses only the standard library so there is no
|
|
17
|
+
third-party supply-chain surface.
|
|
18
18
|
"""
|
|
19
19
|
|
|
20
20
|
from __future__ import annotations
|
|
@@ -57,7 +57,7 @@ _HEADER_RE = re.compile(
|
|
|
57
57
|
(?P<type>[A-Za-z]+)
|
|
58
58
|
(?:\((?P<scope>[^)]+)\))?
|
|
59
59
|
(?P<breaking>!)?
|
|
60
|
-
:\
|
|
60
|
+
:\x20
|
|
61
61
|
(?P<description>.+?)
|
|
62
62
|
\s*$
|
|
63
63
|
""",
|
|
@@ -102,6 +102,7 @@ def _safe_log_value(value: str) -> str:
|
|
|
102
102
|
|
|
103
103
|
|
|
104
104
|
def _iter_messages(args: argparse.Namespace) -> Iterable[str]:
|
|
105
|
+
"""Yield messages from stdin or CLI arguments."""
|
|
105
106
|
if args.stdin:
|
|
106
107
|
for line in sys.stdin:
|
|
107
108
|
stripped = line.rstrip("\n")
|
|
@@ -112,6 +113,7 @@ def _iter_messages(args: argparse.Namespace) -> Iterable[str]:
|
|
|
112
113
|
|
|
113
114
|
|
|
114
115
|
def main(argv: list[str] | None = None) -> int:
|
|
116
|
+
"""Validate all messages and return a process exit code."""
|
|
115
117
|
parser = argparse.ArgumentParser(
|
|
116
118
|
description="Validate Conventional Commits headers.",
|
|
117
119
|
)
|
|
@@ -150,8 +152,8 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
150
152
|
|
|
151
153
|
if failures:
|
|
152
154
|
print(
|
|
153
|
-
f"\n{failures} of {checked} message(s) failed
|
|
154
|
-
"validation.",
|
|
155
|
+
f"\n{failures} of {checked} message(s) failed "
|
|
156
|
+
"Conventional Commits validation.",
|
|
155
157
|
file=sys.stderr,
|
|
156
158
|
)
|
|
157
159
|
return 1
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Smoke-test that the built safeuploads wheel is self-contained.
|
|
3
|
+
|
|
4
|
+
Imports the installed package (not the source tree) and constructs
|
|
5
|
+
a FileValidator to prove the distribution's declared dependencies
|
|
6
|
+
are sufficient to actually use the library.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import sys
|
|
12
|
+
from importlib.metadata import version
|
|
13
|
+
|
|
14
|
+
from safeuploads import FileSecurityConfig, FileValidator
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def main() -> int:
|
|
18
|
+
"""Exercise the installed package and return a process exit code.
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
0 on success, 1 if a check fails.
|
|
22
|
+
"""
|
|
23
|
+
print(f"safeuploads version: {version('safeuploads')}")
|
|
24
|
+
|
|
25
|
+
validator = FileValidator(config=FileSecurityConfig())
|
|
26
|
+
if not isinstance(validator, FileValidator):
|
|
27
|
+
print("FileValidator construction failed.", file=sys.stderr)
|
|
28
|
+
return 1
|
|
29
|
+
|
|
30
|
+
print("Smoke test passed: package imported and validator built.")
|
|
31
|
+
return 0
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
if __name__ == "__main__":
|
|
35
|
+
sys.exit(main())
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
name: Audit dependencies
|
|
2
|
+
|
|
3
|
+
# Dependabot keeps dependencies *fresh*; it does not tell you when a version you
|
|
4
|
+
# are already pinned to turns out to be vulnerable. This job scans the resolved
|
|
5
|
+
# lockfile against the OSV / PyPI advisory databases, on every dependency change
|
|
6
|
+
# and on a schedule so a newly-published advisory against an unchanged pin is
|
|
7
|
+
# still surfaced.
|
|
8
|
+
|
|
9
|
+
on:
|
|
10
|
+
push:
|
|
11
|
+
branches:
|
|
12
|
+
- main
|
|
13
|
+
paths: &paths
|
|
14
|
+
- pyproject.toml
|
|
15
|
+
- uv.lock
|
|
16
|
+
- .github/workflows/audit-safeuploads.yml
|
|
17
|
+
pull_request:
|
|
18
|
+
paths: *paths
|
|
19
|
+
schedule:
|
|
20
|
+
# Daily, offset from the dependency-update run so the two do not contend.
|
|
21
|
+
- cron: '30 5 * * *'
|
|
22
|
+
workflow_dispatch:
|
|
23
|
+
|
|
24
|
+
permissions:
|
|
25
|
+
contents: read
|
|
26
|
+
|
|
27
|
+
concurrency:
|
|
28
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
29
|
+
cancel-in-progress: true
|
|
30
|
+
|
|
31
|
+
jobs:
|
|
32
|
+
audit:
|
|
33
|
+
name: safeuploads (pip-audit)
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
steps:
|
|
36
|
+
- name: Checkout code
|
|
37
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
38
|
+
with:
|
|
39
|
+
fetch-depth: 1
|
|
40
|
+
|
|
41
|
+
- name: Install uv
|
|
42
|
+
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
43
|
+
with:
|
|
44
|
+
# Resolve uv from `[tool.uv] required-version` in pyproject.toml, so CI
|
|
45
|
+
# and local development are held to the same floor and ceiling.
|
|
46
|
+
version-file: pyproject.toml
|
|
47
|
+
|
|
48
|
+
# Audit the full resolved set - runtime dependencies plus every optional
|
|
49
|
+
# extra - because a host enabling ``safeuploads[fastapi]`` inherits those too.
|
|
50
|
+
# ``--no-emit-project`` drops the editable self-reference, which carries no
|
|
51
|
+
# hash and which pip-audit therefore rejects.
|
|
52
|
+
- name: Export the resolved dependency set
|
|
53
|
+
run: >-
|
|
54
|
+
uv export --frozen --all-extras --no-dev --no-emit-project
|
|
55
|
+
--format requirements-txt --output-file "$RUNNER_TEMP/requirements.txt"
|
|
56
|
+
|
|
57
|
+
- name: Scan for known vulnerabilities
|
|
58
|
+
run: uvx pip-audit --strict --requirement "$RUNNER_TEMP/requirements.txt" --disable-pip
|
|
@@ -4,21 +4,20 @@ on:
|
|
|
4
4
|
pull_request:
|
|
5
5
|
types: [opened, edited, synchronize, reopened]
|
|
6
6
|
|
|
7
|
-
permissions:
|
|
8
|
-
contents: read
|
|
9
|
-
pull-requests: read
|
|
10
|
-
|
|
11
7
|
concurrency:
|
|
12
|
-
group: ${{
|
|
8
|
+
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
|
|
13
9
|
cancel-in-progress: true
|
|
14
10
|
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
|
|
15
14
|
jobs:
|
|
16
15
|
validate:
|
|
17
|
-
runs-on:
|
|
16
|
+
runs-on: ubuntu-latest
|
|
18
17
|
|
|
19
18
|
steps:
|
|
20
19
|
- name: Checkout code
|
|
21
|
-
uses:
|
|
20
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
22
21
|
with:
|
|
23
22
|
fetch-depth: 0
|
|
24
23
|
|
|
@@ -26,19 +25,19 @@ jobs:
|
|
|
26
25
|
# The PR title is passed through an environment variable to prevent
|
|
27
26
|
# shell injection via crafted titles (see security-and-owasp guidelines).
|
|
28
27
|
env:
|
|
29
|
-
PR_TITLE: ${{
|
|
28
|
+
PR_TITLE: ${{ github.event.pull_request.title }}
|
|
30
29
|
run: |
|
|
31
|
-
python3 .
|
|
30
|
+
python3 .github/scripts/check_conventional_commits.py "$PR_TITLE"
|
|
32
31
|
|
|
33
32
|
- name: Validate commits in PR
|
|
34
33
|
# Validates every commit subject between the PR base and head. Use the
|
|
35
34
|
# base/head SHAs from the event payload so we don't depend on the local
|
|
36
35
|
# checkout ref layout.
|
|
37
36
|
env:
|
|
38
|
-
BASE_SHA: ${{
|
|
39
|
-
HEAD_SHA: ${{
|
|
37
|
+
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
|
38
|
+
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
|
40
39
|
run: |
|
|
41
40
|
set -euo pipefail
|
|
42
41
|
git fetch --no-tags origin "$BASE_SHA" "$HEAD_SHA"
|
|
43
|
-
git log --format=%s "$BASE_SHA".."$HEAD_SHA" \
|
|
44
|
-
| python3 .
|
|
42
|
+
git log --no-merges --format=%s "$BASE_SHA".."$HEAD_SHA" \
|
|
43
|
+
| python3 .github/scripts/check_conventional_commits.py --stdin
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
name: Deploy documentation
|
|
2
|
+
|
|
3
|
+
# ``dry-run`` only exists on ``workflow_dispatch``. On a push ``inputs.dry-run``
|
|
4
|
+
# is null, so every ``!= true`` guard below passes and the site deploys.
|
|
5
|
+
on:
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
inputs:
|
|
8
|
+
dry-run:
|
|
9
|
+
description: Build docs but do not deploy
|
|
10
|
+
required: false
|
|
11
|
+
default: false
|
|
12
|
+
type: boolean
|
|
13
|
+
push:
|
|
14
|
+
branches:
|
|
15
|
+
- main
|
|
16
|
+
paths:
|
|
17
|
+
- 'docs/**'
|
|
18
|
+
- 'mkdocs.yml'
|
|
19
|
+
- 'safeuploads/**/*.py'
|
|
20
|
+
- '.github/workflows/docs.yml'
|
|
21
|
+
|
|
22
|
+
permissions:
|
|
23
|
+
contents: read
|
|
24
|
+
|
|
25
|
+
concurrency:
|
|
26
|
+
group: github-pages
|
|
27
|
+
cancel-in-progress: false
|
|
28
|
+
|
|
29
|
+
jobs:
|
|
30
|
+
build:
|
|
31
|
+
name: Build documentation
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
permissions:
|
|
34
|
+
contents: read
|
|
35
|
+
pages: write
|
|
36
|
+
steps:
|
|
37
|
+
- name: Checkout code
|
|
38
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
39
|
+
with:
|
|
40
|
+
fetch-depth: 1
|
|
41
|
+
|
|
42
|
+
- name: Install uv
|
|
43
|
+
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
44
|
+
with:
|
|
45
|
+
# Resolve uv from `[tool.uv] required-version` in pyproject.toml, so CI
|
|
46
|
+
# and local development are held to the same floor and ceiling.
|
|
47
|
+
version-file: pyproject.toml
|
|
48
|
+
|
|
49
|
+
# mkdocstrings imports the package to render the API reference, so sync the
|
|
50
|
+
# default dependencies plus the docs group and install the project itself.
|
|
51
|
+
- name: Sync documentation dependencies
|
|
52
|
+
run: uv sync --frozen --group docs
|
|
53
|
+
|
|
54
|
+
- name: Build documentation
|
|
55
|
+
run: uv run --no-sync mkdocs build --site-dir site
|
|
56
|
+
|
|
57
|
+
- name: Configure GitHub Pages
|
|
58
|
+
if: ${{ inputs.dry-run != true }}
|
|
59
|
+
uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0
|
|
60
|
+
|
|
61
|
+
- name: Upload GitHub Pages artifact
|
|
62
|
+
if: ${{ inputs.dry-run != true }}
|
|
63
|
+
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
|
|
64
|
+
with:
|
|
65
|
+
path: site
|
|
66
|
+
|
|
67
|
+
deploy:
|
|
68
|
+
name: Deploy documentation
|
|
69
|
+
if: ${{ inputs.dry-run != true }}
|
|
70
|
+
needs: build
|
|
71
|
+
runs-on: ubuntu-latest
|
|
72
|
+
permissions:
|
|
73
|
+
pages: write
|
|
74
|
+
id-token: write
|
|
75
|
+
environment:
|
|
76
|
+
name: github-pages
|
|
77
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
78
|
+
steps:
|
|
79
|
+
- name: Deploy to GitHub Pages
|
|
80
|
+
id: deployment
|
|
81
|
+
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
|
|
@@ -6,16 +6,15 @@ on:
|
|
|
6
6
|
- cron: "0 3 * * 0"
|
|
7
7
|
workflow_dispatch:
|
|
8
8
|
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
9
12
|
# Cancel in-progress runs for the same branch to avoid wasted
|
|
10
13
|
# runner minutes.
|
|
11
14
|
concurrency:
|
|
12
15
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
13
16
|
cancel-in-progress: true
|
|
14
17
|
|
|
15
|
-
# Principle of least privilege: only read access by default.
|
|
16
|
-
permissions:
|
|
17
|
-
contents: read
|
|
18
|
-
|
|
19
18
|
jobs:
|
|
20
19
|
fuzz:
|
|
21
20
|
name: Fuzz
|
|
@@ -23,21 +22,15 @@ jobs:
|
|
|
23
22
|
|
|
24
23
|
steps:
|
|
25
24
|
- name: Checkout repository
|
|
26
|
-
uses: actions/checkout@
|
|
25
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
27
26
|
|
|
28
27
|
- name: Install system dependencies
|
|
29
28
|
run: sudo apt-get update && sudo apt-get install -y libmagic1
|
|
30
29
|
|
|
31
|
-
- name: Set up Python 3.13
|
|
32
|
-
uses: actions/setup-python@v6
|
|
33
|
-
with:
|
|
34
|
-
python-version: "3.13"
|
|
35
|
-
|
|
36
30
|
- name: Install uv
|
|
37
|
-
uses: astral-sh/setup-uv@
|
|
31
|
+
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
38
32
|
with:
|
|
39
|
-
|
|
40
|
-
cache-dependency-glob: "uv.lock"
|
|
33
|
+
version-file: pyproject.toml
|
|
41
34
|
|
|
42
35
|
- name: Install dependencies
|
|
43
36
|
run: uv sync --frozen --dev
|
|
@@ -6,16 +6,15 @@ on:
|
|
|
6
6
|
pull_request:
|
|
7
7
|
branches: [main]
|
|
8
8
|
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
9
12
|
# Cancel in-progress runs for the same branch/PR to avoid wasted
|
|
10
13
|
# runner minutes and race conditions on shared resources.
|
|
11
14
|
concurrency:
|
|
12
15
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
13
16
|
cancel-in-progress: true
|
|
14
17
|
|
|
15
|
-
# Principle of least privilege: only read access by default.
|
|
16
|
-
permissions:
|
|
17
|
-
contents: read
|
|
18
|
-
|
|
19
18
|
jobs:
|
|
20
19
|
# ----------------------------------------------------------------
|
|
21
20
|
# Lint
|
|
@@ -27,19 +26,17 @@ jobs:
|
|
|
27
26
|
runs-on: ubuntu-latest
|
|
28
27
|
|
|
29
28
|
steps:
|
|
30
|
-
- name: Checkout
|
|
31
|
-
uses: actions/checkout@
|
|
32
|
-
|
|
33
|
-
- name: Set up Python 3.13
|
|
34
|
-
uses: actions/setup-python@v6
|
|
29
|
+
- name: Checkout code
|
|
30
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
35
31
|
with:
|
|
36
|
-
|
|
32
|
+
fetch-depth: 1
|
|
37
33
|
|
|
38
34
|
- name: Install uv
|
|
39
|
-
uses: astral-sh/setup-uv@
|
|
35
|
+
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
40
36
|
with:
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
# Resolve uv from `[tool.uv] required-version` in pyproject.toml, so CI
|
|
38
|
+
# and local development are held to the same floor and ceiling.
|
|
39
|
+
version-file: pyproject.toml
|
|
43
40
|
|
|
44
41
|
- name: Install dependencies
|
|
45
42
|
run: uv sync --frozen --dev
|
|
@@ -50,6 +47,9 @@ jobs:
|
|
|
50
47
|
- name: Check formatting with ruff
|
|
51
48
|
run: uv run ruff format --check safeuploads tests examples
|
|
52
49
|
|
|
50
|
+
- name: Type check with mypy
|
|
51
|
+
run: uv run mypy
|
|
52
|
+
|
|
53
53
|
# ----------------------------------------------------------------
|
|
54
54
|
# Test
|
|
55
55
|
# Runs the full pytest suite with coverage. Only runs after lint
|
|
@@ -62,24 +62,18 @@ jobs:
|
|
|
62
62
|
|
|
63
63
|
steps:
|
|
64
64
|
- name: Checkout repository
|
|
65
|
-
uses: actions/checkout@
|
|
65
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
66
66
|
|
|
67
67
|
- name: Install system dependencies
|
|
68
68
|
run: sudo apt-get update && sudo apt-get install -y libmagic1
|
|
69
69
|
|
|
70
|
-
- name: Set up Python 3.13
|
|
71
|
-
uses: actions/setup-python@v6
|
|
72
|
-
with:
|
|
73
|
-
python-version: "3.13"
|
|
74
|
-
|
|
75
70
|
- name: Install uv
|
|
76
|
-
uses: astral-sh/setup-uv@
|
|
71
|
+
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
77
72
|
with:
|
|
78
|
-
|
|
79
|
-
cache-dependency-glob: "uv.lock"
|
|
73
|
+
version-file: pyproject.toml
|
|
80
74
|
|
|
81
75
|
- name: Install dependencies
|
|
82
|
-
run: uv sync --frozen --dev
|
|
76
|
+
run: uv sync --frozen --dev --extra fastapi
|
|
83
77
|
|
|
84
78
|
- name: Audit Python dependencies
|
|
85
79
|
run: uvx pip-audit --require-hashes --disable-pip -r <(uv export --frozen --no-header --no-emit-project --no-dev)
|
|
@@ -88,9 +82,9 @@ jobs:
|
|
|
88
82
|
run: uv run pytest --cov-fail-under=90
|
|
89
83
|
|
|
90
84
|
- name: Upload coverage report
|
|
91
|
-
uses: actions/upload-artifact@v7
|
|
85
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
92
86
|
if: always()
|
|
93
87
|
with:
|
|
94
88
|
name: safeuploads-coverage
|
|
95
89
|
path: .coverage
|
|
96
|
-
retention-days: 7
|
|
90
|
+
retention-days: 7
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: Mirror to GitLab
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
|
|
6
|
+
concurrency:
|
|
7
|
+
group: gitlab-mirror
|
|
8
|
+
cancel-in-progress: false
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
mirror:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- name: Push full mirror to GitLab
|
|
18
|
+
env:
|
|
19
|
+
GITHUB_TOKEN: ${{ github.token }}
|
|
20
|
+
GITLAB_TOKEN: ${{ secrets.GITLAB_TOKEN }}
|
|
21
|
+
run: |
|
|
22
|
+
set -euo pipefail
|
|
23
|
+
mirror_dir="$RUNNER_TEMP/safeuploads.git"
|
|
24
|
+
git clone --mirror \
|
|
25
|
+
"https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" \
|
|
26
|
+
"$mirror_dir"
|
|
27
|
+
cd "$mirror_dir"
|
|
28
|
+
git remote set-url --push origin \
|
|
29
|
+
"https://oauth2:${GITLAB_TOKEN}@gitlab.com/endurain-project/safeuploads.git"
|
|
30
|
+
git push --mirror
|