fix: next/next-auth/nodemailer/postcss auf gepatchte Versionen heben
Some checks are pending
Playwright Tests / test (push) Waiting to run
Test and SonarQube / quality (push) Waiting to run

Letzter offener Punkt aus dem Sicherheitsdurchgang vor dem Beta-Test: npm audit
meldete 24 Schwachstellen, davon mehrere kritisch in next und next-auth.

- next 14.2.35 -> 15.5.24 (patcht alles, was next@16 sonst erfordert haette --
  ein Major-Sprung statt zwei; next 15 unterstuetzt weiterhin React 18)
- next-auth 4.24.13 -> 4.24.15, @auth/core per overrides auf 0.41.3 erzwungen
  (next-auth pinnt es sonst exakt auf die verwundbare 0.34.3, als ungenutzten
  optionalen Peer)
- nodemailer 7 -> 10 per overrides (next-auth haelt einen ungenutzten optionalen
  Peer auf 7.x)
- postcss auf 8.5.28, auch next's eigene interne Kopie per overrides
- ungenutztes @auth/prisma-adapter entfernt (Projekt nutzt @next-auth/prisma-adapter)

npm audit: 24 -> 0.

Zwei echte Codeaenderungen dafuer noetig:
- Next.js 15: { params } in Routen-Handlern ist jetzt ein Promise. Einzige
  betroffene Route: cross-sync/rules/[id].
- nodemailer.Transporter gibt es seit @types/nodemailer 8 nicht mehr als
  Namespace-Property, nur noch als benannter Type-Export. Einzige Stelle:
  src/lib/email.ts.

Ausserdem google-auth-library als direkte Abhaengigkeit deklariert (vorher nur
ueber googleapis transitiv gehofft) und per overrides auf eine Version gepinnt --
der frische package-lock.json aus den obigen Fixes hob sie sonst nicht mehr
passend, wodurch zwei inkompatible Kopien nebeneinander existierten und der
Typecheck brach.

Verifiziert: Typecheck, Lint, 79 Unit-Tests, Produktions-Build alle gruen.
Login-Seite, Sicherheitsheader und Auth-Middleware manuell im gebauten
Standalone-Server geprueft. Kein Playwright-E2E-Lauf (braucht echte DB/OAuth).

v1.123.0

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
mARTin-B78 2026-09-12 14:41:52 +02:00
parent 3742ae52c6
commit 114131f9e4
5 changed files with 1885 additions and 1545 deletions

View File

@ -2,6 +2,49 @@
All notable changes to this project are documented in this file. All notable changes to this project are documented in this file.
## [1.123.0] - 2026-09-12
### Security
- **`next` 14.2.35 → 15.5.24.** Patcht mehrere kritische Advisories, darunter zwei
unauthentifizierte RCE-Klassen (Windows-Hosts, AVIF-Bildoptimierung — beide auf
diesem Linux-Host ohnehin nicht direkt erreichbar, aber jetzt geschlossen) sowie
diverse DoS-, SSRF- und Cache-Poisoning-Advisories. 15.5.24 patcht bereits alles,
was sonst erst next@16 fordern würde — ein Major-Sprung statt zwei.
- **`next-auth` 4.24.13 → 4.24.15**, plus `@auth/core` per `overrides` auf 0.41.3
erzwungen (next-auth pinnt es sonst exakt auf die verwundbare 0.34.3 — als
optionalen Peer, den dieses Projekt nirgends importiert). Behebt einen
E-Mail-Normalisierungs-Bypass, einen Absturz bei fehlerhaften Bearer-Headern und
eine fehlende Provider-Bindung bei OAuth-State/Nonce/PKCE-Cookies.
- **`nodemailer` 7 → 10** (`overrides`, da next-auth einen ungenutzten optionalen
Peer auf 7.x haelt). Behebt SMTP-Command-Injection, CRLF-Header-Injection, eine
SSRF/Arbitrary-File-Read-Luecke ueber den `raw`-Nachrichtenmodus und eine
IDN/Punycode-Domain-Bypass beim Empfaenger-Check.
- `postcss` (auch die von `next` selbst intern gebündelte Kopie, per `overrides`)
auf 8.5.28. Behebt XSS in der CSS-Ausgabe und mehrfache Pfad-Traversal-/
Arbitrary-File-Read-Lücken über `sourceMappingURL`.
- Ungenutztes `@auth/prisma-adapter` (v5-Adapter, nirgends importiert — das
Projekt nutzt `@next-auth/prisma-adapter` für v4) entfernt.
- **`npm audit`: 24 → 0 gemeldete Schwachstellen.**
### Fixed
- `google-auth-library` war ein undeklarierter, nur über `googleapis` transitiv
gehofftes Hoisting verfügbarer Import in `google-tasks.ts`. Ein frischer
`package-lock.json` (Nebeneffekt der obigen Audit-Fixes) hob ihn nicht mehr
passend, wodurch zwei strukturell inkompatible Kopien nebeneinander existierten
und der Typecheck brach. Jetzt eine direkte Abhängigkeit plus `overrides`-Eintrag,
damit immer genau eine Instanz installiert wird.
- `nodemailer.Transporter` gibt es als Namespace-Property seit `@types/nodemailer`
8 nicht mehr — `Transporter` ist jetzt ein benannter Type-Export. Einzige
betroffene Stelle: `src/lib/email.ts`.
- Next.js 15: dynamische Routen-Parameter (`{ params }`) sind jetzt ein Promise.
Einzige betroffene Route: `PATCH`/`DELETE` unter
`/api/calendar/cross-sync/rules/[id]`.
Verifiziert: Typecheck, Lint und 79 Unit-Tests grün; Produktions-Build erfolgreich;
Login-Seite, Sicherheitsheader und die Auth-Middleware (Redirect bei fehlendem
Token) im gebauten Standalone-Server manuell geprüft. Kein Playwright-E2E-Lauf,
da dafür eine echte Datenbank und OAuth-Zugangsdaten nötig sind.
## [1.122.0] - 2026-09-12 ## [1.122.0] - 2026-09-12
### Security ### Security

3338
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "my-weekly-todo-list", "name": "my-weekly-todo-list",
"version": "1.122.0", "version": "1.123.0",
"description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view", "description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
@ -26,7 +26,6 @@
"author": "My Weekly To Do List Team", "author": "My Weekly To Do List Team",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@auth/prisma-adapter": "^2.11.1",
"@fortawesome/fontawesome-svg-core": "^7.2.0", "@fortawesome/fontawesome-svg-core": "^7.2.0",
"@fortawesome/free-brands-svg-icons": "^7.2.0", "@fortawesome/free-brands-svg-icons": "^7.2.0",
"@fortawesome/free-solid-svg-icons": "^7.2.0", "@fortawesome/free-solid-svg-icons": "^7.2.0",
@ -43,15 +42,16 @@
"@tiptap/react": "^3.20.0", "@tiptap/react": "^3.20.0",
"@tiptap/starter-kit": "^3.20.0", "@tiptap/starter-kit": "^3.20.0",
"@types/bcryptjs": "^2.4.6", "@types/bcryptjs": "^2.4.6",
"@types/nodemailer": "^7.0.11", "@types/nodemailer": "^8.0.1",
"bcryptjs": "^3.0.3", "bcryptjs": "^3.0.3",
"date-fns": "^2.30.0", "date-fns": "^2.30.0",
"google-auth-library": "^10.9.1",
"googleapis": "^170.1.0", "googleapis": "^170.1.0",
"ical.js": "^2.2.1", "ical.js": "^2.2.1",
"lucide-react": "^0.563.0", "lucide-react": "^0.563.0",
"next": "14.2.35", "next": "15.5.24",
"next-auth": "^4.24.13", "next-auth": "^4.24.15",
"nodemailer": "^7.0.13", "nodemailer": "^10.0.8",
"postcss-cli": "^11.0.1", "postcss-cli": "^11.0.1",
"prisma": "5.22.0", "prisma": "5.22.0",
"react": "^18.2.0", "react": "^18.2.0",
@ -62,6 +62,14 @@
"web-push": "^3.6.7", "web-push": "^3.6.7",
"zustand": "^4.4.0" "zustand": "^4.4.0"
}, },
"overrides": {
"@auth/core": "0.41.3",
"nodemailer": "^10.0.8",
"next": {
"postcss": "^8.5.28"
},
"google-auth-library": "^10.9.1"
},
"devDependencies": { "devDependencies": {
"@playwright/test": "^1.58.0", "@playwright/test": "^1.58.0",
"@types/node": "^20.0.0", "@types/node": "^20.0.0",
@ -69,8 +77,8 @@
"@types/react-dom": "^18.2.0", "@types/react-dom": "^18.2.0",
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.19",
"eslint": "^8.0.0", "eslint": "^8.0.0",
"eslint-config-next": "^14.0.0", "eslint-config-next": "15.5.24",
"postcss": "^8.4.38", "postcss": "^8.5.28",
"tailwindcss": "^3.4.1", "tailwindcss": "^3.4.1",
"typescript": "^5.0.0" "typescript": "^5.0.0"
} }

View File

@ -3,15 +3,19 @@ import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth'; import { authOptions } from '@/lib/auth';
import { prisma } from '@/lib/prisma'; import { prisma } from '@/lib/prisma';
// Next.js 15: dynamische Routen-Parameter kommen als Promise, nicht mehr als Objekt.
type RouteParams = { params: Promise<{ id: string }> };
// PATCH /api/calendar/cross-sync/rules/[id] — update a rule // PATCH /api/calendar/cross-sync/rules/[id] — update a rule
export async function PATCH(request: NextRequest, { params }: { params: { id: string } }) { export async function PATCH(request: NextRequest, { params }: RouteParams) {
const { id } = await params;
const session = await getServerSession(authOptions); const session = await getServerSession(authOptions);
if (!session?.user?.email) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); if (!session?.user?.email) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
const user = await prisma.user.findUnique({ where: { email: session.user.email }, select: { id: true } }); const user = await prisma.user.findUnique({ where: { email: session.user.email }, select: { id: true } });
if (!user) return NextResponse.json({ error: 'User not found' }, { status: 404 }); if (!user) return NextResponse.json({ error: 'User not found' }, { status: 404 });
const existing = await prisma.calendarSyncRule.findFirst({ where: { id: params.id, userId: user.id } }); const existing = await prisma.calendarSyncRule.findFirst({ where: { id, userId: user.id } });
if (!existing) return NextResponse.json({ error: 'Rule not found' }, { status: 404 }); if (!existing) return NextResponse.json({ error: 'Rule not found' }, { status: 404 });
const body = await request.json(); const body = await request.json();
@ -24,25 +28,26 @@ export async function PATCH(request: NextRequest, { params }: { params: { id: st
if (key in body) data[key] = body[key]; if (key in body) data[key] = body[key];
} }
const rule = await prisma.calendarSyncRule.update({ where: { id: params.id }, data }); const rule = await prisma.calendarSyncRule.update({ where: { id }, data });
return NextResponse.json({ rule }); return NextResponse.json({ rule });
} }
// DELETE /api/calendar/cross-sync/rules/[id] — delete a rule and all its mappings // DELETE /api/calendar/cross-sync/rules/[id] — delete a rule and all its mappings
export async function DELETE(request: NextRequest, { params }: { params: { id: string } }) { export async function DELETE(request: NextRequest, { params }: RouteParams) {
const { id } = await params;
const session = await getServerSession(authOptions); const session = await getServerSession(authOptions);
if (!session?.user?.email) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); if (!session?.user?.email) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
const user = await prisma.user.findUnique({ where: { email: session.user.email }, select: { id: true } }); const user = await prisma.user.findUnique({ where: { email: session.user.email }, select: { id: true } });
if (!user) return NextResponse.json({ error: 'User not found' }, { status: 404 }); if (!user) return NextResponse.json({ error: 'User not found' }, { status: 404 });
const existing = await prisma.calendarSyncRule.findFirst({ where: { id: params.id, userId: user.id } }); const existing = await prisma.calendarSyncRule.findFirst({ where: { id, userId: user.id } });
if (!existing) return NextResponse.json({ error: 'Rule not found' }, { status: 404 }); if (!existing) return NextResponse.json({ error: 'Rule not found' }, { status: 404 });
// Cascade delete (mappings deleted via onDelete: Cascade in schema) // Cascade delete (mappings deleted via onDelete: Cascade in schema)
await prisma.calendarSyncRule.delete({ where: { id: params.id } }); await prisma.calendarSyncRule.delete({ where: { id } });
// Also clean up reverse mappings stored under the synthetic _R id // Also clean up reverse mappings stored under the synthetic _R id
await prisma.calendarSyncMapping.deleteMany({ where: { ruleId: params.id + '_R' } }); await prisma.calendarSyncMapping.deleteMany({ where: { ruleId: id + '_R' } });
return NextResponse.json({ success: true }); return NextResponse.json({ success: true });
} }

View File

@ -1,7 +1,9 @@
import nodemailer from 'nodemailer'; import nodemailer, { Transporter } from 'nodemailer';
import { getVerificationCopy, getResetCopy } from './emailTemplates'; import { getVerificationCopy, getResetCopy } from './emailTemplates';
let _transporter: nodemailer.Transporter | null = null; // nodemailer 10 / @types/nodemailer 8: Transporter ist ein benannter Type-Export,
// keine Namespace-Eigenschaft mehr (nodemailer.Transporter gab es nur bis v7/@types v7).
let _transporter: Transporter | null = null;
function getTransporter() { function getTransporter() {
if (!_transporter) { if (!_transporter) {