search, seek, delete und update.rdb lesen bei -mh, -mi, -mr nur Indexeintrag und Satz. Hashindizes (.h) müssen neu gebaut werden. total, mean, subtotal und die /act-Befehle rechnen mit BigDecimal, gerundet wird halb auf.
103 lines
6.7 KiB
Markdown
103 lines
6.7 KiB
Markdown
# Code-Review: Schwachstellen, Fehler, Verbesserungspotential
|
||
|
||
Stand: 2026-10-30, Version 0.7.0. Die 114 Tests und der Handbuch-Test liefen grün.
|
||
„Bestätigt“ heißt: nachgestellt. Alles andere stammt aus dem Lesen des Codes.
|
||
|
||
## Schwachstellen (alle vier behoben, siehe CHANGELOG)
|
||
|
||
1. **Befehlseinschleusung über Tabellendaten in `report` (bestätigt).**
|
||
[lib/rdb/cmd/report.rb:11-12](../lib/rdb/cmd/report.rb): Bei `<!cmd <Spalte>!>` werden
|
||
Zellwerte ungefiltert in einen Backtick-Aufruf eingesetzt. Eine Zelle mit
|
||
`touch PWNED` hat die Datei angelegt. Abhilfe: `Shellwords.escape`, oder in der
|
||
Doku als „nur vertrauenswürdige Daten“ kennzeichnen.
|
||
**Behoben:** Werte werden mit `Shellwords.escape` eingesetzt.
|
||
2. **`screen` erzeugt Shell-Code aus Spaltennamen**
|
||
([report.rb:50-70](../lib/rdb/cmd/report.rb)). Namen stehen unquotiert in
|
||
`read NAME` und `echo "$NAME"`; Leerzeichen, `;` oder `$(…)` brechen das
|
||
Skript oder führen Code aus. Werte werden ebenfalls nicht maskiert.
|
||
**Behoben:** Spaltennamen müssen gültige Shell-Variablennamen sein.
|
||
3. **Lock-Dateien in `/tmp`** ([access.rb:121](../lib/rdb/cmd/access.rb)).
|
||
Vorhersagbare Namen, andere Benutzer können sie anlegen oder löschen.
|
||
`lock` liest und hängt an, ohne `flock` (Race). Keine Prüfung, ob die PID noch
|
||
lebt; nach einem Absturz bleibt das Schloss.
|
||
**Behoben:** privates Verzeichnis `/tmp/rdb-locks-<UID>` (0700) und `flock`.
|
||
Offen: verwaiste Schlösser nach Absturz (eine PID-Prüfung bricht Tests und Handbuch,
|
||
die erfundene PIDs nutzen).
|
||
4. **`vilock` nicht atomar** ([access.rb:134](../lib/rdb/cmd/access.rb)).
|
||
Race zwischen `File.exist?` und `rename`; nach `kill -9` bleibt `LOCK…` liegen.
|
||
**Behoben:** atomare Sperre über `File.link`. Offen: nach `kill -9` bleibt `LOCK…` liegen.
|
||
|
||
## Fehler (1–8 behoben, 9 teilweise)
|
||
|
||
1. **Umlaute in Spaltennamen (bestätigt).** `to_awk` erkennt nur `[A-Za-z_#]`
|
||
und ASCII-`\w`. `row 'Über > 3'` scheitert mit awk-Fehler. Relevant für die
|
||
DE/AT/CH-Tarife.
|
||
2. **Nicht-UTF-8-Daten (bestätigt).** Latin-1-Datei führt zu
|
||
`invalid byte sequence in UTF-8` mit Stacktrace
|
||
([table.rb:28](../lib/rdb/table.rb)). Abhilfe: binär lesen und `scrub`/
|
||
`force_encoding`, oder Kodierungsoption.
|
||
3. **Kein Fehlerfang auf oberster Ebene (bestätigt).** Stacktraces statt
|
||
Meldungen bei ungültiger Regex (`update.rdb` mit `/[`,
|
||
[access.rb:277](../lib/rdb/cmd/access.rb); `sale`,
|
||
[act.rb:345](../lib/rdb/cmd/act.rb)), `Date::Error` in `to_julian`, `Errno::*`.
|
||
Abhilfe: `rescue StandardError` in `bin/rdb`/`Rdb.run`.
|
||
4. **`EDITOR` mit Argumenten (bestätigt, behoben).** `system(ENV['EDITOR'], datei)` in
|
||
`vilock` und `edit_cells` ([access.rb:138](../lib/rdb/cmd/access.rb),
|
||
[access.rb:150](../lib/rdb/cmd/access.rb)) behandelt `"nvim -f"` als einen
|
||
Programmnamen. Der Editor startet nie, `update.rdb` meldet trotzdem
|
||
„record replaced“. Abhilfe: `Shellwords.split`, Rückgabewert prüfen.
|
||
5. **Datenverlust-Risiko bei `update.rdb e`**
|
||
([access.rb:289](../lib/rdb/cmd/access.rb)). Der Satz wird vor dem Editieren
|
||
geleert; bei Abbruch ist er weg. Erst nach erfolgreichem Editieren schreiben.
|
||
6. **Tab/Zeilenumbruch in Werten ungeprüft (bestätigt für Tab).** `append`,
|
||
`enter`, `replace`, `ve` schreiben Zellen unverändert; ein Tab erzeugte eine
|
||
Zeile mit 3 statt 2 Spalten (`check.rdb` meldet es).
|
||
7. **Nicht atomares Schreiben.** `splice`, `build`, `substitute` überschreiben
|
||
direkt, ohne temporäre Datei plus `rename`, ohne `flock`.
|
||
8. **Inkonsistente Vergleiche bei `-mb`/`-mi`** (nur im Code gesehen). Index
|
||
sortiert mit `to_s`, Suche vergleicht mit `strip`; führende Leerzeichen und
|
||
`-x` ohne Teilsuche können falsch ausfallen.
|
||
9. **Kleinigkeiten.** `sorttable -r` kehrt gleiche Schlüssel um (`reverse!` nach
|
||
stabiler Sortierung); `-nrf` gilt global für alle Spalten; `jointable -n` wird
|
||
ignoriert; `total`/`mean` lehnen `1e3` ab; eine Zeile aus lauter leeren
|
||
Zellen gilt als gelöscht ([fast.rb:21](../lib/rdb/fast.rb)).
|
||
|
||
**Stand der Behebung:** 1 (Umlaute: Unicode-fähiger Umsetzer), 2 (Latin-1 wird als UTF-8
|
||
gelesen), 3 (Fehler als eine Zeile, `RDB_DEBUG=1` für den Backtrace), 4 (`EDITOR` mit
|
||
Argumenten, Editorfehler wird gemeldet), 5 (Satz wird in jedem Fall zurückgeschrieben),
|
||
6 (`Rdb.check_cells!`), 7 (`Rdb.write_file`: temporäre Datei plus `rename`) und 8
|
||
(Sortierschlüssel ohne Leerzeichen) sind behoben, jeweils mit Test.
|
||
Bei 9 ist nur `sorttable -r` behoben. Bewusst offen: `-nrf` gilt global (so beschreibt es
|
||
das Handbuch), `jointable -n` ist unklar spezifiziert, `1e3` als Zahl und die Zeile aus
|
||
leeren Zellen als „gelöscht“ sind Eigenheiten des Formats. Bei 8 bleibt `-x` (Faltung) beim
|
||
Index ohne Entsprechung in der Suche offen.
|
||
|
||
## Verbesserungspotential
|
||
|
||
1. **Indexe sind nicht schnell. — Behoben** (Seek statt Volllesen, lazy `Data`). Ursprünglich: Jede Suche liest Tabelle und Indexdatei
|
||
komplett ([fast.rb:151](../lib/rdb/fast.rb), [fast.rb:171](../lib/rdb/fast.rb),
|
||
`Data.new` pro Schlüssel in `delete`/`update.rdb`): O(n) pro Schlüssel. Abhilfe:
|
||
mit `seek`/`pread` direkt an der Fixbreiten-Position lesen.
|
||
2. **Schlechte Hash-Funktion — Behoben** (FNV-1a, größter Eimer 4). Ursprünglich: ([fast.rb:68](../lib/rdb/fast.rb)):
|
||
`bytes.sum % slots`. Bestätigt: 3000 fünfstellige Nummern landen in 30 Slots,
|
||
größter Bucket 223. Abhilfe: FNV-1a o. Ä., Primzahl-Slotzahl.
|
||
3. **Geld in `Float` — Behoben** (`BigDecimal` in `total`/`mean`/`subtotal` und /act). Ursprünglich: ([act.rb:11](../lib/rdb/cmd/act.rb), `to_f` im /act-Teil
|
||
und in `total`). Abhilfe: `BigDecimal` oder ganzzahlige Rappen.
|
||
4. **awk-Brücke** ([env.rb:119-138](../lib/rdb/env.rb)): ganze Tabelle als String
|
||
durch `capture3`, kein Streaming; handgeschriebener Parser erkennt Strings und
|
||
Regexe heuristisch (`prev`) und hängt vom awk-Dialekt ab.
|
||
5. **Globale Methoden auf `Object`** (`fast_opts`, `lock_file`, `num`, `money`,
|
||
`ask`, `need`, `show_record` …). In ein Modul kapseln und in `Env` einbinden.
|
||
6. **Doppelter Code.** `awk`/`awk_text`; Schlüsseltabellen-Parsing in `fast_opts`/
|
||
`key_columns`; von Hand gebaute Kopf-/Strichzeilen trotz `Table.dash_row`.
|
||
7. **Tests.** Laufzeit ca. 66 s wegen eines Ruby-Prozesses pro Test. Fehlerfälle
|
||
(Encoding, Tab in Wert, Locks, Editor) nicht abgedeckt; Tests direkt gegen
|
||
`Rdb.run` im Prozess wären schneller.
|
||
8. **Repo.** `demo/fact.pl`, `fact_lc.pl`, `rule.pl` sind ungetrackt;
|
||
`demo/testall.old` (1187 Zeilen) wäre als Golden-File-Test besser gepflegt.
|
||
|
||
## Empfohlene Reihenfolge
|
||
|
||
Schwachstellen, Fehler und Verbesserung 1–3 sind erledigt. Offen sind 4–8: die
|
||
awk-Brücke, globale Methoden auf `Object`, doppelter Code, Testlaufzeit, Repo-Aufräumen.
|