Files
Laufzettel/tests/platzhalter-anzeige.test.tsx
T
Florian WildeandClaude Opus 5 b59fbb4645
Prüfung / pruefung (pull_request) Canceled after 0s
Prüfung / pruefung (push) Canceled after 0s
Prüfung / e2e (pull_request) Canceled after 0s
Prüfung / e2e (push) Canceled after 0s
feat(platzhalter): Aufloesung, Namensregeln und Shell-Maskierung
Aus einer Vorlage wird hier der PowerShell-Befehl, den ein Techniker kopiert und
auf einem Domaenencontroller ausfuehrt. Entsprechend dicht ist die Abdeckung:
810 Tests, und die Maskierung wird **ausgefuehrt** geprueft, nicht gelesen.

- Platzhalter aus Geraet, Lauf, Mitarbeiter und Abteilung, dazu die Variablen
  der Vorlage; ein unbekannter bleibt sichtbar stehen
- `{{ou}}` ist der Eltern-Container des DN — genau der Pfad fuer New-ADUser
- Namensregeln fuer upn, sam, mail und hostname mit Transliteration, `{v}`,
  `{nnn}` und `{kundenkuerzel}`; Kundenregeln ueberlagern die globalen feldweise
- Maskierung je Shell, Skript-Kopie je Abschnitt mit Kommentarzeilen
- `{{generiertes_passwort}}`: 20 Zeichen, bei jeder Anzeige neu, nirgends
  gespeichert; im Druck `[Passwort – nicht gespeichert]`

Der unabhaengige Review fand drei Blocker. Der schwerste war eine
Codeausfuehrung: PowerShell liest auch `“`, `”` und `„` als Begrenzer einer
Zeichenkette, die Maskierung kannte nur das ASCII-Zeichen. Ein Abteilungsname
aus Word — Word wandelt `"` von selbst um — reichte. Gefunden hat es der Review
nicht durch Lesen, sondern indem er sich PowerShell in einen Container geholt
und die Markierungsdatei hat entstehen lassen. Der Test dagegen hatte sich still
uebersprungen; er nimmt jetzt selbst das Abbild und bricht in CI ab.

Dazu: zwei verschiedene Passwoerter in einer Antwort (Anzeige und Skript wurden
getrennt gefuellt), eine Vorlagenvariable, die `{{techniker}}` und `{{kunde}}`
ueberschreiben konnte, und die tragende Annahme der Maskierung — dass der
Platzhalter in Anfuehrungszeichen steht —, die niemand geprueft hat.

Schliesst #012

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-25 15:29:43 +02:00

52 lines
2.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { renderToStaticMarkup } from "react-dom/server";
import { describe, expect, it } from "vitest";
import { Markdown } from "@/components/markdown";
import { platzhalterErsetzen } from "@/lib/platzhalter-werte";
/**
* Was von einem ersetzten Wert wirklich im HTML ankommt (#012).
*
* Das Akzeptanzkriterium lautet: „ein Wert mit `<script>` landet HTML-escaped in
* der Anzeige“. Erfuellt wird es von React und nicht von eigenem Code — und
* genau deshalb steht hier ein Test, der das **am gerenderten HTML** nachweist.
* Eine eigene Maskierung waere doppelt und machte aus „Forschung & Entwicklung“
* sichtbar „Forschung &amp;amp; Entwicklung“.
*/
describe("Ersetzter Wert im gerenderten HTML", () => {
it("maskiert ein script-Element im Schritt-Titel", () => {
const { text } = platzhalterErsetzen("Konto für {{mitarbeiter}} anlegen", {
mitarbeiter: "<script>alert(1)</script>",
});
const html = renderToStaticMarkup(<h3>{text}</h3>);
expect(html).not.toContain("<script>");
expect(html).toContain("&lt;script&gt;");
});
it("zeigt ein Ampersand als Zeichen, nicht als Entität", () => {
// Der alltaegliche Fall – „Forschung & Entwicklung“ ist ein Abteilungsname
// und kein Angriff. Doppelt maskiert stuende hier „&amp;amp;“.
const { text } = platzhalterErsetzen("{{abteilung}}", {
abteilung: "Forschung & Entwicklung",
});
const html = renderToStaticMarkup(<p>{text}</p>);
expect(html).toBe("<p>Forschung &amp; Entwicklung</p>");
});
it("lässt ein script-Element auch im Hilfetext nicht durch", () => {
// Der Hilfetext geht durch Markdown, das ebenfalls maskiert und rohes HTML
// fallen laesst (#010).
const { text } = platzhalterErsetzen("Hinweis für {{mitarbeiter}}", {
mitarbeiter: "<img src=x onerror=alert(1)>",
});
const html = renderToStaticMarkup(<Markdown text={text} />);
expect(html).not.toContain("<img");
expect(html).toContain("&lt;img");
});
it("zeigt einen unbekannten Platzhalter sichtbar an", () => {
const { text } = platzhalterErsetzen("{{tipfehler}} prüfen", {});
expect(renderToStaticMarkup(<span>{text}</span>)).toBe("<span>{{tipfehler}} prüfen</span>");
});
});