[Härtung R3] Stempel-Leak (µs), ReDoS, ISO-Datum-Leak, Range-Regression (Fable 2026-07-11) #3

Closed
opened 2026-07-11 11:56:12 +02:00 by vr6syncro · 1 comment
Owner

Aus einem Fable-Bug-Hunt (2026-07-11, Runde 3) — alle Funde am extrahierten Live-Core ausgeführt (belegt, Beweisskript), nicht nur gelesen. Betrifft index.html. Für keinen Fund existiert bisher ein Testfall in test/run.mjs.

1 — 🔴 HOCH — Stempel-Leak über Mikrosekunden-Zeitstempel

index.html:326 TIME_SRC ms-Gruppe (\d{1,3}) frisst max. 3 Ziffern; bei 4+ Nachkommastellen (µs-Logs ,696123) bleibt der Rest nach dem Blanking (:286) stehen:

  • parseZE("Zeitaufwand (ZE):\n12:17:36,696123 - 12:20:04,305999")123 (statt null)
  • …,6961 - …,30521
    Autofill schreibt den Phantomwert ins ZE-Soll, render() rechnet still falsch. (Der Offset-Strip in :255 erlaubt bereits [.,]\d{1,9} — Inkonsistenz ist das Loch.)
    Fix: ms-Gruppe (\d{1,3})(\d{1,9}); stampParts (:340) sliced ohnehin auf 3.

2 — 🟠 MITTEL-HOCH — ReDoS: parseDecimal quadratisch, belegter 30-s-Freeze

index.html:272 /^[+-]?\d*\.?\d+$/ backtrackt O(n²) bei langem Ziffernblock ohne $-Match. Gemessen: 100k→3,1 s, 200k→13 s, 300k→30 s. Erreichbar via Paste (autofillFromPasteparseZE→Fallback parseDecimal(gesamter Text), :318) — z. B. Zahlenkolonne aus Excel/CSV. Renderer-Thread friert.
Fix: Regex linearisieren /^[+-]?(?:\d+(?:\.\d+)?|\.\d+)$/ + Längen-Guard (if (s.length>40) return null).

3 — 🟠 MITTEL — ISO-Datum ohne Uhrzeit hinter verwaistem Label → ZE = 2026

Blanking (:286) greift nur mit Uhrzeit (STAMP_SRC = Zeit Pflicht). Reines ISO-Datum bleibt, GAP liest die Jahreszahl als ZE:

  • parseZE("Zeitaufwand (ZE):\n2026-07-07 Sichtpruefung")2026
  • parseZE("Zeiteinheiten:\n( 2026-07-07 - 2026-07-08 )")2026
    (Deutsche 10.03.2026 ist zufällig geschützt — parseDecimal lehnt Mehrfach-Punkte ab; nur ISO leakt.)
    Fix: nach Stempel-Blanking auch nackte Daten blanken: s.replace(new RegExp(DATE_SRC,"g")," ").

4 — 🟠 MITTEL — ISO-Offset-Strip frisst kompakte Zeitranges (Regression aus Runde 2)

index.html:255 Lookahead (?![:.]?\d) schützt nur Ranges mit Sekunden im End-Stempel:

  • normalizeText("12:17-12:20")"12:17"parseRangenull (vorher matchte RE_RANGE)
  • 08:00-16:30null; En-Dash 12:17–12:20 ebenso; 12:17:36,696-12:20,305"12:17:36,696,305" (Lookahead kennt kein Komma)
    Wirkung „laut" (leere Felder), aber HH:MM-HH:MM ist plausible Kompakt-Notation und funktionierte vor dem ISO-TZ-Fix.
    Fix: Offset-Strip nur hinter Pflicht-Sekunden; Lookahead auf (?![:.,]?\d).

5 — 🟡 MITTEL — Autofill-Carry-over zwischen zwei Pastes (alter ZE-Soll bleibt still)

index.html:608-620: Autofill überschreibt nur bei Fund, löscht nie. Paste A (ZE 4,5) → Paste B ohne ZE-Zeile → ze.value bleibt 4,5, render() zeigt frisches, nicht-stale Ergebnis mit altem Soll. Analog anfang/ende.
Fix: nicht-dirty Felder bei Paste ohne Fund zurücksetzen bzw. Stale-Markierung statt Voll-Render.

6/7/8 — 🟢 NIEDRIG (belegt)

  • BiDi-Steuerzeichen fehlen in Zero-Width-Klasse (:248): parseDateTime("1‎2:17:36")"02:17:36" (2 statt 12 h). Fix: ‎‏؜⁦-⁩ ergänzen.
  • Autofill schreibt Exponentialnotation (:619): 0,0000001"1e-7" → Re-Parse null, Wert weg.
  • Anzeige „-0,00 ZE" bei winzig-negativer Zeit (:447-453).

Geprüft & sauber: kein exponentielles Backtracking in RE_STAMP/RE_RANGE/Tag-Strip/Entity-Decode; Space-/Dash-Klassen vollständig; 24:00/Schaltsekunde/Fullwidth/30.02./Monat13 → laut null (kein Silent-Wrong); DST-Freiheit (UTC), Gruppen-Offsets, Mitternachts-Vererbung korrekt.

Empfohlene Reihenfolge: 1 → 2 → 3 → 4 (1+3 = Fix-Familie „Blanking dicht machen"), dann 5, dann 6–8.

Aus einem **Fable-Bug-Hunt (2026-07-11, Runde 3)** — alle Funde **am extrahierten Live-Core ausgeführt (belegt, Beweisskript)**, nicht nur gelesen. Betrifft `index.html`. Für keinen Fund existiert bisher ein Testfall in `test/run.mjs`. ## 1 — 🔴 HOCH — Stempel-Leak über Mikrosekunden-Zeitstempel `index.html:326` `TIME_SRC` ms-Gruppe `(\d{1,3})` frisst max. 3 Ziffern; bei 4+ Nachkommastellen (µs-Logs `,696123`) bleibt der Rest nach dem Blanking (`:286`) stehen: - `parseZE("Zeitaufwand (ZE):\n12:17:36,696123 - 12:20:04,305999")` → **`123`** (statt `null`) - `…,6961 - …,3052` → **`1`** Autofill schreibt den Phantomwert ins ZE-Soll, `render()` rechnet still falsch. (Der Offset-Strip in `:255` erlaubt bereits `[.,]\d{1,9}` — Inkonsistenz ist das Loch.) **Fix:** ms-Gruppe `(\d{1,3})`→`(\d{1,9})`; `stampParts` (`:340`) sliced ohnehin auf 3. ## 2 — 🟠 MITTEL-HOCH — ReDoS: `parseDecimal` quadratisch, belegter 30-s-Freeze `index.html:272` `/^[+-]?\d*\.?\d+$/` backtrackt O(n²) bei langem Ziffernblock ohne `$`-Match. Gemessen: 100k→3,1 s, 200k→13 s, 300k→**30 s**. Erreichbar via Paste (`autofillFromPaste`→`parseZE`→Fallback `parseDecimal(gesamter Text)`, `:318`) — z. B. Zahlenkolonne aus Excel/CSV. Renderer-Thread friert. **Fix:** Regex linearisieren `/^[+-]?(?:\d+(?:\.\d+)?|\.\d+)$/` + Längen-Guard (`if (s.length>40) return null`). ## 3 — 🟠 MITTEL — ISO-Datum ohne Uhrzeit hinter verwaistem Label → ZE = 2026 Blanking (`:286`) greift nur mit Uhrzeit (`STAMP_SRC` = Zeit Pflicht). Reines ISO-Datum bleibt, GAP liest die Jahreszahl als ZE: - `parseZE("Zeitaufwand (ZE):\n2026-07-07 Sichtpruefung")` → **`2026`** - `parseZE("Zeiteinheiten:\n( 2026-07-07 - 2026-07-08 )")` → **`2026`** (Deutsche `10.03.2026` ist zufällig geschützt — `parseDecimal` lehnt Mehrfach-Punkte ab; nur ISO leakt.) **Fix:** nach Stempel-Blanking auch nackte Daten blanken: `s.replace(new RegExp(DATE_SRC,"g")," ")`. ## 4 — 🟠 MITTEL — ISO-Offset-Strip frisst kompakte Zeitranges (Regression aus Runde 2) `index.html:255` Lookahead `(?![:.]?\d)` schützt nur Ranges mit Sekunden im End-Stempel: - `normalizeText("12:17-12:20")` → **`"12:17"`** → `parseRange` → `null` (vorher matchte `RE_RANGE`) - `08:00-16:30` → `null`; En-Dash `12:17–12:20` ebenso; `12:17:36,696-12:20,305` → `"12:17:36,696,305"` (Lookahead kennt kein Komma) Wirkung „laut" (leere Felder), aber `HH:MM-HH:MM` ist plausible Kompakt-Notation und funktionierte vor dem ISO-TZ-Fix. **Fix:** Offset-Strip nur hinter Pflicht-Sekunden; Lookahead auf `(?![:.,]?\d)`. ## 5 — 🟡 MITTEL — Autofill-Carry-over zwischen zwei Pastes (alter ZE-Soll bleibt still) `index.html:608-620`: Autofill überschreibt nur bei Fund, löscht nie. Paste A (ZE 4,5) → Paste B ohne ZE-Zeile → `ze.value` bleibt 4,5, `render()` zeigt frisches, nicht-stale Ergebnis mit altem Soll. Analog anfang/ende. **Fix:** nicht-dirty Felder bei Paste ohne Fund zurücksetzen bzw. Stale-Markierung statt Voll-Render. ## 6/7/8 — 🟢 NIEDRIG (belegt) - BiDi-Steuerzeichen fehlen in Zero-Width-Klasse (`:248`): `parseDateTime("1‎2:17:36")` → `"02:17:36"` (2 statt 12 h). Fix: `‎‏؜⁦-⁩` ergänzen. - Autofill schreibt Exponentialnotation (`:619`): `0,0000001` → `"1e-7"` → Re-Parse `null`, Wert weg. - Anzeige „-0,00 ZE" bei winzig-negativer Zeit (`:447-453`). **Geprüft & sauber:** kein exponentielles Backtracking in `RE_STAMP/RE_RANGE/Tag-Strip/Entity-Decode`; Space-/Dash-Klassen vollständig; 24:00/Schaltsekunde/Fullwidth/30.02./Monat13 → laut `null` (kein Silent-Wrong); DST-Freiheit (UTC), Gruppen-Offsets, Mitternachts-Vererbung korrekt. Empfohlene Reihenfolge: 1 → 2 → 3 → 4 (1+3 = Fix-Familie „Blanking dicht machen"), dann 5, dann 6–8.
Author
Owner

Umgesetzt in PR #4 (squash-merged, main @ 9ddb1f4). Fixes #1–#4 + Bonus #6 (BiDi). Tests: 241 grün (vorher 216). Optional verbleibend: #5 Autofill-Carry-over, #7 Exponential-Autofill, #8 "-0,00 ZE".

Umgesetzt in PR #4 (squash-merged, main @ 9ddb1f4). Fixes #1–#4 + Bonus #6 (BiDi). Tests: 241 grün (vorher 216). Optional verbleibend: #5 Autofill-Carry-over, #7 Exponential-Autofill, #8 "-0,00 ZE".
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
vr6syncro/protokollrechner#3
No description provided.