feat: harden remote diagnostics authentication

Bind the diagnostics flow to bearer-only authentication and reject query token attempts with controlled responses. Compare bearer token bytes with timing-safe equality, remove wildcard CORS, and mark diagnostics/support responses as no-store.

Move trace configuration mutation behind POST, add method failure handling, add a small per-IP/loopback in-memory request limit, and keep generated setup and support-manifest URLs token-free while pointing support access at a local bridge/tunnel flow.

Sanitize backup remote diagnostics on export and restore so legacy token, endpoint, host mode, and port values are not persisted; restores only keep the allowlist and force local binding.

Tests cover bearer accept/reject, query rejection, GET mutation rejection, no-store/CORS behavior, loopback default binding, rate limiting, token-free hints, and backup sanitation.
This commit is contained in:
Sucukdeluxe
2026-08-12 00:59:07 +02:00
parent ba413010c8
commit ebfd98226b
7 changed files with 601 additions and 365 deletions
+20 -20
View File
@@ -348,14 +348,14 @@ export function getDebugSetupCheck(baseDir: string): DebugSetupCheckResult {
};
const supportBundle = getSupportBundleEstimate(baseDir, logSummary);
if (!token) {
warnings.push("debug_token.txt fehlt oder ist leer. Der Debug-Server startet dann nicht.");
}
if (localOnly) {
warnings.push("Der Debug-Server ist aktuell nur lokal erreichbar. Für Remote-Support debug_host.txt auf 0.0.0.0 setzen.");
} else {
notes.push("Der Debug-Server ist für Remote-Zugriff konfiguriert. Firewall oder Provider-Regeln müssen separat offen sein.");
}
if (!token) {
warnings.push("debug_token.txt fehlt oder ist leer. Der Debug-Server startet dann nicht.");
}
if (localOnly) {
notes.push("Der Debug-Server ist lokal gebunden. Remote-Support nutzt eine vertrauenswürdige Bridge oder einen Tunnel auf diese lokale Adresse.");
} else {
warnings.push("Der Debug-Server ist nicht lokal gebunden. Für Support sicherheitshalber auf 127.0.0.1 zurückstellen und eine Bridge oder einen Tunnel nutzen.");
}
if (!fs.existsSync(supportManifestPath)) {
warnings.push("debug_support_manifest.json fehlt. App einmal neu starten, damit das Support-Manifest neu geschrieben wird.");
}
@@ -421,15 +421,15 @@ export function getDebugSetupCheck(baseDir: string): DebugSetupCheckResult {
supportBundle,
warnings,
notes,
localUrls: {
health: `http://127.0.0.1:${port}/health?token=${token || "<TOKEN>"}`,
meta: `http://127.0.0.1:${port}/meta?token=${token || "<TOKEN>"}`,
diagnostics: `http://127.0.0.1:${port}/diagnostics?token=${token || "<TOKEN>"}`
},
remoteUrlTemplates: {
health: `http://<SERVER_IP_OR_DNS>:${port}/health?token=${token || "<TOKEN>"}`,
meta: `http://<SERVER_IP_OR_DNS>:${port}/meta?token=${token || "<TOKEN>"}`,
diagnostics: `http://<SERVER_IP_OR_DNS>:${port}/diagnostics?token=${token || "<TOKEN>"}`
}
};
}
localUrls: {
health: `http://127.0.0.1:${port}/health`,
meta: `http://127.0.0.1:${port}/meta`,
diagnostics: `http://127.0.0.1:${port}/diagnostics`
},
remoteUrlTemplates: {
health: `http://127.0.0.1:${port}/health`,
meta: `http://127.0.0.1:${port}/meta`,
diagnostics: `http://127.0.0.1:${port}/diagnostics`
}
};
}