@open3cl/engine 1.8.6 → 1.8.7

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/README.md CHANGED
@@ -403,24 +403,24 @@ Ces résultats sont **générés automatiquement** à la fin de `npm run test:co
403
403
 
404
404
  <table>
405
405
  <tr>
406
- <td align="center"><strong>5,01 ms</strong><br/><sub>moyenne</sub></td>
407
- <td align="center"><strong>4,31 ms</strong><br/><sub>médiane</sub></td>
408
- <td align="center"><strong>0,13 ms</strong><br/><sub>min</sub></td>
409
- <td align="center"><strong>567,2 ms</strong><br/><sub>max</sub></td>
406
+ <td align="center"><strong>4,99 ms</strong><br/><sub>moyenne</sub></td>
407
+ <td align="center"><strong>4,34 ms</strong><br/><sub>médiane</sub></td>
408
+ <td align="center"><strong>0,12 ms</strong><br/><sub>min</sub></td>
409
+ <td align="center"><strong>795,0 ms</strong><br/><sub>max</sub></td>
410
410
  </tr>
411
411
  </table>
412
412
 
413
413
  | Corpus | Moyenne | Médiane | Min | Max | p95 | p99 |
414
414
  | :--------------------------------------------- | ------: | ------: | ------: | -------: | ------: | ------: |
415
- | **Généraliste** | 4,04 ms | 3,63 ms | 0,21 ms | 24,9 ms | 7,45 ms | 10,8 ms |
416
- | **Appartement · chauffage individuel (2025)** | 3,20 ms | 2,93 ms | 1,18 ms | 28,6 ms | 5,51 ms | 7,45 ms |
417
- | **Logement individuel (2025)** | 4,73 ms | 4,22 ms | 1,38 ms | 33,1 ms | 8,88 ms | 12,4 ms |
418
- | **Maison individuelle (2025)** | 5,37 ms | 4,79 ms | 1,68 ms | 52,5 ms | 9,69 ms | 13,3 ms |
419
- | **Immeuble · chauffage individuel** | 7,43 ms | 6,08 ms | 0,19 ms | 137,8 ms | 16,0 ms | 28,0 ms |
420
- | **Appartement · chauffage collectif (2025)** | 3,82 ms | 3,40 ms | 1,24 ms | 31,6 ms | 6,52 ms | 8,64 ms |
421
- | **Immeuble · chauffage collectif** | 5,47 ms | 4,55 ms | 0,13 ms | 104,3 ms | 10,4 ms | 19,2 ms |
422
- | **Immeuble · chauffage mixte** | 5,31 ms | 4,38 ms | 0,22 ms | 567,2 ms | 10,9 ms | 17,8 ms |
423
- | **Individuel généré depuis l'immeuble (2026)** | 5,72 ms | 4,85 ms | 1,75 ms | 61,2 ms | 11,0 ms | 17,3 ms |
415
+ | **Généraliste** | 4,35 ms | 3,85 ms | 0,20 ms | 33,1 ms | 8,16 ms | 12,1 ms |
416
+ | **Appartement · chauffage individuel (2025)** | 3,41 ms | 3,06 ms | 1,22 ms | 28,0 ms | 6,11 ms | 8,14 ms |
417
+ | **Logement individuel (2025)** | 4,94 ms | 4,40 ms | 1,34 ms | 39,1 ms | 9,35 ms | 13,0 ms |
418
+ | **Maison individuelle (2025)** | 5,73 ms | 5,16 ms | 1,68 ms | 46,0 ms | 10,3 ms | 14,4 ms |
419
+ | **Immeuble · chauffage individuel** | 6,80 ms | 5,84 ms | 0,17 ms | 103,2 ms | 13,6 ms | 21,9 ms |
420
+ | **Appartement · chauffage collectif (2025)** | 3,94 ms | 3,59 ms | 1,28 ms | 32,1 ms | 6,56 ms | 8,87 ms |
421
+ | **Immeuble · chauffage collectif** | 5,42 ms | 4,60 ms | 0,12 ms | 107,9 ms | 10,3 ms | 17,0 ms |
422
+ | **Immeuble · chauffage mixte** | 5,14 ms | 4,17 ms | 0,19 ms | 795,0 ms | 10,7 ms | 17,3 ms |
423
+ | **Individuel généré depuis l'immeuble (2026)** | 5,14 ms | 4,41 ms | 1,54 ms | 44,0 ms | 9,66 ms | 14,5 ms |
424
424
 
425
425
  <sub>La moyenne est tirée vers le haut par les DPE collectifs, dont le coût atteint plusieurs
426
426
  dizaines de fois la médiane : c’est la médiane qui décrit le cas courant, et p95/p99 le cas
@@ -10,7 +10,11 @@ export class FileStore {
10
10
  * @return {Promise<any>}
11
11
  */
12
12
  async downloadXlsxFileAndConvertToJson(url) {
13
- return fetch(url)
13
+ const parsedUrl = new URL(url);
14
+ if (!['http:', 'https:'].includes(parsedUrl.protocol)) {
15
+ throw new Error(`Unsupported protocol for url: ${url}`);
16
+ }
17
+ return fetch(parsedUrl)
14
18
  .then((res) => res.arrayBuffer())
15
19
  .then((buffer) =>
16
20
  this.#excelWorkBookToJson(XLSX.read(buffer, { type: 'string', raw: false }))
@@ -39,6 +39,7 @@ describe('FileStore', () => {
39
39
  vi.mocked(XLSX.utils.sheet_to_json).mockReset();
40
40
  vi.mocked(XLSX.utils.encode_col).mockClear();
41
41
  vi.mocked(XLSX.utils.encode_row).mockClear();
42
+ vi.mocked(global.fetch).mockClear();
42
43
 
43
44
  // Comportements par défaut « nominaux ».
44
45
  vi.mocked(readFile).mockImplementation((path, cb) => cb(null, Buffer.from(new ArrayBuffer(1))));
@@ -57,13 +58,44 @@ describe('FileStore', () => {
57
58
  const output = await fileStore.downloadXlsxFileAndConvertToJson('http://localhost:8080');
58
59
 
59
60
  expect(output).toEqual({ Sheet1: [] });
60
- // Le buffer téléchargé est lu par XLSX avec les bonnes options.
61
- expect(fetch).toHaveBeenCalledWith('http://localhost:8080');
61
+ // La protection CWE-918 construit une véritable instance `URL` (validée) avant l'appel à
62
+ // `fetch` : on vérifie le type et le `href` réel plutôt qu'une égalité de chaîne, car
63
+ // `new URL()` normalise l'URL (ajout du `/` final).
64
+ expect(fetch).toHaveBeenCalledTimes(1);
65
+ const [urlArgument] = vi.mocked(fetch).mock.calls[0];
66
+ expect(urlArgument).toBeInstanceOf(URL);
67
+ expect(urlArgument.href).toBe('http://localhost:8080/');
62
68
  expect(XLSX.read).toHaveBeenCalledWith(expect.anything(), {
63
69
  type: 'string',
64
70
  raw: false
65
71
  });
66
72
  });
73
+
74
+ describe('validation du protocole (CWE-918)', () => {
75
+ test.each(['http://localhost:8080', 'https://localhost:8080'])(
76
+ 'accepte les URLs avec le protocole %s',
77
+ async (url) => {
78
+ const fileStore = new FileStore();
79
+
80
+ await expect(fileStore.downloadXlsxFileAndConvertToJson(url)).resolves.toEqual({
81
+ Sheet1: []
82
+ });
83
+
84
+ expect(fetch).toHaveBeenCalledTimes(1);
85
+ }
86
+ );
87
+
88
+ test("rejette une URL avec un protocole non http/https (ex. file://) et n'appelle pas fetch", async () => {
89
+ const fileStore = new FileStore();
90
+ const url = 'file:///etc/passwd';
91
+
92
+ await expect(fileStore.downloadXlsxFileAndConvertToJson(url)).rejects.toThrow(
93
+ `Unsupported protocol for url: ${url}`
94
+ );
95
+
96
+ expect(fetch).not.toHaveBeenCalled();
97
+ });
98
+ });
67
99
  });
68
100
 
69
101
  describe('readLocalOdsFileAndConvertToJson', () => {
package/engine.js CHANGED
@@ -33,7 +33,7 @@ import getFicheTechnique from './ficheTechnique.js';
33
33
  import { ProductionENR } from './16.2_production_enr.js';
34
34
  import DpeSanitizerService from './dpe-sanitizer.service.js';
35
35
 
36
- const LIB_VERSION = '1.8.6';
36
+ const LIB_VERSION = '1.8.7';
37
37
 
38
38
  const dpeSanitizerService = new DpeSanitizerService();
39
39
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open3cl/engine",
3
- "version": "1.8.6",
3
+ "version": "1.8.7",
4
4
  "description": "Open Source 3CL-DPE engine",
5
5
  "main": "index.js",
6
6
  "directories": {