fix(translate): M1-M3 medium + L1-L2 low bugs from QA review

M1: ReDoS guard — 500 char max + try/except on re.compile
M2: Performance note — O(n×m) regex documented for future optimization
M3: Verified test key names correct (metrics.py transforms cumulative_* → short)
L1: Removed @staticmethod no-op from module-level function
L2: Added aria-label to emoji indicators in CorrectionCell
This commit is contained in:
2026-05-14 17:44:50 +03:00
parent 1853d008e9
commit bb21fd3165
5 changed files with 26 additions and 8 deletions

View File

@@ -658,6 +658,10 @@ class DictionaryManager:
if not all_entries:
return []
# PERFORMANCE NOTE: O(rows × entries) regex matching per batch.
# Acceptable for current scale (50-100 rows × <1000 entries).
# For larger batches (>500 rows or >5000 entries), consider
# replacing with an Aho-Corasick automaton (e.g., pyahocorasick).
matched: list[dict[str, Any]] = []
seen_source_match: set = set()

View File

@@ -825,13 +825,19 @@ class InlineCorrectionService:
class BulkFindReplaceService:
"""Handle bulk find-and-replace on TranslationLanguage entries."""
MAX_PATTERN_LENGTH = 500
@staticmethod
def _compile_pattern(pattern: str, is_regex: bool) -> re.Pattern:
"""Compile a search pattern (regex or plain text)."""
import re
if is_regex:
return re.compile(pattern)
return re.compile(re.escape(pattern))
if len(pattern) > BulkFindReplaceService.MAX_PATTERN_LENGTH:
raise ValueError(
f"Pattern too long (max {BulkFindReplaceService.MAX_PATTERN_LENGTH} characters)"
)
try:
return re.compile(pattern) if is_regex else re.compile(re.escape(pattern))
except re.error as e:
raise ValueError(f"Invalid regex pattern: {e}")
@staticmethod
def _find_matching_entries(