# SRC-01 – Prüfprotokoll: Manticore-Suchindex für Mails Voraussetzung ARC-01, ARC-03 (beide Fertig). ## Umsetzung - `mail/internal/search/fields.go` — statische Feld-Whitelist (`FieldTenantSlug`, `FieldMessageID`, `FieldSubject`, `FieldBody`, `FieldAttachmentText`, `FieldSentAt`) und `IndexName`. Bekannten Fehler vermeiden (known-issues-archivmail.md #11/#12): archivmail baute WHERE-Klauseln und teils Spalten-/Tabellennamen dynamisch über `fmt.Sprintf`/`strings.Join`. Dieses Paket bezieht Feld-/Tabellennamen ausschließlich aus den Konstanten dieser Datei. - `mail/internal/search/migrations/0001_mail_documents.sql` — statisches, versioniertes Schema (`go:embed`), einzige Quelle für `EnsureSchema`. - `mail/internal/search/client.go` — `Client`: - `EnsureSchema` legt den Index über den Manticore `/sql?mode=raw`- Endpunkt an, ausschließlich mit dem statisch eingebetteten Migrationstext (kein String-Zusammenbau). - `Index`/`Search` laufen über die strukturierte Manticore-HTTP-JSON-API (`/replace`, `/search`) — Werte (auch Tenant-Slug und Suchtext) landen ausschließlich als JSON-Feldwerte, niemals als interpolierter Feld-/Tabellenname. - `Search` filtert zwingend über `FieldTenantSlug` (Akzeptanzkriterium 3). - Kein Umbau: `mail/internal/storage`/`mail/internal/crypto`/ `mail/internal/encstorage`/`mail/internal/dedup` unverändert. ## Prüfungen | # | Prüfung | Ergebnis | |---|---|---| | 1 | Codereview bestätigt: keine Sprintf/Join-basierte SQL-Klauselbildung im Index-Zugriff | **bestanden** – `TestNoDynamicSQLClauseBuilding`: automatisierter Quelltext-Scan von `client.go` bestätigt, dass kein `fmt.Sprintf` verwendet wird und in der Nähe des `/sql?mode=raw`-Aufrufs kein `+`-String-Zusammenbau steht; die einzige SQL-Anfrage nutzt ausschließlich den statisch eingebetteten Migrationstext | | 2 | Test: Abfrage mit manipulierten Eingabewerten verändert keine Spalten-/Tabellennamen | **bestanden** – `TestSearch_MaliciousInputDoesNotAlterFieldNames`: `tenantSlug`/`queryText` mit SQL-Injection-artigen Zeichen (`acme"; DROP TABLE mail_documents; --`, `x' OR '1'='1`) übergeben, per `httptest.Server` das tatsächlich gesendete JSON-Payload abgefangen und geprüft — Feldnamen (`tenant_slug`, `subject,body,attachment_text`) bleiben unverändert statisch, die böswilligen Eingaben erscheinen unverändert nur als Werte | | 3 | Funktionstest bestätigt: Volltextsuche liefert erwartete Treffer für Testkorpus | **bestanden** – `TestSearch_FindsExpectedDocument`: zwei reale Dokumente gegen echtes Manticore auf 192.168.1.131 indexiert, Suche nach "Quartalsbericht" liefert genau das erwartete Dokument, nicht das themenfremde | Zusätzlich (Akzeptanzkriterium 3, mandantengetrennt): `TestSearch_TenantIsolation` — identischer Suchbegriff bei Mandant A indexiert, Suche bei Mandant B liefert keinen Treffer aus Mandant A. ## Build/Test-Ergebnis (192.168.1.131) ``` go build ./... -> clean go vet ./... -> clean golangci-lint run ./... -> 0 issues TEST_TENANT_DSN=postgresql://nexarch_test:***@localhost:5432/tenant_acme?sslmode=disable \ TEST_MANTICORE_URL=http://127.0.0.1:9308 \ go test ./... -v -p 1 -> alle Pakete bestanden, inkl. internal/search (4 Tests) ``` Manticore lief bereits produktiv auf 192.168.1.131 (Port 9308, Version 7.4.1, Dienst `manticore.service` aktiv seit 2026-08-28). Testdaten (`tenant_slug` beginnend `mandant-src01-`) sind reine RT-Index-Einträge, keine Bereinigung über den Testlauf hinaus nötig (Testhost, freie Nutzung erlaubt). ## Gesamtergebnis **Bestanden.** Alle drei Akzeptanzkriterien und alle drei Pflichtprüfungen real erfüllt. Entsperrt SRC-02, SRC-03, SRC-09.