rebreak-monorepo/backend/server/utils/gambling-keywords.mjs
chahinebrini c3de7055a5 feat(mail): Sucht-Compound-Regel + Phase-1-Training-Foundation
Task B — linguistische FP-Fix:
- mail-classifier.ts: Subject-Keyword-Loop überspringt Keyword-Score wenn
  Subject das Keyword als Sucht-Compound enthält (z.B. "glücksspiel" in
  "Glücksspielsucht" → kein +50 Score). Globale linguistische Invariante
  Deutsch — Gambling-Marketer schreiben nie "Glücksspielsucht-Bonus".
- gambling-keywords.mjs: GAMBLING_WHITELIST erweitert um Stamm-Varianten
  (wettsucht, spielsucht, suchtberatung, suchthilfe) als Fallback für
  Compounds wo keyword ≠ exakter Stamm.
- 4 neue Tests: Forum Glücksspielsucht → PASS, Hilfe bei Spielsucht → PASS,
  Wettsucht-Selbsthilfe → PASS, Glücksspiel-Bonus 100€ → BLOCK.

Task C — Phase-1-Data-Foundation:
- mail-training-utils.ts: sanitizeSubjectForTraining() (PII-Stripping via
  Regex: EMAIL/URL/NUM/Greeting/ALL-CAPS) + detectSubjectLanguage() via
  franc (iso639-3). 26 Unit-Tests.
- franc@6.2.0 installiert (~50KB ESM).
- mail.ts insertMailClassificationSample(): ruft sanitizeSubjectForTraining()
  auf, schreibt detectedLang + subjectSanitized in features-JSON
  (Interim bis Schema-Migration).
- mail-retention-cron.ts: Subject-Nullification nach 30 Tagen (täglich) +
  Sample-Purge nach 12 Monaten (monatlich). DSGVO Art. 5 Abs. 1e.

105 Tests grün (58 classifier + 26 training-utils + 11 display-name + 10 gmail).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 08:14:57 +02:00

74 lines
2.4 KiB
JavaScript

/**
* Single-Source-of-Truth für Gambling-Keyword-Detection.
*
* Importiert von:
* - server/api/mail/scan.post.ts
* - server/api/mail/scan-internal.post.ts
* - imap-proxy/session.mjs
* - imap-idle/index.mjs
*
* Mo's DSGVO-Finding #4: vorher in 4 Files dupliziert → Drift-Risk.
*/
export const GAMBLING_KEYWORDS = [
// Major Anbieter
"casino", "bet365", "bwin", "tipico", "unibet", "betway", "888casino",
"pokerstars", "interwetten", "netbet", "leovegas", "mrgreen", "mr green",
"betsson", "neobet", "mybet", "lottoland", "betano", "william hill",
"paddypower", "betfair", "stake", "rolletto", "vbet", "1xbet", "melbet",
"mostbet", "luckyvibe", "lucky vibe", "spinz", "casinoly", "rabona",
"justcasino", "getslots", "rocketplay", "fresh casino", "freshcasino",
"nom nom",
// Generic Begriffe
"sportwetten", "jackpot", "freispiel", "free spin", "bonus code",
"auszahlung", "glücksspiel", "slots", "roulette",
// ⚠️ Risk: matcht auch unschuldige Wörter (Mo's Finding #5)
// TODO Whitelist: "wette" matcht "wettervorhersage" → False-Positive
// siehe gambling-whitelist.mjs (TODO)
"wette",
];
/**
* Whitelist — Begriffe die NICHT als Gambling gelten dürfen.
* Bei Match in GAMBLING_KEYWORDS, vor Block prüfen ob in Whitelist.
*
* TODO Mo's Finding #5: ausführen Mail-Whitelist-Check vor Auto-Delete.
*/
export const GAMBLING_WHITELIST = [
"wettervorhersage",
"wetter",
"wetterbericht",
"wettkampf", // kein Glücksspiel
"wettbewerb", // dito
// Recovery-/Anti-Gambling-Compounds mit "sucht"-Suffix.
// Ergänzung zu Sucht-Compound-Regel in mail-classifier.ts:
// Regel deckt Fälle wo keyword als exaktes Präfix vorkommt (glücksspiel→glücksspielsucht).
// "wettsucht" kann nicht via concat-Regel abgedeckt werden (kw="wette" ≠ "wett"),
// daher Whitelist als Fallback für Stamm-Varianten.
"wettsucht",
"spielsucht",
"suchtberatung",
"suchthilfe",
];
/**
* Helper: prüft ob ein Text Gambling-Keywords enthält, mit Whitelist-Check.
*/
export function isGamblingText(text) {
if (!text) return false;
const lower = text.toLowerCase();
// Erst Whitelist — wenn matched, kein Gambling
for (const w of GAMBLING_WHITELIST) {
if (lower.includes(w)) return false;
}
// Dann Gambling-Keywords
for (const kw of GAMBLING_KEYWORDS) {
if (lower.includes(kw)) return true;
}
return false;
}