Files
archivmail/features/PROJ-85-fix-undeklarierte-8bit-header-charset.md
sysopsandClaude Sonnet 5 7727ad5cdf docs(PROJ-84,PROJ-85): Status auf Deployed - Backfill/Reindex auf 131+132 verifiziert
PROJ-84: 131 hatte 0 Kandidaten, 132 541/541 korrigiert (bereits
vorher bestätigt). PROJ-85: --apply + reindex auf 132 (54/54) und
131 (Produktiv) ohne Fehler durchgelaufen, User-Gegenprobe bestätigt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WapWkrQusDuBMhaN8WyuXB
2026-08-06 22:38:44 +02:00

5.9 KiB
Raw Permalink Blame History

PROJ-85: Fix undeklarierte 8-Bit-Zeichen in Header/Body ohne Encoded-Word

Status: Deployed

Created: 2026-08-06 Last Updated: 2026-08-06

Kontext

Anlass war eine gemeldete Mojibake-Mail ("Grü?e" statt "Grüße"). Diagnose ergab: diese konkrete Mail ist korrekt archiviert — der Fehler (=3F statt =DC im Original-Encoded-Word) kam bereits so vom Absender (Amazon, 2018, DKIM-signiert) und darf laut GoBD nicht nachträglich verändert werden.

Beim Scan wurde daneben aber ein echter archivmail-Bug gefunden, verschieden von PROJ-84: Header (insbesondere Subject) mit rohen 8-Bit-Bytes ohne RFC-2047-Encoded-Word-Syntax (technisch RFC-5322-widrig, in freier Wildbahn bei Marketing-/Newsletter-Systemen üblich) wurden nicht auf ihr tatsächliches Charset geprüft. decodeCharset() hatte dieselbe Lücke für den Body bei fehlendem/leerem Content-Type. Ergebnis: ungültige UTF-8-Bytes landeten unverändert in emails.subject (Postgres, DB-Encoding SQL_ASCII, akzeptiert das klaglos) und im Manticore-Index.

Dependencies

  • Ergänzt PROJ-84 (Fix MIME-Header-Charset-Dekodierung) — andere Ursache (kein Encoded-Word-Marker vorhanden), daher eigenes Ticket statt Erweiterung
  • Nutzt dieselbe Backfill-Infrastruktur (fix-subjects-Kommando, Audit-Log)

User Stories

  • Als User will ich, dass Betreffs mit undeklarierten 8-Bit-Sonderzeichen (ä/ö/ü/ß/„–"/„®" etc.) korrekt lesbar sind, wenn der Absender kein valides Encoded-Word genutzt hat.
  • Als User will ich, dass auch der Mail-Body korrekt dargestellt/durchsuchbar ist, wenn das deklarierte oder fehlende Charset nicht zu den tatsächlichen Bytes passt.
  • Als Auditor will ich, dass eine Reparatur klar von echter Absenderseiten-Korruption unterschieden wird — Mails, die der Absender bereits fehlerhaft verschickt hat (wie das Encoded-Word-Beispiel), werden NICHT verändert.

Acceptance Criteria

  • RepairUTF8/RepairUTF8Bytes erkennt Bytes außerhalb gültiger UTF-8-Sequenzen und versucht Windows-1252-Fallback-Dekodierung, bytegenau (nicht bufferweise) — gültiges UTF-8 bleibt unverändert
  • decodeMIMEHeader() wendet die Reparatur auf das Ergebnis an (inkl. Fehlerpfad)
  • Body-Decoding (Single-Part und Multipart, alle Content-Type-Fehlerpfade) wendet dieselbe Reparatur an, außer bei binären Attachments (Attachment.Data bleibt byte-exakt)
  • fix-subjects-Kommando erkennt zusätzlich zu Encoded-Word-Fällen (PROJ-84) auch rohe 8-Bit-Subjects ohne Encoded-Word-Marker (NeedsCharsetRepair)
  • Tests decken ab: gültiges UTF-8 bleibt unangetastet, Windows-1252-Sonderzeichen (ä/ö/ü/ß//®) werden korrekt repariert
  • --apply auf 132 durchgeführt (54/54 aktualisiert, 0 Fehler), danach archivmail reindex für Body-Korrektur (User-Gegenprobe bestätigt, 2026-08-06)
  • --apply + reindex auf 131 (Produktiv) durchgeführt, ohne Fehler (2026-08-06)

Edge Cases

  • Encoded-Word mit RFC-5322-widriger Syntax (Leerzeichen im codierten Teil) → bleibt unverändert, als „undecodable" gezählt, nicht Teil dieses Fixes (schon in PROJ-84 als Grenzfall dokumentiert)
  • Mail, bei der der Absender selbst bereits fehlerhaft kodiert hat (z.B. =3F statt korrektem Byte im Encoded-Word) → wird NICHT repariert, da das die archivierte Originalnachricht selbst verfälschen würde (GoBD-Grundsatz: Archiv bildet ab, was ankam, nicht was gemeint war)
  • Binäre Attachments (z.B. CSV mit Windows-1252) → Attachment.Data bleibt byte-exakt, keine Reparatur, sonst wäre der heruntergeladene Anhang verändert
  • Text, der zufällig wie gültiges UTF-8 aussieht, aber eigentlich Windows-1252 ist → nicht erkennbar/nicht behandelt (Kollisionsfall, in der Praxis selten und nicht zuverlässig unterscheidbar)

Technical Requirements (optional)

  • Kein neues Datenbankschema
  • Postgres-DB-Encoding bleibt SQL_ASCII (Bestand, nicht Teil dieses Fixes) — Nebenbefund, der erklärt, warum ungültige Bytes bisher klaglos angenommen wurden
  • Body-Korrektur wirkt erst nach zusätzlichem archivmail reindex-Lauf (Body liegt nur in Manticore, nicht in Postgres)

Tech Design (Solution Architect)

Direkt umgesetzt ohne vorgelagerte Architektur-Phase — Bugfix + Backfill-Erweiterung auf bestehender Infrastruktur (PROJ-84), kein neues UI, kein neues Datenmodell.

Implementation Notes (Backend, 2026-08-06)

Neue/geänderte Dateien:

  • pkg/mailparser/charset_repair.go (neu) — RepairUTF8(), RepairUTF8Bytes(), NeedsCharsetRepair(). Strategie: erst UTF-8-Validität prüfen, nur für Bytes, die nicht Teil einer gültigen UTF-8-Sequenz sein können, Windows-1252-Fallback (golang.org/x/text/encoding/charmap)
  • pkg/mailparser/charset_repair_test.go (neu) — 12 Testfälle
  • pkg/mailparser/parser.godecodeMIMEHeader()-Ergebnis, Single-Part-Body, Multipart-Text-/HTML-Buffer, Content-Type-Fehlerpfad laufen durch die Reparatur; Attachment.Data bewusst ausgenommen
  • internal/storage/subject_charset_backfill.go (neu) — ListNonASCIISubjects()
  • cmd/archivmail/cmd_fix_subjects_candidates.go (neu) — merged/dedupliziert Encoded-Word- und Charset-Repair-Kandidaten
  • cmd/archivmail/cmd_fix_subjects.go — Filter erweitert auf HasEncodedWord || NeedsCharsetRepair

Stolperstein auf dem Server: Prefilter octet_length(subject) <> length(subject) liefert in einer SQL_ASCII-DB immer 0 Treffer, weil length() dort Bytes zählt. Ersetzt durch subject ~ '[^[:ascii:]]'.

Messung auf 192.168.1.132 (Dry-Run, read-only): 21.004 geprüfte Kandidaten (Encoded-Word- + Charset-Repair-Muster kombiniert), 54 zusätzliche Betreffs durch Charset-Repair betroffen (alle Tenant 3), 0 Fehlklassifikationen. Body-Verifikation über alle 52.961 Mails mit gepatchtem Parser: parsedSubjectInvalidUTF8: 54 → 0, bodyInvalidUTF8: 219 → 0.

Build, go vet, go test ./pkg/mailparser/ auf 132 grün. --apply noch nicht ausgeführt.

QA Test Results

To be added by /qa

Deployment

To be added by /deploy