feat: category zoom + secure AddKeywordDialog (#74) #93
No reviewers
Labels
No labels
source:analyste
source:defenseur
source:human
source:medic
status:approved
status:blocked
status:in-progress
status:needs-fix
status:ready
status:review
status:triage
type:bug
type:feature
type:infra
type:refactor
type:schema
type:security
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: maximus/Simpl-Resultat#93
Loading…
Reference in a new issue
No description provided.
Delete branch "issue-74-zoom-add-keyword"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fixes #74
Implémente le zoom catégorie et l'édition contextuelle des mots-clés. Cette PR porte toutes les garanties de sécurité listées au review expert.
Service —
getCategoryZoomWHERE ct.depth < 5) contre les cyclesparent_id(CWE-835){ rollupTotal, byChild, monthlyEvolution, transactions }AddKeywordDialog — 7 garanties de sécurité
.trim()→ anti-ReDoS (CWE-1333)LIKE $1+ filtrage regex en mémoire (CWE-89)db.execute(CWE-662){tx.description}comme enfants React, troncature CSS (CWE-79), 0dangerouslySetInnerHTMLComposants
CategoryZoomHeader— combobox + toggle include-subcategoriesCategoryDonutChart—Pie innerRadius={55}+ChartPatternDefsCategoryEvolutionChart— AreaChart monthlyCategoryTransactionsTable— sortable, onContextMenu → ContextMenu → AddKeywordDialogAddKeywordDialogplacé danscomponents/categories/(pas reports) — composant du domaine édition mot-cléExports depuis categorizationService
normalizeDescription,buildKeywordRegex,compileKeywords(avant privés)validateKeyword,previewKeywordMatches,applyKeywordWithReassignmentKEYWORD_MIN_LENGTH=2,KEYWORD_MAX_LENGTH=64,KEYWORD_PREVIEW_LIMIT=50i18n
reports.category.*+reports.keyword.*(FR + EN)nMatches_one/nMatches_otherTests
npm run build✅npm test✅ 62/62 (20 nouveaux)cargo check✅getCategoryZoom: 3 tests (CTE bornée, cycle guard, direct-only)categorizationService: 13 tests couvrant validation boundaries, paramétrisation LIKE, filtrage regex, BEGIN/COMMIT wrap, ROLLBACK on failure, reassignment policy, reject-before-dbgrep -rn "dangerouslySetInnerHTML" src/components/categories src/components/reports→ 0 occurrenceScope du clic droit
Branché uniquement sur
CategoryTransactionsTabledans cette issue. La propagation aux autres tables (Highlights, Compare, Transactions) est traitée en Issue #75.Review — APPROVE
PR qui porte le gros du travail sécurité du sprint. Je vérifie chaque garantie du spec review une par une.
Sécurité — 9 findings 🔴/🟡 du review expert, tous résolus ✅
LIKE $1paramétré + regex en mémoire (testbinds the LIKE pattern as a parameter)validateKeywordcappe à 64 chars (testrejects keywords longer than 64 characters)WHERE ct.depth < 5(testuses a bounded recursive CTE,terminates on a cyclic category tree)BEGIN/COMMIT/ROLLBACKviadb.execute(testwraps INSERT + UPDATEs in a BEGIN/COMMIT transaction,rolls back when an UPDATE throws)applyToHiddenopt-in checkbox + user cochées uniquementallowReplaceExisting, prompt UI, UPDATE explicite (testblocks reassignment... without allowReplaceExisting)dangerouslySetInnerHTMLdans components/categories + components/reports (grep vérifié), rendering{tx.description}+ CSS truncatesrc/components/categories/(pas reports/) comme specnormalizeDescription,buildKeywordRegex,compileKeywordsexportésCorrectness ✅
getCategoryZoom: CTE bornée testée sur chaîne cyclique ; direct-only skip le CTEapplyKeywordWithReassignment:rejects invalid keywords before touching the database)ROLLBACKsur exception avecthrow equi propagereplacedExistingtrue uniquement quand category_id change réellementuseCategoryZoom: refetch sur refetch() appel (exposé pour AddKeywordDialog.onApplied)Qualité ✅
npm run build✅npm test✅ 62/62 (20 nouveaux pour ce PR)cargo check✅_one/_otherCategory*dans reports/)Non-bloquant
replacePromptutilise un message générique sans montrer le nom de la catégorie actuelle — pourrait être amélioré avecreports.keyword.alreadyExistsprenant un paramètre, mais le comportement sécurité est correctpreviewKeywordMatchesest une défense en profondeur (pas dans la spec explicitement, mais cohérent avec l'esprit anti-ReDoS)9/9 findings sécurité du spec review sont résolus. Ready to merge.