koa-classic-server 1.1.0 → 2.0.0
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/BENCHMARKS.md +317 -0
- package/CHANGELOG.md +181 -0
- package/CREATE_RELEASE.sh +53 -0
- package/DEBUG_REPORT.md +593 -0
- package/DOCUMENTATION.md +1585 -0
- package/EXAMPLES_INDEX_OPTION.md +395 -0
- package/INDEX_OPTION_PRIORITY.md +527 -0
- package/LICENSE +21 -0
- package/OPTIMIZATION_HTTP_CACHING.md +687 -0
- package/PERFORMANCE_ANALYSIS.md +839 -0
- package/PERFORMANCE_COMPARISON.md +388 -0
- package/README.md +278 -103
- package/__tests__/index-option.test.js +447 -0
- package/__tests__/index.test.js +15 -11
- package/__tests__/performance.test.js +301 -0
- package/__tests__/publicWwwTest/cartella vuota con spazi nel nome/file con spazio nel nome .txt +1 -0
- package/__tests__/security.test.js +336 -0
- package/benchmark-results-baseline-v1.2.0.txt +354 -0
- package/benchmark-results-optimized-v2.0.0.txt +354 -0
- package/benchmark.js +239 -0
- package/demo-regex-index.js +140 -0
- package/index.cjs +386 -156
- package/jest.config.js +18 -0
- package/package.json +18 -5
- package/publish-to-npm.sh +65 -0
- package/scripts/setup-benchmark.js +178 -0
- package/test-regex-quick.js +158 -0
package/DEBUG_REPORT.md
ADDED
|
@@ -0,0 +1,593 @@
|
|
|
1
|
+
# DEBUG REPORT - koa-classic-server
|
|
2
|
+
|
|
3
|
+
**Data Analisi:** 2025-11-17
|
|
4
|
+
**Versione Analizzata:** 1.1.0
|
|
5
|
+
**Branch:** claude/featuring-koa-smart-server-016TZsdaPURgHLiQHmCFJFsk
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Sommario Esecutivo
|
|
10
|
+
|
|
11
|
+
Sono stati identificati **8 problemi** di cui:
|
|
12
|
+
- 🔴 **CRITICI:** 2 (sicurezza)
|
|
13
|
+
- 🟠 **ALTA PRIORITÀ:** 3 (funzionalità core)
|
|
14
|
+
- 🟡 **MEDIA PRIORITÀ:** 2 (robustezza)
|
|
15
|
+
- 🔵 **BASSA PRIORITÀ:** 1 (qualità codice)
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 🔴 PROBLEMI CRITICI
|
|
20
|
+
|
|
21
|
+
### 1. Path Traversal Vulnerability (CRITICO - SICUREZZA)
|
|
22
|
+
|
|
23
|
+
**Location:** `index.cjs:104`
|
|
24
|
+
|
|
25
|
+
**Codice:**
|
|
26
|
+
```javascript
|
|
27
|
+
toOpen = rootDir + decodeURIComponent(pageHrefOutPrefix.pathname);
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Problema:**
|
|
31
|
+
Non c'è validazione del path per prevenire attacchi di path traversal. Un attaccante può accedere a file al di fuori di `rootDir`.
|
|
32
|
+
|
|
33
|
+
**Attacco Esempio:**
|
|
34
|
+
```
|
|
35
|
+
GET /../../../etc/passwd
|
|
36
|
+
GET /../config/database.yml
|
|
37
|
+
GET /../.env
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Impatto:**
|
|
41
|
+
- Accesso non autorizzato a file sensibili del server
|
|
42
|
+
- Potenziale lettura di credenziali, configurazioni, chiavi private
|
|
43
|
+
- Violazione della sicurezza del sistema
|
|
44
|
+
|
|
45
|
+
**Severità:** 🔴 CRITICA
|
|
46
|
+
|
|
47
|
+
**Fix Raccomandato:**
|
|
48
|
+
```javascript
|
|
49
|
+
const path = require('path');
|
|
50
|
+
|
|
51
|
+
// Normalizza e verifica che il path risultante sia dentro rootDir
|
|
52
|
+
const requestedPath = path.normalize(decodeURIComponent(pageHrefOutPrefix.pathname));
|
|
53
|
+
const fullPath = path.join(rootDir, requestedPath);
|
|
54
|
+
|
|
55
|
+
// Verifica che il path finale sia dentro rootDir
|
|
56
|
+
if (!fullPath.startsWith(path.resolve(rootDir))) {
|
|
57
|
+
ctx.status = 403;
|
|
58
|
+
ctx.body = 'Forbidden';
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
toOpen = fullPath;
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Riferimenti:**
|
|
66
|
+
- OWASP: Path Traversal
|
|
67
|
+
- CWE-22: Improper Limitation of a Pathname to a Restricted Directory
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
### 2. Mancanza di Gestione Errori Template Rendering (CRITICO - DISPONIBILITÀ)
|
|
72
|
+
|
|
73
|
+
**Location:** `index.cjs:167`
|
|
74
|
+
|
|
75
|
+
**Codice:**
|
|
76
|
+
```javascript
|
|
77
|
+
await options.template.render(ctx, next, toOpen);
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Problema:**
|
|
81
|
+
Nessun try-catch attorno alla chiamata `template.render`. Se il rendering fallisce, l'errore non gestito può crashare l'applicazione.
|
|
82
|
+
|
|
83
|
+
**Impatto:**
|
|
84
|
+
- Crash del server su errore di rendering
|
|
85
|
+
- Denial of Service potenziale
|
|
86
|
+
- Esperienza utente degradata
|
|
87
|
+
|
|
88
|
+
**Severità:** 🔴 CRITICA
|
|
89
|
+
|
|
90
|
+
**Fix Raccomandato:**
|
|
91
|
+
```javascript
|
|
92
|
+
if (options.template.ext.includes(fileExt)) {
|
|
93
|
+
try {
|
|
94
|
+
await options.template.render(ctx, next, toOpen);
|
|
95
|
+
return;
|
|
96
|
+
} catch (error) {
|
|
97
|
+
console.error('Template rendering error:', error);
|
|
98
|
+
ctx.status = 500;
|
|
99
|
+
ctx.body = 'Internal Server Error - Template Rendering Failed';
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 🟠 PROBLEMI ALTA PRIORITÀ
|
|
108
|
+
|
|
109
|
+
### 3. Status Code 404 Non Settato (ALTA - STANDARD HTTP)
|
|
110
|
+
|
|
111
|
+
**Location:**
|
|
112
|
+
- `index.cjs:110` (file non esiste)
|
|
113
|
+
- `index.cjs:128` (directory listing disabilitato)
|
|
114
|
+
|
|
115
|
+
**Codice:**
|
|
116
|
+
```javascript
|
|
117
|
+
// Linea 110
|
|
118
|
+
if (!fs.existsSync(toOpen)) {
|
|
119
|
+
ctx.body = requestedUrlNotFound();
|
|
120
|
+
// Manca: ctx.status = 404;
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Linea 128
|
|
125
|
+
} else {
|
|
126
|
+
// allora non devo mostrare il contenuto della directory
|
|
127
|
+
ctx.body = requestedUrlNotFound();
|
|
128
|
+
// Manca: ctx.status = 404;
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Problema:**
|
|
133
|
+
Quando una risorsa non viene trovata, viene restituito un body HTML con "Not Found", ma lo status HTTP rimane 200 (OK) invece di 404 (Not Found).
|
|
134
|
+
|
|
135
|
+
**Impatto:**
|
|
136
|
+
- Violazione standard HTTP
|
|
137
|
+
- Cache proxy potrebbero cachare errori come successi
|
|
138
|
+
- SEO negativo (motori di ricerca indicizzano pagine 404 come valide)
|
|
139
|
+
- Client non possono distinguere successo da errore basandosi sullo status code
|
|
140
|
+
|
|
141
|
+
**Verifica:**
|
|
142
|
+
```bash
|
|
143
|
+
curl -I http://localhost:3000/file-che-non-esiste.txt
|
|
144
|
+
# Atteso: HTTP/1.1 404 Not Found
|
|
145
|
+
# Attuale: HTTP/1.1 200 OK
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**Severità:** 🟠 ALTA
|
|
149
|
+
|
|
150
|
+
**Fix Raccomandato:**
|
|
151
|
+
```javascript
|
|
152
|
+
// Linea 110
|
|
153
|
+
if (!fs.existsSync(toOpen)) {
|
|
154
|
+
ctx.status = 404;
|
|
155
|
+
ctx.body = requestedUrlNotFound();
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Linea 128
|
|
160
|
+
} else {
|
|
161
|
+
ctx.status = 404;
|
|
162
|
+
ctx.body = requestedUrlNotFound();
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
### 4. Race Condition nella Lettura File (ALTA - AFFIDABILITÀ)
|
|
169
|
+
|
|
170
|
+
**Location:** `index.cjs:107-172`
|
|
171
|
+
|
|
172
|
+
**Codice:**
|
|
173
|
+
```javascript
|
|
174
|
+
if (!fs.existsSync(toOpen)) {
|
|
175
|
+
// ...
|
|
176
|
+
}
|
|
177
|
+
// ... altre operazioni ...
|
|
178
|
+
const src = fs.createReadStream(toOpen); // Linea 172
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Problema:**
|
|
182
|
+
C'è un gap temporale (TOCTOU - Time-of-check to Time-of-use) tra:
|
|
183
|
+
1. Controllo esistenza file (`existsSync`)
|
|
184
|
+
2. Lettura file (`createReadStream`)
|
|
185
|
+
|
|
186
|
+
Se il file viene cancellato tra questi due momenti, `createReadStream` lancia un errore non gestito.
|
|
187
|
+
|
|
188
|
+
**Impatto:**
|
|
189
|
+
- Possibile crash del server
|
|
190
|
+
- Errore non gestito raggiunge l'utente
|
|
191
|
+
- Log inquinati da stack trace
|
|
192
|
+
|
|
193
|
+
**Severità:** 🟠 ALTA
|
|
194
|
+
|
|
195
|
+
**Fix Raccomandato:**
|
|
196
|
+
```javascript
|
|
197
|
+
async function loadFile(toOpen) {
|
|
198
|
+
// Template rendering logic...
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
// Verifica esistenza prima di stream
|
|
202
|
+
await fs.promises.access(toOpen, fs.constants.R_OK);
|
|
203
|
+
|
|
204
|
+
let mimeType = mime.lookup(toOpen);
|
|
205
|
+
const src = fs.createReadStream(toOpen);
|
|
206
|
+
|
|
207
|
+
// Gestisci errori stream
|
|
208
|
+
src.on('error', (err) => {
|
|
209
|
+
console.error('Stream error:', err);
|
|
210
|
+
if (!ctx.headerSent) {
|
|
211
|
+
ctx.status = 500;
|
|
212
|
+
ctx.body = 'Error reading file';
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
ctx.response.set("content-type", mimeType);
|
|
217
|
+
ctx.response.set("content-disposition",
|
|
218
|
+
`inline; filename=${pageHrefOutPrefix.pathname.substring(1)}`);
|
|
219
|
+
ctx.body = src;
|
|
220
|
+
} catch (error) {
|
|
221
|
+
console.error('File access error:', error);
|
|
222
|
+
ctx.status = 404;
|
|
223
|
+
ctx.body = requestedUrlNotFound();
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
### 5. Estrazione Estensione File Fragile (ALTA - ROBUSTEZZA)
|
|
231
|
+
|
|
232
|
+
**Location:** `index.cjs:163-164`
|
|
233
|
+
|
|
234
|
+
**Codice:**
|
|
235
|
+
```javascript
|
|
236
|
+
const a_path = toOpen.split(".");
|
|
237
|
+
const fileExt = a_path[a_path.length - 1];
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
**Problema:**
|
|
241
|
+
Metodo fragile per estrarre l'estensione:
|
|
242
|
+
- File senza estensione: `README` → estensione = "README" (errato)
|
|
243
|
+
- File con più punti: `archive.tar.gz` → estensione = "gz" (potrebbe essere errato)
|
|
244
|
+
- File nascosti Unix: `.gitignore` → estensione = "gitignore" (errato)
|
|
245
|
+
- Path con punti: `/folder.backup/file` → estensione = "backup/file" (errato)
|
|
246
|
+
|
|
247
|
+
**Impatto:**
|
|
248
|
+
- Template rendering potrebbe attivarsi su file sbagliati
|
|
249
|
+
- File nascosti potrebbero essere processati erroneamente
|
|
250
|
+
|
|
251
|
+
**Severità:** 🟠 ALTA
|
|
252
|
+
|
|
253
|
+
**Fix Raccomandato:**
|
|
254
|
+
```javascript
|
|
255
|
+
const path = require('path');
|
|
256
|
+
|
|
257
|
+
async function loadFile(toOpen) {
|
|
258
|
+
if (options.template.ext.length > 0) {
|
|
259
|
+
// Usa path.extname che gestisce correttamente tutti i casi
|
|
260
|
+
const fileExt = path.extname(toOpen).slice(1); // .slice(1) rimuove il punto
|
|
261
|
+
|
|
262
|
+
if (fileExt && options.template.ext.includes(fileExt)) {
|
|
263
|
+
try {
|
|
264
|
+
await options.template.render(ctx, next, toOpen);
|
|
265
|
+
return;
|
|
266
|
+
} catch (error) {
|
|
267
|
+
console.error('Template rendering error:', error);
|
|
268
|
+
ctx.status = 500;
|
|
269
|
+
ctx.body = 'Internal Server Error';
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
// ... resto del codice
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## 🟡 PROBLEMI MEDIA PRIORITÀ
|
|
281
|
+
|
|
282
|
+
### 6. Mancanza Gestione Errori fs.readdirSync (MEDIA - ROBUSTEZZA)
|
|
283
|
+
|
|
284
|
+
**Location:** `index.cjs:183`
|
|
285
|
+
|
|
286
|
+
**Codice:**
|
|
287
|
+
```javascript
|
|
288
|
+
function show_dir(toOpen) {
|
|
289
|
+
dir = fs.readdirSync(toOpen, { withFileTypes: true }); // possibile error error.code == "ENOENT" ???
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
**Problema:**
|
|
293
|
+
Il commento stesso riconosce il problema: `readdirSync` può lanciare errori (permessi insufficienti, directory cancellata, etc.) ma non c'è gestione.
|
|
294
|
+
|
|
295
|
+
**Errori Possibili:**
|
|
296
|
+
- `ENOENT`: Directory non esiste più
|
|
297
|
+
- `EACCES`: Permessi insufficienti
|
|
298
|
+
- `ENOTDIR`: Path non è una directory
|
|
299
|
+
|
|
300
|
+
**Impatto:**
|
|
301
|
+
- Crash su errori filesystem
|
|
302
|
+
- Messaggi di errore criptici all'utente
|
|
303
|
+
|
|
304
|
+
**Severità:** 🟡 MEDIA
|
|
305
|
+
|
|
306
|
+
**Fix Raccomandato:**
|
|
307
|
+
```javascript
|
|
308
|
+
function show_dir(toOpen) {
|
|
309
|
+
let dir;
|
|
310
|
+
try {
|
|
311
|
+
dir = fs.readdirSync(toOpen, { withFileTypes: true });
|
|
312
|
+
} catch (error) {
|
|
313
|
+
console.error('Directory read error:', error);
|
|
314
|
+
return `
|
|
315
|
+
<!DOCTYPE html>
|
|
316
|
+
<html>
|
|
317
|
+
<head><title>Error</title></head>
|
|
318
|
+
<body>
|
|
319
|
+
<h1>Error Reading Directory</h1>
|
|
320
|
+
<p>Unable to access directory contents.</p>
|
|
321
|
+
</body>
|
|
322
|
+
</html>
|
|
323
|
+
`;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// ... resto della funzione
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
### 7. Gestione Inconsistente Content-Disposition (MEDIA - QUALITÀ)
|
|
333
|
+
|
|
334
|
+
**Location:** `index.cjs:174-177`
|
|
335
|
+
|
|
336
|
+
**Codice:**
|
|
337
|
+
```javascript
|
|
338
|
+
ctx.response.set(
|
|
339
|
+
"content-disposition",
|
|
340
|
+
`inline; filename=${pageHrefOutPrefix.pathname.substring(1)}`
|
|
341
|
+
);
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
**Problema:**
|
|
345
|
+
Il filename in Content-Disposition non è quotato e non è sanitizzato:
|
|
346
|
+
- Caratteri speciali nel filename potrebbero causare problemi
|
|
347
|
+
- Spazi e caratteri non-ASCII non sono gestiti
|
|
348
|
+
- Secondo RFC 6266, il filename dovrebbe essere quotato se contiene caratteri speciali
|
|
349
|
+
|
|
350
|
+
**Impatto:**
|
|
351
|
+
- Download file con nomi strani potrebbero fallire
|
|
352
|
+
- Alcuni browser potrebbero interpretare male il filename
|
|
353
|
+
|
|
354
|
+
**Severità:** 🟡 MEDIA
|
|
355
|
+
|
|
356
|
+
**Fix Raccomandato:**
|
|
357
|
+
```javascript
|
|
358
|
+
const path = require('path');
|
|
359
|
+
|
|
360
|
+
// Estrai solo il basename, non l'intero path
|
|
361
|
+
const filename = path.basename(pageHrefOutPrefix.pathname);
|
|
362
|
+
|
|
363
|
+
// Quota il filename per sicurezza
|
|
364
|
+
ctx.response.set(
|
|
365
|
+
"content-disposition",
|
|
366
|
+
`inline; filename="${filename.replace(/"/g, '\\"')}"`
|
|
367
|
+
);
|
|
368
|
+
|
|
369
|
+
// O ancora meglio, usa una libreria come content-disposition:
|
|
370
|
+
// const contentDisposition = require('content-disposition');
|
|
371
|
+
// ctx.response.set("content-disposition", contentDisposition(filename, { type: 'inline' }));
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
## 🔵 PROBLEMI BASSA PRIORITÀ
|
|
377
|
+
|
|
378
|
+
### 8. Uso di Array() Invece di [] (BASSA - STILE)
|
|
379
|
+
|
|
380
|
+
**Location:** Multiple locations
|
|
381
|
+
|
|
382
|
+
**Codice:**
|
|
383
|
+
```javascript
|
|
384
|
+
options.method = Array.isArray( options.method ) ? options.method : Array('GET');
|
|
385
|
+
options.urlsReserved = Array.isArray( options.urlsReserved ) ? options.urlsReserved : Array();
|
|
386
|
+
options.template.ext = ( Array.isArray(options.template.ext) ) ? options.template.ext : Array();
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
**Problema:**
|
|
390
|
+
`Array('GET')` crea un array con un elemento, ma è meno idiomatico di `['GET']`. Inoltre `Array()` è meno leggibile di `[]`.
|
|
391
|
+
|
|
392
|
+
**Impatto:**
|
|
393
|
+
- Nessun impatto funzionale
|
|
394
|
+
- Ridotta leggibilità codice
|
|
395
|
+
|
|
396
|
+
**Severità:** 🔵 BASSA
|
|
397
|
+
|
|
398
|
+
**Fix Raccomandato:**
|
|
399
|
+
```javascript
|
|
400
|
+
options.method = Array.isArray(options.method) ? options.method : ['GET'];
|
|
401
|
+
options.urlsReserved = Array.isArray(options.urlsReserved) ? options.urlsReserved : [];
|
|
402
|
+
options.template.ext = Array.isArray(options.template.ext) ? options.template.ext : [];
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
---
|
|
406
|
+
|
|
407
|
+
## Test dei Problemi
|
|
408
|
+
|
|
409
|
+
### Test Path Traversal (Problema #1)
|
|
410
|
+
|
|
411
|
+
```javascript
|
|
412
|
+
// test-path-traversal.js
|
|
413
|
+
const supertest = require('supertest');
|
|
414
|
+
const Koa = require('koa');
|
|
415
|
+
const koaClassicServer = require('./index.cjs');
|
|
416
|
+
|
|
417
|
+
const app = new Koa();
|
|
418
|
+
app.use(koaClassicServer(__dirname + '/public'));
|
|
419
|
+
const server = app.listen();
|
|
420
|
+
|
|
421
|
+
// Test attacco path traversal
|
|
422
|
+
supertest(server)
|
|
423
|
+
.get('/../package.json') // Tenta di accedere fuori da public/
|
|
424
|
+
.end((err, res) => {
|
|
425
|
+
console.log('Status:', res.status);
|
|
426
|
+
console.log('Body contains package.json?', res.text.includes('"name"'));
|
|
427
|
+
// Se vedi il contenuto di package.json, la vulnerabilità è confermata
|
|
428
|
+
server.close();
|
|
429
|
+
});
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
### Test Status Code 404 (Problema #3)
|
|
433
|
+
|
|
434
|
+
```javascript
|
|
435
|
+
// test-404-status.js
|
|
436
|
+
const supertest = require('supertest');
|
|
437
|
+
const Koa = require('koa');
|
|
438
|
+
const koaClassicServer = require('./index.cjs');
|
|
439
|
+
|
|
440
|
+
const app = new Koa();
|
|
441
|
+
app.use(koaClassicServer(__dirname + '/public'));
|
|
442
|
+
const server = app.listen();
|
|
443
|
+
|
|
444
|
+
supertest(server)
|
|
445
|
+
.get('/file-che-non-esiste.txt')
|
|
446
|
+
.end((err, res) => {
|
|
447
|
+
console.log('Status Code:', res.status);
|
|
448
|
+
console.log('Expected: 404, Got:', res.status);
|
|
449
|
+
console.log('BUG CONFIRMED:', res.status === 200); // true = bug presente
|
|
450
|
+
server.close();
|
|
451
|
+
});
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### Test Template Error (Problema #2)
|
|
455
|
+
|
|
456
|
+
```javascript
|
|
457
|
+
// test-template-error.js
|
|
458
|
+
const Koa = require('koa');
|
|
459
|
+
const koaClassicServer = require('./index.cjs');
|
|
460
|
+
|
|
461
|
+
const app = new Koa();
|
|
462
|
+
|
|
463
|
+
// Template render che lancia errore
|
|
464
|
+
const brokenRender = async (ctx, next, filePath) => {
|
|
465
|
+
throw new Error('Simulated template error');
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
app.use(koaClassicServer(__dirname + '/public', {
|
|
469
|
+
template: {
|
|
470
|
+
render: brokenRender,
|
|
471
|
+
ext: ['html']
|
|
472
|
+
}
|
|
473
|
+
}));
|
|
474
|
+
|
|
475
|
+
const server = app.listen(3000);
|
|
476
|
+
|
|
477
|
+
// Accedi a un file .html
|
|
478
|
+
// Il server crasherà con errore non gestito
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
---
|
|
482
|
+
|
|
483
|
+
## Priorità di Fix
|
|
484
|
+
|
|
485
|
+
### Immediate (Prima del Deploy)
|
|
486
|
+
1. **Path Traversal** (Problema #1) - CRITICO
|
|
487
|
+
2. **Status Code 404** (Problema #3) - ALTA
|
|
488
|
+
|
|
489
|
+
### Breve Termine (Prossimo Release)
|
|
490
|
+
3. **Template Error Handling** (Problema #2) - CRITICO
|
|
491
|
+
4. **Race Condition File** (Problema #4) - ALTA
|
|
492
|
+
5. **Estrazione Estensione** (Problema #5) - ALTA
|
|
493
|
+
|
|
494
|
+
### Medio Termine (Miglioramenti)
|
|
495
|
+
6. **fs.readdirSync Error** (Problema #6) - MEDIA
|
|
496
|
+
7. **Content-Disposition** (Problema #7) - MEDIA
|
|
497
|
+
|
|
498
|
+
### Opzionale (Refactoring)
|
|
499
|
+
8. **Array() vs []** (Problema #8) - BASSA
|
|
500
|
+
|
|
501
|
+
---
|
|
502
|
+
|
|
503
|
+
## Miglioramenti Generali Raccomandati
|
|
504
|
+
|
|
505
|
+
### 1. Aggiungere Validazione Input
|
|
506
|
+
```javascript
|
|
507
|
+
// All'inizio della funzione principale
|
|
508
|
+
if (!rootDir || typeof rootDir !== 'string') {
|
|
509
|
+
throw new TypeError('rootDir must be a non-empty string');
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
if (!path.isAbsolute(rootDir)) {
|
|
513
|
+
throw new Error('rootDir must be an absolute path');
|
|
514
|
+
}
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
### 2. Logging Strutturato
|
|
518
|
+
```javascript
|
|
519
|
+
// Opzione per logging
|
|
520
|
+
options.logger = options.logger || console;
|
|
521
|
+
|
|
522
|
+
// Usa nel codice
|
|
523
|
+
options.logger.error('File not found:', toOpen);
|
|
524
|
+
options.logger.warn('Template rendering failed:', error);
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
### 3. Timeout per Template Rendering
|
|
528
|
+
```javascript
|
|
529
|
+
// Previeni template rendering infiniti
|
|
530
|
+
const renderWithTimeout = (renderFn, timeout = 5000) => {
|
|
531
|
+
return Promise.race([
|
|
532
|
+
renderFn(),
|
|
533
|
+
new Promise((_, reject) =>
|
|
534
|
+
setTimeout(() => reject(new Error('Render timeout')), timeout)
|
|
535
|
+
)
|
|
536
|
+
]);
|
|
537
|
+
};
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
### 4. Sanitizzazione HTML in Directory Listing
|
|
541
|
+
```javascript
|
|
542
|
+
// Previeni XSS in nomi file
|
|
543
|
+
function escapeHtml(unsafe) {
|
|
544
|
+
return unsafe
|
|
545
|
+
.replace(/&/g, "&")
|
|
546
|
+
.replace(/</g, "<")
|
|
547
|
+
.replace(/>/g, ">")
|
|
548
|
+
.replace(/"/g, """)
|
|
549
|
+
.replace(/'/g, "'");
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// Usa quando mostri nomi file
|
|
553
|
+
s_dir += ` <a href="${itemUri}">${escapeHtml(s_name)}</a>`;
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
---
|
|
557
|
+
|
|
558
|
+
## Metriche Codice
|
|
559
|
+
|
|
560
|
+
### Complessità Ciclomatica
|
|
561
|
+
- **Funzione principale:** ~25 (Alta - da ridurre)
|
|
562
|
+
- **show_dir:** ~15 (Media)
|
|
563
|
+
- **loadFile:** ~5 (Bassa)
|
|
564
|
+
|
|
565
|
+
### Raccomandazione
|
|
566
|
+
Spezzare la funzione principale in sottofunzioni più piccole:
|
|
567
|
+
- `validateRequest()`
|
|
568
|
+
- `checkReservedUrls()`
|
|
569
|
+
- `resolveFilePath()`
|
|
570
|
+
- `handleResource()`
|
|
571
|
+
|
|
572
|
+
---
|
|
573
|
+
|
|
574
|
+
## Conclusioni
|
|
575
|
+
|
|
576
|
+
Il progetto koa-classic-server presenta **2 vulnerabilità critiche di sicurezza** che devono essere risolte immediatamente prima di qualsiasi deploy in produzione:
|
|
577
|
+
|
|
578
|
+
1. **Path Traversal** - permette accesso a file arbitrari
|
|
579
|
+
2. **Template Error Unhandled** - può causare crash del server
|
|
580
|
+
|
|
581
|
+
Inoltre, ci sono **3 problemi di alta priorità** che impattano conformità agli standard HTTP e affidabilità:
|
|
582
|
+
|
|
583
|
+
3. Status code 404 mancante
|
|
584
|
+
4. Race condition nella lettura file
|
|
585
|
+
5. Estrazione estensione fragile
|
|
586
|
+
|
|
587
|
+
**Raccomandazione:** Implementare i fix per problemi #1, #2, #3 prima del prossimo release.
|
|
588
|
+
|
|
589
|
+
---
|
|
590
|
+
|
|
591
|
+
**Report compilato da:** Claude Code Analysis
|
|
592
|
+
**Branch analizzata:** claude/featuring-koa-smart-server-016TZsdaPURgHLiQHmCFJFsk
|
|
593
|
+
**Commit:** f8693a0
|