bug: un jeton d'activation d'une autre machine bloque la licence en Gratuite, sans issue dans l'interface #360

Closed
opened 2026-09-27 00:28:31 +00:00 by maximus · 1 comment
Owner

Trouvé en vérifiant la revue de la PR #359 (#270). Un jeton d'activation signé pour une autre machine fait retomber la licence en édition Gratuite, et l'interface n'offre aucun moyen d'en sortir. Le cas se produit après une réinstallation du système ou une migration de poste, dès que le dossier de données de l'application est restauré.

Mécanisme (vérifié dans le code le 2026-09-26)

  • current_entitlements → resolve_license_entitlements (src-tauri/src/commands/license_commands.rs) : si activation.token existe et ne valide pas contre l'identifiant de cette machine, l'édition retombe en Gratuite (règle CWE-863 de l'ADR 0017).
  • L'identifiant vient de machine_uid::get() (/etc/machine-id sous Linux, MachineGuid sous Windows). Il change quand le système est réinstallé ou quand on passe à un autre poste.
  • Le dossier de données (app_data_dir, identifiant com.simpl.resultat) contient license.key et activation.token. Un utilisateur qui le restaure après une réinstallation, ou qui le copie sur un nouveau poste, se retrouve donc en Gratuite.
  • La carte Licence masque la section Machines en Gratuite (hasLicense = edition !== "free", LicenseCard.tsx:17) : aucun bouton « Activer » n'est proposé.
  • Ressaisir la clé ne sert à rien : store_license (l.170-177) n'écrit que license.key, l'ancien jeton reste et l'édition reste Gratuite.
  • Seule sortie aujourd'hui : supprimer à la main le fichier activation.token.
  • Le commentaire de machine_id_internal (l.362) annonce pourtant « the user must re-activate (handled in Issue #53) ». La réactivation existe, mais elle est hors d'atteinte dans cet état.

Ce que la règle protège réellement

Une copie de license.key seule, sans jeton, donne l'édition complète : l'état « pré-activation » est toléré. Faire retomber en Gratuite une machine dont le jeton ne correspond pas n'empêche donc pas la copie. Cela ne pénalise qu'un utilisateur légitime qui a restauré ses données. Le jeton reste en revanche indispensable côté serveur pour les cours du Bilan (/v1/prices), qui le vérifie.

Pistes (à trancher)

  • (a) Interface : afficher « Réactiver cette machine » quand une clé valide est stockée mais que l'édition retombe en Gratuite à cause du jeton. Il faut une commande qui distingue ce cas des autres causes de la Gratuite.
  • (b) Ressaisie de la clé : store_license supprime un jeton qui ne correspond pas à cette machine.
  • (c) Règle de résolution : un jeton d'une autre machine compte comme absent, comme le jeton expiré dans #347 (décision D10). La Gratuite ne s'appliquerait plus qu'aux clés invalides ou expirées, et le serveur continuerait d'exiger un jeton valide pour les cours. C'est un amendement de l'ADR 0017, à pondérer au regard de la règle CWE-863.

Liens

  • #347 (D10 : un jeton expiré mais signé pour cette machine compte comme absent).
  • #350 : changer la dérivation de l'identifiant produirait exactement ce blocage pour toutes les machines déjà activées ; c'est pourquoi #350 prévoit une transition.
  • À traiter avant le go-live (maximus/la-compagnie-maximus#141) : un acheteur qui réinstalle son système perd son édition sans recours.
Trouvé en vérifiant la revue de la PR #359 (#270). Un jeton d'activation signé pour **une autre machine** fait retomber la licence en édition Gratuite, et l'interface n'offre aucun moyen d'en sortir. Le cas se produit après une réinstallation du système ou une migration de poste, dès que le dossier de données de l'application est restauré. ## Mécanisme (vérifié dans le code le 2026-09-26) - `current_entitlements` → `resolve_license_entitlements` (`src-tauri/src/commands/license_commands.rs`) : si `activation.token` existe et ne valide pas contre l'identifiant de cette machine, l'édition retombe en Gratuite (règle CWE-863 de l'ADR 0017). - L'identifiant vient de `machine_uid::get()` (`/etc/machine-id` sous Linux, `MachineGuid` sous Windows). Il change quand le système est réinstallé ou quand on passe à un autre poste. - Le dossier de données (`app_data_dir`, identifiant `com.simpl.resultat`) contient `license.key` **et** `activation.token`. Un utilisateur qui le restaure après une réinstallation, ou qui le copie sur un nouveau poste, se retrouve donc en Gratuite. - La carte Licence masque la section Machines en Gratuite (`hasLicense = edition !== "free"`, `LicenseCard.tsx:17`) : aucun bouton « Activer » n'est proposé. - Ressaisir la clé ne sert à rien : `store_license` (l.170-177) n'écrit que `license.key`, l'ancien jeton reste et l'édition reste Gratuite. - Seule sortie aujourd'hui : supprimer à la main le fichier `activation.token`. - Le commentaire de `machine_id_internal` (l.362) annonce pourtant « the user must re-activate (handled in Issue #53) ». La réactivation existe, mais elle est hors d'atteinte dans cet état. ## Ce que la règle protège réellement Une copie de `license.key` **seule**, sans jeton, donne l'édition complète : l'état « pré-activation » est toléré. Faire retomber en Gratuite une machine dont le jeton ne correspond pas n'empêche donc pas la copie. Cela ne pénalise qu'un utilisateur légitime qui a restauré ses données. Le jeton reste en revanche indispensable côté serveur pour les cours du Bilan (`/v1/prices`), qui le vérifie. ## Pistes (à trancher) - (a) **Interface** : afficher « Réactiver cette machine » quand une clé valide est stockée mais que l'édition retombe en Gratuite à cause du jeton. Il faut une commande qui distingue ce cas des autres causes de la Gratuite. - (b) **Ressaisie de la clé** : `store_license` supprime un jeton qui ne correspond pas à cette machine. - (c) **Règle de résolution** : un jeton d'une autre machine compte comme absent, comme le jeton expiré dans #347 (décision D10). La Gratuite ne s'appliquerait plus qu'aux clés invalides ou expirées, et le serveur continuerait d'exiger un jeton valide pour les cours. C'est un amendement de l'ADR 0017, à pondérer au regard de la règle CWE-863. ## Liens - #347 (D10 : un jeton expiré mais signé pour cette machine compte comme absent). - #350 : changer la dérivation de l'identifiant produirait exactement ce blocage pour toutes les machines déjà activées ; c'est pourquoi #350 prévoit une transition. - À traiter avant le go-live (maximus/la-compagnie-maximus#141) : un acheteur qui réinstalle son système perd son édition sans recours.
maximus added this to the spec-paiements milestone 2026-09-27 00:28:31 +00:00
maximus added the
status:triage
type:bug
source:analyste
labels 2026-09-27 00:28:31 +00:00
Author
Owner

Tranchée par l'analyse de #347 (2026-09-29) : piste (c), élargie. Décision D12 : le jeton d'activation ne fait plus jamais retomber en Gratuite. L'édition suit la seule clé ; le jeton ne sert plus qu'à l'état « Activée » de la carte et aux cours du Bilan, que le serveur vérifie. Après une réinstallation avec restauration des données, la clé garde son édition, et la section Machines reste visible avec « Non activée » et « Activer ».

Motif : une license.key copiée seule garde déjà son édition (état pré-activation toléré), donc la règle ne protégeait rien et ne pénalisait que ce cas légitime. L'ADR 0017 sera amendé (contrainte 2 et règle CWE-863 locale).

Réalisée par #347, qui fermera cette issue à son merge.

Tranchée par l'analyse de #347 (2026-09-29) : piste (c), élargie. Décision **D12** : le jeton d'activation ne fait plus jamais retomber en Gratuite. L'édition suit la seule clé ; le jeton ne sert plus qu'à l'état « Activée » de la carte et aux cours du Bilan, que le serveur vérifie. Après une réinstallation avec restauration des données, la clé garde son édition, et la section Machines reste visible avec « Non activée » et « Activer ». Motif : une `license.key` copiée seule garde déjà son édition (état pré-activation toléré), donc la règle ne protégeait rien et ne pénalisait que ce cas légitime. L'ADR 0017 sera amendé (contrainte 2 et règle CWE-863 locale). Réalisée par #347, qui fermera cette issue à son merge.
maximus added
status:ready
and removed
status:triage
labels 2026-09-30 00:54:19 +00:00
maximus added
status:review
and removed
status:ready
labels 2026-09-30 01:47:53 +00:00
maximus added
status:approved
and removed
status:review
labels 2026-09-30 23:48:22 +00:00
maximus added
status:review
and removed
status:approved
labels 2026-10-01 00:01:08 +00:00
maximus added
status:approved
and removed
status:review
labels 2026-10-01 00:35:43 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: maximus/Simpl-Resultat#360
No description provided.