/** * Was mit einer Navigation geschehen soll, die das Fenster verlassen will. * * WARUM DAS HIER STEHT UND NICHT IN `createWindow`. Bis hierher entschieden das * zwei Ereignisbehandlungen mitten in `electron/main.ts`, und zwar in einer * Weise, die von aussen niemand nachlesen konnte: * * - Beschrieben war nur die Haelfte: `will-navigate` breche "jede * Navigation ab", `setWindowOpenHandler` lehne "jedes neue Fenster ab". * Beides stimmt - und beide reichen die Adresse anschliessend an den * Standardbrowser weiter, wenn sie mit `https:` beginnt. Das stand * nirgends. * - Der Kartenabruf galt als "Einzige Ausnahme" vom Grundsatz, dass das * Programm nicht ins Netz geht. Der Weg ueber den Browser ist eine zweite. * - `vitest.config.ts` begruendete die Abdeckungsluecke in `electron/main.ts` * damit, genau diese Behandlungen pruefe der Rauchtest am gebauten Stand. * GEMESSEN: `e2e/rauchtest.spec.ts` hat sechs Faelle und nennt weder * `will-navigate` noch `setWindowOpenHandler`; das Wort "navigate" kommt * dort ueberhaupt nicht vor. Eine Abdeckungsausnahme, gedeckt von einem * Test, den es nicht gab. * * Als reine Funktion laesst sich die Entscheidung erschoepfend pruefen, ohne * ein Fenster zu oeffnen und ohne einen Browser zu starten. Die beiden * Behandlungen in `main.ts` tun danach nur noch, was die Entscheidung sagt. */ /** Was mit einer Adresse geschehen soll. */ export type Navigationsentscheidung = /** Im eigenen Fenster laden - allein die geladene `index.html`. */ | 'laden' /** Abbrechen und sonst nichts. */ | 'abbrechen' /** Abbrechen und dem Betriebssystem uebergeben. */ | 'extern'; /** * Darf diese Adresse geladen, muss sie abgebrochen oder nach draussen gegeben * werden? * * `erlaubt` ist die Adresse der geladenen `index.html` als Zeichenkette - der * einzige Ort, den das Fenster selbst anzeigen darf. * * DIE REIHENFOLGE DER PRUEFUNGEN IST DIE SICHERHEITSRICHTUNG: Erst der * Vergleich auf Gleichheit, dann alles Uebrige. Wer zuerst nach `https:` * fragte, gaebe eine Adresse nach draussen, die vielleicht die eigene ist. */ export function entscheideNavigation(adresse: string, erlaubt: string): Navigationsentscheidung { let ziel: URL; try { ziel = new URL(adresse); } catch { /* * Was sich nicht als Adresse lesen laesst, wird abgebrochen und NICHT * weitergereicht. `shell.openExternal` gibt die Zeichenkette an das * Betriebssystem; was Windows aus etwas Unlesbarem macht, entscheidet dann * nicht mehr dieses Programm. */ return 'abbrechen'; } if (ziel.toString() === erlaubt) return 'laden'; /* * NUR `https:` GEHT NACH DRAUSSEN. Nicht `http:` - eine unverschluesselte * Adresse aus einer fremden Datei ist ein Weg, Daten unbemerkt abfliessen zu * lassen. Nicht `file:` - das oeffnete ein beliebiges Programm mit einer * beliebigen Datei. Nicht `javascript:`, nicht `data:`. Die Pruefung fragt * das Protokoll der ausgewerteten Adresse und nicht den Anfang der * Zeichenkette: `startsWith('https://')` traefe auch * "https://example.com\\@boeses.example" anders als der Auswerter. */ if (ziel.protocol === 'https:') return 'extern'; return 'abbrechen'; }