Abhängigkeiten auf Sicherheits-Updates prüfen und Versionen verbindlich machen #11

Closed
opened 2026-09-17 16:23:47 +00:00 by AxonByteDev · 0 comments
Owner

Problem

Die Abhängigkeiten wurden noch nie systematisch auf bekannte Schwachstellen geprüft, und es gibt keine Stelle im Projekt, die das automatisch tut (scripts/publish.sh::run_local_checks() ersetzt seit dem Wegfall der Forgejo-Actions die CI, prüft aber nur ruff/pytest/tsc/eslint/vitest/build — kein npm audit, kein pip-audit).

Eine Prüfung am 17.09.2026 (npm audit + OSV-Datenbank) hat 6 Advisories im Frontend, veraltete Basis-Images und eine strukturelle Lücke bei der Versionsbindung im Backend ergeben. Details unten, jeweils mit Bewertung, ob PatchPilot tatsächlich betroffen ist.

Befunde

1. Frontend: react-router-dom 6.30.4 — wird an den Browser ausgeliefert

Advisory Schwere Behoben in Betrifft PatchPilot?
GHSA-jjmj-jmhj-qwj2 — Open Redirect → XSS moderate 6.30.6 Patch verfügbar, sollte eingespielt werden
GHSA-wrjc-x8rr-h8h6 — Open Redirect via Backslash in <Link>/useNavigate moderate erst 7.18.0 nicht ausnutzbar (s.u.)
GHSA-337j-9hxr-rhxg — Constructor Injection in deserializeErrors() (SSR-Hydration) moderate erst 7.18.0 nicht betroffen — PatchPilot ist eine reine SPA ohne SSR

Zur Ausnutzbarkeit: useNavigate() wird nur mit fest verdrahteten Zielen aufgerufen (frontend/src/App.tsx:51 → /login, frontend/src/pages/Login.tsx:22 → /), es gibt kein <Link to={…}> mit Fremddaten. Die beiden Open-Redirect-Advisories sind damit aktuell nicht auslösbar — das kann sich aber mit jeder neuen Route ändern.

Der 6.x-Zweig bekommt für GHSA-wrjc und GHSA-337j keinen Fix mehr. Hier ist eine bewusste Entscheidung nötig: bei 6.30.6 bleiben und die beiden als nicht-ausnutzbar dokumentieren, oder auf react-router 7 migrieren (Breaking Change).

2. Frontend: Build-/Testwerkzeuge (laufen nicht im ausgelieferten Bundle)

Paket Aktuell (package-lock.json) Advisory Behoben in
vite 5.4.21 GHSA-fx2h-pf6j-xcff (high) — server.fs.deny-Bypass, dazu GHSA-4w7w-66w2-5vf9 und GHSA-v6wh-96g9-6wx3 6.4.3 / 7.3.5 / 8.0.16
esbuild 0.21.5 GHSA-67mh-4wv8-2f99 — jede Website kann Requests an den Dev-Server stellen und die Antwort lesen 0.25.0 (kommt mit dem vite-Upgrade)
vitest 3.2.7 GHSA-82fw-gwwq-j7x9 — Path Traversal / Arbitrary File Read via @vitest/mocker 4.1.11

Diese Pakete landen nicht im Produktions-Bundle — betroffen ist der Entwicklungsrechner, solange npm run dev läuft (der Dev-Server ist über das Netz erreichbar). Beide Upgrades sind Major-Sprünge (vite 5 → 6, vitest 3 → 4) und brauchen einen Testlauf, kein npm audit fix --force auf gut Glück.

3. Backend: requirements.txt ohne Versionsbindung

Alle zehn Einträge stehen als >=X ohne Obergrenze, es gibt keine Lock-Datei. Ein heute aufgelöster Build ergibt 36 Pakete — davon hat aktuell keines eine bekannte Schwachstelle (gegen OSV geprüft). Das ist aber Zufall und kein Zustand:

  • Nicht reproduzierbar: zwei Builds am selben Commit können unterschiedliche Versionen enthalten. Welche Version im laufenden Container steckt, lässt sich aus dem Repo nicht ablesen.

  • Ein gebautes Image altert still: es behält seine Versionen, bis jemand neu baut. Genau das zeigt das lokale .venv dieses Projekts — dort stecken noch:

    • starlette 1.2.1 → CVE-2026-54283 (high, DoS: request.form()-Limits werden ignoriert) und CVE-2026-54282. Behoben in 1.3.1; ein frischer Build zieht 1.6.0.
    • cryptography 48.0.0 → vier Advisories, u.a. GHSA-537c-gmf6-5ccf (verwundbares OpenSSL in den Wheels) und CVE-2026-69247 (Bleichenbacher-Orakel in PKCS#7). Behoben in 50.0.0; ein frischer Build zieht 50.0.1. Relevant, weil cryptography unter paramiko die SSH-Krypto stellt.

    Über die App selbst ist der Starlette-DoS nicht erreichbar (PatchPilot hat keinen einzigen Form- oder Upload-Endpunkt, geprüft per grep auf request.form/UploadFile/Form(/File(), und die cryptography-Punkte betreffen X.509-Pfadprüfung bzw. PKCS#7, die PatchPilot nicht selbst aufruft. Der Punkt ist nicht die konkrete Ausnutzbarkeit, sondern dass das Projekt es nicht merken würde.

4. Docker: node:20-alpine ist End-of-Life

Dockerfile Zeile 2 baut das Frontend auf Node 20 — EOL seit 2026-04-30, es gibt keine Sicherheitspatches mehr für dieses Image. Empfehlung: node:22-alpine (EOL 2027-04-30) oder node:24-alpine (EOL 2028-04-30).

python:3.12-slim (Zeile 10) ist bis 2028-10-31 unterstützt und damit unkritisch; ein Wechsel auf 3.13 wäre optional. Beide Tags ohne Patch-Level sind hier richtig so — ein Rebuild holt die Patches automatisch.

5. Nebenbefund: Altlasten im lokalen .venv

Im Entwicklungs-venv liegen Pakete, die weder in requirements.txt stehen noch irgendwo im Code importiert werden (geprüft per grep) — vermutlich Reste der früheren matrix-nio-Bot-Implementierung, bevor backend/bot.py auf requests umgestellt wurde: aiohttp 3.13.5 (28 Advisories!), matrix-nio, python-multipart, h2, python-socks, aiohttp_socks.

Die kommen nicht ins Docker-Image (dort wird nur requirements.txt installiert), verfälschen aber jedes lokale Audit und lassen die Lage schlimmer aussehen als sie ist.

Was geändert werden muss

Sofort (bekannte Schwachstellen im ausgelieferten Code):

  • react-router-dom auf 6.30.6 heben (npm update react-router-dom — Patch, kein Breaking Change), package-lock.json mit committen

Entwicklungsumgebung:

  • vite auf ≥ 6.4.3 heben (bringt esbuild ≥ 0.25.0 mit) — Major-Upgrade, npm run build und npm run dev danach prüfen
  • vitest auf 4.1.11 heben — Major-Upgrade, npm test danach prüfen
  • Lokales .venv neu aufsetzen (rm -rf .venv && python3 -m venv .venv && .venv/bin/pip install -r requirements-dev.txt), damit dort nicht länger starlette 1.2.1, cryptography 48.0.0 und die matrix-nio-Altlasten liegen

Struktur (verhindert, dass das Problem wiederkommt):

  • requirements.txt auf exakte Versionen (==) umstellen und eine reproduzierbare Auflösung committen (pip freeze der Prod-Closure oder pip-compile/uv pip compile aus einer requirements.in)
  • run_local_checks() in scripts/publish.sh um Audits ergänzen, da es kein CI gibt:
    pip-audit -r requirements.txt und npm audit --audit-level=high (im frontend/-Block)
  • pip-audit in requirements-dev.txt aufnehmen
  • Entscheidung zu react-router 6 vs. 7 treffen und im Repo festhalten (die beiden 6.x-Advisories ohne Fix sind sonst ein stiller Dauerzustand)

Docker:

  • node:20-alpine → node:22-alpine (oder 24) in Dockerfile Zeile 2, danach ein kompletter docker build als Gegenprobe

Akzeptanzkriterien

  • npm audit --audit-level=high im frontend/ meldet keine Treffer mehr (verbleibende moderate-Treffer sind im Repo begründet dokumentiert).
  • pip-audit -r requirements.txt meldet keine Treffer.
  • scripts/publish.sh bricht ab, wenn eines der beiden Audits anschlägt — ein Release kann nicht mehr unbemerkt mit einer bekannten Schwachstelle rausgehen.
  • Aus dem Repo ist ablesbar, welche Python-Versionen in einem Image stecken (exakte Versionen statt >=).
  • Das Docker-Image baut auf einer Node-Version, die noch Sicherheitspatches bekommt.
## Problem Die Abhängigkeiten wurden noch nie systematisch auf bekannte Schwachstellen geprüft, und es gibt keine Stelle im Projekt, die das automatisch tut (`scripts/publish.sh::run_local_checks()` ersetzt seit dem Wegfall der Forgejo-Actions die CI, prüft aber nur ruff/pytest/tsc/eslint/vitest/build — kein `npm audit`, kein `pip-audit`). Eine Prüfung am 17.09.2026 (npm audit + OSV-Datenbank) hat 6 Advisories im Frontend, veraltete Basis-Images und eine strukturelle Lücke bei der Versionsbindung im Backend ergeben. Details unten, jeweils mit Bewertung, ob PatchPilot tatsächlich betroffen ist. ## Befunde ### 1. Frontend: `react-router-dom` 6.30.4 — wird an den Browser ausgeliefert | Advisory | Schwere | Behoben in | Betrifft PatchPilot? | |---|---|---|---| | [GHSA-jjmj-jmhj-qwj2](https://github.com/advisories/GHSA-jjmj-jmhj-qwj2) — Open Redirect → XSS | moderate | **6.30.6** | Patch verfügbar, sollte eingespielt werden | | [GHSA-wrjc-x8rr-h8h6](https://github.com/advisories/GHSA-wrjc-x8rr-h8h6) — Open Redirect via Backslash in `<Link>`/`useNavigate` | moderate | erst 7.18.0 | nicht ausnutzbar (s.u.) | | [GHSA-337j-9hxr-rhxg](https://github.com/advisories/GHSA-337j-9hxr-rhxg) — Constructor Injection in `deserializeErrors()` (SSR-Hydration) | moderate | erst 7.18.0 | nicht betroffen — PatchPilot ist eine reine SPA ohne SSR | Zur Ausnutzbarkeit: `useNavigate()` wird nur mit fest verdrahteten Zielen aufgerufen (`frontend/src/App.tsx:51` → `/login`, `frontend/src/pages/Login.tsx:22` → `/`), es gibt kein `<Link to={…}>` mit Fremddaten. Die beiden Open-Redirect-Advisories sind damit aktuell nicht auslösbar — das kann sich aber mit jeder neuen Route ändern. **Der 6.x-Zweig bekommt für GHSA-wrjc und GHSA-337j keinen Fix mehr.** Hier ist eine bewusste Entscheidung nötig: bei 6.30.6 bleiben und die beiden als nicht-ausnutzbar dokumentieren, oder auf react-router 7 migrieren (Breaking Change). ### 2. Frontend: Build-/Testwerkzeuge (laufen nicht im ausgelieferten Bundle) | Paket | Aktuell (package-lock.json) | Advisory | Behoben in | |---|---|---|---| | `vite` | 5.4.21 | [GHSA-fx2h-pf6j-xcff](https://github.com/advisories/GHSA-fx2h-pf6j-xcff) **(high)** — `server.fs.deny`-Bypass, dazu GHSA-4w7w-66w2-5vf9 und GHSA-v6wh-96g9-6wx3 | 6.4.3 / 7.3.5 / 8.0.16 | | `esbuild` | 0.21.5 | [GHSA-67mh-4wv8-2f99](https://github.com/advisories/GHSA-67mh-4wv8-2f99) — jede Website kann Requests an den Dev-Server stellen und die Antwort lesen | 0.25.0 (kommt mit dem vite-Upgrade) | | `vitest` | 3.2.7 | [GHSA-82fw-gwwq-j7x9](https://github.com/advisories/GHSA-82fw-gwwq-j7x9) — Path Traversal / Arbitrary File Read via `@vitest/mocker` | 4.1.11 | Diese Pakete landen nicht im Produktions-Bundle — betroffen ist der Entwicklungsrechner, solange `npm run dev` läuft (der Dev-Server ist über das Netz erreichbar). Beide Upgrades sind Major-Sprünge (vite 5 → 6, vitest 3 → 4) und brauchen einen Testlauf, kein `npm audit fix --force` auf gut Glück. ### 3. Backend: `requirements.txt` ohne Versionsbindung Alle zehn Einträge stehen als `>=X` ohne Obergrenze, es gibt keine Lock-Datei. Ein heute aufgelöster Build ergibt 36 Pakete — davon hat aktuell **keines** eine bekannte Schwachstelle (gegen OSV geprüft). Das ist aber Zufall und kein Zustand: - **Nicht reproduzierbar:** zwei Builds am selben Commit können unterschiedliche Versionen enthalten. Welche Version im laufenden Container steckt, lässt sich aus dem Repo nicht ablesen. - **Ein gebautes Image altert still:** es behält seine Versionen, bis jemand neu baut. Genau das zeigt das lokale `.venv` dieses Projekts — dort stecken noch: - `starlette` 1.2.1 → [CVE-2026-54283](https://github.com/advisories/GHSA-82w8-qh3p-5jfq) (high, DoS: `request.form()`-Limits werden ignoriert) und [CVE-2026-54282](https://github.com/advisories/GHSA-jp82-jpqv-5vv3). Behoben in 1.3.1; ein frischer Build zieht 1.6.0. - `cryptography` 48.0.0 → vier Advisories, u.a. [GHSA-537c-gmf6-5ccf](https://github.com/advisories/GHSA-537c-gmf6-5ccf) (verwundbares OpenSSL in den Wheels) und [CVE-2026-69247](https://github.com/advisories/GHSA-g6cj-pr64-35w5) (Bleichenbacher-Orakel in PKCS#7). Behoben in 50.0.0; ein frischer Build zieht 50.0.1. Relevant, weil `cryptography` unter `paramiko` die SSH-Krypto stellt. Über die App selbst ist der Starlette-DoS nicht erreichbar (PatchPilot hat keinen einzigen Form- oder Upload-Endpunkt, geprüft per `grep` auf `request.form`/`UploadFile`/`Form(`/`File(`), und die `cryptography`-Punkte betreffen X.509-Pfadprüfung bzw. PKCS#7, die PatchPilot nicht selbst aufruft. Der Punkt ist nicht die konkrete Ausnutzbarkeit, sondern dass das Projekt es nicht merken würde. ### 4. Docker: `node:20-alpine` ist End-of-Life `Dockerfile` Zeile 2 baut das Frontend auf Node 20 — **EOL seit 2026-04-30**, es gibt keine Sicherheitspatches mehr für dieses Image. Empfehlung: `node:22-alpine` (EOL 2027-04-30) oder `node:24-alpine` (EOL 2028-04-30). `python:3.12-slim` (Zeile 10) ist bis 2028-10-31 unterstützt und damit unkritisch; ein Wechsel auf 3.13 wäre optional. Beide Tags ohne Patch-Level sind hier richtig so — ein Rebuild holt die Patches automatisch. ### 5. Nebenbefund: Altlasten im lokalen `.venv` Im Entwicklungs-venv liegen Pakete, die weder in `requirements.txt` stehen noch irgendwo im Code importiert werden (geprüft per `grep`) — vermutlich Reste der früheren `matrix-nio`-Bot-Implementierung, bevor `backend/bot.py` auf `requests` umgestellt wurde: `aiohttp` 3.13.5 (28 Advisories!), `matrix-nio`, `python-multipart`, `h2`, `python-socks`, `aiohttp_socks`. Die kommen **nicht** ins Docker-Image (dort wird nur `requirements.txt` installiert), verfälschen aber jedes lokale Audit und lassen die Lage schlimmer aussehen als sie ist. ## Was geändert werden muss **Sofort (bekannte Schwachstellen im ausgelieferten Code):** - [ ] `react-router-dom` auf 6.30.6 heben (`npm update react-router-dom` — Patch, kein Breaking Change), `package-lock.json` mit committen **Entwicklungsumgebung:** - [ ] `vite` auf ≥ 6.4.3 heben (bringt `esbuild` ≥ 0.25.0 mit) — Major-Upgrade, `npm run build` und `npm run dev` danach prüfen - [ ] `vitest` auf 4.1.11 heben — Major-Upgrade, `npm test` danach prüfen - [ ] Lokales `.venv` neu aufsetzen (`rm -rf .venv && python3 -m venv .venv && .venv/bin/pip install -r requirements-dev.txt`), damit dort nicht länger `starlette` 1.2.1, `cryptography` 48.0.0 und die `matrix-nio`-Altlasten liegen **Struktur (verhindert, dass das Problem wiederkommt):** - [ ] `requirements.txt` auf exakte Versionen (`==`) umstellen und eine reproduzierbare Auflösung committen (`pip freeze` der Prod-Closure oder `pip-compile`/`uv pip compile` aus einer `requirements.in`) - [ ] `run_local_checks()` in `scripts/publish.sh` um Audits ergänzen, da es kein CI gibt: `pip-audit -r requirements.txt` und `npm audit --audit-level=high` (im `frontend/`-Block) - [ ] `pip-audit` in `requirements-dev.txt` aufnehmen - [ ] Entscheidung zu react-router 6 vs. 7 treffen und im Repo festhalten (die beiden 6.x-Advisories ohne Fix sind sonst ein stiller Dauerzustand) **Docker:** - [ ] `node:20-alpine` → `node:22-alpine` (oder 24) in `Dockerfile` Zeile 2, danach ein kompletter `docker build` als Gegenprobe ## Akzeptanzkriterien - `npm audit --audit-level=high` im `frontend/` meldet keine Treffer mehr (verbleibende moderate-Treffer sind im Repo begründet dokumentiert). - `pip-audit -r requirements.txt` meldet keine Treffer. - `scripts/publish.sh` bricht ab, wenn eines der beiden Audits anschlägt — ein Release kann nicht mehr unbemerkt mit einer bekannten Schwachstelle rausgehen. - Aus dem Repo ist ablesbar, welche Python-Versionen in einem Image stecken (exakte Versionen statt `>=`). - Das Docker-Image baut auf einer Node-Version, die noch Sicherheitspatches bekommt.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
AxonByteDev/PatchPilot#11
No description provided.