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>
This commit is contained in:
co-authored by
Claude Opus 5
parent
f85bc87324
commit
b59fbb4645
@@ -150,6 +150,32 @@ Die Content-Security-Policy in `src/proxy.ts` ist deshalb zweigeteilt: im Entwic
|
||||
Prüfung der Funktion mit selbst gesetztem Argument hätte genau den Fehler durchgelassen, vor dem
|
||||
der Kommentar darüber warnt.
|
||||
|
||||
## 5c. Warum die Maskierung dort sitzt, wo sie sitzt
|
||||
|
||||
Aus einer Vorlage entsteht ein PowerShell-Befehl, den ein Techniker kopiert und auf einem
|
||||
Domänencontroller ausführt. Drei Entscheidungen tragen das (#012):
|
||||
|
||||
1. **Maskiert wird der eingesetzte Wert, nicht der fertige Befehl.** Sonst maskierte man die
|
||||
Anführungszeichen des Befehls gleich mit. `platzhalterErsetzen` bekommt die Maskierung deshalb
|
||||
als Funktion und wendet sie auf jeden Wert einzeln an.
|
||||
2. **Die Maskierung setzt keine Anführungszeichen dazu** — die stehen in der Vorlage. Das ist eine
|
||||
Annahme, und sie wird geprüft: steht ein `{{platzhalter}}` in einem `BEFEHL`-Schritt nicht
|
||||
zwischen den erwarteten Anführungszeichen, wird der Schritt abgewiesen statt gefüllt. Ein halb
|
||||
maskierter Befehl zum Kopieren wäre die gefährlichste aller Antworten.
|
||||
3. **Was sich nicht sicher maskieren lässt, wird abgelehnt.** Für Cmd gibt es keine Regel, die ein
|
||||
`"` in jedem Fall richtig behandelt — ein solcher Wert bekommt eine Fehlermeldung, keinen
|
||||
Befehl. PowerShell liest auch `“`, `”` und `„` als Begrenzer; die werden auf ihre ASCII-Form
|
||||
gebracht und dann maskiert, weil sie sich als Text ohnehin nicht durchreichen lassen.
|
||||
|
||||
Geprüft wird das **ausgeführt**, nicht gelesen: `tests/shell.test.ts` schickt bösartige Werte durch
|
||||
ein echtes `bash -c` und durch ein echtes PowerShell — und wenn auf der Maschine kein `pwsh` liegt,
|
||||
nimmt der Test das Container-Abbild. In CI bricht er ab, statt sich zu überspringen; ein stilles
|
||||
Überspringen sieht genauso grün aus wie ein bestandener Lauf.
|
||||
|
||||
Das **generierte Passwort** entsteht erst beim Ersetzen und wird nirgends zurückgegeben — es gibt
|
||||
keinen Weg, es außerhalb des fertigen Texts zu bekommen. Gäbe es ein Feld dafür, wäre die
|
||||
Versuchung groß, es „nur fürs Protokoll“ mitzuschreiben.
|
||||
|
||||
## 6. Betrieb: zwei Wege, eine Regel
|
||||
|
||||
Die Datenbank ist auf beiden Wegen von außen nicht erreichbar:
|
||||
|
||||
+55
-2
@@ -14,11 +14,12 @@ als Unterpunkte mit Aufgabe und Dauer.
|
||||
| #009 | M4 – Vorlagen-Versionierung im Service | 1 h 20 min |
|
||||
| #010 | M5 – Vorlagen-Editor | 2 h 10 min |
|
||||
| #011 | M6 – Mitarbeiter, Abteilungen, Geräte | 2 h 15 min |
|
||||
| | **Summe** | **11 h 15 min** |
|
||||
| #012 | M7 – Platzhalter, Namensregeln, Escaping | 2 h 30 min |
|
||||
| | **Summe** | **13 h 45 min** |
|
||||
|
||||
| Meilenstein | Tatsächlich | Status |
|
||||
|---|---|---|
|
||||
| v0.1.0 | 11 h 15 min | in Arbeit |
|
||||
| v0.1.0 | 13 h 45 min | in Arbeit |
|
||||
|
||||
---
|
||||
|
||||
@@ -452,3 +453,55 @@ als Unterpunkte mit Aufgabe und Dauer.
|
||||
|
||||
- #012 Platzhalter, Namensregeln und Escaping: die reine Service-Schicht, aus der die fertigen
|
||||
PowerShell-Befehle entstehen — mit dichter Testabdeckung, denn hier darf nichts schiefgehen.
|
||||
|
||||
---
|
||||
|
||||
## 2026-09-25 · Sitzung 8 · 12:55–15:35
|
||||
|
||||
**Issue:** #012 (M7)
|
||||
**Version:** 0.1.0
|
||||
|
||||
### Erledigt
|
||||
|
||||
- `src/lib/shell.ts` (Sub-Agent): Maskierung je Shell, Skript-Kopie mit Kommentarzeilen
|
||||
- `src/lib/namensregeln.ts` (Sub-Agent): Transliteration, `{v}`, `{nnn}`, `{kundenkuerzel}`,
|
||||
Grenzen für `sam` und `hostname`
|
||||
- `src/lib/passwort.ts`, `src/lib/platzhalter-werte.ts`, `src/server/services/platzhalter.ts` und
|
||||
`src/server/services/befehle.ts`: das generierte Passwort, das Ersetzen und die fertigen Befehle
|
||||
- Unabhängiger Review: **drei Blocker**, sieben weitere Befunde, alle abgearbeitet
|
||||
- **810 Tests** grün (vorher 797 mit einem stillen Übersprung)
|
||||
|
||||
### Entscheidungen
|
||||
|
||||
- **PowerShell wird wirklich gegen PowerShell geprüft.** Auf dieser Maschine gibt es kein `pwsh`,
|
||||
also nimmt der Test das Container-Abbild — und in CI bricht er ab, statt sich zu überspringen.
|
||||
Der schwerste Befund des Meilensteins wäre ohne das nie aufgefallen.
|
||||
- **Typografische Anführungszeichen werden auf ihre ASCII-Form gebracht und dann maskiert.** Als
|
||||
Text durchreichen lassen sie sich nicht: PowerShell liest sie ohnehin als Begrenzer. Lieber ein
|
||||
Zeichen, das anders aussieht als gedacht, als ein Befehl, der etwas anderes tut als er liest.
|
||||
- **Ein Platzhalter außerhalb von Anführungszeichen wird abgewiesen**, nicht gefüllt. Die
|
||||
Maskierung kann daraus kein Argument machen, und ein halb maskierter Befehl zum Kopieren wäre die
|
||||
gefährlichste aller Antworten.
|
||||
- **Gesperrt sind fünf Variablennamen, nicht alle eingebauten.** `techniker`, `datum`, `kunde`,
|
||||
`kundenkuerzel` und `generiertes_passwort` machen eine Aussage über wer, wann und für wen. `upn`,
|
||||
`sam` und `mail` bleiben frei — beim Onboarding gibt es die Person im Verzeichnis noch nicht.
|
||||
- **Ein `sam` hält drei Stellen frei.** Ein einmal notierter Anmeldename muss auch morgen aus der
|
||||
Regel herausfallen, wenn ein zweiter Träger desselben Namens dazukommt.
|
||||
|
||||
### Probleme
|
||||
|
||||
- **Ich bin in genau die Falle getappt, die ich in #010 repariert habe:** `$'` als Rückverweis in
|
||||
`String.replaceAll`. Gefunden hat es mein eigener Test gegen echtes Bash — Lesen hätte es nicht
|
||||
gefunden.
|
||||
- **Meine erste HTML-Maskierung war doppelt**, weil ich vergessen hatte, dass React Textknoten
|
||||
selbst maskiert. Aufgefallen beim Nachdenken über den Test, nicht beim Schreiben des Codes.
|
||||
- **Der Review hat sich PowerShell in einem Container geholt.** Das ist der Unterschied zwischen
|
||||
„nach bestem Wissen richtig“ und „gemessen“ — und der Unterschied war hier eine
|
||||
Codeausführung auf dem Domänencontroller.
|
||||
- **Vierter Meilenstein in Folge mit einem Kommentar, der ins Leere zeigt:** ein Testkommentar
|
||||
verwies auf einen Beweis in einer anderen Datei, den es dort nicht gab.
|
||||
|
||||
### Nächster Schritt
|
||||
|
||||
- #013 Läufe: Service und Abarbeitung — Lauf anlegen, abarbeiten, abschließen, archivieren, mit
|
||||
der Abarbeitungsansicht am Handy.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# #012 M7 – Platzhalter, Namensregeln, Escaping
|
||||
|
||||
**Status:** offen
|
||||
**Status:** erledigt
|
||||
**Meilenstein:** v0.1.0
|
||||
**Labels:** phase-1, backend, security
|
||||
**Geschätzt:** 8 h **Tatsächlich:** –
|
||||
**Geschätzt:** 8 h **Tatsächlich:** 2 h 30 min
|
||||
|
||||
## Ziel
|
||||
|
||||
@@ -11,26 +11,91 @@ Reine, framework-freie Service-Schicht mit dichter Testabdeckung. Hier entstehen
|
||||
|
||||
## Aufgaben
|
||||
|
||||
- [ ] Platzhalter-Auflösung `{{key}}` in Schritt-Titel, Hilfetext und Befehlstext
|
||||
- [ ] Eingebaut aus Gerät und Lauf: `{{hostname}}`, `{{seriennummer}}`, `{{inventarnummer}}`, `{{ticket}}`, `{{datum}}`, `{{techniker}}`; im Modus `DIENSTLEISTUNG` zusätzlich `{{kunde}}` und `{{kundenkuerzel}}`, im Modus `INTERN` leer
|
||||
- [ ] Eingebaut aus dem Mitarbeiter: `{{mitarbeiter}}` (Alias `{{benutzer}}`), `{{vorname}}`, `{{nachname}}`, `{{upn}}`, `{{sam}}`, `{{mail}}`, `{{abteilung}}`, `{{titel}}`, `{{vorgesetzter}}`, `{{vorgesetzter_sam}}`, `{{dn}}`, `{{ou}}`
|
||||
- [ ] Aus der gewählten Abteilung: `{{ou}}` falls nicht vom Mitarbeiter, `{{gruppen}}` kommagetrennt
|
||||
- [ ] `{{ou}}` eines synchronisierten Mitarbeiters ist der Eltern-Container seines `dn` (alles nach dem ersten Komma)
|
||||
- [ ] Dazu die in der Vorlage definierten Variablen; unbekannte Platzhalter bleiben sichtbar stehen
|
||||
- [ ] `{{generiertes_passwort}}`: 20 Zeichen aus Groß, Klein, Ziffern und Sonderzeichen ohne verwechselbare Zeichen, je Anzeige neu erzeugt, **nie** gespeichert; Hinweis „Passwort sofort in den Passwortmanager übernehmen“; in der Druckansicht `[Passwort – nicht gespeichert]`
|
||||
- [ ] Namensregeln (`Einstellung.namensregeln` global, im Modus `DIENSTLEISTUNG` zusätzlich `Kunde.namensregeln` mit Fallback): `upn`, `sam` (höchstens 20 Zeichen), `mail`, `hostname`
|
||||
- [ ] Transliteration (ä→ae, ö→oe, ü→ue, ß→ss, Akzente entfernt), Kleinschreibung, Leerzeichen entfernt, `{nnn}`-Zähler, `{v}` für den ersten Buchstaben des Vornamens, `{kundenkuerzel}`
|
||||
- [ ] Shell-Escaping je Shell: PowerShell (doppelte Anführungszeichen verdoppeln, Backtick maskieren), Bash, Cmd
|
||||
- [ ] HTML-Escaping ersetzter Werte; Markdown im Hilfetext sicher gerendert, kein rohes HTML
|
||||
- [ ] „Alle Befehle als Skript kopieren“ je Abschnitt: alle `BEFEHL`-Schritte in Reihenfolge, getrennt durch Leerzeilen, je mit `# Schritt: <Titel>` davor, Zeilenumbruch passend zur Shell
|
||||
- [x] Platzhalter-Auflösung `{{key}}` in Schritt-Titel, Hilfetext und Befehlstext
|
||||
- [x] Eingebaut aus Gerät und Lauf: `{{hostname}}`, `{{seriennummer}}`, `{{inventarnummer}}`, `{{ticket}}`, `{{datum}}`, `{{techniker}}`; im Modus `DIENSTLEISTUNG` zusätzlich `{{kunde}}` und `{{kundenkuerzel}}`, im Modus `INTERN` leer
|
||||
- [x] Eingebaut aus dem Mitarbeiter: `{{mitarbeiter}}` (Alias `{{benutzer}}`), `{{vorname}}`, `{{nachname}}`, `{{upn}}`, `{{sam}}`, `{{mail}}`, `{{abteilung}}`, `{{titel}}`, `{{vorgesetzter}}`, `{{vorgesetzter_sam}}`, `{{dn}}`, `{{ou}}`
|
||||
- [x] Aus der gewählten Abteilung: `{{ou}}` falls nicht vom Mitarbeiter, `{{gruppen}}` kommagetrennt
|
||||
- [x] `{{ou}}` eines synchronisierten Mitarbeiters ist der Eltern-Container seines `dn` (alles nach dem ersten Komma)
|
||||
- [x] Dazu die in der Vorlage definierten Variablen; unbekannte Platzhalter bleiben sichtbar stehen
|
||||
- [x] `{{generiertes_passwort}}`: 20 Zeichen aus Groß, Klein, Ziffern und Sonderzeichen ohne verwechselbare Zeichen, je Anzeige neu erzeugt, **nie** gespeichert; Hinweis „Passwort sofort in den Passwortmanager übernehmen“; in der Druckansicht `[Passwort – nicht gespeichert]`
|
||||
- [x] Namensregeln (`Einstellung.namensregeln` global, im Modus `DIENSTLEISTUNG` zusätzlich `Kunde.namensregeln` mit Fallback): `upn`, `sam` (höchstens 20 Zeichen), `mail`, `hostname`
|
||||
- [x] Transliteration (ä→ae, ö→oe, ü→ue, ß→ss, Akzente entfernt), Kleinschreibung, Leerzeichen entfernt, `{nnn}`-Zähler, `{v}` für den ersten Buchstaben des Vornamens, `{kundenkuerzel}`
|
||||
- [x] Shell-Escaping je Shell: PowerShell (doppelte Anführungszeichen verdoppeln, Backtick maskieren), Bash, Cmd
|
||||
- [x] HTML-Escaping ersetzter Werte; Markdown im Hilfetext sicher gerendert, kein rohes HTML
|
||||
- [x] „Alle Befehle als Skript kopieren“ je Abschnitt: alle `BEFEHL`-Schritte in Reihenfolge, getrennt durch Leerzeilen, je mit `# Schritt: <Titel>` davor, Zeilenumbruch passend zur Shell
|
||||
|
||||
## Akzeptanzkriterien
|
||||
|
||||
- [ ] Test: „Jörg Müller-Lüdenscheid“ ergibt `joerg.mueller-luedenscheid@firma.de` und `jmuellerluedensch` (auf 20 Zeichen gekürzt)
|
||||
- [ ] Test: `{{generiertes_passwort}}` erscheint im angezeigten Befehl, steht aber nicht in `Schrittergebnis`, `Kommentar`, `Protokoll` oder der Druckansicht; zwei Aufrufe liefern verschiedene Passwörter
|
||||
- [ ] Test: ein Wert mit `"` und `` ` `` wird für PowerShell korrekt maskiert und bricht den Befehl nicht
|
||||
- [ ] Test: unbekannte Platzhalter bleiben als `{{key}}` stehen
|
||||
- [ ] Test: `{{ou}}` wird aus `CN=Maria Huber,OU=Buchhaltung,OU=Benutzer,DC=firma,DC=local` zu `OU=Buchhaltung,OU=Benutzer,DC=firma,DC=local`
|
||||
- [ ] Test: Skript-Kopie liefert die Befehle in Abschnittsreihenfolge mit Kommentarzeilen
|
||||
- [ ] Test: im Modus `INTERN` sind `{{kunde}}` und `{{kundenkuerzel}}` leer
|
||||
- [ ] Test: ein Wert mit `<script>` landet HTML-escaped in der Anzeige
|
||||
- [x] Test: „Jörg Müller-Lüdenscheid“ ergibt `joerg.mueller-luedenscheid@firma.de` und `jmuellerluedensch` (17 Zeichen — siehe unten, die Klammer im ursprünglichen Text war falsch)
|
||||
- [x] Test: `{{generiertes_passwort}}` erscheint im angezeigten Befehl, steht aber nicht in `Schrittergebnis`, `Kommentar`, `Protokoll` oder der Druckansicht; zwei Aufrufe liefern verschiedene Passwörter
|
||||
- [x] Test: ein Wert mit `"` und `` ` `` wird für PowerShell korrekt maskiert und bricht den Befehl nicht
|
||||
- [x] Test: unbekannte Platzhalter bleiben als `{{key}}` stehen
|
||||
- [x] Test: `{{ou}}` wird aus `CN=Maria Huber,OU=Buchhaltung,OU=Benutzer,DC=firma,DC=local` zu `OU=Buchhaltung,OU=Benutzer,DC=firma,DC=local`
|
||||
- [x] Test: Skript-Kopie liefert die Befehle in Abschnittsreihenfolge mit Kommentarzeilen
|
||||
- [x] Test: im Modus `INTERN` sind `{{kunde}}` und `{{kundenkuerzel}}` leer
|
||||
- [x] Test: ein Wert mit `<script>` landet HTML-escaped in der Anzeige
|
||||
|
||||
## Beim Bauen aufgefallen
|
||||
|
||||
- **PowerShell liest auch typografische Anführungszeichen als Begrenzer.** `“`, `”` und `„`
|
||||
beenden eine `"…"`-Zeichenkette genauso wie `"`, `‘`, `’`, `‚` und `‛` eine `'…'`-Zeichenkette.
|
||||
Die Maskierung kannte nur das ASCII-Zeichen. Ein Abteilungsname, den jemand aus Word kopiert —
|
||||
Word wandelt `"` von selbst in `“`/`”` —, führte damit fremden Code auf dem Domänencontroller
|
||||
aus. Der Review hat es nicht gelesen, sondern gemessen: er hat sich PowerShell 7 in einem
|
||||
Container geholt und die Markierungsdatei entstehen lassen. Behoben und mit demselben Aufbau
|
||||
gegengeprüft: alle Werte kommen unverändert an, nichts wird ausgeführt.
|
||||
- **Der pwsh-Test hat sich still übersprungen** — dieselbe Falle, die `tests/helpers/db.ts` für die
|
||||
Datenbank längst zugemacht hat: ein stilles Überspringen sieht genauso grün aus wie ein
|
||||
bestandener Lauf. Er nimmt jetzt das Container-Abbild, wenn kein `pwsh` da ist, und bricht in CI
|
||||
ab, statt zu schweigen.
|
||||
- **Zwei verschiedene Passwörter in einer Antwort.** `befehleDesLaufs` hat jeden Schritt zweimal
|
||||
gefüllt — einmal für die Anzeige, einmal für das Skript —, und weil das Passwort bei jedem Füllen
|
||||
neu entsteht, standen auf dem Bildschirm und in der Kopie verschiedene. Der Techniker legt das
|
||||
Konto mit dem einen an und nennt dem Benutzer das andere; weil nichts gespeichert wird, ist es
|
||||
danach unwiederbringlich.
|
||||
- **Eine Vorlagenvariable konnte jeden eingebauten Platzhalter überschreiben.** Eine Variable
|
||||
`techniker` ließ einen Befehl mit fremdem Namen unterschreiben, eine Variable `kunde` holte im
|
||||
Modus Intern die Kundenebene zurück, die es dort ausdrücklich nicht gibt. Gesperrt sind jetzt
|
||||
genau fünf Namen; alles andere bleibt erlaubt, weil eine Onboarding-Vorlage nach `upn`, `sam` und
|
||||
`mail` fragen können muss — die Person steht ja noch nicht im Verzeichnis.
|
||||
- **Die tragende Annahme der Maskierung hat niemand geprüft.** Sie gilt nur, wenn der Platzhalter
|
||||
in der Vorlage in Anführungszeichen steht; `Write-Host {{vorname}}` lässt sich nicht sicher
|
||||
füllen. Das stand als Kommentar da und wurde nirgends eingelöst — der Review hat mit
|
||||
`$(touch …)` eine Datei entstehen lassen. Jetzt wird es geprüft und der Schritt abgewiesen.
|
||||
- **Ich bin selbst in die Falle getappt, die ich in #010 repariert habe:** `$'` ist in
|
||||
`String.replaceAll` ein Rückverweis auf alles hinter dem Treffer. Aus `erste\nzweite` wurde
|
||||
`erste'zweite\n''zweite` — ein Befehl, der nicht mehr zugeht. Gefunden hat es mein eigener Test,
|
||||
der gegen echtes Bash läuft.
|
||||
- **Meine erste HTML-Maskierung war doppelt gemoppelt.** React maskiert Textknoten selbst; eine
|
||||
zweite Maskierung hätte „Forschung & Entwicklung“ sichtbar gemacht. Der Beweis für das
|
||||
Akzeptanzkriterium steht jetzt am gerenderten HTML statt in einer eigenen Funktion.
|
||||
- **`{{ou}}` bei einem maskierten Backslash im DN.** `CN=X\\,OU=Y` ist ein literaler Backslash im
|
||||
Wert, das Komma **ist** der Trenner — die Vorausschau hielt es für maskiert, lieferte nichts, und
|
||||
der Aufrufer fiel still auf die Ziel-OU der Abteilung zurück. Das Konto wäre in der falschen OU
|
||||
gelandet.
|
||||
- **Hostnamen, die Windows ablehnt:** ein Nachname „Con“ reichte, damit die Regel `CON`
|
||||
vorschlägt — das ist die MS-DOS-Konsole. Reservierte Gerätenamen und rein numerische Namen
|
||||
liefern jetzt keinen Vorschlag.
|
||||
- **`!` ist aus dem Passwort geflogen**, und ein Passwort fängt nie mit `= + - @` an: bei
|
||||
eingeschalteter verzögerter Ersetzung frisst `cmd.exe` das `!` still, und die vier anderen liest
|
||||
eine Tabellenkalkulation am Zellenanfang als Formel.
|
||||
|
||||
## Eine Entscheidung, die den Auftragstext korrigiert
|
||||
|
||||
Das Akzeptanzkriterium nennt `jmuellerluedensch` und setzt in Klammern „auf 20 Zeichen gekürzt“.
|
||||
Der Name hat **17** Zeichen, und beides zusammen geht nur mit zwei Regeln, die so jetzt im Code
|
||||
stehen: der Bindestrich fällt im `sam` weg, und eine Regel ohne eigenen `{nnn}` hält drei Stellen
|
||||
für die spätere Unterscheidung frei (20 − 3 = 17). Der Vorrat wird **immer** zurückgehalten, auch
|
||||
solange der Name frei ist — sonst hieße derselbe Mensch `jmuellerluedenscheid`, solange er der
|
||||
Erste ist, und `jmuellerluedensch2`, sobald ein Zweiter dazukommt. Ein einmal notierter
|
||||
Anmeldename muss auch morgen noch aus der Regel herausfallen.
|
||||
|
||||
## Offen geblieben
|
||||
|
||||
- **Cmd ist nur begründet, nicht gemessen.** Es gibt hier kein Windows. Die Ablehnung von `"`, CR,
|
||||
LF und Null-Byte und die Bewertung von `%` und `!` stehen als Begründung im Code — mit dem
|
||||
ausdrücklichen Hinweis, was daran Annahme ist.
|
||||
- **Kein Aufrufer.** Dieser Meilenstein hat keine Oberfläche; die Funktionen werden von #013
|
||||
(Abarbeitung) und #014 (Geräteseite) gerufen. Der Hinweis „Passwort sofort in den
|
||||
Passwortmanager übernehmen“ und die Druckansicht `[Passwort – nicht gespeichert]` sind damit
|
||||
vorbereitet, aber noch nirgends zu sehen.
|
||||
|
||||
@@ -16,7 +16,7 @@ Diese Dateien sind die Quelle; `pnpm issues:sync` spiegelt sie in den Gitea-Trac
|
||||
| #009 | [M4 – Vorlagen-Versionierung im Service](009-vorlagen-versionierung.md) | erledigt | v0.1.0 | 6 h | 1 h 20 min |
|
||||
| #010 | [M5 – Vorlagen-Editor](010-vorlagen-editor.md) | erledigt | v0.1.0 | 10 h | 2 h 10 min |
|
||||
| #011 | [M6 – Mitarbeiter, Abteilungen, Geräte](011-mitarbeiter-abteilungen-geraete.md) | erledigt | v0.1.0 | 9 h | 2 h 15 min |
|
||||
| #012 | [M7 – Platzhalter, Namensregeln, Escaping](012-platzhalter-namensregeln.md) | offen | v0.1.0 | 8 h | – |
|
||||
| #012 | [M7 – Platzhalter, Namensregeln, Escaping](012-platzhalter-namensregeln.md) | erledigt | v0.1.0 | 8 h | 2 h 30 min |
|
||||
| #013 | [M8 – Läufe: Service und Abarbeitung](013-laeufe-abarbeitung.md) | offen | v0.1.0 | 14 h | – |
|
||||
| #014 | [M9 – QR-Tags, Geräteseite, Etiketten, Scanner](014-qr-tags-geraeteseite.md) | offen | v0.1.0 | 12 h | – |
|
||||
| #015 | [M10 – Dashboard und Archiv](015-dashboard-archiv.md) | offen | v0.1.0 | 8 h | – |
|
||||
@@ -24,4 +24,4 @@ Diese Dateien sind die Quelle; `pnpm issues:sync` spiegelt sie in den Gitea-Trac
|
||||
| #017 | [M12 – Seed der sieben Vorlagen](017-seed-sieben-vorlagen.md) | offen | v0.1.0 | 8 h | – |
|
||||
| #018 | [M13 – E2E-Tests, Anleitung, PDF](018-e2e-anleitung-pdf.md) | offen | v0.1.0 | 14 h | – |
|
||||
| #019 | [M14 – Betriebswege und Release v0.1.0](019-betrieb-release.md) | offen | v0.1.0 | 10 h | – |
|
||||
| | **Summe** | | | **136 h** | **11 h 15 min** |
|
||||
| | **Summe** | | | **136 h** | **13 h 45 min** |
|
||||
|
||||
Reference in New Issue
Block a user