Add test and security baseline

- make quick/check/mutation from cj/security-baseline (Makefile unchanged,
  settings in baseline.mk), pre-commit, gitleaks, Semgrep rules, ESLint
  security rules, Forgejo workflow
- tests/security: generic baseline tests plus app config and role tests
  against createApp() with an in-memory database
- extract createApp() from src/server/index.ts; LB_DATABASE_PATH for tests
- fix gaps found by the tests: reject unknown fields on project create,
  answer malformed JSON with 400 instead of 500, set nosniff on the API
- npm audit fix (next 16.4.1 and others, no high/critical left)
- clear ESLint errors, safeFileName without control-character regex
- per-project ownership stays an open, documented gap (idorGap -> TODO)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Grovy311 2026-10-15 09:40:33 +02:00
parent 67476ea59b
commit a7a1595cbf
38 changed files with 5852 additions and 442 deletions

82
eslint.config.mjs Normal file
View file

@ -0,0 +1,82 @@
// ESLint-Sicherheitsregeln der Test- & Security-Baseline (Flat Config, ESLint 9.22+; ESLint 10
// scheitert Stand 10/2026 an der Peer-Dependency von eslint-plugin-react).
// Die Blöcke bis einschließlich der Sicherheitsregeln stammen aus der Baseline; die
// App-spezifischen Ergänzungen stehen darunter und sind einzeln begründet.
import js from "@eslint/js";
import { defineConfig } from "eslint/config";
import noUnsanitized from "eslint-plugin-no-unsanitized";
import react from "eslint-plugin-react";
import reactHooks from "eslint-plugin-react-hooks";
import security from "eslint-plugin-security";
import globals from "globals";
import tseslint from "typescript-eslint";
export default defineConfig(
{
ignores: [
"dist/**",
".next/**",
"node_modules/**",
"coverage/**",
"playwright-report/**",
// Lokale Artefakte der Baseline bzw. der App, nicht versioniert.
"reports/**",
"mutants/**",
".venv-tools/**",
"data/**",
"next-env.d.ts",
],
},
js.configs.recommended,
tseslint.configs.recommended,
security.configs.recommended,
noUnsanitized.configs.recommended,
{
files: ["**/*.{js,jsx,mjs,ts,tsx}"],
plugins: { react },
languageOptions: {
globals: { ...globals.browser, ...globals.node },
parserOptions: { ecmaFeatures: { jsx: true } },
},
settings: { react: { version: "detect" } },
rules: {
// XSS
"react/no-danger": "error",
"react/no-danger-with-children": "error",
"react/jsx-no-script-url": "error",
"react/jsx-no-target-blank": "error",
// Code aus Strings
"no-eval": "error",
"no-implied-eval": "error",
"no-new-func": "error",
// Steuerzeichen in Regex: nicht abschalten, sondern Zeichencodes per Schleife prüfen.
"no-control-regex": "error",
// obj[key] meldet fast jede Zeile; IDOR/Prototype-Pollution fangen Tests und Review.
"security/detect-object-injection": "off",
},
},
// --- App-spezifisch (Leistungsbilanz) ---
{
files: ["**/*.{ts,tsx}"],
plugins: { "react-hooks": reactHooks },
rules: {
// Hook-Regeln für die Next.js-Oberfläche; der Code nutzt sie bereits per Kommentar.
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
// Express verlangt für Fehler-Middleware vier Parameter; ungenutzte heißen _next usw.
"@typescript-eslint/no-unused-vars": [
"error",
{ argsIgnorePattern: "^_", varsIgnorePattern: "^_", caughtErrorsIgnorePattern: "^_" },
],
// `declare global { namespace Express { ... } }` ist der dokumentierte Weg, Request zu erweitern.
"@typescript-eslint/no-namespace": ["error", { allowDeclarations: true }],
},
},
{
// Betriebs-Skripte in scripts/*.js sind CommonJS (laufen im Container ohne Build-Schritt).
files: ["scripts/**/*.js"],
languageOptions: { sourceType: "commonjs" },
rules: { "@typescript-eslint/no-require-imports": "off" },
},
);