Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
bb13367
chore: update safeuploads version to 1.1.1 in uv.lock
joaovitoriasilva Aug 19, 2026
e98faec
feat: implement resource monitoring for gzip and zip inspectors, enha…
joaovitoriasilva Aug 20, 2026
b19ee7a
feat: Enhance security and performance of compression and XML validators
joaovitoriasilva Aug 20, 2026
ae2ae1d
refactor: code structure for improved readability and maintainability
joaovitoriasilva Aug 21, 2026
8383419
feat: enhance audit logging with client IP tracking and refine valida…
joaovitoriasilva Aug 21, 2026
ed25b34
refactor: code structure for improved readability and maintainability
joaovitoriasilva Aug 21, 2026
e6be664
feat: release version 2.0.0 with breaking changes, enhanced memory ma…
joaovitoriasilva Aug 21, 2026
338ef0a
refactor: remove unused ignore rule for abstract methods in linting c…
joaovitoriasilva Aug 21, 2026
61756be
feat: enhance content analysis and validation error handling in file …
joaovitoriasilva Aug 21, 2026
becc8bf
feat: enhance documentation with security policy details and integrat…
joaovitoriasilva Aug 21, 2026
4adf515
feat: standardize heading capitalization in documentation files
joaovitoriasilva Aug 21, 2026
8c657ad
feat: update changelog to remove outdated security workflow details a…
joaovitoriasilva Aug 21, 2026
e0467ac
feat: enhance filename sanitization and validation error handling
joaovitoriasilva Aug 21, 2026
4ed094c
feat: enhance ZIP entry rejection logic with configurable threat cate…
joaovitoriasilva Aug 21, 2026
a74bd9b
feat: enhance error handling and logging for file uploads, ensuring u…
joaovitoriasilva Aug 21, 2026
2a222ac
feat: update Python version in documentation and enhance gzip analysi…
joaovitoriasilva Aug 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
- Ask for clarification if the scope is unclear rather than assuming additional deliverables are wanted.

## Style Expectations
- Target Python 3.13+. Use modern type hint syntax (`int | None`, `list[str]`, `dict[str, Any]`) instead of `Optional`, `List`, `Dict`, etc.
- Target Python 3.11+. Use modern type hint syntax (`int | None`, `list[str]`, `dict[str, Any]`) instead of `Optional`, `List`, `Dict`, etc.
- Preserve async boundaries in validator methods; do not block event loops with synchronous I/O inside `async` functions.
- Use module-level `logging.getLogger(__name__)` for security-relevant events; never rely on application-specific loggers.
- Enforce PEP 8 line limits:
Expand Down
10 changes: 5 additions & 5 deletions .github/instructions/python.instructions.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
---
description: 'Python 3.13 + FastAPI + SQLAlchemy + Alembic coding standards, docstring format, testing patterns, and module organization for the Endurain backend'
description: 'Python 3.11 + FastAPI + SQLAlchemy + Alembic coding standards, docstring format, testing patterns, and module organization for the Endurain backend'
applyTo: '**/*.py'
---
# Project Context
- **Python Version:** 3.13+ (required)
- **Python Version:** 3.11+ (required)
- **Framework:** FastAPI with SQLAlchemy ORM and Alembic migrations
- **Dependency Management:** uv (see `pyproject.toml`)
- **Project Structure:** All backend code in `backend/app/`
Expand All @@ -12,7 +12,7 @@ applyTo: '**/*.py'
# Development Setup
- **Install uv:** `pip install uv`
- **Install dependencies:** `uv sync --group dev`
- **Use Docker:** If system Python < 3.13, use Docker for
- **Use Docker:** If system Python < 3.11, use Docker for
development

# SQLAlchemy 2.0 Standards
Expand Down Expand Up @@ -48,11 +48,11 @@ applyTo: '**/*.py'
- **No hardcoded secrets:** Use environment variables
- **Async file I/O:** Use `await file.read()`, not sync

# Modern Python Syntax (Python 3.13+)
# Modern Python Syntax (Python 3.11+)
- Use modern type hint syntax: `int | None`, `list[str]`,
`dict[str, Any]`
- Do NOT use `typing.Optional`, `typing.List`, `typing.Dict`, etc.
- Target Python 3.13+ features and syntax
- Target Python 3.11+ features and syntax
- Always prioritize readability and clarity

# PEP 8 Line Limits
Expand Down
58 changes: 58 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: CodeQL

# `pip-audit` covers known vulnerabilities in our dependencies; ruff's
# flake8-bandit rules cover single-line patterns in our own code. Neither does
# interprocedural taint tracking, which is what actually catches an untrusted
# filename reaching a filesystem or subprocess sink. That is CodeQL's job.

on:
push:
branches:
- main
pull_request:
branches:
- main
schedule:
# Weekly, so a newly-published query pack is run against unchanged code.
- cron: '15 4 * * 1'
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
analyze:
name: Analyze Python
runs-on: ubuntu-latest
permissions:
# Required to upload the SARIF result to the Security tab.
security-events: write
contents: read

steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 1
persist-credentials: false

# `build-mode: none` is the correct setting for an interpreted language:
# CodeQL reads the sources directly instead of watching a compiler.
- name: Initialize CodeQL
uses: github/codeql-action/init@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
with:
languages: python
build-mode: none
# `security-extended` adds lower-severity and precision queries on top
# of the default pack. Appropriate for a security library, where a
# false positive costs far less than a missed sink.
queries: security-extended

- name: Perform CodeQL analysis
uses: github/codeql-action/analyze@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
with:
category: /language:python
85 changes: 85 additions & 0 deletions .github/workflows/mutation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
name: Mutation testing

# 98%+ line coverage says every line runs; it does not say anything asserted on
# the result. Mutation testing perturbs the source and reports which changes the
# suite fails to notice. Scheduled and non-blocking: a surviving mutant is a
# lead to investigate, not a build break.

on:
schedule:
# Every Sunday at 05:00 UTC, after the fuzz run.
- cron: '0 5 * * 0'
workflow_dispatch:
inputs:
filter:
description: Mutant name glob, e.g. safeuploads.utils.*
required: false
default: ''
type: string

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
mutate:
name: Mutate and score
runs-on: ubuntu-latest
# The full run is a few thousand mutants; cap it so a pathological
# mutant that hangs cannot hold a runner all day.
timeout-minutes: 60

steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 1
persist-credentials: false

- name: Install system dependencies
run: sudo apt-get update && sudo apt-get install -y libmagic1

- name: Install uv
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
version-file: pyproject.toml

- name: Install dependencies
run: uv sync --frozen --dev --extra fastapi --group mutation

# A failing suite makes every mutant "killed" and the score meaningless,
# so establish that the baseline is green before mutating anything.
- name: Verify the baseline suite passes
run: uv run pytest -q --no-cov -m "not performance and not fuzz"

# The filter goes through the environment, never through `${{ }}`
# interpolation into the script body, which would let anyone able to
# dispatch the workflow inject shell.
- name: Run mutation testing
continue-on-error: true
env:
MUTMUT_FILTER: ${{ inputs.filter }}
run: |
set -euo pipefail
if [ -n "${MUTMUT_FILTER}" ]; then
uv run mutmut run --max-children 4 "${MUTMUT_FILTER}"
else
uv run mutmut run --max-children 4
fi

- name: Summarise surviving mutants
if: always()
run: |
set -euo pipefail
{
echo '## Surviving mutants'
echo
echo 'Each line is a source change the test suite did not notice.'
echo
echo '```'
uv run mutmut results || true
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
65 changes: 65 additions & 0 deletions .github/workflows/scorecard.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: OpenSSF Scorecard

# Scores this repository's *supply-chain posture* rather than its code: are
# actions pinned to SHAs, are workflow permissions least-privilege, is branch
# protection on, are releases signed. Those are the properties a consumer of a
# security library cannot verify from the source alone.

on:
branch_protection_rule:
push:
branches:
- main
schedule:
# Weekly, offset from the CodeQL run so the two do not contend.
- cron: '45 4 * * 1'
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
analysis:
name: Scorecard analysis
runs-on: ubuntu-latest
permissions:
# Upload the SARIF result to the Security tab.
security-events: write
# Publish the result to the OpenSSF REST API so the badge resolves.
id-token: write
contents: read
actions: read

steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 1
persist-credentials: false

- name: Run analysis
uses: ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc # v2.4.4
with:
results_file: results.sarif
results_format: sarif
# Publishing makes the score readable by anyone evaluating the
# package, which is the point of running it. Requires a public repo.
publish_results: true

# Retained separately from the Security tab so a score regression can be
# diffed against a specific run.
- name: Upload artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: scorecard-results
path: results.sarif
retention-days: 7

- name: Upload to code-scanning
uses: github/codeql-action/upload-sarif@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
with:
sarif_file: results.sarif
4 changes: 2 additions & 2 deletions .github/workflows/test-matrix-safeuploads.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: Test matrix safeuploads

# Portability matrix on top of the fast default `Lint & Test` workflow:
# * every supported Python version (3.13, 3.14) with the full
# * every supported Python version (3.11 - 3.14) with the full
# dependency set (dev group + fastapi extra), proving the suite
# passes on every Python the package declares support for; and
# * a bare `import safeuploads` with only the base dependencies
Expand Down Expand Up @@ -40,7 +40,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python: ["3.13", "3.14"]
python: ["3.11", "3.12", "3.13", "3.14"]
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand Down
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,9 @@ __pycache__
# Tests
.coverage
htmlcov/
.pytest_cache/
.pytest_cache/

# Mutation testing
mutants/
.mutmut-cache
mutants.sqlite
Loading
Loading