@mostajs/pdf-sign-ui 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,53 @@
4
4
 
5
5
  Le format suit [Keep a Changelog](https://keepachangelog.com/fr/) ; versionnage [SemVer](https://semver.org/lang/fr/).
6
6
 
7
+ ## [0.3.1] — 2026-09-29
8
+
9
+ ### Corrigé — « valide » exige intact ET valid ÉTABLIS
10
+ - `buildVerifyViewModel` tenait une signature pour valide dès que `intact` et `valid` n'étaient
11
+ **pas false**. `undefined` passait donc — or c'est ce que rend la détection du moteur Node :
12
+ une signature **présente**, rien de **vérifié**. Un document non vérifié s'affichait
13
+ « Signature(s) valide(s) ». Il faut désormais `intact === true` **et** `valid === true`.
14
+ - `statusLabel` affichait « Valide et de confiance » dès que `trusted` était vrai, **avant** de
15
+ regarder l'intégrité. La confiance ne se lit plus qu'après elle.
16
+ - Aucun libellé nouveau : les quatre existants suffisent, rien à traduire.
17
+
18
+ Relevé le 29/09/2026 en vérifiant `@mostajs/pdf-sign` 0.4.1, qui cesse de son côté de retomber
19
+ en silence sur la détection Node. Les deux corrections se complètent : l'une ne produit plus le
20
+ cas, l'autre ne le prend plus pour un succès s'il arrive.
21
+
22
+ ### Essais
23
+ - V-UI-1 à 5 : détection Node, document altéré (`valid:true, intact:false`), confiance sans
24
+ intégrité, valide sans confiance, une signature non vérifiée parmi deux valides. Contre-épreuve :
25
+ l'ancienne règle fait échouer V-UI-1, 3 et 5.
26
+
27
+ ### Modifié
28
+ - `peerDependencies["@mostajs/pdf-sign"]` : `^0.3.0` → `^0.3.0 || ^0.4.0` (en 0.x, `^0.3.0`
29
+ refusait la 0.4).
30
+
31
+ ## [0.3.0] — 2026-09-26
32
+
33
+ ### Ajouté
34
+ - `buildSignRequest` transmet **`timestampUrl`** (horodatage RFC 3161, `@mostajs/pdf-sign` 0.3.0).
35
+ L'option s'y perdait **en silence** : le document sortait sans horodatage alors que l'appelant
36
+ croyait la date attestée par un tiers — donc opposable. Découvert en production.
37
+ - **Avertissement sur les clés perdues.** C'est la DEUXIÈME option évanouie de la même façon après
38
+ `openPassword` en 0.2.0, et le commentaire du code mettait pourtant en garde contre ce piège
39
+ exact. Un jeu de clés fermé est défendable ; le fermer **en silence** ne l'est pas.
40
+ `buildSignRequest` nomme désormais les options qu'il ignore.
41
+
42
+ On avertit sans lever : lever casserait un appelant passant une clé supplémentaire inoffensive,
43
+ alors que le but est seulement que la perte cesse d'être invisible.
44
+
45
+ ### Essais
46
+ - Quatre essais tiennent désormais ce que les commentaires ne retenaient pas : `timestampUrl`
47
+ transmis, vide écarté, clé inconnue **nommée** dans l'avertissement, et silence sur un
48
+ formulaire normal — un avertissement qui se déclenche toujours ne se lit plus.
49
+
50
+ ### Modifié
51
+ - `peerDependencies["@mostajs/pdf-sign"]` : `^0.2.0` → `^0.3.0`.
52
+
53
+
7
54
  ## 0.2.0 — 2026-09-21
8
55
 
9
56
  ### Ajouté
package/llms.txt CHANGED
@@ -1,7 +1,7 @@
1
1
  # @mostajs/pdf-sign-ui — fiche LLM
2
2
  > Couche de PRÉSENTATION de @mostajs/pdf-sign : logique HEADLESS (validation du formulaire de signature, préréglages de documents officiels/SIH, view-models moteur & vérification, contrôleur orchestrant un `service` injecté). Framework-agnostique, zéro dépendance, testable sans navigateur. IMPLÉMENTÉ 0.2.0.
3
3
 
4
- - Version: 0.2.0 · Licence: AGPL-3.0-or-later · Auteur: Dr Hamid MADANI <drmdh@msn.com>
4
+ - Version: 0.3.1 · Licence: AGPL-3.0-or-later · Auteur: Dr Hamid MADANI <drmdh@msn.com>
5
5
  - Chemin: mostajs/mosta-pdf-sign-stack/mosta-pdf-sign-ui · Moteur: ../mosta-pdf-sign
6
6
 
7
7
  ## RÔLE
@@ -49,3 +49,5 @@ Composants en React.createElement (pas de JSX à transpiler ; bundler pour les .
49
49
  NOMME (et non celui des permissions).
50
50
  - buildEngineViewModel expose `canLock`.
51
51
  - peerDependency @mostajs/pdf-sign passe à ^0.2.0 (l'option n'existe pas avant).
52
+ - buildVerifyViewModel (0.3.1) : « valide » EXIGE intact===true ET valid===true. undefined (détection
53
+ Node : présente, non vérifiée) ⇒ « Présente (non validée) ». valid:true + intact:false = ALTÉRÉ ⇒ « Invalide ».
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mostajs/pdf-sign-ui",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "Couche de présentation de @mostajs/pdf-sign — logique headless (validation du formulaire de signature, préréglages de documents officiels/SIH, view-models de vérification, contrôleur). Framework-agnostique, zéro dépendance.",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "author": "Dr Hamid MADANI <drmdh@msn.com>",
@@ -22,7 +22,7 @@
22
22
  "example": "node examples/verify-viewmodel.mjs"
23
23
  },
24
24
  "peerDependencies": {
25
- "@mostajs/pdf-sign": "^0.2.0",
25
+ "@mostajs/pdf-sign": "^0.3.0 || ^0.4.0",
26
26
  "react": ">=18"
27
27
  },
28
28
  "peerDependenciesMeta": {
package/src/headless.js CHANGED
@@ -68,6 +68,33 @@ function isBox(b) {
68
68
  * @param {object} data
69
69
  * @returns {import("@mostajs/pdf-sign").SignOptions}
70
70
  */
71
+ /**
72
+ * Les clés que `buildSignRequest` sait recopier. Tout le reste est PERDU.
73
+ *
74
+ * ⚠️ Deux options s'y sont déjà évanouies sans bruit : `openPassword` — le document sortait
75
+ * déverrouillé alors que l'appelant le croyait fermé — puis `timestampUrl` — le document sortait
76
+ * sans horodatage alors que l'appelant croyait la date attestée. Deux fois, le même mécanisme, et
77
+ * deux fois découvert en production.
78
+ *
79
+ * Fermer le jeu est un choix défendable ; le fermer EN SILENCE ne l'est pas. On avertit donc, sans
80
+ * lever : lever casserait un appelant qui passe une clé supplémentaire inoffensive, alors que le
81
+ * but est seulement que la perte cesse d'être invisible.
82
+ */
83
+ const CLES_CONNUES = new Set([
84
+ 'input', 'output', 'p12', 'passphrase', 'passphraseFile', 'engine',
85
+ 'reason', 'name', 'location', 'contact', 'preset',
86
+ 'page', 'box', 'stampImage', 'permissions', 'restrict',
87
+ 'openPassword', 'timestampUrl',
88
+ ]);
89
+
90
+ function signalerClesPerdues(data) {
91
+ const perdues = Object.keys(data || {}).filter((k) => !CLES_CONNUES.has(k) && data[k] !== undefined);
92
+ if (perdues.length) {
93
+ console.warn('[pdf-sign-ui] option(s) ignorée(s) par buildSignRequest, donc PERDUE(S) : '
94
+ + perdues.join(', ') + '. Ajoutez-les au jeu de clés ou retirez-les de l’appel.');
95
+ }
96
+ }
97
+
71
98
  export function buildSignRequest(data = {}) {
72
99
  const preset = data.preset ? getPreset(data.preset) : undefined;
73
100
  const reason = data.reason || preset?.reason;
@@ -99,7 +126,12 @@ export function buildSignRequest(data = {}) {
99
126
  // et le document sort sans elle — sans erreur. `openPassword` a été perdu ainsi jusqu'en
100
127
  // 0.2.0 : le document s'ouvrait alors que l'appelant le croyait verrouillé.
101
128
  openPassword: data.openPassword || undefined,
129
+ // Horodatage RFC 3161 (`@mostajs/pdf-sign` 0.3.0) : fait passer la signature de PAdES-B à
130
+ // PAdES-T. Perdu ici en silence lors de sa première intégration — exactement comme
131
+ // `openPassword` avant lui. D'où l'avertissement ci-dessous.
132
+ timestampUrl: data.timestampUrl || undefined,
102
133
  };
134
+ signalerClesPerdues(data);
103
135
  return prune(req);
104
136
  }
105
137
 
@@ -151,7 +183,14 @@ export function buildVerifyViewModel(result, opts = {}) {
151
183
  signer: s.signer,
152
184
  status: statusLabel(s, t),
153
185
  }));
154
- const allValid = rows.length > 0 && rows.every((r) => r.valid !== false && r.intact !== false);
186
+ // ⚠️ « Valide » exige que les DEUX soient établis — `intact === true` ET `valid === true`.
187
+ //
188
+ // Jusqu'en 0.3.0, on lisait « pas false » : `undefined` passait pour valide. Or c'est exactement
189
+ // ce que rend la détection du moteur Node (`intact` et `valid` indéfinis : une signature est
190
+ // PRÉSENTE, rien n'a été VÉRIFIÉ) — et un document non vérifié s'affichait « Signature(s)
191
+ // valide(s) ». `valid` seul ne suffit pas non plus : sur un document ALTÉRÉ, pyhanko rend
192
+ // `valid: true` et `intact: false`.
193
+ const allValid = rows.length > 0 && rows.every((r) => r.valid === true && r.intact === true);
155
194
  return {
156
195
  signed: true,
157
196
  engine: result.engine,
@@ -162,8 +201,11 @@ export function buildVerifyViewModel(result, opts = {}) {
162
201
 
163
202
  function statusLabel(s, t) {
164
203
  if (s.valid === false || s.intact === false) return t("Invalide");
165
- if (s.trusted) return t("Valide et de confiance");
166
- if (s.valid || s.intact) return t("Valide (confiance non établie)");
204
+ // La confiance ne se lit qu'APRÈS l'intégrité : un certificat reconnu sur un document dont on
205
+ // ne sait pas s'il a changé ne rend pas ce document « valide et de confiance ».
206
+ if (s.valid === true && s.intact === true) {
207
+ return s.trusted ? t("Valide et de confiance") : t("Valide (confiance non établie)");
208
+ }
167
209
  return t("Présente (non validée)");
168
210
  }
169
211