cdd-cli 3.1.1 → 3.1.3

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.
Files changed (42) hide show
  1. package/AUDIT_REPORT.md +1004 -0
  2. package/CHANGELOG.md +22 -0
  3. package/FIXES_APPLIED.md +421 -0
  4. package/dist/App.js +7 -9
  5. package/dist/components/ContainerCreationPrompt.js +1 -1
  6. package/dist/components/ContainerList.js +2 -4
  7. package/dist/components/ContainerRow.js +20 -14
  8. package/dist/components/ContainerSection.js +1 -1
  9. package/dist/helpers/actionHelpers.js +14 -1
  10. package/dist/helpers/dockerService/dockerService.js +6 -3
  11. package/dist/helpers/dockerService/serviceComponents/containerActions.js +60 -10
  12. package/dist/helpers/dockerService/serviceComponents/containerList.js +7 -2
  13. package/dist/helpers/dockerService/serviceComponents/containerLogs.js +33 -20
  14. package/dist/helpers/dockerService/serviceComponents/containerStats.js +12 -3
  15. package/dist/helpers/dockerService/serviceComponents/imageUtils.js +12 -0
  16. package/dist/helpers/exitWithMessage.js +11 -0
  17. package/dist/helpers/validationHelpers.js +15 -2
  18. package/dist/hooks/creation/useContainerCreation.js +2 -6
  19. package/dist/hooks/useContainers.js +1 -3
  20. package/dist/hooks/useControls.js +27 -2
  21. package/dist/hooks/useLogsStream.js +6 -0
  22. package/dist/index.js +1 -4
  23. package/fix-imports.cjs +3 -0
  24. package/package.json +1 -1
  25. package/src/App.jsx +0 -2
  26. package/src/components/ContainerList.jsx +1 -3
  27. package/src/components/ContainerRow.jsx +18 -6
  28. package/src/helpers/actionHelpers.js +14 -1
  29. package/src/helpers/dockerService/dockerService.js +7 -1
  30. package/src/helpers/dockerService/serviceComponents/containerActions.js +55 -9
  31. package/src/helpers/dockerService/serviceComponents/containerList.js +6 -2
  32. package/src/helpers/dockerService/serviceComponents/containerLogs.js +27 -15
  33. package/src/helpers/dockerService/serviceComponents/containerStats.js +15 -1
  34. package/src/helpers/dockerService/serviceComponents/imageUtils.js +10 -0
  35. package/src/helpers/exitWithMessage.js +10 -0
  36. package/src/helpers/validationHelpers.js +14 -2
  37. package/src/hooks/creation/useContainerCreation.js +2 -6
  38. package/src/hooks/useContainers.js +1 -3
  39. package/src/hooks/useControls.js +29 -2
  40. package/src/hooks/useLogsStream.js +5 -0
  41. package/src/index.js +1 -5
  42. package/test/validationHelpers.test.js +32 -0
@@ -0,0 +1,1004 @@
1
+ # Informe de Auditoría de Código - CDD CLI
2
+
3
+ **Fecha:** 2025-10-17
4
+ **Proyecto:** CDD-CLI (CLI Docker Dashboard)
5
+ **Versión:** 3.1.2
6
+
7
+ ---
8
+
9
+ ## Resumen Ejecutivo
10
+
11
+ Este informe presenta los resultados de una auditoría completa del código fuente del proyecto CDD-CLI. Se identificaron **11 problemas críticos** y **15 mejoras recomendadas** que afectan la seguridad, rendimiento, mantenibilidad y robustez de la aplicación.
12
+
13
+ ---
14
+
15
+ ## 1. ERRORES CRÍTICOS Y DE SEGURIDAD
16
+
17
+ ### 1.1. ❌ Error de Orden de Declaración en `containerActions.js`
18
+
19
+ **Ubicación:** `src/helpers/dockerService/serviceComponents/containerActions.js` (líneas 1-16)
20
+
21
+ **Problema:**
22
+ ```javascript
23
+ export async function removeContainer(containerId) {
24
+ const container = docker.getContainer(containerId);
25
+ // ...
26
+ }
27
+ import { docker } from "../dockerService";
28
+ ```
29
+
30
+ La función `removeContainer` está declarada **antes** de la importación del módulo `docker`, lo que viola el estándar de ES6 modules y puede causar errores de referencia.
31
+
32
+ **¿Por qué está mal?**
33
+ - Las importaciones deben ir al principio del archivo en ES6 modules
34
+ - Esto puede causar errores en tiempo de ejecución dependiendo del compilador
35
+ - Viola las convenciones de código JavaScript/ES6
36
+
37
+ **Consecuencias:**
38
+ - Error potencial de `ReferenceError` si el código se ejecuta sin transpilación
39
+ - Confusión para otros desarrolladores
40
+ - Problemas de mantenibilidad
41
+
42
+ **Cómo mejorarlo:**
43
+ Mover la importación al inicio del archivo:
44
+ ```javascript
45
+ import { docker } from "../dockerService";
46
+ import { imageExists, pullImage } from "./imageUtils.js";
47
+
48
+ export async function removeContainer(containerId) {
49
+ const container = docker.getContainer(containerId);
50
+ // ...
51
+ }
52
+ ```
53
+
54
+ ---
55
+
56
+ ### 1.2. ❌ Validación de Puertos Obligatoria Incorrecta
57
+
58
+ **Ubicación:** `src/hooks/creation/useContainerCreation.js` (líneas 44-48)
59
+
60
+ **Problema:**
61
+ ```javascript
62
+ if (!portInput.trim()) {
63
+ setMessage("You must specify at least one port to expose (e.g. 8080:80)");
64
+ setMessageColor("red");
65
+ return;
66
+ }
67
+ ```
68
+
69
+ El código obliga al usuario a especificar puertos, pero muchos contenedores Docker **no necesitan** exponer puertos (servicios internos, workers, cron jobs, etc.).
70
+
71
+ **¿Por qué está mal?**
72
+ - No todos los contenedores requieren puertos expuestos
73
+ - Impide crear contenedores válidos sin puertos
74
+ - La lógica contradice el mensaje "Optional" mostrado al usuario
75
+
76
+ **Consecuencias:**
77
+ - Imposibilidad de crear contenedores sin puertos
78
+ - Mala experiencia de usuario
79
+ - Limitación funcional artificial
80
+
81
+ **Cómo mejorarlo:**
82
+ Hacer los puertos verdaderamente opcionales:
83
+ ```javascript
84
+ if (portInput.trim() && !validatePorts(portInput)) {
85
+ setMessage("Port format must be host:container and both must be numbers (e.g. 8080:80)");
86
+ setMessageColor("red");
87
+ return;
88
+ }
89
+ ```
90
+
91
+ ---
92
+
93
+ ### 1.3. ❌ Falta de Manejo de Errores en `getLogsStream`
94
+
95
+ **Ubicación:** `src/helpers/dockerService/serviceComponents/containerLogs.js` (líneas 11-27)
96
+
97
+ **Problema:**
98
+ ```javascript
99
+ export function getLogsStream(containerId, onData, onEnd, onError) {
100
+ const container = docker.getContainer(containerId);
101
+ container.logs({
102
+ follow: true,
103
+ stdout: true,
104
+ stderr: true,
105
+ tail: 100
106
+ }, (err, stream) => {
107
+ if (err) {
108
+ onError?.(err);
109
+ return;
110
+ }
111
+ stream.on('data', chunk => onData?.(chunk.toString()));
112
+ stream.on('end', () => onEnd?.());
113
+ stream.on('error', err => onError?.(err));
114
+ });
115
+ }
116
+ ```
117
+
118
+ **¿Por qué está mal?**
119
+ - No hay try-catch para manejar excepciones síncronas
120
+ - Si `docker.getContainer()` falla, no se captura el error
121
+ - Puede causar crashes no manejados
122
+
123
+ **Consecuencias:**
124
+ - Crash de la aplicación si el contenedor no existe
125
+ - Mensajes de error no informativos
126
+ - Mal manejo de condiciones de error
127
+
128
+ **Cómo mejorarlo:**
129
+ ```javascript
130
+ export function getLogsStream(containerId, onData, onEnd, onError) {
131
+ try {
132
+ const container = docker.getContainer(containerId);
133
+ container.logs({
134
+ follow: true,
135
+ stdout: true,
136
+ stderr: true,
137
+ tail: 100
138
+ }, (err, stream) => {
139
+ if (err) {
140
+ onError?.(err);
141
+ return;
142
+ }
143
+ stream.on('data', chunk => onData?.(chunk.toString()));
144
+ stream.on('end', () => onEnd?.());
145
+ stream.on('error', err => onError?.(err));
146
+ });
147
+ } catch (err) {
148
+ onError?.(err);
149
+ }
150
+ }
151
+ ```
152
+
153
+ ---
154
+
155
+ ### 1.4. ❌ Path del Socket Docker Hardcodeado (Problema de Portabilidad)
156
+
157
+ **Ubicación:** `src/helpers/dockerService/dockerService.js` (línea 2)
158
+
159
+ **Problema:**
160
+ ```javascript
161
+ const docker = new Docker({ socketPath: "/var/run/docker.sock" });
162
+ ```
163
+
164
+ **¿Por qué está mal?**
165
+ - El path `/var/run/docker.sock` solo funciona en Linux/Mac
166
+ - En Windows, Docker usa named pipes o TCP
167
+ - No permite configuración para diferentes entornos
168
+
169
+ **Consecuencias:**
170
+ - La aplicación **no funciona en Windows**
171
+ - Imposibilidad de usar Docker remoto
172
+ - Falta de flexibilidad para diferentes configuraciones
173
+
174
+ **Cómo mejorarlo:**
175
+ ```javascript
176
+ import Docker from "dockerode";
177
+
178
+ // Usa la configuración por defecto de dockerode que maneja automáticamente:
179
+ // - /var/run/docker.sock en Linux/Mac
180
+ // - //./pipe/docker_engine en Windows
181
+ // - Variables de entorno DOCKER_HOST, DOCKER_CERT_PATH, etc.
182
+ const docker = new Docker();
183
+
184
+ export { docker };
185
+ ```
186
+
187
+ ---
188
+
189
+ ### 1.5. ❌ Race Condition en Stats Fetching
190
+
191
+ **Ubicación:** `src/components/ContainerRow.jsx` (líneas 31-46)
192
+
193
+ **Problema:**
194
+ ```javascript
195
+ useEffect(() => {
196
+ if (state !== "running") return;
197
+ const fetchStats = async () => {
198
+ try {
199
+ const s = await getStats(id);
200
+ setStats(s);
201
+ setStatsError("");
202
+ } catch (err) {
203
+ setStats({ cpuPercent: 0, memPercent: 0, netIO: { rx: 0, tx: 0 } });
204
+ setStatsError("Error fetching stats");
205
+ }
206
+ };
207
+ fetchStats();
208
+ const timer = setInterval(fetchStats, 1500);
209
+ return () => clearInterval(timer);
210
+ }, [id, state]);
211
+ ```
212
+
213
+ **¿Por qué está mal?**
214
+ - Si el estado del contenedor cambia rápidamente, pueden quedar llamadas pendientes
215
+ - No se cancela la promesa anterior cuando el componente se desmonta
216
+ - Puede causar memory leaks o actualizaciones de estado en componentes desmontados
217
+
218
+ **Consecuencias:**
219
+ - Warning: "Can't perform a React state update on an unmounted component"
220
+ - Posibles memory leaks
221
+ - Consumo innecesario de recursos
222
+
223
+ **Cómo mejorarlo:**
224
+ ```javascript
225
+ useEffect(() => {
226
+ if (state !== "running") return;
227
+
228
+ let isMounted = true;
229
+
230
+ const fetchStats = async () => {
231
+ try {
232
+ const s = await getStats(id);
233
+ if (isMounted) {
234
+ setStats(s);
235
+ setStatsError("");
236
+ }
237
+ } catch (err) {
238
+ if (isMounted) {
239
+ setStats({ cpuPercent: 0, memPercent: 0, netIO: { rx: 0, tx: 0 } });
240
+ setStatsError("Error fetching stats");
241
+ }
242
+ }
243
+ };
244
+
245
+ fetchStats();
246
+ const timer = setInterval(fetchStats, 1500);
247
+
248
+ return () => {
249
+ isMounted = false;
250
+ clearInterval(timer);
251
+ };
252
+ }, [id, state]);
253
+ ```
254
+
255
+ ---
256
+
257
+ ### 1.6. ❌ Falta de Validación de Container Index
258
+
259
+ **Ubicación:** `src/hooks/useControls.js` (múltiples líneas)
260
+
261
+ **Problema:**
262
+ ```javascript
263
+ if (input === "l" && containers[selected]) {
264
+ logsViewer.openLogs();
265
+ getLogsStream(
266
+ containers[selected].id,
267
+ // ...
268
+ );
269
+ }
270
+ ```
271
+
272
+ Aunque hay validación en algunos lugares (`containers[selected]`), no es consistente en todas las operaciones.
273
+
274
+ **¿Por qué está mal?**
275
+ - Si `containers` está vacío o `selected` es inválido, puede causar errores
276
+ - No hay validación consistente en todas las operaciones
277
+ - Puede causar crashes inesperados
278
+
279
+ **Consecuencias:**
280
+ - Posibles errores de tipo `Cannot read property 'id' of undefined`
281
+ - Experiencia de usuario inconsistente
282
+ - Crashes potenciales
283
+
284
+ **Cómo mejorarlo:**
285
+ Agregar validación consistente al inicio de cada operación:
286
+ ```javascript
287
+ const container = containers[selected];
288
+ if (!container) {
289
+ actions.setMessage("No container selected");
290
+ actions.setMessageColor("red");
291
+ return;
292
+ }
293
+ ```
294
+
295
+ ---
296
+
297
+ ### 1.7. ❌ Memory Leak en Log Streaming
298
+
299
+ **Ubicación:** `src/hooks/useControls.js` (líneas 199-208)
300
+
301
+ **Problema:**
302
+ ```javascript
303
+ if (input === "l" && containers[selected]) {
304
+ logsViewer.openLogs();
305
+ getLogsStream(
306
+ containers[selected].id,
307
+ (data) => logsViewer.setLogs((prev) => [...prev, ...data.split("\n").filter(Boolean)]),
308
+ () => {},
309
+ (err) => logsViewer.setLogs((prev) => [...prev, `Error: ${err.message}`])
310
+ );
311
+ return;
312
+ }
313
+ ```
314
+
315
+ **¿Por qué está mal?**
316
+ - El stream de logs nunca se cierra explícitamente
317
+ - Los logs se acumulan indefinidamente en memoria (`[...prev, ...]`)
318
+ - No hay límite máximo de logs almacenados
319
+
320
+ **Consecuencias:**
321
+ - Memory leak creciente con el tiempo
322
+ - Posible crash por falta de memoria en streams largos
323
+ - Rendimiento degradado
324
+
325
+ **Cómo mejorarlo:**
326
+ ```javascript
327
+ // Mantener referencia al stream para poder cerrarlo
328
+ let logStream = null;
329
+
330
+ // Al abrir logs:
331
+ logsViewer.openLogs();
332
+ logStream = getLogsStream(
333
+ containers[selected].id,
334
+ (data) => {
335
+ logsViewer.setLogs((prev) => {
336
+ const newLogs = [...prev, ...data.split("\n").filter(Boolean)];
337
+ // Limitar a últimas 1000 líneas
338
+ return newLogs.slice(-1000);
339
+ });
340
+ },
341
+ () => {},
342
+ (err) => logsViewer.setLogs((prev) => [...prev, `Error: ${err.message}`])
343
+ );
344
+
345
+ // Al cerrar logs:
346
+ if (logStream) {
347
+ logStream.destroy();
348
+ logStream = null;
349
+ }
350
+ ```
351
+
352
+ ---
353
+
354
+ ### 1.8. ❌ Falta de Validación en Variables de Entorno
355
+
356
+ **Ubicación:** `src/helpers/validationHelpers.js` (líneas 13-16)
357
+
358
+ **Problema:**
359
+ ```javascript
360
+ export function validateEnvVars(envInput) {
361
+ // Future specific validations
362
+ return true;
363
+ }
364
+ ```
365
+
366
+ **¿Por qué está mal?**
367
+ - No valida el formato de las variables de entorno
368
+ - Acepta cualquier entrada, incluso inválida
369
+ - El formato esperado `VAR=value` no se valida
370
+
371
+ **Consecuencias:**
372
+ - Variables de entorno malformadas pasan sin error
373
+ - Fallo silencioso al crear contenedores
374
+ - Mala experiencia de usuario
375
+
376
+ **Cómo mejorarlo:**
377
+ ```javascript
378
+ export function validateEnvVars(envInput) {
379
+ if (!envInput || !envInput.trim()) return true; // Empty is valid
380
+
381
+ const vars = envInput.split(",").map(v => v.trim()).filter(Boolean);
382
+ const invalid = vars.find(v => {
383
+ const parts = v.split("=");
384
+ // Must have at least VAR=value format
385
+ if (parts.length < 2) return true;
386
+ const varName = parts[0].trim();
387
+ // Variable names should be alphanumeric with underscores
388
+ if (!/^[A-Z_][A-Z0-9_]*$/i.test(varName)) return true;
389
+ return false;
390
+ });
391
+
392
+ return !invalid;
393
+ }
394
+ ```
395
+
396
+ ---
397
+
398
+ ### 1.9. ❌ CPU Stats Calculation Puede Dar Valores Incorrectos
399
+
400
+ **Ubicación:** `src/helpers/dockerService/serviceComponents/containerStats.js` (líneas 13-18)
401
+
402
+ **Problema:**
403
+ ```javascript
404
+ const cpuDelta =
405
+ stream.cpu_stats.cpu_usage.total_usage -
406
+ stream.precpu_stats.cpu_usage.total_usage;
407
+ const systemDelta =
408
+ stream.cpu_stats.system_cpu_usage - stream.precpu_stats.system_cpu_usage;
409
+ const cpuPercent = systemDelta > 0 ? (cpuDelta / systemDelta) * 100 : 0;
410
+ ```
411
+
412
+ **¿Por qué está mal?**
413
+ - No se multiplica por el número de CPUs
414
+ - El cálculo puede dar valores > 100% en sistemas multi-core
415
+ - No normaliza correctamente el porcentaje
416
+
417
+ **Consecuencias:**
418
+ - Estadísticas de CPU incorrectas/confusas
419
+ - Valores que exceden 100% en sistemas multi-core
420
+ - Información engañosa para el usuario
421
+
422
+ **Cómo mejorarlo:**
423
+ ```javascript
424
+ const cpuDelta =
425
+ stream.cpu_stats.cpu_usage.total_usage -
426
+ stream.precpu_stats.cpu_usage.total_usage;
427
+ const systemDelta =
428
+ stream.cpu_stats.system_cpu_usage - stream.precpu_stats.system_cpu_usage;
429
+
430
+ // Número de CPUs
431
+ const numCpus = stream.cpu_stats.online_cpus ||
432
+ stream.cpu_stats.cpu_usage.percpu_usage?.length || 1;
433
+
434
+ // Calcular porcentaje normalizado
435
+ const cpuPercent = systemDelta > 0
436
+ ? ((cpuDelta / systemDelta) * numCpus * 100)
437
+ : 0;
438
+ ```
439
+
440
+ ---
441
+
442
+ ### 1.10. ❌ Falta de Timeout en Container Actions
443
+
444
+ **Ubicación:** `src/helpers/dockerService/serviceComponents/containerActions.js` (líneas 59-82)
445
+
446
+ **Problema:**
447
+ ```javascript
448
+ export async function startContainer(containerId) {
449
+ const container = docker.getContainer(containerId);
450
+ await container.start();
451
+ }
452
+ ```
453
+
454
+ **¿Por qué está mal?**
455
+ - No hay timeout para operaciones que pueden colgarse
456
+ - Un contenedor que no arranca puede dejar la UI bloqueada
457
+ - No hay forma de cancelar operaciones largas
458
+
459
+ **Consecuencias:**
460
+ - UI congelada en operaciones que fallan
461
+ - Imposibilidad de cancelar operaciones largas
462
+ - Mala experiencia de usuario
463
+
464
+ **Cómo mejorarlo:**
465
+ ```javascript
466
+ // Helper para añadir timeout
467
+ function withTimeout(promise, ms = 30000) {
468
+ return Promise.race([
469
+ promise,
470
+ new Promise((_, reject) =>
471
+ setTimeout(() => reject(new Error('Operation timed out')), ms)
472
+ )
473
+ ]);
474
+ }
475
+
476
+ export async function startContainer(containerId) {
477
+ const container = docker.getContainer(containerId);
478
+ await withTimeout(container.start(), 30000);
479
+ }
480
+ ```
481
+
482
+ ---
483
+
484
+ ### 1.11. ❌ Mensaje de Error Duplicado en `handleAction`
485
+
486
+ **Ubicación:** `src/helpers/actionHelpers.js` (líneas 31-42)
487
+
488
+ **Problema:**
489
+ ```javascript
490
+ try {
491
+ await actionFn(c.id);
492
+ setMessage(`${actionLabel} container...`); // ← Mismo mensaje que línea 31
493
+ setMessageColor("green");
494
+ setTimeout(() => setMessage(""), 3000);
495
+ } catch (err) {
496
+ setMessage(`Failed to ${actionLabel.toLowerCase()} container.`);
497
+ setMessageColor("red");
498
+ setTimeout(() => setMessage(""), 3000);
499
+ }
500
+ ```
501
+
502
+ **¿Por qué está mal?**
503
+ - El mensaje de éxito es idéntico al mensaje de inicio
504
+ - No informa al usuario que la acción se completó
505
+ - Confunde "iniciando" con "completado"
506
+
507
+ **Consecuencias:**
508
+ - Usuario no sabe si la acción se completó
509
+ - Feedback confuso
510
+ - Mala experiencia de usuario
511
+
512
+ **Cómo mejorarlo:**
513
+ ```javascript
514
+ try {
515
+ await actionFn(c.id);
516
+ setMessage(`${actionLabel} container completed successfully`);
517
+ setMessageColor("green");
518
+ setTimeout(() => setMessage(""), 3000);
519
+ }
520
+ ```
521
+
522
+ ---
523
+
524
+ ## 2. PROBLEMAS DE CÓDIGO Y MEJORES PRÁCTICAS
525
+
526
+ ### 2.1. ⚠️ Uso Inconsistente de Optional Chaining
527
+
528
+ **Ubicación:** Múltiples archivos
529
+
530
+ **Problema:**
531
+ El código usa `?.` en algunos lugares pero no en otros de forma inconsistente.
532
+
533
+ **Impacto:** Código propenso a errores
534
+ **Severidad:** Media
535
+
536
+ **Solución:**
537
+ Usar optional chaining consistentemente donde hay posibilidad de valores null/undefined.
538
+
539
+ ---
540
+
541
+ ### 2.2. ⚠️ Hardcoded Timeouts y Magic Numbers
542
+
543
+ **Ubicación:** Múltiples archivos
544
+ - `useContainers.js`: `3000` ms (línea 18)
545
+ - `ContainerRow.jsx`: `1500` ms (línea 44)
546
+ - `useControls.js`: `500` ms, `2000` ms, `3000` ms
547
+
548
+ **Problema:**
549
+ Números mágicos dispersos por el código sin constantes nombradas.
550
+
551
+ **Impacto:** Dificulta mantenimiento y ajuste
552
+ **Severidad:** Baja
553
+
554
+ **Solución:**
555
+ ```javascript
556
+ // constants.js
557
+ export const REFRESH_INTERVALS = {
558
+ CONTAINER_LIST: 3000,
559
+ CONTAINER_STATS: 1500,
560
+ MESSAGE_TIMEOUT: 3000,
561
+ EXIT_DELAY: 500
562
+ };
563
+ ```
564
+
565
+ ---
566
+
567
+ ### 2.3. ⚠️ Falta de PropTypes o TypeScript
568
+
569
+ **Ubicación:** Todos los componentes React
570
+
571
+ **Problema:**
572
+ No hay validación de tipos en props de componentes.
573
+
574
+ **Impacto:** Errores en tiempo de ejecución difíciles de debuggear
575
+ **Severidad:** Media
576
+
577
+ **Solución:**
578
+ Agregar PropTypes o migrar a TypeScript:
579
+ ```javascript
580
+ import PropTypes from 'prop-types';
581
+
582
+ ContainerRow.propTypes = {
583
+ container: PropTypes.shape({
584
+ id: PropTypes.string.isRequired,
585
+ name: PropTypes.string.isRequired,
586
+ image: PropTypes.string.isRequired,
587
+ state: PropTypes.string.isRequired,
588
+ ports: PropTypes.array
589
+ }).isRequired
590
+ };
591
+ ```
592
+
593
+ ---
594
+
595
+ ### 2.4. ⚠️ Falta de Tests Unitarios
596
+
597
+ **Ubicación:** General
598
+
599
+ **Problema:**
600
+ Solo hay 1 archivo de tests (validationHelpers.test.js) con 5 tests.
601
+
602
+ **Áreas sin cobertura:**
603
+ - Hooks personalizados
604
+ - Componentes React
605
+ - Servicios Docker
606
+ - Helpers de acciones
607
+
608
+ **Impacto:** Alto riesgo de regresiones
609
+ **Severidad:** Alta
610
+
611
+ **Solución:**
612
+ Agregar tests para:
613
+ - `useContainerActions`
614
+ - `useContainerCreation`
615
+ - `actionHelpers`
616
+ - Componentes críticos
617
+
618
+ ---
619
+
620
+ ### 2.5. ⚠️ Falta de Manejo de Casos Edge
621
+
622
+ **Ubicación:** `src/helpers/dockerService/serviceComponents/containerList.js`
623
+
624
+ **Problema:**
625
+ ```javascript
626
+ name: container.Names[0].replace("/", ""),
627
+ ```
628
+
629
+ No valida si `Names` está vacío.
630
+
631
+ **Impacto:** Crash potencial si Docker devuelve datos inesperados
632
+ **Severidad:** Media
633
+
634
+ **Solución:**
635
+ ```javascript
636
+ name: (container.Names && container.Names[0] || 'Unknown').replace("/", ""),
637
+ ```
638
+
639
+ ---
640
+
641
+ ### 2.6. ⚠️ Callback Hell en useControls
642
+
643
+ **Ubicación:** `src/hooks/useControls.js` (líneas 86-219)
644
+
645
+ **Problema:**
646
+ La función `useInput` tiene 133 líneas con múltiples niveles de anidación.
647
+
648
+ **Impacto:** Difícil de mantener y testear
649
+ **Severidad:** Media
650
+
651
+ **Solución:**
652
+ Extraer handlers a funciones separadas:
653
+ ```javascript
654
+ function useControls(containers = []) {
655
+ // ... state ...
656
+
657
+ const handleEraseConfirmation = useCallback((input, key) => {
658
+ // lógica de confirmación
659
+ }, []);
660
+
661
+ const handleLogsInput = useCallback((input, key) => {
662
+ // lógica de logs
663
+ }, []);
664
+
665
+ const handleContainerCreation = useCallback((input, key) => {
666
+ // lógica de creación
667
+ }, []);
668
+
669
+ useInput((input, key) => {
670
+ if (confirmErase) return handleEraseConfirmation(input, key);
671
+ if (logsViewer.showLogs) return handleLogsInput(input, key);
672
+ if (creatingContainer) return handleContainerCreation(input, key);
673
+ // ... navigation and commands
674
+ });
675
+ }
676
+ ```
677
+
678
+ ---
679
+
680
+ ### 2.7. ⚠️ Falta de Logging
681
+
682
+ **Ubicación:** General
683
+
684
+ **Problema:**
685
+ No hay sistema de logging para debuggear problemas.
686
+
687
+ **Impacto:** Difícil diagnosticar problemas en producción
688
+ **Severidad:** Media
689
+
690
+ **Solución:**
691
+ Implementar logger con niveles:
692
+ ```javascript
693
+ // logger.js
694
+ export const logger = {
695
+ debug: (msg, ...args) => {
696
+ if (process.env.DEBUG) console.log('[DEBUG]', msg, ...args);
697
+ },
698
+ info: (msg, ...args) => console.log('[INFO]', msg, ...args),
699
+ warn: (msg, ...args) => console.warn('[WARN]', msg, ...args),
700
+ error: (msg, ...args) => console.error('[ERROR]', msg, ...args)
701
+ };
702
+ ```
703
+
704
+ ---
705
+
706
+ ### 2.8. ⚠️ No Hay Manejo de Reconexión Docker
707
+
708
+ **Ubicación:** `src/helpers/dockerService/dockerService.js`
709
+
710
+ **Problema:**
711
+ Si la conexión a Docker se pierde, no hay reintentos ni manejo.
712
+
713
+ **Impacto:** La aplicación se rompe si Docker se reinicia
714
+ **Severidad:** Media
715
+
716
+ **Solución:**
717
+ Implementar lógica de retry con backoff exponencial.
718
+
719
+ ---
720
+
721
+ ### 2.9. ⚠️ Falta de Internacionalización (i18n)
722
+
723
+ **Ubicación:** Todos los mensajes están hardcodeados en inglés
724
+
725
+ **Problema:**
726
+ ```javascript
727
+ setMessage("Container is already running.");
728
+ ```
729
+
730
+ **Impacto:** No hay soporte multi-idioma
731
+ **Severidad:** Baja
732
+
733
+ **Solución:**
734
+ Implementar sistema i18n:
735
+ ```javascript
736
+ import { t } from './i18n';
737
+ setMessage(t('container.already_running'));
738
+ ```
739
+
740
+ ---
741
+
742
+ ### 2.10. ⚠️ Falta de Documentación de API
743
+
744
+ **Ubicación:** Varios archivos
745
+
746
+ **Problema:**
747
+ Aunque hay JSDoc en algunos lugares, es inconsistente y falta en muchas funciones importantes.
748
+
749
+ **Impacto:** Dificulta onboarding y mantenimiento
750
+ **Severidad:** Baja
751
+
752
+ **Solución:**
753
+ Completar JSDoc en todas las funciones públicas.
754
+
755
+ ---
756
+
757
+ ### 2.11. ⚠️ No Hay Validación de Imagen Name Format
758
+
759
+ **Ubicación:** `src/hooks/creation/useContainerCreation.js`
760
+
761
+ **Problema:**
762
+ ```javascript
763
+ if (!imageName.trim()) {
764
+ setMessage("Image name cannot be empty.");
765
+ setMessageColor("red");
766
+ return;
767
+ }
768
+ ```
769
+
770
+ Solo valida que no esté vacío, pero no valida el formato.
771
+
772
+ **Impacto:** Fallos al crear contenedores con nombres inválidos
773
+ **Severidad:** Media
774
+
775
+ **Solución:**
776
+ ```javascript
777
+ // Validar formato de imagen: [registry/]name[:tag]
778
+ const imageRegex = /^([\w\-\.]+\/)?[\w\-\.]+(:[\w\-\.]+)?$/;
779
+ if (!imageRegex.test(imageName.trim())) {
780
+ setMessage("Invalid image name format. Use: [registry/]name[:tag]");
781
+ setMessageColor("red");
782
+ return;
783
+ }
784
+ ```
785
+
786
+ ---
787
+
788
+ ### 2.12. ⚠️ Falta de Accessibility (a11y)
789
+
790
+ **Ubicación:** Componentes de UI
791
+
792
+ **Problema:**
793
+ No hay consideraciones de accesibilidad en la UI de terminal.
794
+
795
+ **Impacto:** Usuarios con screen readers o necesidades especiales no pueden usar la app
796
+ **Severidad:** Baja
797
+
798
+ **Solución:**
799
+ Agregar roles ARIA y mejor soporte de navegación por teclado.
800
+
801
+ ---
802
+
803
+ ### 2.13. ⚠️ Falta de Rate Limiting en Refresh
804
+
805
+ **Ubicación:** `src/hooks/useContainers.js`
806
+
807
+ **Problema:**
808
+ ```javascript
809
+ const timer = setInterval(fetch, 3000);
810
+ ```
811
+
812
+ Si `fetch` toma más de 3 segundos, se acumulan llamadas.
813
+
814
+ **Impacto:** Posible sobrecarga del sistema
815
+ **Severidad:** Media
816
+
817
+ **Solución:**
818
+ Usar setTimeout en lugar de setInterval:
819
+ ```javascript
820
+ useEffect(() => {
821
+ let timeoutId;
822
+ const fetch = async () => {
823
+ setContainers(await getContainers());
824
+ timeoutId = setTimeout(fetch, 3000);
825
+ };
826
+ fetch();
827
+ return () => clearTimeout(timeoutId);
828
+ }, []);
829
+ ```
830
+
831
+ ---
832
+
833
+ ### 2.14. ⚠️ Estados Intermedios No Manejados
834
+
835
+ **Ubicación:** `src/components/ContainerRow.jsx`
836
+
837
+ **Problema:**
838
+ ```javascript
839
+ const stateText = (state) => {
840
+ if (state === "running") return { text: "🟢 RUNNING", color: "green" };
841
+ if (state === "exited") return { text: "🔴 EXITED", color: "red" };
842
+ if (state === "paused") return { text: "🟠 PAUSED", color: "yellow" };
843
+ return { text: state.toUpperCase(), color: "gray" };
844
+ };
845
+ ```
846
+
847
+ Faltan estados como: `created`, `restarting`, `removing`, `dead`.
848
+
849
+ **Impacto:** Feedback pobre para estados menos comunes
850
+ **Severidad:** Baja
851
+
852
+ **Solución:**
853
+ Agregar todos los estados posibles de Docker.
854
+
855
+ ---
856
+
857
+ ### 2.15. ⚠️ Falta de Cleanup en Exit
858
+
859
+ **Ubicación:** `src/hooks/useControls.js`
860
+
861
+ **Problema:**
862
+ ```javascript
863
+ if (input === "q") {
864
+ actions.setMessage("Exiting...");
865
+ actions.setMessageColor("yellow");
866
+ setTimeout(() => process.exit(0), 500);
867
+ return;
868
+ }
869
+ ```
870
+
871
+ No cierra streams ni limpia recursos antes de salir.
872
+
873
+ **Impacto:** Posibles recursos no liberados
874
+ **Severidad:** Baja
875
+
876
+ **Solución:**
877
+ ```javascript
878
+ if (input === "q") {
879
+ actions.setMessage("Exiting...");
880
+ actions.setMessageColor("yellow");
881
+ // Cerrar todos los streams activos
882
+ logsViewer.closeLogs();
883
+ // Cleanup adicional
884
+ setTimeout(() => process.exit(0), 500);
885
+ return;
886
+ }
887
+ ```
888
+
889
+ ---
890
+
891
+ ## 3. PROBLEMAS DE RENDIMIENTO
892
+
893
+ ### 3.1. 🐌 Exceso de Re-renders en ContainerRow
894
+
895
+ **Problema:** Cada actualización de stats causa re-render completo del row.
896
+
897
+ **Solución:** Usar `React.memo()` y separar stats en componente aparte.
898
+
899
+ ---
900
+
901
+ ### 3.2. 🐌 Polling Innecesario
902
+
903
+ **Problema:** Polling cada 3 segundos incluso cuando la app está en background.
904
+
905
+ **Solución:** Usar Page Visibility API o websockets para actualizaciones en tiempo real.
906
+
907
+ ---
908
+
909
+ ### 3.3. 🐌 Arrays Spread en Loop
910
+
911
+ **Ubicación:** Log accumulation
912
+
913
+ **Problema:**
914
+ ```javascript
915
+ logsViewer.setLogs((prev) => [...prev, ...data.split("\n").filter(Boolean)])
916
+ ```
917
+
918
+ Crea nuevo array en cada línea de log.
919
+
920
+ **Solución:** Usar estructura más eficiente o batching.
921
+
922
+ ---
923
+
924
+ ## 4. RECOMENDACIONES GENERALES
925
+
926
+ ### 4.1. Estructura de Proyecto
927
+ - ✅ Buena separación de concerns (components, hooks, helpers)
928
+ - ⚠️ Podría beneficiarse de un directorio `utils/` separado
929
+ - ⚠️ Considerar directorio `constants/` para valores constantes
930
+
931
+ ### 4.2. Calidad de Código
932
+ - ✅ Buen uso de hooks personalizados
933
+ - ✅ Código generalmente legible
934
+ - ⚠️ Necesita más comentarios en lógica compleja
935
+ - ⚠️ Algunos archivos muy largos (useControls.js)
936
+
937
+ ### 4.3. Testing
938
+ - ❌ Cobertura de tests insuficiente
939
+ - ❌ No hay tests de integración
940
+ - ❌ No hay tests E2E
941
+
942
+ ### 4.4. Documentación
943
+ - ✅ README básico útil
944
+ - ⚠️ Falta documentación de arquitectura
945
+ - ⚠️ JSDoc inconsistente
946
+
947
+ ### 4.5. Seguridad
948
+ - ⚠️ No hay validación de input de usuario en varios lugares
949
+ - ⚠️ Errores exponen información sensible
950
+ - ⚠️ No hay sanitización de output de Docker
951
+
952
+ ---
953
+
954
+ ## 5. PLAN DE ACCIÓN PRIORIZADO
955
+
956
+ ### Prioridad ALTA (Crítico - resolver inmediatamente)
957
+ 1. ✅ Corregir orden de imports en `containerActions.js`
958
+ 2. ✅ Hacer puertos opcionales en creación de contenedores
959
+ 3. ✅ Agregar manejo de errores en `getLogsStream`
960
+ 4. ✅ Configurar Docker socket para cross-platform
961
+ 5. ✅ Prevenir race conditions en stats fetching
962
+
963
+ ### Prioridad MEDIA (Importante - resolver pronto)
964
+ 6. Implementar validación de variables de entorno
965
+ 7. Corregir cálculo de CPU stats
966
+ 8. Agregar timeouts a operaciones Docker
967
+ 9. Implementar límite de logs en memoria
968
+ 10. Agregar tests unitarios básicos
969
+
970
+ ### Prioridad BAJA (Mejoras - cuando sea posible)
971
+ 11. Extraer magic numbers a constantes
972
+ 12. Agregar PropTypes o TypeScript
973
+ 13. Mejorar logging y debugging
974
+ 14. Implementar i18n
975
+ 15. Mejorar documentación
976
+
977
+ ---
978
+
979
+ ## 6. MÉTRICAS DE CÓDIGO
980
+
981
+ - **Líneas de código:** ~1,200
982
+ - **Archivos JavaScript/JSX:** 28
983
+ - **Cobertura de tests:** ~5%
984
+ - **Deuda técnica estimada:** 3-4 semanas de trabajo
985
+
986
+ ---
987
+
988
+ ## 7. CONCLUSIÓN
989
+
990
+ El proyecto CDD-CLI tiene una base sólida con buena separación de concerns y uso de React hooks. Sin embargo, presenta varios problemas críticos relacionados con:
991
+
992
+ 1. **Manejo de errores insuficiente**
993
+ 2. **Memory leaks potenciales**
994
+ 3. **Falta de validación de inputs**
995
+ 4. **Problemas de portabilidad**
996
+ 5. **Cobertura de tests mínima**
997
+
998
+ Se recomienda abordar primero los **5 problemas de alta prioridad** antes de considerar nuevas features. Estos problemas pueden causar crashes, memory leaks y una mala experiencia de usuario.
999
+
1000
+ La implementación de las mejoras propuestas mejoraría significativamente la robustez, mantenibilidad y calidad general de la aplicación.
1001
+
1002
+ ---
1003
+
1004
+ **Fin del informe de auditoría**