cdd-cli 3.1.3 → 3.1.4
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 +7 -0
- package/FIXES_APPLIED.md +145 -147
- package/package.json +1 -1
- package/AUDIT_REPORT.md +0 -1004
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
6
6
|
|
|
7
|
+
## [3.1.4] - 2025-10-17
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Documentation cleanup: translate `FIXES_APPLIED.md` to English, remove Spanish duplicate and references to internal audit doc.
|
|
12
|
+
- No functional code changes.
|
|
13
|
+
|
|
7
14
|
## [3.1.3] - 2025-10-17
|
|
8
15
|
|
|
9
16
|
### Fixed
|
package/FIXES_APPLIED.md
CHANGED
|
@@ -1,127 +1,127 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Applied Fixes - CDD CLI
|
|
2
2
|
|
|
3
|
-
**
|
|
4
|
-
**
|
|
3
|
+
**Date:** 2025-10-17
|
|
4
|
+
**Project:** CDD-CLI (CLI Docker Dashboard)
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## Fixes Summary
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
A total of 11 critical fixes were applied to address security, stability, and portability issues identified during the code audit.
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
-
## 1.
|
|
14
|
+
## 1. APPLIED CRITICAL FIXES
|
|
15
15
|
|
|
16
|
-
### ✅ 1.1.
|
|
16
|
+
### ✅ 1.1. Import Order Fix
|
|
17
17
|
|
|
18
|
-
**
|
|
18
|
+
**File:** `src/helpers/dockerService/serviceComponents/containerActions.js`
|
|
19
19
|
|
|
20
|
-
**
|
|
20
|
+
**Issue:** Imports were declared after exports.
|
|
21
21
|
|
|
22
|
-
**
|
|
22
|
+
**Applied fix:**
|
|
23
23
|
```javascript
|
|
24
|
-
//
|
|
24
|
+
// BEFORE (incorrect):
|
|
25
25
|
export async function removeContainer(containerId) { ... }
|
|
26
26
|
import { docker } from "../dockerService";
|
|
27
27
|
|
|
28
|
-
//
|
|
28
|
+
// AFTER (correct):
|
|
29
29
|
import { docker } from "../dockerService";
|
|
30
30
|
import { imageExists, pullImage } from "./imageUtils.js";
|
|
31
31
|
export async function removeContainer(containerId) { ... }
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
**
|
|
34
|
+
**Impact:** Removes potential reference errors and follows ES6 standards.
|
|
35
35
|
|
|
36
36
|
---
|
|
37
37
|
|
|
38
|
-
### ✅ 1.2.
|
|
38
|
+
### ✅ 1.2. Optional Ports in Container Creation
|
|
39
39
|
|
|
40
|
-
**
|
|
40
|
+
**File:** `src/hooks/creation/useContainerCreation.js`
|
|
41
41
|
|
|
42
|
-
**
|
|
42
|
+
**Issue:** Users were forced to specify ports, but many containers don’t need them.
|
|
43
43
|
|
|
44
|
-
**
|
|
44
|
+
**Applied fix:**
|
|
45
45
|
```javascript
|
|
46
|
-
//
|
|
46
|
+
// BEFORE:
|
|
47
47
|
if (!portInput.trim()) {
|
|
48
48
|
setMessage("You must specify at least one port to expose");
|
|
49
49
|
return;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
//
|
|
53
|
-
//
|
|
52
|
+
// AFTER:
|
|
53
|
+
// Ports are optional — only validate if provided
|
|
54
54
|
if (portInput.trim() && !validatePorts(portInput)) {
|
|
55
55
|
setMessage("Port format must be host:container...");
|
|
56
56
|
return;
|
|
57
57
|
}
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
-
**
|
|
60
|
+
**Impact:** Allows creating containers without exposed ports (workers, internal services, etc.).
|
|
61
61
|
|
|
62
62
|
---
|
|
63
63
|
|
|
64
|
-
### ✅ 1.3.
|
|
64
|
+
### ✅ 1.3. Error Handling in getLogsStream
|
|
65
65
|
|
|
66
|
-
**
|
|
66
|
+
**File:** `src/helpers/dockerService/serviceComponents/containerLogs.js`
|
|
67
67
|
|
|
68
|
-
**
|
|
68
|
+
**Issue:** No try-catch to handle synchronous exceptions.
|
|
69
69
|
|
|
70
|
-
**
|
|
70
|
+
**Applied fix:**
|
|
71
71
|
```javascript
|
|
72
72
|
export function getLogsStream(containerId, onData, onEnd, onError) {
|
|
73
73
|
try {
|
|
74
74
|
const container = docker.getContainer(containerId);
|
|
75
|
-
// ...
|
|
75
|
+
// ... rest of the code
|
|
76
76
|
} catch (err) {
|
|
77
77
|
onError?.(err);
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
-
**
|
|
82
|
+
**Impact:** Prevents crashes when the container doesn’t exist or connection errors occur.
|
|
83
83
|
|
|
84
84
|
---
|
|
85
85
|
|
|
86
|
-
### ✅ 1.4.
|
|
86
|
+
### ✅ 1.4. Cross-Platform Docker Socket Configuration
|
|
87
87
|
|
|
88
|
-
**
|
|
88
|
+
**File:** `src/helpers/dockerService/dockerService.js`
|
|
89
89
|
|
|
90
|
-
**
|
|
90
|
+
**Issue:** Hardcoded path `/var/run/docker.sock` only works on Linux/macOS.
|
|
91
91
|
|
|
92
|
-
**
|
|
92
|
+
**Applied fix:**
|
|
93
93
|
```javascript
|
|
94
|
-
//
|
|
94
|
+
// BEFORE:
|
|
95
95
|
const docker = new Docker({ socketPath: "/var/run/docker.sock" });
|
|
96
96
|
|
|
97
|
-
//
|
|
98
|
-
//
|
|
99
|
-
// - /var/run/docker.sock
|
|
100
|
-
// - //./pipe/docker_engine
|
|
97
|
+
// AFTER:
|
|
98
|
+
// Use default configuration that automatically handles:
|
|
99
|
+
// - /var/run/docker.sock on Linux/macOS
|
|
100
|
+
// - //./pipe/docker_engine on Windows
|
|
101
101
|
const docker = new Docker();
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
-
**
|
|
104
|
+
**Impact:** Works on Windows, Linux, and macOS without manual changes.
|
|
105
105
|
|
|
106
106
|
---
|
|
107
107
|
|
|
108
|
-
### ✅ 1.5.
|
|
108
|
+
### ✅ 1.5. Race Condition Prevention in Stats
|
|
109
109
|
|
|
110
|
-
**
|
|
110
|
+
**File:** `src/components/ContainerRow.jsx`
|
|
111
111
|
|
|
112
|
-
**
|
|
112
|
+
**Issue:** State updates on unmounted components caused memory leaks.
|
|
113
113
|
|
|
114
|
-
**
|
|
114
|
+
**Applied fix:**
|
|
115
115
|
```javascript
|
|
116
116
|
useEffect(() => {
|
|
117
117
|
if (state !== "running") return;
|
|
118
118
|
|
|
119
|
-
let isMounted = true; // ←
|
|
119
|
+
let isMounted = true; // ← Mount flag
|
|
120
120
|
|
|
121
121
|
const fetchStats = async () => {
|
|
122
122
|
try {
|
|
123
123
|
const s = await getStats(id);
|
|
124
|
-
if (isMounted) { // ←
|
|
124
|
+
if (isMounted) { // ← Only update if mounted
|
|
125
125
|
setStats(s);
|
|
126
126
|
}
|
|
127
127
|
} catch (err) {
|
|
@@ -135,23 +135,23 @@ useEffect(() => {
|
|
|
135
135
|
const timer = setInterval(fetchStats, 1500);
|
|
136
136
|
|
|
137
137
|
return () => {
|
|
138
|
-
isMounted = false; // ←
|
|
138
|
+
isMounted = false; // ← Cleanup
|
|
139
139
|
clearInterval(timer);
|
|
140
140
|
};
|
|
141
141
|
}, [id, state]);
|
|
142
142
|
```
|
|
143
143
|
|
|
144
|
-
**
|
|
144
|
+
**Impact:** Eliminates React warnings and prevents memory leaks.
|
|
145
145
|
|
|
146
146
|
---
|
|
147
147
|
|
|
148
|
-
### ✅ 1.6.
|
|
148
|
+
### ✅ 1.6. Environment Variables Validation
|
|
149
149
|
|
|
150
|
-
**
|
|
150
|
+
**File:** `src/helpers/validationHelpers.js`
|
|
151
151
|
|
|
152
|
-
**
|
|
152
|
+
**Issue:** `validateEnvVars` always returned `true`.
|
|
153
153
|
|
|
154
|
-
**
|
|
154
|
+
**Applied fix:**
|
|
155
155
|
```javascript
|
|
156
156
|
export function validateEnvVars(envInput) {
|
|
157
157
|
if (!envInput || !envInput.trim()) return true; // Empty is valid
|
|
@@ -159,9 +159,9 @@ export function validateEnvVars(envInput) {
|
|
|
159
159
|
const vars = envInput.split(",").map(v => v.trim()).filter(Boolean);
|
|
160
160
|
const invalid = vars.find(v => {
|
|
161
161
|
const parts = v.split("=");
|
|
162
|
-
if (parts.length < 2) return true; //
|
|
162
|
+
if (parts.length < 2) return true; // Must be VAR=value
|
|
163
163
|
const varName = parts[0].trim();
|
|
164
|
-
//
|
|
164
|
+
// Names must be alphanumeric with underscores
|
|
165
165
|
if (!/^[A-Z_][A-Z0-9_]*$/i.test(varName)) return true;
|
|
166
166
|
return false;
|
|
167
167
|
});
|
|
@@ -170,39 +170,39 @@ export function validateEnvVars(envInput) {
|
|
|
170
170
|
}
|
|
171
171
|
```
|
|
172
172
|
|
|
173
|
-
**
|
|
173
|
+
**Impact:** Detects malformed variables before sending them to Docker.
|
|
174
174
|
|
|
175
175
|
---
|
|
176
176
|
|
|
177
|
-
### ✅ 1.7.
|
|
177
|
+
### ✅ 1.7. CPU Stats Calculation Fix
|
|
178
178
|
|
|
179
|
-
**
|
|
179
|
+
**File:** `src/helpers/dockerService/serviceComponents/containerStats.js`
|
|
180
180
|
|
|
181
|
-
**
|
|
181
|
+
**Issue:** Not normalized by number of CPUs, producing wrong values on multi-core hosts.
|
|
182
182
|
|
|
183
|
-
**
|
|
183
|
+
**Applied fix:**
|
|
184
184
|
```javascript
|
|
185
|
-
//
|
|
185
|
+
// Determine number of CPUs
|
|
186
186
|
const numCpus = stream.cpu_stats.online_cpus ||
|
|
187
187
|
stream.cpu_stats.cpu_usage.percpu_usage?.length || 1;
|
|
188
188
|
|
|
189
|
-
//
|
|
189
|
+
// Compute normalized percentage
|
|
190
190
|
const cpuPercent = systemDelta > 0
|
|
191
191
|
? ((cpuDelta / systemDelta) * numCpus * 100)
|
|
192
192
|
: 0;
|
|
193
193
|
```
|
|
194
194
|
|
|
195
|
-
**
|
|
195
|
+
**Impact:** Correct CPU stats on multi-core systems.
|
|
196
196
|
|
|
197
197
|
---
|
|
198
198
|
|
|
199
|
-
### ✅ 1.8. Timeouts
|
|
199
|
+
### ✅ 1.8. Timeouts in Docker Operations
|
|
200
200
|
|
|
201
|
-
**
|
|
201
|
+
**File:** `src/helpers/dockerService/serviceComponents/containerActions.js`
|
|
202
202
|
|
|
203
|
-
**
|
|
203
|
+
**Issue:** Operations without timeouts could freeze the UI indefinitely.
|
|
204
204
|
|
|
205
|
-
**
|
|
205
|
+
**Applied fix:**
|
|
206
206
|
```javascript
|
|
207
207
|
function withTimeout(promise, ms = 30000) {
|
|
208
208
|
return Promise.race([
|
|
@@ -219,82 +219,82 @@ export async function startContainer(containerId) {
|
|
|
219
219
|
}
|
|
220
220
|
```
|
|
221
221
|
|
|
222
|
-
**
|
|
222
|
+
**Impact:** Prevents frozen UI during long or failing operations.
|
|
223
223
|
|
|
224
224
|
---
|
|
225
225
|
|
|
226
|
-
### ✅ 1.9.
|
|
226
|
+
### ✅ 1.9. In-Memory Log Limit
|
|
227
227
|
|
|
228
|
-
**
|
|
228
|
+
**File:** `src/hooks/useControls.js`
|
|
229
229
|
|
|
230
|
-
**
|
|
230
|
+
**Issue:** Logs accumulated indefinitely causing a memory leak.
|
|
231
231
|
|
|
232
|
-
**
|
|
232
|
+
**Applied fix:**
|
|
233
233
|
```javascript
|
|
234
234
|
getLogsStream(
|
|
235
235
|
containers[selected].id,
|
|
236
236
|
(data) => logsViewer.setLogs((prev) => {
|
|
237
237
|
const newLogs = [...prev, ...data.split("\n").filter(Boolean)];
|
|
238
|
-
//
|
|
238
|
+
// Limit to last 1000 lines
|
|
239
239
|
return newLogs.slice(-1000);
|
|
240
240
|
}),
|
|
241
241
|
// ...
|
|
242
242
|
);
|
|
243
243
|
```
|
|
244
244
|
|
|
245
|
-
**
|
|
245
|
+
**Impact:** Prevents memory leaks on long-running log streams.
|
|
246
246
|
|
|
247
247
|
---
|
|
248
248
|
|
|
249
|
-
### ✅ 1.10.
|
|
249
|
+
### ✅ 1.10. Duplicate Message Fix
|
|
250
250
|
|
|
251
|
-
**
|
|
251
|
+
**File:** `src/helpers/actionHelpers.js`
|
|
252
252
|
|
|
253
|
-
**
|
|
253
|
+
**Issue:** Success message was identical to the start message.
|
|
254
254
|
|
|
255
|
-
**
|
|
255
|
+
**Applied fix:**
|
|
256
256
|
```javascript
|
|
257
|
-
//
|
|
258
|
-
setMessage(`${actionLabel} container...`); //
|
|
257
|
+
// BEFORE:
|
|
258
|
+
setMessage(`${actionLabel} container...`); // start
|
|
259
259
|
await actionFn(c.id);
|
|
260
|
-
setMessage(`${actionLabel} container...`); //
|
|
260
|
+
setMessage(`${actionLabel} container...`); // success (duplicate)
|
|
261
261
|
|
|
262
|
-
//
|
|
263
|
-
setMessage(`${actionLabel} container...`); //
|
|
262
|
+
// AFTER:
|
|
263
|
+
setMessage(`${actionLabel} container...`); // start
|
|
264
264
|
await actionFn(c.id);
|
|
265
|
-
setMessage(`${actionLabel} container completed successfully`); //
|
|
265
|
+
setMessage(`${actionLabel} container completed successfully`); // success
|
|
266
266
|
```
|
|
267
267
|
|
|
268
|
-
**
|
|
268
|
+
**Impact:** Clear feedback that the operation completed.
|
|
269
269
|
|
|
270
270
|
---
|
|
271
271
|
|
|
272
|
-
### ✅ 1.11.
|
|
272
|
+
### ✅ 1.11. Container Names Validation
|
|
273
273
|
|
|
274
|
-
**
|
|
274
|
+
**File:** `src/helpers/dockerService/serviceComponents/containerList.js`
|
|
275
275
|
|
|
276
|
-
**
|
|
276
|
+
**Issue:** Didn’t validate when `Names` was empty.
|
|
277
277
|
|
|
278
|
-
**
|
|
278
|
+
**Applied fix:**
|
|
279
279
|
```javascript
|
|
280
|
-
//
|
|
280
|
+
// BEFORE:
|
|
281
281
|
name: container.Names[0].replace("/", ""),
|
|
282
282
|
|
|
283
|
-
//
|
|
283
|
+
// AFTER:
|
|
284
284
|
name: (container.Names && container.Names[0] || 'Unknown').replace("/", ""),
|
|
285
285
|
```
|
|
286
286
|
|
|
287
|
-
**
|
|
287
|
+
**Impact:** Prevents crashes when Docker returns unexpected data.
|
|
288
288
|
|
|
289
289
|
---
|
|
290
290
|
|
|
291
|
-
## 2.
|
|
291
|
+
## 2. TEST IMPROVEMENTS
|
|
292
292
|
|
|
293
|
-
### ✅ Tests
|
|
293
|
+
### ✅ Tests for validateEnvVars
|
|
294
294
|
|
|
295
|
-
**
|
|
295
|
+
**File:** `test/validationHelpers.test.js`
|
|
296
296
|
|
|
297
|
-
**
|
|
297
|
+
**New tests added:**
|
|
298
298
|
- Empty input is valid
|
|
299
299
|
- Valid single env var
|
|
300
300
|
- Valid multiple env vars
|
|
@@ -303,19 +303,19 @@ name: (container.Names && container.Names[0] || 'Unknown').replace("/", ""),
|
|
|
303
303
|
- Invalid env var with invalid name
|
|
304
304
|
- Invalid env var with special characters in name
|
|
305
305
|
|
|
306
|
-
**
|
|
306
|
+
**Result:**
|
|
307
307
|
```
|
|
308
308
|
Test Suites: 1 passed, 1 total
|
|
309
|
-
Tests: 12 passed, 12 total (
|
|
309
|
+
Tests: 12 passed, 12 total (previously: 5)
|
|
310
310
|
```
|
|
311
311
|
|
|
312
312
|
---
|
|
313
313
|
|
|
314
|
-
## 3.
|
|
314
|
+
## 3. SECURITY ANALYSIS
|
|
315
315
|
|
|
316
316
|
### ✅ CodeQL Security Scan
|
|
317
317
|
|
|
318
|
-
**
|
|
318
|
+
**Result:** ✅ 0 vulnerabilities found
|
|
319
319
|
|
|
320
320
|
```
|
|
321
321
|
Analysis Result for 'javascript'. Found 0 alert(s):
|
|
@@ -324,9 +324,9 @@ Analysis Result for 'javascript'. Found 0 alert(s):
|
|
|
324
324
|
|
|
325
325
|
---
|
|
326
326
|
|
|
327
|
-
## 4.
|
|
327
|
+
## 4. BUILD VERIFICATION
|
|
328
328
|
|
|
329
|
-
### ✅ Build
|
|
329
|
+
### ✅ Successful Build
|
|
330
330
|
|
|
331
331
|
```bash
|
|
332
332
|
$ npm run build
|
|
@@ -335,38 +335,38 @@ Successfully compiled 28 files with Babel (815ms).
|
|
|
335
335
|
|
|
336
336
|
---
|
|
337
337
|
|
|
338
|
-
## 5.
|
|
338
|
+
## 5. OVERALL IMPACT OF FIXES
|
|
339
339
|
|
|
340
|
-
###
|
|
341
|
-
- ✅
|
|
342
|
-
- ✅
|
|
343
|
-
- ✅
|
|
340
|
+
### Security
|
|
341
|
+
- ✅ No security vulnerabilities detected
|
|
342
|
+
- ✅ Improved input validation
|
|
343
|
+
- ✅ Robust error handling
|
|
344
344
|
|
|
345
|
-
###
|
|
346
|
-
- ✅
|
|
347
|
-
- ✅
|
|
348
|
-
- ✅
|
|
349
|
-
- ✅ Timeouts
|
|
345
|
+
### Stability
|
|
346
|
+
- ✅ Memory leak prevention
|
|
347
|
+
- ✅ Race condition prevention
|
|
348
|
+
- ✅ Crash prevention due to unhandled errors
|
|
349
|
+
- ✅ Timeouts for potentially long operations
|
|
350
350
|
|
|
351
|
-
###
|
|
352
|
-
- ✅
|
|
353
|
-
- ✅
|
|
354
|
-
- ✅
|
|
351
|
+
### Portability
|
|
352
|
+
- ✅ Windows compatibility
|
|
353
|
+
- ✅ Linux compatibility
|
|
354
|
+
- ✅ macOS compatibility
|
|
355
355
|
|
|
356
|
-
###
|
|
357
|
-
- ✅
|
|
358
|
-
- ✅
|
|
359
|
-
- ✅
|
|
360
|
-
- ✅
|
|
356
|
+
### User Experience
|
|
357
|
+
- ✅ Optional ports in container creation
|
|
358
|
+
- ✅ Clearer feedback messages
|
|
359
|
+
- ✅ Correct CPU statistics
|
|
360
|
+
- ✅ Better error handling with informative messages
|
|
361
361
|
|
|
362
|
-
###
|
|
363
|
-
- ✅
|
|
364
|
-
- ✅
|
|
365
|
-
- ✅
|
|
362
|
+
### Code Quality
|
|
363
|
+
- ✅ ES6 compliant
|
|
364
|
+
- ✅ Better test coverage (5 → 12 tests)
|
|
365
|
+
- ✅ More maintainable code
|
|
366
366
|
|
|
367
367
|
---
|
|
368
368
|
|
|
369
|
-
## 6.
|
|
369
|
+
## 6. MODIFIED FILES
|
|
370
370
|
|
|
371
371
|
1. `src/helpers/dockerService/serviceComponents/containerActions.js`
|
|
372
372
|
2. `src/hooks/creation/useContainerCreation.js`
|
|
@@ -382,40 +382,38 @@ Successfully compiled 28 files with Babel (815ms).
|
|
|
382
382
|
|
|
383
383
|
---
|
|
384
384
|
|
|
385
|
-
## 7.
|
|
385
|
+
## 7. FUTURE RECOMMENDATIONS
|
|
386
386
|
|
|
387
|
-
|
|
387
|
+
While the critical issues have been addressed, the internal audit yielded additional recommendations for future improvements:
|
|
388
388
|
|
|
389
|
-
###
|
|
390
|
-
-
|
|
391
|
-
-
|
|
392
|
-
-
|
|
393
|
-
-
|
|
389
|
+
### Medium Priority
|
|
390
|
+
- Add more unit tests
|
|
391
|
+
- Implement PropTypes or migrate to TypeScript
|
|
392
|
+
- Extract magic numbers into constants
|
|
393
|
+
- Improve the logging system
|
|
394
394
|
|
|
395
|
-
###
|
|
396
|
-
-
|
|
397
|
-
-
|
|
398
|
-
-
|
|
399
|
-
-
|
|
395
|
+
### Low Priority
|
|
396
|
+
- Implement i18n (internationalization)
|
|
397
|
+
- Improve JSDoc documentation
|
|
398
|
+
- Consider websockets for real-time updates
|
|
399
|
+
- Implement retry logic for Docker reconnection
|
|
400
400
|
|
|
401
401
|
---
|
|
402
402
|
|
|
403
|
-
## 8.
|
|
403
|
+
## 8. CONCLUSION
|
|
404
404
|
|
|
405
|
-
|
|
405
|
+
We applied 11 critical fixes that significantly improve:
|
|
406
406
|
|
|
407
|
-
-
|
|
408
|
-
-
|
|
409
|
-
-
|
|
410
|
-
-
|
|
407
|
+
- Security: 0 vulnerabilities
|
|
408
|
+
- Stability: Prevention of memory leaks and race conditions
|
|
409
|
+
- Portability: Works on Windows, Linux, and macOS
|
|
410
|
+
- Quality: +140% more tests (5 → 12)
|
|
411
411
|
|
|
412
|
-
|
|
413
|
-
- ✅
|
|
414
|
-
- ✅
|
|
415
|
-
- ✅
|
|
416
|
-
|
|
417
|
-
El proyecto ahora tiene una base más sólida y está listo para uso en producción.
|
|
412
|
+
All fixes have been tested and verified via:
|
|
413
|
+
- ✅ Unit tests (12/12 passing)
|
|
414
|
+
- ✅ Successful build
|
|
415
|
+
- ✅ CodeQL security analysis (0 alerts)
|
|
418
416
|
|
|
419
417
|
---
|
|
420
418
|
|
|
421
|
-
**
|
|
419
|
+
**End of fixes document**
|