Skip to content

Fix container vulnerabilities: upgrade dependencies and harden Docker image (v4.0.1) - #48

Merged
Kadazaras merged 2 commits into
masterfrom
chore/upgrade-dependencies
Oct 19, 2026
Merged

Kadazaras merged 2 commits into
masterfrom
chore/upgrade-dependencies

Conversation

@Kadazaras

Copy link
Copy Markdown
Contributor

Summary

Security found vulnerabilities in the Docker image caused by its age. The image is only rebuilt on release, the lockfile hadn't changed since October 2025, and the final image shipped build tools and other unneeded packages.

This PR upgrades the Python dependencies, hardens the Docker image, fixes the issues the upgrade surfaced, and gets CI passing again. It bumps the version to 4.0.1.

Trivy (0.69.0) results:

Current production image This PR
Alpine 3.22.2 3.24.2
OS findings (critical / high) 3 / 54 0 / 0
Python findings (critical / high) 2 / 16 0 / 0
Image size 569 MB 210 MB

Dependency upgrade

Ran uv lock --upgrade. The security-relevant packages are now on current versions (starlette, fastapi, urllib3, aiohttp, PyJWT, requests, certifi).

Some upgrades cross major versions:

  • openai 1.97 → 3.35
  • starlette 0.47 → 1.9, fastapi 0.116 → 0.146
  • sqlalchemy 2.0 → 2.1, sqlmodel 0.0.24 → 0.0.48

Fixes needed after the upgrade:

  • Reports were silently not saved. Newer sqlmodel rejects datetimes without a timezone, and the save error is caught and logged. The SQL datetime columns are now NaiveDatetime, which keeps current behaviour and stays compatible with existing data.
  • The service crashed on its first query. query_service/api.py imports httpx, which used to come in through openai (now on httpx2). It's now declared as a direct dependency.

Docker image hardening

  • Two-stage build: build tools (build-base, openssl-dev, git, …) are no longer in the final image.
  • Pinned versions: python:3.13-alpine3.24 and uv 0.13.1, plus apk upgrade for the latest Alpine patches.
  • Removed from the final image: pip (its vendored packages were being flagged), curl, uv, dev dependencies and the uv cache.
  • Startup: the healthcheck uses Python, and the app runs as python -m bigdata_briefs straight from the venv.
  • build.yml: the build that gets scanned now pulls a fresh base image and skips the cache, so OS patches aren't taken from a stale layer. It also loads the image locally so the scan checks this build. Previously nothing was loaded, so the scan may have been checking the previous :latest. The push step reuses the layers from the scanned build.

Getting CI to pass

CI was already failing on master: the Makefile ran uvx ruff and uvx ty without versions, and newer releases flag existing code. Because type-check runs first, the unit tests were being skipped as well.

  • Pinned ruff to 0.15.22 (the newest version where lint passes) and ty to 0.0.89.
  • SentimentFilter: changed the type to the ranges format the code already sends (values is deprecated by the API). The request itself is unchanged.
  • metrics.py: declared metrics_queue and lock on the base class, and aligned the WarningsMetrics.track_usage signature with the base class.
  • Bug fix: entity tickers were always empty. Entity.ticker still read from metadata_1, a field name left over from the old SDK, so tickers were dropped. This changes prompt output: entities now appear as e.g. "Apple (AAPL)".

Other

  • Removed the UI section from the README. The frontend has been disabled since Temporarily disable the frontend #44 and / redirects to /docs.
  • Updated CHANGELOG.md: added the 4.0.1 entry and set the 4.0.0 release date (2025-11-21).

Testing

  • make type-check, make lint-check and make format all pass.
  • Unit tests pass on Python 3.11, 3.12 and 3.13 (36 passed).
  • Ran the new image locally:
    • /health returns 4.0.1, / redirects to /docs, and static files load.
    • The app runs as the non-root bigdata user, the SQLite database is written to /data, and the container reports healthy.
  • Trivy scan of the new image: 0 vulnerabilities.

Upgrade all locked dependencies with `uv lock --upgrade` to address
vulnerabilities reported in the Docker image scan, and fix the issues
surfaced by the upgrade:

- Declare httpx as a direct dependency; it was only available
  transitively through openai, which now depends on httpx2.
- Annotate SQL model datetime fields as NaiveDatetime, as newer sqlmodel
  rejects naive datetimes and reports were silently not being saved.

Make CI pass again with current tooling:

- Pin ruff (0.15.22) and ty (0.0.89) in the Makefile.
- Update SentimentFilter to the `ranges` format sent to the search API.
- Fix type errors in the metrics classes.
- Fix entity tickers always being empty due to a stale `metadata_1`
  alias from the old SDK response.

Also remove the UI section from the README, as the frontend is
temporarily disabled and `/` redirects to `/docs`.
- Use a multi-stage build so build tools (build-base, openssl-dev, git,
  etc.) are not shipped in the runtime image.
- Pin the base image (python:3.13-alpine3.24) and uv (0.13.1), and run
  `apk upgrade` to pick up the latest Alpine security patches.
- Remove pip, curl, uv and dev dependencies from the runtime image; the
  healthcheck now uses Python and the app runs directly from the venv.
- Build release images without cache and with a fresh base image pull,
  and load the image locally so the security scan checks the new build.

Trivy reports 0 vulnerabilities on the new image (vs. 5 critical and 70
high on the current production image), and the image size drops from
569MB to 210MB.
@Kadazaras
Kadazaras requested a review from a team as a code owner October 19, 2026 09:48

@paco-ravenpack paco-ravenpack left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@Kadazaras
Kadazaras merged commit fecbba7 into master Oct 19, 2026
4 checks passed
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.

2 participants