Bilan #5 — Price-fetching premium via maximus-api #143
Labels
No labels
autopilot:pending-human
source:analyste
source:defenseur
source:human
source:medic
status:approved
status:blocked
status:in-progress
status:needs-clarification
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#143
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
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?
Refs: spec-decisions-bilan.md + spec-plan-bilan.md (v2 + overnight-2026-04-26)
Depends on #142
Reste dans spec-bilan (PAS dans overnight-2026-04-26-bilan) : autopilot ne peut pas exécuter une issue blockée externe. Reprise manuelle quand maximus-api Phase 2 sera en prod.
Pré-requis externe (BLOQUANT)
Tâches client (déclenchées une fois maximus-api prêt)
Backend
Service (avec rate-limit)
Tests
UI
Décisions prises ce soir
Critères dacceptation
/review-spec — findings sécurité bloquants pour le price-fetching
3 critiques, 2 améliorations, 2 suggestions remontés par l'agent Sécurité. Voir spec-plan-bilan.md section "Issue 5" pour les annotations inline complètes.
🔴 Critiques (à trancher avant implémentation)
1. license_id mal stocké
Le spec dit que
license_idvit dansuser_preferences, mais le projet stocke license_key + activation_token dans des fichiers vialicense_path/activation_path(pas dans la table SQL). Implémenter tel qu'écrit = duplication du secret en plaintext SQLite OU casse l'auth.Fix : Réutiliser les accessors existants. Envoyer
activation_token(paslicense_key) en Bearer pour/v1/prices.Ref : CWE-836
2. license_id en query string (token-in-URL)
Le sketch maximus-api montre
?license=<license_id>comme query param. Token en URL = leak dans logs Traefik, accès VPS, history navigateur, headers Referer.Fix : Authorization header uniquement. Aucune license/token en query. Critère d'acceptation : test que la requête envoyée ne contient aucun segment license/token dans l'URL.
Ref : OWASP API2:2023 / CWE-598
3. Privacy claim repose sur hygiène headers stricte
"IP jamais exposée à Yahoo/CoinGecko" suppose que le proxy ne forwarde NI client IP, NI User-Agent, NI Accept-Language.
reqwest::Client::new()envoie par défautUser-Agent: reqwest/0.12— corrélable avec license_id sur un petit VPS.Fix : Dans l'ADR
proxy-price-fetching-via-maximus-api.md:Authorization+ minimalAccept+ explicitUser-Agent: simpl-resultatRef : OWASP A09:2021
🟡 Améliorations
4. Aucun rate-limit / circuit breaker côté client
Le spec gère les erreurs serveur (429, 401, 404) mais pas le rate-flood côté client. Bug ou retry loop = spam maximus-api + déanonymisation potentielle via timing/volume même sur le cache.
Fix : Rate-limit client (max 1 fetch / 2s, dedup in-flight par symbole), backoff exponentiel sur 5xx/network, plafond hard par session snapshot. Tests dans Issue 6.
5. Consent storage trop grossier
Boolean unique
price_fetching_consentdansuser_preferences: une fois accepté, tous les fetches futurs sont auto. Pas explicite que la clé est par-profil. Pas de re-consent après pause.Fix : Préciser que la clé vit dans
user_preferencesper-profile. Considérer re-consent visible si > 30j sans usage.Sub-fix : Default non spécifié — NE PAS seeder la clé (absence = jamais demandé). Premier clic écrit
{consented_at: ISO, version: 1}.6. 3 services pour 1 domaine sur-découpe
priceFetcher.service.ts+returnCalculator.service.tsséparés alors que pattern projet = 1 service par domaine. Voir aussi commentaire sur Issue #141.Fix : Tout intégrer dans
balance.service.ts(CRUD + prices + returns).🟢 Suggestions
7. Premium check server-side
Le check "non-premium → tooltip 'Disponible avec abonnement'" est UI-only. Un client modifié peut appeler
fetch_pricedirectement et brûler le quota.Fix : Dans l'issue maximus-api :
/v1/pricesrejette les licences non-premium avec 403 avant cache/provider. Test correspondant dans Issue 6.Ref : OWASP A01:2021 / CWE-602
Revue de spec —
docs/api-contract-prices.mdRevue multi-expert (Sécurité, Architecture, Technique) du contrat API
/v1/pricesajoutée dansdocs/api-contract-prices.md(commit à venir).Verdict : 🔴 CRITIQUES À CORRIGER avant gel — 8 critiques + 11 améliorations + 3 suggestions.
Critiques bloquantes (résumé)
rateLimit.tsactuel keye par IP avec un Map global ; le quota par-licence promis (30/min, 2000/j) requiert Redis ou token-bucket Postgres atomique./licenses/*retourne{error: "string"}plat, le contrat propose{error: {code, message}}nesté. Deux shapes dans la même app Hono./v1/pricesintroduit/v1/mais/licenses/*reste non versionné.productmanquant — JWT activation porte une claimproduct, jamais validée par le contrat. Un futur 2e produit pourrait hitter prices.mockito-rsinexistant — le crate Rust s'appellemockito(sans suffixe), et le client est en TS de toute façon.editiondéjà exposé — la spec référence à tort une issue maximus-api à créer pour ce champ.Améliorations clés
activation_token: ajouterjti+ revocation list, OU TTL court + refresh/v1/pricesdans Traefik (sinon corrélation timestamp casse §9.1)kiddans header JWT pour préparer la prochaine rotation Ed25519licenseAuth.tsDoc annotée inline avec tous les détails et résolutions concrètes.
Revue automatique via
/review-specClosing in favor of decomposed sub-issues in milestone
spec-price-fetching(this same milestone, renamed fromspec-bilan). Decomposition based on the multi-expert review ofdocs/api-contract-prices.md(see prior comment).Sub-issues being created: contract commit, Tauri fetch_price command, service prices section, premium tier detection, PriceFetchControl + consent modal, Settings revocation toggle, i18n + CHANGELOG, production wiring. Server-side work tracked in
maximus-apirepo under milestoneprices-proxy.The original BLOCKED status was overcautious — most client work is mock-driven and decoupled from server availability via the now-frozen contract.