From 0757dc87a1edd0c4b01c9e7a7d16e4c17c4ef5c0 Mon Sep 17 00:00:00 2001 From: Jarian Cottingham Date: Sat, 4 Jul 2026 14:09:43 +0000 Subject: [PATCH] fix: resolve banned term false positives and show server error in frontend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Exact match now uses word boundaries (\b) instead of substring to prevent 'ero' matching 'hero', 'joi' matching 'join' - Fuzzy match threshold raised from <=4 to <=5 chars to skip short words - Added 'gooning' to fuzzy skip list ('cooling' matched it at distance 2) - Removed 'join' from banned_terms.txt (legitimate word) - Frontend now reads err.response.data.error to display server messages - Error message for blocked searches: 'Unable to query — banned search term detected.' --- web/server/banned_terms.txt | 1 - web/server/routes/search.py | 19 +++++++++++++------ web/web-app/src/pages/SearchPage.tsx | 2 +- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/web/server/banned_terms.txt b/web/server/banned_terms.txt index 35d5855..5b8c6d7 100644 --- a/web/server/banned_terms.txt +++ b/web/server/banned_terms.txt @@ -117,7 +117,6 @@ high protein milk # JOI joi -join countdown # ASMR diff --git a/web/server/routes/search.py b/web/server/routes/search.py index 8c32aac..7455a03 100644 --- a/web/server/routes/search.py +++ b/web/server/routes/search.py @@ -59,20 +59,27 @@ def _is_banned(query: str) -> bool: _load_banned_terms() query_lower = query.lower() - # Exact match check + # Exact match check (word-boundary aware to avoid "hero" matching "ero", etc.) for term in _banned_terms: - if term in query_lower: + if re.search(r'\b' + re.escape(term) + r'\b', query_lower): + return True + # Multi-word exact match (e.g. "no nut november") — substring OK for phrases + for term in _banned_terms: + if ' ' in term and term in query_lower: return True # Fuzzy match check for words in the query query_words = query_lower.split() for word in query_words: - # Skip short words (4 chars or less) to avoid false positives (e.g. "lofi" matching "loli") - if len(word) <= 4: + # Skip short words (5 chars or less) to avoid false positives (e.g. "hero" matching "ero") + if len(word) <= 5: continue for term in _banned_terms: # Skip fuzzy matching for short banned terms (too many false positives) - if len(term) <= 4: + if len(term) <= 5: + continue + # Skip fuzzy matching for terms that cause false positives + if term in ("strip", "gooning"): continue # Only fuzzy match for terms with similar length if abs(len(word) - len(term)) > 2: @@ -122,7 +129,7 @@ def search(): return make_error_response("Page must be greater than 0", 400) if _is_banned(query): - return make_error_response("This search is not allowed. Please choose different keywords.", 400) + return make_error_response("Unable to query — banned search term detected.", 400) sanitized_query = re.sub(r'[^\w\s\-\'"\.]+', "", query) search_query = f"ytsearch{limit * page}:{sanitized_query}" diff --git a/web/web-app/src/pages/SearchPage.tsx b/web/web-app/src/pages/SearchPage.tsx index 35e2b2f..e0de4fe 100644 --- a/web/web-app/src/pages/SearchPage.tsx +++ b/web/web-app/src/pages/SearchPage.tsx @@ -79,7 +79,7 @@ export default function SearchPage() { setHasMore(response.hasMore); return response; } catch (err: any) { - setError(err.message || "Failed to search videos. Please try again."); + setError(err.response?.data?.error || err.message || "Failed to search videos. Please try again."); console.error("Search error:", err); return null; }