high priority low complexity backend pending backend specialist Tier 0

Acceptance Criteria

A pure Dart function or class QuerySanitiser is created and used inside ContactSearchService before any query is dispatched to a data source
Leading and trailing whitespace is removed from all queries
Multiple consecutive internal whitespace characters are collapsed to a single space
Norwegian diacritics (æ, ø, å and uppercase equivalents) are preserved — not stripped or normalised to ASCII equivalents
Unicode characters outside the Basic Multilingual Plane (emoji, control characters) are stripped
Queries exceeding a configurable maximum length (default 100 characters) are truncated to that length
Strings containing SQL keyword injection patterns (e.g., '; DROP TABLE', ' OR 1=1') are sanitised by stripping the offending tokens — query is not rejected outright, to avoid false-positive blocking of legitimate names
Strings containing HTML/script tags (e.g., <script>, <img onerror=) are stripped of the tag markup, leaving the inner text
An empty string after sanitisation returns an empty string (not null)
Unit tests cover all of the above cases and pass

Technical Requirements

frameworks
dart:core (RegExp, String)
flutter_test
data models
contact
performance requirements
Sanitisation must complete in under 1ms for queries up to 1000 characters
RegExp patterns must be compiled once (static const) not on every call
security requirements
Norwegian names containing characters like Ø, Æ, Å must not be mangled — test with 'Bjørn Ødegård', 'Åse Æsøy'
Supabase uses parameterised queries so SQL injection via search is low risk — but sanitisation is a defence-in-depth measure and must not break valid input
Sanitised query must never be longer than the original query (no expansion attacks)
XSS sanitisation is relevant because search results may be rendered as HTML in the visualisation layer — strip tags defensively

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Implement as a static method class: abstract class QuerySanitiser { static String sanitise(String raw, {int maxLength = 100}) { ... } }. This makes it easy to inject in tests and avoids instantiation overhead. The SQL injection pattern matching should be conservative — only strip clear attack patterns (semicolons followed by keywords, UNION SELECT, OR 1=1) not all SQL keywords, as legitimate Norwegian names could contain fragments that match overly broad patterns.

Use RegExp with the unicode flag (unicode: true) for correct handling of multi-byte characters. For diacritic normalisation, do NOT use String.normalize() or NFD decomposition — it would silently break Norwegian name matching. The Supabase PostgREST text search uses plainto_tsquery which handles its own SQL-safety; the sanitisation here is a belt-and-suspenders layer, not the primary injection defence.

Testing Requirements

Pure unit tests — no Flutter widget tree required. Test file at test/core/search/query_sanitiser_test.dart. Cover: empty string, whitespace-only string, Norwegian names with diacritics (verify NOT modified), emoji-containing string, SQL injection pattern, HTML script tag injection, maximum length truncation, normal Latin query (verify unchanged). Use parameterised test tables where possible to keep tests concise.

All tests must pass with flutter test.

Component
Contact Search Service
service low
Epic Risks (2)
medium impact medium prob technical

Cancelling in-flight Supabase HTTP requests via RxDart switchMap may not actually abort the server-side query if the Supabase Dart client does not support request cancellation tokens, leading to wasted API calls and potential race conditions where a slow earlier response arrives after a faster later one.

Mitigation & Contingency

Mitigation: Audit the supabase-flutter client's cancellation support before implementation. Use RxDart switchMap to discard stale emissions even if HTTP cancellation is unavailable, ensuring only the latest result reaches the UI.

Contingency: If race conditions surface in testing, add a query sequence counter to tag each emission and discard any response whose sequence number is lower than the most recently emitted one.

medium impact low prob technical

Connectivity detection used to route between online and offline repositories may have latency or give false positives on flaky connections, causing the service to attempt Supabase queries that time out instead of falling back to the cache promptly.

Mitigation & Contingency

Mitigation: Use a try/catch with a short timeout on Supabase calls. On network error, immediately fall back to the offline repository and emit a cached result with an offline indicator rather than surfacing an error state.

Contingency: If the timeout-based fallback proves insufficient, implement a connection health check stream that pre-validates connectivity before each query batch.