From ac963ad6175b3a41ac2c3ce55524d87878fe2b01 Mon Sep 17 00:00:00 2001 From: PARTH J ROHIT Date: Mon, 27 Jul 2026 13:33:06 +0100 Subject: [PATCH] security(infra): harden compose exposure and split pytest out of runtime deps - docker-compose.yml: drop host port bindings for postgres (5432) and redis (6379); both stay reachable only on the internal 'data' network. The API (8000) remains host-exposed (issue #186). - requirements.txt: remove pytest/iniconfig/pluggy from the runtime lockfile so the container image no longer ships a test framework (issue #185). - requirements-dev.txt: new file (-r requirements.txt + pytest stack) for CI and local dev. - pyproject.toml: move pytest into a [project.optional-dependencies] test extra so 'pip install .' is runtime-only. - ci.yml / release.yml: install requirements-dev.txt (or [test] extra) so pytest is present in CI without living in the runtime image. - .gitattributes: normalise line endings, keep lockfiles/requirements as text, mark binaries as binary (issue #187). Tested: backend suite 701 passed / 4 skipped; compose YAML validated. --- .gitattributes | 33 +++++++++++++++++++++++++++++++ .github/workflows/ci.yml | 4 ++-- .github/workflows/release.yml | 2 +- apps/backend/pyproject.toml | 8 ++++++-- apps/backend/requirements-dev.txt | 8 ++++++++ apps/backend/requirements.txt | 3 --- docker-compose.yml | 9 +++++---- 7 files changed, 55 insertions(+), 12 deletions(-) create mode 100644 .gitattributes create mode 100644 apps/backend/requirements-dev.txt diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..75324ff --- /dev/null +++ b/.gitattributes @@ -0,0 +1,33 @@ +# PARTHA .gitattributes +# Normalise line endings and keep binary assets safe across platforms. +# Generated lockfiles (package-lock.json) are large but text; Git/Linguist +# already treats them as generated, so we keep them as text for sane diffs. + +# Default: normalise to LF on commit, convert to native on checkout. +* text=auto eol=lf + +# Lockfiles and dependency manifests are text (large diffs, not binary). +*.lock text +package-lock.json text +pnpm-lock.yaml text +requirements*.txt text + +# Hand-authored SVG stays text (editable in-repo). +*.svg text + +# Binary assets must never be line-ending-converted. +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.webp binary +*.ico binary +*.woff binary +*.woff2 binary +*.ttf binary +*.otf binary +*.eot binary +*.zip binary +*.gz binary +*.tar binary +*.pdf binary diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8eb19e6..5b5dc12 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -126,7 +126,7 @@ jobs: - name: Install dependencies run: | - python -m pip install -r apps/backend/requirements.txt + python -m pip install -r apps/backend/requirements-dev.txt python -m pip install -e apps/backend --no-deps - name: Test @@ -218,7 +218,7 @@ jobs: - name: Install backend run: | - python -m pip install -r apps/backend/requirements.txt + python -m pip install -r apps/backend/requirements-dev.txt python -m pip install -e apps/backend --no-deps - name: Install Chromium diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5a45833..d68d34a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,7 +31,7 @@ jobs: run: npm ci --prefix apps/frontend - name: Install backend dependencies - run: python -m pip install -e apps/backend + run: python -m pip install -e "apps/backend[test]" - name: Lint frontend run: npm --prefix apps/frontend run lint diff --git a/apps/backend/pyproject.toml b/apps/backend/pyproject.toml index 63bc072..8287e97 100644 --- a/apps/backend/pyproject.toml +++ b/apps/backend/pyproject.toml @@ -27,8 +27,12 @@ dependencies = [ # extension while connecting to an IP-pinned URL. "httpcore>=1.0.9,<2.0.0", "httpx>=0.27.0,<1.0.0", - "xhtml2pdf>=0.2.16", - "pytest>=8.3.0" + "xhtml2pdf>=0.2.16" +] + +[project.optional-dependencies] +test = [ + "pytest>=8.3.0", ] [tool.setuptools.packages.find] diff --git a/apps/backend/requirements-dev.txt b/apps/backend/requirements-dev.txt new file mode 100644 index 0000000..825163a --- /dev/null +++ b/apps/backend/requirements-dev.txt @@ -0,0 +1,8 @@ +# Development/test dependencies for PARTHA backend. +# Extends the pinned runtime lockfile (requirements.txt) with the test +# toolchain so CI and local dev can run pytest. The runtime image installs +# only requirements.txt; pytest must never ship in the container (issue #185). +-r requirements.txt +iniconfig==2.3.0 +pluggy==1.6.0 +pytest==9.1.1 diff --git a/apps/backend/requirements.txt b/apps/backend/requirements.txt index 55befe9..8ba8922 100644 --- a/apps/backend/requirements.txt +++ b/apps/backend/requirements.txt @@ -30,14 +30,12 @@ httpcore==1.0.9 httptools==0.8.0 httpx==0.28.1 idna==3.18 -iniconfig==2.3.0 lxml==6.1.1 Mako==1.3.12 MarkupSafe==3.0.3 oscrypto==1.3.0 packaging==26.2 pillow==12.3.0 -pluggy==1.6.0 psycopg-binary==3.3.4 psycopg==3.3.4 pycparser==3.0 @@ -49,7 +47,6 @@ pyhanko-certvalidator==0.31.1 pyHanko==0.35.2 PyJWT==2.13.0 pypdf==6.14.2 -pytest==9.1.1 python-bidi==0.6.11 python-dotenv==1.2.2 python-multipart==0.0.32 diff --git a/docker-compose.yml b/docker-compose.yml index 3f2b063..88f9cbd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -56,8 +56,9 @@ services: POSTGRES_USER: ${POSTGRES_USER:-partha} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-partha} POSTGRES_DB: ${POSTGRES_DB:-partha} - ports: - - "5432:5432" + # Intentionally no host port binding: the database is only reachable on the + # internal `data` network. Self-hosted deployments should not expose Postgres + # to the host/Internet (issue #186). volumes: - postgres_data:/var/lib/postgresql/data healthcheck: @@ -70,8 +71,8 @@ services: redis: image: redis:7-alpine - ports: - - "6379:6379" + # Intentionally no host port binding: Redis is only reachable on the internal + # `data` network (issue #186). networks: - data