/* ===================================================================== helfer-openssl.mjs — wo liegt openssl? WAS HIER SCHIEFGING (29.09.2026 gemessen): Im nächtlichen Lauf standen dreizehn Prüfungen mit derselben Zeile rot: [uncaughtException] Error: spawnSync openssl ENOENT Kein Programmfehler. Die Prüfungen brauchen einen https-Vorbau (der Sitzungskeks des Hauses ist `secure`, über http kommt er nicht an), und dafür erzeugen sie ein Wegwerf-Zertifikat mit `openssl`. AUF DIESEM RECHNER LIEGT ES IN Git Bash: C:\Program Files\Git\mingw64\bin\openssl.exe C:\Program Files\Git\usr\bin\openssl.exe Im PATH des Systems steht aber nur `C:\Program Files\Git\cmd` — dort liegt `git.exe` und sonst nichts. Wer aus Git Bash heraus startet, erbt die mingw-Pfade und merkt nie etwas. Die Aufgabenplanung startet `node.exe` direkt, ohne Shell — und bekommt sie nicht. Das ist die unangenehmste Sorte Fehler: Bei mir grün, nachts rot, und der Grund steht nicht im Code. Dreizehn dauerhaft rote Zeilen in einer Notiz, die Filipe morgens liest, gewöhnen einem das Hinsehen ab — und decken dabei die echten Befunde zu. WAS DIESE DATEI TUT: Sie sucht `openssl` zuerst dort, wo es ohnehin gefunden würde (PATH), und danach an den Orten, an denen es auf einem Windows-Rechner mit Git tatsächlich liegt. Damit läuft dieselbe Prüfung in beiden Umgebungen. WARUM KEIN EINTRAG IN DEN SYSTEM-PATH: `mingw64\bin` enthält rund hundert Programme mit Unix-Namen (`find`, `sort`, `link` …), die gleichnamige Windows-Befehle verdecken. Das für eine einzige Prüfung zu ändern, würde an ganz anderen Stellen Fehler verursachen, die niemand mehr hierher zurückverfolgt. ===================================================================== */ import { existsSync } from "node:fs"; import { execFileSync } from "node:child_process"; import { delimiter, join } from "node:path"; /* Die Orte, an denen Git für Windows sein openssl ablegt. Beide kommen vor: `mingw64` bei der 64-Bit-Fassung, `usr` bei der mitgelieferten Unix-Umgebung. */ const BEKANNTE_ORTE = [ "C:/Program Files/Git/mingw64/bin/openssl.exe", "C:/Program Files/Git/usr/bin/openssl.exe", "C:/Program Files (x86)/Git/mingw64/bin/openssl.exe", "C:/Program Files (x86)/Git/usr/bin/openssl.exe", "C:/Program Files/OpenSSL-Win64/bin/openssl.exe", ]; let gemerkt = null; /* IM PATH SUCHEN, OHNE `where` ODER `which` ZU BRAUCHEN. Beide sind selbst Programme und können genauso fehlen; und `which` gibt es unter Windows außerhalb von Git Bash gar nicht. */ const imPfad = () => { const pfade = (process.env.PATH || "").split(delimiter).filter(Boolean); const namen = process.platform === "win32" ? ["openssl.exe", "openssl.com", "openssl"] : ["openssl"]; for (const p of pfade) { for (const n of namen) { const voll = join(p, n); if (existsSync(voll)) return voll; } } return null; }; /* Der Pfad zu openssl — oder ein Abbruch mit Anleitung. * * DRITTER AUSGANG: Wird nichts gefunden, ist das weder „in Ordnung" * noch „das Programm ist kaputt", sondern „konnte nicht nachsehen". * Der Fehler sagt deshalb, WAS fehlt und WO man es herbekommt — * `spawnSync openssl ENOENT` sagt nur, dass etwas nicht da war. */ export function opensslPfad() { if (gemerkt) return gemerkt; const gefunden = imPfad() || BEKANNTE_ORTE.find((o) => existsSync(o)); if (!gefunden) { throw new Error( "openssl ist nicht auffindbar. Es wird fuer den https-Vorbau gebraucht " + "(der Sitzungskeks ist `secure` und kommt ueber http nicht an).\n" + " Gesucht im PATH und an diesen Orten:\n" + BEKANNTE_ORTE.map((o) => " " + o).join("\n") + "\n Unter Windows bringt Git fuer Windows es mit; sonst " + "https://slproweb.com/products/Win32OpenSSL.html"); } gemerkt = gefunden; return gefunden; } /* Ein Wegwerf-Zertifikat für einen Hostnamen. * * WARUM HIER UND NICHT IN JEDER DATEI: Die Zeile stand * neunundzwanzigmal fast gleich im Haus. Zwei Tage bevor ich das * hier schrieb, war eine davon um ein `-addext` ärmer als die * anderen — gemerkt hat es niemand, weil der Browser den fehlenden * Alternativnamen erst bei einer Weiterleitung anmahnt. Eine Stelle * kann nicht von sich selbst abweichen. */ export function zertifikatBauen(schluessel, zertifikat, host, tage = 2) { execFileSync(opensslPfad(), [ "req", "-x509", "-newkey", "rsa:2048", "-nodes", "-keyout", schluessel, "-out", zertifikat, "-days", String(tage), "-subj", `/CN=${host}`, "-addext", `subjectAltName=DNS:${host}`, ], { stdio: "ignore" }); }