feat(security): alle Daten auf den Besitzer einschraenken

Bisher sah jede Anfrage alles. Jetzt traegt jede Abfrage eine
Besitzerbedingung, und zwar an erster Stelle in der where-Klausel statt als
nachtraeglicher Vergleich - so kann sie nicht versehentlich uebersprungen
werden.

- Lesen: findFirst statt findUnique, immer mit ownerId
- Schreiben und Loeschen: updateMany/deleteMany mit ownerId in der
  where-Klausel. Fremde Datensaetze werden dadurch gar nicht erst getroffen,
  und die betroffene Zeilenzahl sagt, ob es den Datensatz gab.
- Notizen haengen ueber project.ownerId am Besitzer
- Beim Verknuepfen eines Projekts wird geprueft, dass die Git-Verbindung dem
  Aufrufer gehoert - sonst liesse sich eine fremde Verbindung und damit ein
  fremder Token unterschieben
- Fremde IDs liefern 404 statt 403: die Antwort bestaetigt nicht einmal,
  dass es den Datensatz gibt
- Ausgehende Endpunkte (Sync, Verbindungstest, Repo-Liste) zusaetzlich
  ratenbegrenzt, Basis-URLs gegen den SSRF-Schutz geprueft
- Slug und Verbindungsname werden je Besitzer auf Eindeutigkeit geprueft

Nachgewiesen: ein zweites Konto erhaelt auf GET, PATCH, DELETE, Notiz
anlegen und Sync eines fremden Projekts durchgaengig 404, auf die
Admin-Endpunkte 403, und das Zielprojekt bleibt unveraendert.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-25 21:30:59 +02:00
co-authored by Claude Opus 5
parent cf4103a46c
commit ed77ffaed2
13 changed files with 351 additions and 122 deletions
+5 -1
View File
@@ -1,5 +1,6 @@
import type { Metadata } from "next";
import { ConnectionsManager } from "@/components/ConnectionsManager";
import { requireUserPage } from "@/lib/auth/guard";
import { prisma } from "@/lib/db";
import { connectionSafeSelect } from "@/lib/projects";
import type { ConnectionWithCount } from "@/types";
@@ -8,11 +9,14 @@ export const dynamic = "force-dynamic";
export const metadata: Metadata = {
title: "Git-Verbindungen",
description: "GitHub, GitLab und Gitea mit Nebula verbinden.",
description: "GitHub, GitLab und Gitea verbinden.",
};
export default async function ConnectionsPage() {
const user = await requireUserPage();
const connections = await prisma.gitConnection.findMany({
where: { ownerId: user.id },
select: { ...connectionSafeSelect, _count: { select: { projects: true } } },
orderBy: { name: "asc" },
});