Skip to content

Security: Pillow 13 (56 advisories), dependency patch bumps, webhook secret fail-closed - #395

Merged
ohld merged 1 commit into
productionfrom
sec/deps-2026-11-01
Nov 1, 2026
Merged

ohld merged 1 commit into
productionfrom
sec/deps-2026-11-01

Conversation

@ohld

@ohld ohld commented Nov 1, 2026

Copy link
Copy Markdown
Member

CVEs fixed

pip-audit on a fresh resolution of requirements/requirements.txt (and on prod.txt, dev.txt and requirements-e2e.txt one by one) found issues in Pillow only.

Package From → To Advisories
Pillow 11.3.0 → 13.0.0 55 CVEs + GHSA-78g4-xqwv-q3g8. Fixed in 12.1.1, 12.2.0, 12.3.0 and 13.0.0, so 13.0 is the only version that fixes all of them.
CVE list

CVE-2026-112390, CVE-2026-112391, CVE-2026-112392, CVE-2026-112393, CVE-2026-112394, CVE-2026-112395, CVE-2026-112396, CVE-2026-112397, CVE-2026-112398, CVE-2026-112399, CVE-2026-112400, CVE-2026-112401, CVE-2026-112402, CVE-2026-112403, CVE-2026-112404, CVE-2026-112406, CVE-2026-112891, CVE-2026-112892, CVE-2026-112893, CVE-2026-112894, CVE-2026-112895, CVE-2026-112896, CVE-2026-112897, CVE-2026-112899, CVE-2026-112900, CVE-2026-112901, CVE-2026-114501, CVE-2026-25990, CVE-2026-40192, CVE-2026-42308, CVE-2026-42309, CVE-2026-42310, CVE-2026-42311, CVE-2026-54058, CVE-2026-54059, CVE-2026-54060, CVE-2026-55379, CVE-2026-55380, CVE-2026-55798, CVE-2026-59197, CVE-2026-59198, CVE-2026-59199, CVE-2026-59200, CVE-2026-59204, CVE-2026-59205, CVE-2026-96636, CVE-2026-96637, CVE-2026-96638, CVE-2026-96639, CVE-2026-96640, CVE-2026-96641, CVE-2026-96642, CVE-2026-96643, CVE-2026-96644, CVE-2026-96778, GHSA-78g4-xqwv-q3g8

This is a major-version bump because a CVE requires it. The code uses only current Pillow APIs (textbbox, Image.Resampling.LANCZOS, ImageStat, alpha_composite), and the watermark and image-hash tests pass. After the update, pip-audit reports no known vulnerabilities.

Other bumps (minor/patch)

  • alembic 1.18 → 1.20
  • SQLAlchemy 2.0.49 → 2.0.55 (stays on the 2.0 line)
  • pydantic 2.13 → 2.14
  • pydantic-settings 2.14 → 2.16
  • sentry-sdk 2.60 → 2.73
  • beautifulsoup4 4.14 → 4.15
  • python-telegram-bot 22.7 → 22.8
  • telethon 1.43 → 1.45 (and requirements-e2e.txt raised to >=1.45.0)
  • dev: pytest 9.0 → 9.2, pytest-asyncio 1.3 → 1.4, pytest-env 1.6 → 1.8, ipython 9.13 → 9.18
  • prefecthq/prefect server image 3.6.28 → 3.6.29 (matches the client that prefect==3.6.* resolves to)
  • GitHub Actions: actions/checkout v4 → v7, actions/setup-python v5 → v7 (both first-party; there are no third-party actions)
  • Base image python:3.14-slim-bookworm is a floating tag that picks up the latest patch on rebuild, so it is unchanged. Moving to trixie would change the distro and is skipped.

Skipped (no CVE requires them)

Package Current → Available Reason
prefect 3.6 → 3.9 Client and the self-hosted server must be upgraded together
fastapi 0.136 → 0.162 0.x minors can break things; do as its own PR
uvicorn 0.46 → 0.54 Same reason (0.x)
asyncpg 0.31 → 0.32 Same reason (0.x)
redis 5 → 8 Major
SQLAlchemy 2.0 → 2.1 Behaviour changes
ruff 0.15 → 0.17 Would reformat code; CI pins 0.15
python-dateutil 2 → 4 Transitive major
python-slugify 8 → 9 Transitive major
apprise 1 → 2 Transitive major
websockets 16 → 17 Transitive major

Code-level security pass

Fixed in this PR:

  • Webhook secret check failed open. validate_webhook_secret compared the header to TELEGRAM_BOT_WEBHOOK_SECRET with ==. If the secret was unset, None == None let header-less requests reach /tgbot/webhook. It now rejects requests when the secret is unset or the header is missing, and compares with secrets.compare_digest. Production has TELEGRAM_BOT_WEBHOOK_SECRET configured. If the value there were empty, webhook updates would now be rejected (403), so check bot logs after deploy. New test: tests/tgbot/test_webhook_secret.py.
  • get_users_with_language built its query by interpolating language_code into a string. It is now a bound parameter. The only caller passes the constant "ru", so this could not be exploited, but it closes a footgun.

Reviewed with no issue found:

  • /admin/* requires ADMIN_API_TOKEN (fails closed with 503, uses compare_digest). /healthcheck is the only unauthenticated route.
  • The other SQL f-strings interpolate only ints, enum or constant values, or bound-parameter fragments (exclude_meme_ids_sql_filter uses ANY(:param)).
  • No hardcoded secrets found (pattern scan plus scripts/redaction_audit.py clean).

Not fixed (follow-ups):

  • Dockerfile.prod runs as root: useradd app exists but there is no USER app. Switching needs a check of the ./:/src bind mount and file permissions in prod.
  • docker-compose.prod.yml bind-mounts the repo (./:/src) into prod containers and publishes port 9898 on all host interfaces.

Tests

  • pytest tests/ against disposable Postgres 14 and Redis 6.2: 596 passed, 2 skipped
  • ruff check and ruff format --check: clean
  • scripts/redaction_audit.py: clean
  • Built Dockerfile.prod locally; import src.main, scripts.serve_flows works in the image (Pillow 13.0.0, Prefect 3.6.29)
  • pip-audit after the update: no known vulnerabilities

Deploy note

Not merged by the author. Production deploys are Coolify docker-compose builds that auto-deploy on push to production. The compose file uses fixed container_names and has no health checks, so containers are recreated in place and a deploy is not zero-downtime. The webhook and Prefect runner restart briefly.

- Pillow 11.3 -> 13.0 (fixes 55 CVEs + 1 GHSA reported by pip-audit)
- Patch/minor bumps: alembic, SQLAlchemy 2.0.55, pydantic, pydantic-settings,
  sentry-sdk, beautifulsoup4, python-telegram-bot, telethon, dev tooling
- Prefect server image 3.6.28 -> 3.6.29 (matches resolved client)
- GitHub Actions checkout/setup-python v4/v5 -> v7
- Webhook secret check fails closed when unset; constant-time compare
- Bind language_code parameter instead of string interpolation
@ohld
ohld merged commit f652843 into production Nov 1, 2026
2 checks passed
@ohld
ohld deleted the sec/deps-2026-11-01 branch November 1, 2026 21:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant