Files
ssh-jumphost/FORTSETZUNG_Aufgabe11.md
2026-09-02 20:30:44 +02:00

9.8 KiB

Fortsetzungsdokument: Aufgabe #11 (SSH Copy-Paste + Terminal-Farben)

Stand: 2026-09-02, Code-Stand abgeschlossen und regressionsfrei verifiziert; Live-Browser-Test weiterhin ausstehend (siehe Abschnitt 3 + Nachtrag in Abschnitt 4 -- dieselbe Einschraenkung wie bei Teil F). Repo: ssh_jumphost, erreichbar ueber die device_bash-Bridge unter $HOME/mnt/ssh_jumphost.

Einordnung: Aufgabe #11 ist vom Nutzer nachtraeglich hinzugefuegt worden, nicht Teil von Umsetzungsauftrag_Sonnet5.md und hatte bis zu dieser Session keine Detail-Spec -- nur das Stichwort "SSH Copy-Paste + Terminal-Farben" (siehe FORTSETZUNG_Teil_C.md/_D.md/_F.md). Das Scope wurde zu Beginn dieser Session per Rueckfrage geklaert (zwei AskUserQuestion-Antworten):

  1. Copy-Paste: "kopieren mit markieren des textes und einfuegen mit rechtsklick, wenn es umsetzbar ist sonst mit Rechtsklickkontextmenu."
  2. Terminal-Farben: "Vom Benutzer waehlbar."

Beides betrifft ausschliesslich SSH (static/js/terminal.js, SshTerminalSession) -- RDP hat ueber Guacamole bereits eine eigene, funktionierende Zwischenablage-Anbindung (onclipboard in rdp.js) und war nie Teil der Aufgabe (Titel: "SSH Copy-Paste").


1) SSH Copy-Paste

  • Kopieren: copyOnSelect: true bei der Terminal-Konstruktion (_setupTerminal()). xterm.js kopiert eine Markierung damit sofort in die System-Zwischenablage -- kein Strg+C/Cmd+C mehr noetig. Strg+C OHNE Markierung bleibt unveraendert SIGINT (xterm.js' Standardverhalten, von copyOnSelect nicht beruehrt -- kein neues Verhalten, keine Ueberraschung fuer bestehende Nutzung).
  • Einfuegen per Rechtsklick, zweistufig, genau wie gewuenscht:
    1. contextmenu-Listener auf this.terminalContainer (_wireClipboard()) ruft event.preventDefault() auf (unterdrueckt das native Browser-Kontextmenue) und versucht direkt navigator.clipboard.readText() (_attemptDirectPaste()). In den meisten Chromium-Browsern reicht ein Rechtsklick als "transiente Aktivierung" fuer diese Berechtigung -- Einfuegen passiert dann sofort, ohne sichtbares Menue.
    2. Schlaegt das fehl (kein navigator.clipboard, Berechtigung verweigert -- typischerweise Firefox/Safari ausserhalb einer frischen Klick-Geste, oder unsicherer Kontext), erscheint ersatzweise ein winziges Kontextmenue mit einem "Einfuegen"-Eintrag (_showPasteMenu()). Dessen Klick ist selbst wieder eine frische Benutzergeste und hat dadurch nochmal bessere Chancen auf die Berechtigung. Schlaegt auch das fehl, zeigt der Status-Bereich der Sitzung einen erklaerenden Hinweis ("... bitte Strg+Umschalt+V verwenden") statt eines stillen No-Ops.
    3. Das eigentliche Einfuegen laeuft ueber this.term.paste(text) (xterm.js v5 API, bestaetigt durch .paste=-Vorkommen in vendor/xterm.js) -- das ist KEIN separater Codepfad zum normalen Tippen: paste() loest intern denselben onData-Callback aus wie eine Tastatureingabe, geht also automatisch durch die bestehende WS-"input"-Sendefunktion.
  • CSP-Falle vermieden: der erste Entwurf positionierte das Ruecksicherungs-Kontextmenue ueber menu.style.left/top an der Klickposition -- das haette der Server-CSP (style-src 'self', kein unsafe-inline) im echten Browser stillschweigend blockiert (dieselbe Regel, die JS-gesetzte element.style-Aenderungen genauso trifft wie style="..."-Attribute, siehe app.css-Kopfkommentar zu .hidden). Ersetzt durch vier feste CSS-Klassen (.term-paste-menu-{top,bottom} x {left,right}, app.css), die je nach Klick-Viertel von .session-shell gesetzt werden -- keine Pixelgenauigkeit, aber vollstaendig CSP-konform und konsistent mit der einzigen im ganzen Projekt verwendeten Sichtbarkeits-/Positionierungstechnik (Klassen, nie .style).
  • Aufraeumen: _closePasteMenu() entfernt das Menue plus seine Dokument-Listener (Klick-daneben/Escape/Scroll) -- aufgerufen aus dispose() UND aus setActive(false) (ein Umschalten auf eine andere Sitzung soll kein Geister-Kontextmenue im Hintergrund hinterlassen).

2) Terminal-Farben (vom Benutzer waehlbar)

  • Vier feste Farbschemata (TERMINAL_THEMES, Modul-Konstante): Standard (dunkel, bisheriges Verhalten unveraendert als Vorgabe), Dunkel/kontrastreich, Solarized Dark, Solarized Light. Bewusst eine kleine feste Auswahl statt eines vollen Theme-Editors -- deckt "vom Benutzer waehlbar" ab, ohne eine neue Einstellungsseite einzufuehren.
  • <select class="theme-select"> in der Sitzungs-Toolbar (zwischen "Dateitransfer" und dem Status-Text). Aenderung setzt this.term.options.theme = ... (xterm.js v5: options ist ein Getter/Setter-Objekt, bestaetigt durch OptionsService/onOptionChange in vendor/xterm.js -- kein setOption() mehr, das gab es nur bis v4).
  • Persistenz: localStorage (Schluessel jumphost_terminal_theme"), rein clientseitig, kein Server-Roundtrip -- konsistent mit der ueblichen Behandlung reiner Darstellungspraeferenzen. Bewusst KEIN neues DB-Feld/ keine neue Migration: die Wahl ist eine Eigenschaft des Browsers/Nutzers vor Ort, keine sicherheits- oder audit-relevante Einstellung. Try/catch um jeden Zugriff (privater Modus/blockierter Speicher faellt dann einfach auf das Standardschema zurueck, kein Fehler).
  • Scope-Grenze, bewusst so belassen: die Wahl gilt fuer NEUE Sitzungen ab dem Zeitpunkt der Aenderung (liest localStorage beim Sitzungsaufbau in _setupTerminal()) -- sie synchronisiert sich NICHT live in andere, gerade schon offene Sitzungen (F4: mehrere gleichzeitige Instanzen moeglich). Dieselbe Klasse von Eventual-Consistency wie an mehreren anderen Stellen im Projekt bereits akzeptiert (z.B. keine Live-Aktualisierung der Sitzungsliste zwischen Tabs, FORTSETZUNG_Teil_F.md). Ein Wechsel-Knopf in jeder einzelnen Toolbar ist bewusst so belassen statt einer neuen globalen Einstellungsseite -- kein Mehrwert gegenueber "einmal in einer beliebigen Sitzung waehlen, gilt danach ueberall".

3) Verifikation

  • node --check static/js/terminal.js -- fehlerfrei.

  • CSS-Klammerbalance ({/}) in app.css geprueft -- ausgeglichen.

  • Voller Regressionslauf ueber alle 23 Testdateien (unveraendert gegenueber Teil-F-Stufe F5 -- diese Aufgabe aendert ausschliesslich static/js/terminal.js und static/css/app.css, keine Python-Datei, keine Migration, kein Template):

    Gruppe Ergebnis
    csp_compliance/teil_f_schritt2/teil_f_schritt3/teil_f_schritt5 22 passed, 0 failed
    admin_crud/admin_groups_tokens/audit_chain/auth_flow/phase9 36 passed, 1 failed
    passwords_totp/pentest_security/phase10/phase11 37 passed, 2 failed
    phase12/phase13 22 passed, 6 failed
    phase14/phase15/rbac/teil_d_schritt5/teil_d_schritt6/session_recorder/session_reaper 29 passed, 9 failed
    Summe 146 passed, 18 failed, 164 total

    Identisch zu Teil F Stufe F5 -- dieselben 18 laengst bekannten, dokumentierten Fehlschlaege (FORTSETZUNG_Teil_C.md), null neue Regressionen.

  • Kein Live-Browser-Test moeglich (dieselbe Sandbox-Einschraenkung wie bei Teil F, siehe FORTSETZUNG_Teil_F.md Abschnitt 6): weder das direkte Rechtsklick-Einfuegen (haengt vom tatsaechlichen Berechtigungsverhalten des jeweiligen Browsers ab, nicht vollstaendig durch Code-Lesen verifizierbar) noch das Ruecksicherungs-Kontextmenue noch die visuelle Wirkung der vier Farbschemata wurden tatsaechlich im Browser durchgeklickt. Empfehlung: bei Gelegenheit einmal in einem echten Chromium- UND einem echten Firefox-Fenster gegenstesten (Chromium deckt den Direktpfad ab, Firefox eher den Menue-Fallback) -- am besten zusammen mit dem noch ausstehenden F.6-Live-Test aus Teil F, da beide dieselbe echte SSH-Zielumgebung brauchen.

4) Nachtrag: Rueckfrage zum urspruenglichen Symptom

Bei der Klaerung des Scopes (Abschnitt oben) kam in einer spaeteren Rueckfrage eine praezisere Beschreibung des urspruenglichen Problems: "Ich konnte in der SSH-Sitzung nichts mit der linken Maustaste markieren" -- und zwar an einem GANZ NORMALEN Shell-Prompt, nicht in einem Vollbild-Programm mit eigener Maussteuerung (tmux/vim/htop/...). Das schliesst die naheliegendste Erklaerung ("Mausklicks gehen an ein Programm wie tmux, nicht an die lokale Markierung -- kein Bug, Standardverhalten jedes Terminals") fuer DIESEN konkreten Fall aus.

Wahrscheinlichste Erklaerung (kein Live-Test moeglich, daher nicht absicherbar): xterm.js unterstuetzt Markieren-per-Ziehen schon immer nativ, unabhaengig von copyOnSelect -- das war also vermutlich nie kaputt. Ohne copyOnSelect (Zustand VOR dieser Session) landet eine Markierung aber NICHT automatisch in der Zwischenablage; der naheliegende Reflex Strg+C loest bei xterm.js ohne Markierung SIGINT aus (unveraendertes Standardverhalten) -- aus Nutzersicht sieht das exakt so aus wie "Markieren geht nicht", obwohl technisch nur der Kopiervorgang danach fehlte. Genau das behebt copyOnSelect: true (Abschnitt 1 oben): Ziehen MARKIERT UND kopiert in einem Schritt, ganz ohne Strg+C.

Zusaetzlich ergaenzt, unabhaengig vom obigen Verdacht: ein title-Tooltip auf .session-terminal erklaert das (bereits vorher bestehende, unveraenderte) Shift+Ziehen-Verhalten fuer den Fall, dass doch einmal ein Vollbild-Programm mit eigener Maussteuerung im Spiel ist -- rein zusaetzliche Dokumentation direkt in der Oberflaeche, kein neuer Codepfad, kein Risiko fuer bestehendes Verhalten.

Weiterhin nicht abschliessend geklaert ohne Live-Test: ob copyOnSelect: true das urspruenglich beschriebene Symptom tatsaechlich vollstaendig behebt, kann nur der Nutzer selbst durch Ausprobieren bestaetigen (Text im Terminal markieren, dann an anderer Stelle einfuegen -- sollte jetzt ohne Strg+C funktionieren). Sollte es weiterhin nicht gehen, ist das ein Hinweis auf eine andere, noch nicht gefundene Ursache und braucht eine zweite Runde.