@firedesktop/react-base 2.1.24 → 3.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.
Files changed (114) hide show
  1. package/LICENSE +162 -0
  2. package/README.md +174 -94
  3. package/dist/components/AppIcon.d.ts +20 -20
  4. package/dist/components/AppIcon.js +64 -53
  5. package/dist/components/AppIcon.js.map +1 -1
  6. package/dist/components/AppInput.d.ts +20 -20
  7. package/dist/components/AppInput.js +18 -7
  8. package/dist/components/AppInput.js.map +1 -1
  9. package/dist/components/AppPagination.d.ts +11 -11
  10. package/dist/components/AppPagination.js +74 -63
  11. package/dist/components/AppPagination.js.map +1 -1
  12. package/dist/components/Spin.d.ts +6 -6
  13. package/dist/components/Spin.js +21 -17
  14. package/dist/components/Spin.js.map +1 -1
  15. package/dist/components/Toaster/Toaster.d.ts +7 -7
  16. package/dist/components/Toaster/Toaster.js +41 -29
  17. package/dist/components/Toaster/Toaster.js.map +1 -1
  18. package/dist/components/Toaster/Types.d.ts +6 -6
  19. package/dist/components/Toaster/Types.js +1 -1
  20. package/dist/components/index.d.ts +7 -7
  21. package/dist/components/index.js +7 -7
  22. package/dist/index.d.ts +8 -7
  23. package/dist/index.js +8 -7
  24. package/dist/index.js.map +1 -1
  25. package/dist/utils/CurrencyUtiles.d.ts +4 -4
  26. package/dist/utils/CurrencyUtiles.js +35 -30
  27. package/dist/utils/CurrencyUtiles.js.map +1 -1
  28. package/dist/utils/DateUtils.d.ts +7 -7
  29. package/dist/utils/DateUtils.js +111 -100
  30. package/dist/utils/DateUtils.js.map +1 -1
  31. package/dist/utils/FileUtils.d.ts +5 -5
  32. package/dist/utils/FileUtils.js +73 -73
  33. package/dist/utils/FileUtils.js.map +1 -1
  34. package/dist/utils/Logger.d.ts +13 -0
  35. package/dist/utils/Logger.js +44 -0
  36. package/dist/utils/Logger.js.map +1 -0
  37. package/dist/utils/RegExValidation.d.ts +4 -4
  38. package/dist/utils/RegExValidation.js +19 -19
  39. package/dist/utils/UrlUtils.d.ts +4 -4
  40. package/dist/utils/UrlUtils.js +15 -15
  41. package/dist/utils/UrlUtils.js.map +1 -1
  42. package/dist/utils/configuration/ConfigurationLoader.d.ts +6 -6
  43. package/dist/utils/configuration/ConfigurationLoader.js +76 -75
  44. package/dist/utils/configuration/ConfigurationLoader.js.map +1 -1
  45. package/dist/utils/configuration/ConfigurationManager.d.ts +5 -5
  46. package/dist/utils/configuration/ConfigurationManager.js +96 -89
  47. package/dist/utils/configuration/ConfigurationManager.js.map +1 -1
  48. package/dist/utils/configuration/ConfigurationReturner.d.ts +9 -9
  49. package/dist/utils/configuration/ConfigurationReturner.js +70 -69
  50. package/dist/utils/configuration/ConfigurationReturner.js.map +1 -1
  51. package/dist/utils/configuration/index.d.ts +4 -4
  52. package/dist/utils/configuration/index.js +4 -4
  53. package/dist/utils/fetch/Types.d.ts +10 -10
  54. package/dist/utils/fetch/Types.js +1 -1
  55. package/dist/utils/fetch/fetchWrapper.d.ts +14 -14
  56. package/dist/utils/fetch/fetchWrapper.js +239 -262
  57. package/dist/utils/fetch/fetchWrapper.js.map +1 -1
  58. package/dist/utils/fetch/index.d.ts +3 -3
  59. package/dist/utils/fetch/index.js +3 -3
  60. package/dist/utils/index.d.ts +10 -9
  61. package/dist/utils/index.js +10 -9
  62. package/dist/utils/index.js.map +1 -1
  63. package/dist/utils/labels/LanguageLoader.d.ts +7 -7
  64. package/dist/utils/labels/LanguageLoader.js +130 -100
  65. package/dist/utils/labels/LanguageLoader.js.map +1 -1
  66. package/dist/utils/labels/LanguageManager.d.ts +6 -6
  67. package/dist/utils/labels/LanguageManager.js +118 -110
  68. package/dist/utils/labels/LanguageManager.js.map +1 -1
  69. package/dist/utils/labels/LanguageReturner.d.ts +10 -10
  70. package/dist/utils/labels/LanguageReturner.js +70 -69
  71. package/dist/utils/labels/LanguageReturner.js.map +1 -1
  72. package/dist/utils/labels/index.d.ts +4 -4
  73. package/dist/utils/labels/index.js +4 -4
  74. package/docs/APP_ICON.md +82 -0
  75. package/docs/APP_INPUT.md +81 -0
  76. package/docs/APP_PAGINATION.md +61 -0
  77. package/docs/CONFIGURATION.md +105 -0
  78. package/docs/FETCH_WRAPPER.md +163 -0
  79. package/docs/LABELS.md +148 -0
  80. package/docs/LOGGER.md +112 -0
  81. package/docs/SECURITY_AUDIT_2026-02-05.md +468 -0
  82. package/docs/SPIN.md +45 -0
  83. package/docs/TOASTER.md +75 -0
  84. package/docs/UTILITIES.md +177 -0
  85. package/package.json +25 -35
  86. package/src/App.css +0 -12
  87. package/src/lib/components/AppIcon.tsx +0 -784
  88. package/src/lib/components/AppInput.tsx +0 -66
  89. package/src/lib/components/AppPagination.tsx +0 -124
  90. package/src/lib/components/Spin.tsx +0 -31
  91. package/src/lib/components/Toaster/Toaster.tsx +0 -50
  92. package/src/lib/components/Toaster/Types.ts +0 -11
  93. package/src/lib/components/index.ts +0 -8
  94. package/src/lib/index.ts +0 -15
  95. package/src/lib/styles/base.css +0 -392
  96. package/src/lib/styles/syncfusion_bootstrap4.css +0 -10
  97. package/src/lib/styles/toaster.css +0 -50
  98. package/src/lib/utils/CurrencyUtiles.ts +0 -48
  99. package/src/lib/utils/DateUtils.ts +0 -135
  100. package/src/lib/utils/FileUtils.ts +0 -40
  101. package/src/lib/utils/RegExValidation.ts +0 -49
  102. package/src/lib/utils/UrlUtils.ts +0 -17
  103. package/src/lib/utils/configuration/ConfigurationLoader.tsx +0 -43
  104. package/src/lib/utils/configuration/ConfigurationManager.ts +0 -38
  105. package/src/lib/utils/configuration/ConfigurationReturner.tsx +0 -39
  106. package/src/lib/utils/configuration/index.ts +0 -9
  107. package/src/lib/utils/fetch/Types.ts +0 -11
  108. package/src/lib/utils/fetch/fetchWrapper.ts +0 -174
  109. package/src/lib/utils/fetch/index.ts +0 -4
  110. package/src/lib/utils/index.ts +0 -11
  111. package/src/lib/utils/labels/LanguageLoader.tsx +0 -69
  112. package/src/lib/utils/labels/LanguageManager.ts +0 -61
  113. package/src/lib/utils/labels/LanguageReturner.tsx +0 -41
  114. package/src/lib/utils/labels/index.ts +0 -9
@@ -0,0 +1,468 @@
1
+ # Security Audit Report
2
+
3
+ ## @firedesktop/react-base v2.1.25
4
+
5
+ **Audit Date**: 2026-02-05
6
+ **Scope**: Full static analysis of `src/lib/` (27 source files), `package.json`, `tsconfig.json`, `.eslintrc`, `.npmignore`
7
+ **Method**: Manual source code review + `npm audit`
8
+ **Findings**: 0 Critical, 2 High, 5 Medium, 5 Low, 4 Info (16 total)
9
+ **Remediated**: 9 of 16 findings fixed, 2 accepted risk (SEC-014, SEC-015)
10
+
11
+ ---
12
+
13
+ ## Executive Summary
14
+
15
+ No remote code execution or direct injection vulnerabilities were found. The codebase avoids `dangerouslySetInnerHTML`, `eval()`, `new Function()`, `innerHTML`, and `document.write()` entirely. `npm audit` reports zero known dependency vulnerabilities. All HIGH-severity issues have been remediated: XSS through Syncfusion's Toast HTML rendering (SEC-001, fixed with HTML entity escaping) and prototype pollution via `lodash.merge()` (SEC-002, fixed with `safeMerge()` and lodash removal). Three MEDIUM-severity issues were also fixed: missing HTTP response validation (SEC-004), excessive production logging (SEC-005, replaced with opt-in Logger), and unvalidated JSON dispatched to Redux (SEC-007). Four additional fixes addressed silent error swallowing (SEC-011, Logger warnings added), missing Content-Type validation (SEC-012), TypeScript strict mode (SEC-013, `strict: true` enabled), and unchecked DOM null references (SEC-016, null guards added). Two INFO findings (SEC-014 source maps, SEC-015 TypeScript source in package) were marked as accepted risk: this is an internal company package and consumers require these assets for debugging and IDE navigation. Remaining 5 open findings span credentials configuration (SEC-003), non-standard authentication headers (SEC-006), path/language input validation (SEC-008), ReDoS in URL regex (SEC-009), and `encodeURI` misuse (SEC-010).
16
+
17
+ ---
18
+
19
+ ## HIGH Severity
20
+
21
+ ### SEC-001 -- Potential XSS via Syncfusion Toast HTML Content Rendering -- FIXED
22
+
23
+ **File**: `src/lib/components/Toaster/Toaster.tsx:26,29,33,35`
24
+ **Category**: Cross-Site Scripting (XSS)
25
+ **CVSS**: 7.1
26
+ **Status**: FIXED
27
+
28
+ Syncfusion's `ToastComponent.show()` renders the `content` and `title` properties as HTML by default. The `propertiesObject.content` and `propertiesObject.title` values were passed directly from consumer code without sanitization.
29
+
30
+ ```typescript
31
+ // Toaster.tsx:26 (before fix)
32
+ toastObj.current.show({
33
+ title: propertiesObject.title,
34
+ content: propertiesObject.content ?? '',
35
+ cssClass: 'e-toast-info',
36
+ icon: 'e-info toast-icons'
37
+ });
38
+ ```
39
+
40
+ If a consumer application passes user-controlled data (e.g., error messages containing user input), payloads like `<img src=x onerror=alert(1)>` would execute in the user's browser context.
41
+
42
+ **Applied Fix**: Added an `escapeHtml()` function that replaces `&`, `<`, `>`, `"`, and `'` with their HTML entity equivalents. Both `title` and `content` are sanitized through this function before being passed to all `.show()` calls. This is a zero-dependency solution that neutralizes HTML injection without requiring DOMPurify.
43
+
44
+ ---
45
+
46
+ ### SEC-002 -- Prototype Pollution via `lodash.merge()` on Untrusted Server Response -- FIXED
47
+
48
+ **File**: `src/lib/utils/labels/LanguageLoader.tsx:47`
49
+ **Category**: Prototype Pollution
50
+ **CVSS**: 7.3
51
+ **Status**: FIXED
52
+
53
+ `_.merge()` deep-merged two objects where both originate from server-fetched JSON. A compromised or MITM'd response containing `{"__proto__": {"polluted": true}}` could modify `Object.prototype` for the entire application.
54
+
55
+ ```typescript
56
+ // LanguageLoader.tsx:47 (before fix)
57
+ const merged = _.merge(allLabels, response);
58
+ ```
59
+
60
+ Additionally, `lodash` was listed as a `devDependency` (package.json:31), not a runtime dependency. The compiled output still imported lodash, but the version used at runtime depended on whatever the consumer had installed.
61
+
62
+ **Applied Fix**: Removed the `lodash` import entirely from `LanguageLoader.tsx` and replaced `_.merge()` with a custom `safeMerge()` function. The function performs recursive deep merge while explicitly skipping keys in `__proto__`, `constructor`, and `prototype` (via a `BLOCKED_KEYS` Set). This eliminates both the prototype pollution vector and the lodash runtime dependency from the library. The lodash dependency classification issue is now moot since lodash is no longer used in any published source file.
63
+
64
+ ---
65
+
66
+ ## MEDIUM Severity
67
+
68
+ ### SEC-003 -- Credentials Sent Cross-Origin via Hardcoded `credentials: 'include'`
69
+
70
+ **File**: `src/lib/utils/fetch/fetchWrapper.ts:98-100`
71
+ **Category**: Session Security
72
+ **CVSS**: 5.3
73
+
74
+ `credentials: 'include'` is hardcoded for every request and cannot be overridden by consumers. Combined with `mode: 'cors'`, cookies are sent to any URL passed to the FetchWrapper, including potential third-party origins.
75
+
76
+ ```typescript
77
+ // fetchWrapper.ts:95-103
78
+ const requestInit = {
79
+ body,
80
+ method: method,
81
+ mode: 'cors',
82
+ cache: 'no-cache',
83
+ credentials: 'include',
84
+ headers,
85
+ signal: abortController.signal
86
+ } as RequestInit;
87
+ ```
88
+
89
+ If a consuming application constructs a URL from user input and passes it to FetchWrapper, session cookies leak cross-origin.
90
+
91
+ **Fix**: Default to `credentials: 'same-origin'`. Allow callers to override to `'include'` via a parameter when explicitly needed.
92
+
93
+ ---
94
+
95
+ ### SEC-004 -- Missing HTTP Response Status Validation in ConfigurationManager and LanguageManager -- FIXED
96
+
97
+ **Files**:
98
+ - `src/lib/utils/configuration/ConfigurationManager.ts:5-14`
99
+ - `src/lib/utils/labels/LanguageManager.ts:3-13`
100
+
101
+ **Category**: Insecure HTTP Handling
102
+ **CVSS**: 5.3
103
+ **Status**: FIXED
104
+
105
+ Both manager functions called `fetch()` then immediately called `res.json()` without checking `res.ok` or `res.status`. Error responses (404, 500) with JSON bodies were silently treated as valid configuration/labels. Non-JSON error pages caused a parse exception caught by the silent catch block.
106
+
107
+ ```typescript
108
+ // ConfigurationManager.ts:5-14 (before fix)
109
+ const res = await fetch(fullPath, {
110
+ headers: { ... }
111
+ });
112
+ return await res.json(); // no res.ok check
113
+ ```
114
+
115
+ Note: The FetchWrapper (`fetchWrapper.ts:113`) does check response status correctly. The issue was isolated to the two manager utility functions that use raw `fetch()`.
116
+
117
+ **Applied Fix**: Added `if (!res.ok) throw new Error(`Configuration load failed: HTTP ${res.status}`)` in `ConfigurationManager.ts` and the equivalent `Labels load failed` check in `LanguageManager.ts`, both placed immediately after the `fetch()` call and before `res.json()`. Non-2xx responses now throw descriptive errors instead of being silently parsed.
118
+
119
+ ---
120
+
121
+ ### SEC-005 -- Excessive Console Logging in Published Library Code -- FIXED
122
+
123
+ **Files**: 7 files, 25+ occurrences
124
+ **Category**: Information Leakage
125
+ **CVSS**: 4.3
126
+ **Status**: FIXED
127
+
128
+ The published library contained `console.log`, `console.warn`, and `console.error` calls that output internal file paths, full response payloads, and fetch URLs to the browser console in production.
129
+
130
+ Key examples:
131
+ ```typescript
132
+ // ConfigurationManager.ts:27 -- logged full configuration response object (before fix)
133
+ console.log(`Loaded Configuration for this Site in this path: ${fullPath}`, response);
134
+
135
+ // LanguageLoader.tsx:37 -- logged full label data (before fix)
136
+ console.log(`Loaded Base Language Labels...`, allLabels);
137
+
138
+ // fetchWrapper.ts:122 -- logged URL and full error object (before fix)
139
+ console.warn(`Error on fetch url: ${url}`, error);
140
+ ```
141
+
142
+ **Affected files**: `ConfigurationLoader.tsx` (3), `ConfigurationReturner.tsx` (3), `ConfigurationManager.ts` (4), `LanguageLoader.tsx` (7), `LanguageReturner.tsx` (3), `LanguageManager.ts` (4), `fetchWrapper.ts` (1).
143
+
144
+ **Applied Fix**: Created a centralized `Logger` utility (`src/lib/utils/Logger.ts`) that is disabled by default and produces zero console output unless explicitly enabled by the consumer via `Logger.enableDebug(true)`. Replaced all 25 `console.log/warn/error` calls across all 7 affected files with `Logger.debug/warn/error`. All messages are prefixed with `[react-base]` for easy filtering. The Logger is exported at the top level so consumers can opt in during development. See [LOGGER.md](LOGGER.md) for the full API reference.
145
+
146
+ ---
147
+
148
+ ### SEC-006 -- Custom SessionToken Header Bypasses Browser Security Mechanisms
149
+
150
+ **File**: `src/lib/utils/fetch/fetchWrapper.ts:70-73`
151
+ **Category**: Authentication
152
+ **CVSS**: 5.0
153
+
154
+ The authentication token is sent via a custom `SessionToken` header instead of the standard `Authorization: Bearer` scheme. The code contains a comment acknowledging this deviation.
155
+
156
+ ```typescript
157
+ // fetchWrapper.ts:70-73
158
+ if (token)
159
+ headers.append('SessionToken', token);
160
+ // This should be the right way, but here we use SessionToken
161
+ // headers.append('Authorization', 'Bearer ' + token);
162
+ ```
163
+
164
+ The standard `Authorization` header is automatically stripped by browsers on cross-origin redirects (per Fetch spec). Custom headers like `SessionToken` are not, meaning tokens can leak if a request is redirected to a different origin. Security tools (WAFs, proxies, audit logs) also do not recognize custom headers as authentication credentials.
165
+
166
+ **Fix**: Migrate to `Authorization: Bearer <token>`. If the backend requires `SessionToken`, document the security implications and ensure the backend does not issue cross-origin redirects.
167
+
168
+ ---
169
+
170
+ ### SEC-007 -- Unvalidated JSON Responses Dispatched to Redux Store -- FIXED
171
+
172
+ **Files**:
173
+ - `src/lib/utils/configuration/ConfigurationLoader.tsx:26`
174
+ - `src/lib/utils/configuration/ConfigurationManager.ts:28`
175
+ - `src/lib/utils/labels/LanguageLoader.tsx:47-48`
176
+ - `src/lib/utils/labels/LanguageManager.ts:27`
177
+
178
+ **Category**: Insecure Deserialization
179
+ **CVSS**: 4.3
180
+ **Status**: FIXED
181
+
182
+ JSON responses from configuration and label endpoints were parsed and dispatched directly to the Redux store without schema validation. A compromised server or MITM attack could inject arbitrary data structures into application state.
183
+
184
+ ```typescript
185
+ // ConfigurationLoader.tsx:26 (before fix)
186
+ dispatch(updateAppState('configuration', response));
187
+
188
+ // LanguageLoader.tsx:47-48 (before fix)
189
+ const merged = _.merge(allLabels, response);
190
+ dispatch(updateAppState('labels', merged));
191
+ ```
192
+
193
+ **Applied Fix**: Added plain object validation in `ConfigurationManager.ts` and `LanguageManager.ts` immediately after `res.json()`. The check rejects `null`, `undefined`, primitives, and arrays -- only plain objects (`typeof data === 'object' && !Array.isArray(data)`) are accepted and returned. Invalid responses throw a descriptive error (e.g., `Configuration response is not a valid JSON object`). This prevents non-object payloads from reaching the Redux store.
194
+
195
+ ---
196
+
197
+ ## LOW Severity
198
+
199
+ ### SEC-008 -- No Input Validation on Path and Language Parameters
200
+
201
+ **Files**:
202
+ - `src/lib/utils/labels/LanguageLoader.tsx:20-26`
203
+ - `src/lib/utils/labels/LanguageReturner.tsx:18`
204
+ - `src/lib/utils/configuration/ConfigurationLoader.tsx:16-18`
205
+ - `src/lib/utils/configuration/ConfigurationReturner.tsx:17`
206
+
207
+ **Category**: Path Traversal / Input Validation
208
+ **CVSS**: 3.5
209
+
210
+ The `language` and `path` props are interpolated directly into fetch URLs without validation.
211
+
212
+ ```typescript
213
+ // LanguageLoader.tsx:20-26
214
+ const fileName = `${language}.json`;
215
+ let fullPath = `./labels/${fileName}?${avoidCache}`;
216
+ if (path)
217
+ fullPath = `./${path}/${fileName}?${avoidCache}`;
218
+
219
+ // ConfigurationLoader.tsx:16-18
220
+ let fullPath = '/configuration/config.json';
221
+ if (path)
222
+ fullPath = path; // completely replaces default, no validation
223
+ ```
224
+
225
+ If consumer code passes user-controlled values (e.g., from URL query parameters), `../../` sequences or absolute URLs could force the app to fetch arbitrary JSON.
226
+
227
+ **Fix**: Validate `language` against an allowlist of supported locale codes. Validate `path` to reject `..`, absolute URLs, and protocol prefixes.
228
+
229
+ ---
230
+
231
+ ### SEC-009 -- ReDoS Risk in URL Validation Regex
232
+
233
+ **File**: `src/lib/utils/UrlUtils.ts:3-8`
234
+ **Category**: Denial of Service (ReDoS)
235
+ **CVSS**: 3.7
236
+
237
+ The URL validation regex contains nested quantifiers in the domain section.
238
+
239
+ ```typescript
240
+ // UrlUtils.ts:3-8
241
+ var pattern = new RegExp('^(https?:\\/\\/)?' +
242
+ '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // nested quantifier
243
+ '((\\d{1,3}\\.){3}\\d{1,3}))' +
244
+ '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' +
245
+ '(\\?[;&a-z\\d%_.~+=-]*)?' +
246
+ '(\\#[-a-z\\d_]*)?$', 'i');
247
+ ```
248
+
249
+ The pattern `([a-z\d-]*[a-z\d])*` in the domain section creates ambiguity: both `[a-z\d-]*` and `[a-z\d]` match the same characters (minus hyphen), allowing exponential partitioning on crafted non-matching input. The `.` delimiter between domain labels limits but does not eliminate the attack. The path section is safe due to `/` delimiters.
250
+
251
+ **Fix**: Replace with the `URL` constructor: `try { new URL(value); return true; } catch { return false; }`.
252
+
253
+ ---
254
+
255
+ ### SEC-010 -- `encodeURI` Applied to Full URL Instead of Components
256
+
257
+ **File**: `src/lib/utils/fetch/fetchWrapper.ts:110`
258
+ **Category**: URL Injection
259
+ **CVSS**: 3.1
260
+
261
+ ```typescript
262
+ // fetchWrapper.ts:110
263
+ return await fetch(encodeURI(url), requestInit)
264
+ ```
265
+
266
+ `encodeURI()` does not encode `#`, `?`, `&`, `=`, `/`. User-controlled path segments embedded in the URL could inject query parameters or fragment identifiers.
267
+
268
+ **Fix**: Remove `encodeURI()` from the full URL. Ensure callers encode individual path segments and query values with `encodeURIComponent()`, or provide a URL builder utility.
269
+
270
+ ---
271
+
272
+ ### SEC-011 -- Silent Error Swallowing in Utility Functions -- FIXED
273
+
274
+ **Files**:
275
+ - `src/lib/utils/CurrencyUtiles.ts:19,38` -- empty `catch (err) { }`
276
+ - `src/lib/utils/DateUtils.ts:20,66,77,100,121` -- empty `catch (err) { }`
277
+ - `src/lib/utils/configuration/ConfigurationManager.ts:16-18`
278
+ - `src/lib/utils/labels/LanguageManager.ts:15-17`
279
+
280
+ **Category**: Error Handling
281
+ **CVSS**: 2.5
282
+ **Status**: FIXED
283
+
284
+ Empty catch blocks silently discarded exceptions. In CurrencyUtiles and DateUtils, 7 catch blocks returned empty strings or undefined without any indication of failure. In the managers, errors during fetch were logged but the actual error object was discarded. In LanguageManager, the `getLabel` catch was completely silent.
285
+
286
+ ```typescript
287
+ // CurrencyUtiles.ts:19 (before fix)
288
+ } catch (err) { }
289
+ return '';
290
+
291
+ // DateUtils.ts:66 (before fix)
292
+ } catch (err) { }
293
+ return '';
294
+ ```
295
+
296
+ **Applied Fix**: Added `Logger.warn` calls with descriptive messages and the error object to all 7 empty catch blocks in `CurrencyUtiles.ts` (2) and `DateUtils.ts` (5). Each log identifies the function that failed (e.g., `DateUtils: dateToString failed`). In `ConfigurationManager.ts` and `LanguageManager.ts`, updated existing Logger calls to include the `err` object as a second argument. Added a `Logger.warn` call to `LanguageManager.getLabel`'s previously silent catch. All errors are now visible when debug logging is enabled via `Logger.enableDebug(true)`.
297
+
298
+ ---
299
+
300
+ ### SEC-012 -- No Content-Type Validation on Fetch Responses -- FIXED
301
+
302
+ **File**: `src/lib/utils/fetch/fetchWrapper.ts:116`
303
+ **Category**: Response Validation
304
+ **CVSS**: 2.5
305
+ **Status**: FIXED
306
+
307
+ Response `Content-Type` was not validated before calling `.json()` or `.blob()`. If the server returned an HTML error page with status 200, the `.json()` call threw a generic `SyntaxError` parse error.
308
+
309
+ ```typescript
310
+ // fetchWrapper.ts:116 (before fix)
311
+ return isBlobInReturn ? response.blob() : response.json();
312
+ ```
313
+
314
+ **Applied Fix**: Split the ternary into separate branches. For JSON responses, the `Content-Type` header is now checked for `application/json` before calling `.json()`. If the header is missing or does not match, a descriptive `Error` is thrown (`Expected JSON response but received Content-Type: ...`). Blob responses are unaffected since `.blob()` accepts any content type.
315
+
316
+ ---
317
+
318
+ ## INFO
319
+
320
+ ### SEC-013 -- TypeScript Strict Mode Disabled -- FIXED
321
+
322
+ **File**: `tsconfig.json:25`
323
+ **CVSS**: N/A
324
+ **Status**: FIXED
325
+
326
+ `"strict": false` disabled `strictNullChecks`, `noImplicitAny`, `strictFunctionTypes`, and related safety checks. Combined with 32+ explicit `any` annotations across the codebase (e.g., `state: any`, `response: any`, `error: any`, `params?: Blob | any`), this weakened the type system's ability to catch null dereferences and type confusion bugs at compile time.
327
+
328
+ **Applied Fix**: Changed `"strict": false` to `"strict": true` in `tsconfig.json`, enabling `strictNullChecks`, `noImplicitAny`, `strictFunctionTypes`, `strictBindCallApply`, `strictPropertyInitialization`, `noImplicitThis`, `alwaysStrict`, and `useUnknownInCatchVariables`. Fixed 4 compilation errors: typed empty array literals in `AppPagination.tsx` (`number[]`) and `FileUtils.ts` (`Uint8Array[]`), guarded optional `url` parameter in `UrlUtils.ts`, and changed `body` variable type to `BodyInit | undefined` in `fetchWrapper.ts`. The 32+ existing explicit `any` annotations compile without error under `noImplicitAny` since they are already explicitly typed. Replacing those with proper types is recommended as a future improvement but is not a compilation blocker.
329
+
330
+ ---
331
+
332
+ ### SEC-014 -- Source Maps Included in Published npm Package -- ACCEPTED RISK
333
+
334
+ **File**: `tsconfig.json:24`
335
+ **CVSS**: N/A
336
+ **Status**: Accepted Risk
337
+
338
+ `"sourceMap": true` generates `.js.map` files in `dist/`. The `.npmignore` does not exclude map files, so they are published. Despite `"removeComments": true`, source maps allow reconstruction of the original TypeScript source, exposing internal structure and variable names.
339
+
340
+ **Decision**: Accepted as intentional. This is an internal company package (`@firedesktop/react-base`) consumed by company developers. Source maps are required by consumers to debug through the library code in browser DevTools during development. The information exposure risk is negligible for an internal package with no untrusted consumers.
341
+
342
+ ---
343
+
344
+ ### SEC-015 -- TypeScript Source Code Included in Published npm Package -- ACCEPTED RISK
345
+
346
+ **File**: `.npmignore`, `package.json`
347
+ **CVSS**: N/A
348
+ **Status**: Accepted Risk
349
+
350
+ The `.npmignore` excludes specific demo files but does not exclude the `src/lib/` directory. There is no `"files"` field in `package.json` to whitelist published content. The original TypeScript source is published alongside compiled output.
351
+
352
+ ```
353
+ # .npmignore -- only excludes demo files:
354
+ /.vscode
355
+ /public
356
+ /src/App.test.tsx
357
+ /src/App.tsx
358
+ /src/index.tsx
359
+ ...
360
+ # src/lib/ is NOT excluded
361
+ ```
362
+
363
+ **Decision**: Accepted as intentional. Publishing TypeScript source allows consumer developers to navigate to original definitions in their IDEs ("Go to Definition") and understand the library internals when debugging. Since all consumers are internal company projects, the exposure risk does not apply.
364
+
365
+ ---
366
+
367
+ ### SEC-016 -- Unchecked DOM Null References in Spin Component -- FIXED
368
+
369
+ **File**: `src/lib/components/Spin.tsx:16,22,24`
370
+ **CVSS**: N/A
371
+ **Status**: FIXED
372
+
373
+ `document.getElementById(targetId)` was cast to `HTMLElement` without a null check. If `targetId` didn't exist in the DOM, the cast lied about the null value and Syncfusion's `createSpinner`/`showSpinner`/`hideSpinner` threw runtime errors.
374
+
375
+ ```typescript
376
+ // Spin.tsx:16 (before fix)
377
+ createSpinner({
378
+ target: document.getElementById(targetId) as HTMLElement
379
+ });
380
+ // Spin.tsx:22 (before fix)
381
+ showSpinner(document.getElementById(targetId) as HTMLElement);
382
+ ```
383
+
384
+ **Applied Fix**: Replaced all three `as HTMLElement` casts with proper null guards. In the `useEffect`, the element is looked up and an early `return` is used if null. In the render body, `showSpinner`/`hideSpinner` are only called when the element exists. Removed all unsafe type assertions.
385
+
386
+ ---
387
+
388
+ ## Positive Findings
389
+
390
+ These security-positive patterns should be maintained:
391
+
392
+ 1. **No `dangerouslySetInnerHTML`** -- Zero usage across all components
393
+ 2. **No `eval()` or `new Function()`** -- No dynamic code execution
394
+ 3. **No `document.write()` or `innerHTML`** -- No unsafe DOM content insertion
395
+ 4. **No localStorage/sessionStorage for tokens** -- Session tokens not persisted in browser storage
396
+ 5. **Cache-busting headers** -- `Cache-Control: no-cache, no-store, must-revalidate` applied consistently in FetchWrapper and managers
397
+ 6. **`X-Content-Type-Options: nosniff`** -- Set in FetchWrapper and ConfigurationManager headers
398
+ 7. **AbortController timeout** -- FetchWrapper implements configurable request timeouts (default 60s)
399
+ 8. **FetchWrapper response validation** -- `fetchWrapper.ts:113` correctly checks `response.status < 200 || response.status >= 300`
400
+ 9. **Zero npm audit vulnerabilities** -- `npm audit` reports 0 known vulnerabilities
401
+ 10. **lodash removed from library** -- `lodash.merge()` replaced with safe custom merge; lodash is no longer imported in any published source file
402
+ 11. **React JSX rendering** -- Components use JSX which auto-escapes interpolated strings
403
+ 12. **Type-safe component props** -- Union type literals for `Toaster_Type`, `AppInputType_Type`, and `AppIcon` `name` prop
404
+
405
+ ---
406
+
407
+ ## Summary Table
408
+
409
+ | ID | Severity | Category | File(s) | CVSS | Status |
410
+ |----|----------|----------|---------|------|--------|
411
+ | SEC-001 | HIGH | XSS | Toaster.tsx | 7.1 | FIXED |
412
+ | SEC-002 | HIGH | Prototype Pollution | LanguageLoader.tsx | 7.3 | FIXED |
413
+ | SEC-003 | MEDIUM | Session Security | fetchWrapper.ts | 5.3 | Open |
414
+ | SEC-004 | MEDIUM | HTTP Handling | ConfigurationManager.ts, LanguageManager.ts | 5.3 | FIXED |
415
+ | SEC-005 | MEDIUM | Information Leakage | 7 files (25+ occurrences) | 4.3 | FIXED |
416
+ | SEC-006 | MEDIUM | Authentication | fetchWrapper.ts | 5.0 | Open |
417
+ | SEC-007 | MEDIUM | Deserialization | ConfigurationLoader.tsx, LanguageLoader.tsx + 2 | 4.3 | FIXED |
418
+ | SEC-008 | LOW | Path Traversal | LanguageLoader.tsx, ConfigurationLoader.tsx + 2 | 3.5 | Open |
419
+ | SEC-009 | LOW | ReDoS | UrlUtils.ts | 3.7 | Open |
420
+ | SEC-010 | LOW | URL Injection | fetchWrapper.ts | 3.1 | Open |
421
+ | SEC-011 | LOW | Error Handling | CurrencyUtiles.ts, DateUtils.ts + 2 | 2.5 | FIXED |
422
+ | SEC-012 | LOW | Response Validation | fetchWrapper.ts | 2.5 | FIXED |
423
+ | SEC-013 | INFO | Type Safety | tsconfig.json | -- | FIXED |
424
+ | SEC-014 | INFO | Information Leakage | tsconfig.json, .npmignore | -- | Accepted Risk |
425
+ | SEC-015 | INFO | Information Leakage | .npmignore, package.json | -- | Accepted Risk |
426
+ | SEC-016 | INFO | Runtime Safety | Spin.tsx | -- | FIXED |
427
+
428
+ ---
429
+
430
+ ## Remediation Priority
431
+
432
+ | Priority | Findings | Action |
433
+ |----------|----------|--------|
434
+ | ~~**Immediate**~~ | ~~SEC-001, SEC-002~~ | ~~Sanitize Toaster content; replace `_.merge()` with safe alternative~~ -- **ALL FIXED** |
435
+ | **Short-term** | ~~SEC-004, SEC-005~~, SEC-003, SEC-006 | ~~Add `res.ok` checks; remove console logging~~ **FIXED**; Default credentials to `same-origin`; evaluate SessionToken migration |
436
+ | **Medium-term** | ~~SEC-007~~, SEC-008, SEC-009 | ~~Add JSON validation~~ **FIXED**; validate path/language inputs; replace URL regex with `URL` constructor |
437
+ | **Long-term** | SEC-010, ~~SEC-011, SEC-012, SEC-013, SEC-014, SEC-015, SEC-016~~ | Fix URL encoding; ~~improve error handling~~ **FIXED**; ~~validate Content-Type~~ **FIXED**; ~~enable strict mode~~ **FIXED**; ~~source map/source publishing~~ **ACCEPTED RISK**; ~~null-guard Spin~~ **FIXED** |
438
+
439
+ ---
440
+
441
+ ## Remediation Recap
442
+
443
+ | ID | Severity | Finding | Fix Applied | Files Changed |
444
+ |----|----------|---------|-------------|---------------|
445
+ | SEC-001 | HIGH | XSS via Syncfusion Toast HTML rendering | Added `escapeHtml()` function that encodes `&`, `<`, `>`, `"`, `'` as HTML entities. Applied to `title` and `content` before all `.show()` calls. | `Toaster.tsx` |
446
+ | SEC-002 | HIGH | Prototype pollution via `lodash.merge()` | Removed lodash import. Replaced with `safeMerge()` that skips `__proto__`, `constructor`, `prototype` keys via a `BLOCKED_KEYS` Set. | `LanguageLoader.tsx` |
447
+ | SEC-004 | MEDIUM | Missing `res.ok` check on fetch responses | Added `if (!res.ok) throw new Error(...)` immediately after `fetch()` and before `res.json()` in both manager functions. | `ConfigurationManager.ts`, `LanguageManager.ts` |
448
+ | SEC-005 | MEDIUM | 25+ console calls leaking paths and data in production | Created centralized `Logger` utility (disabled by default, opt-in via `Logger.enableDebug()`). Replaced all 25 `console.*` calls across 7 files. All output prefixed with `[react-base]`. | `Logger.ts` (new), `ConfigurationLoader.tsx`, `ConfigurationReturner.tsx`, `ConfigurationManager.ts`, `LanguageLoader.tsx`, `LanguageReturner.tsx`, `LanguageManager.ts`, `fetchWrapper.ts` |
449
+ | SEC-007 | MEDIUM | Unvalidated JSON dispatched to Redux store | Added plain object validation after `res.json()`: rejects `null`, primitives, and arrays. Only `typeof === 'object' && !Array.isArray()` passes through. | `ConfigurationManager.ts`, `LanguageManager.ts` |
450
+ | SEC-011 | LOW | Silent error swallowing in utility functions | Added `Logger.warn` with descriptive messages and error objects to all 7 empty catch blocks in `CurrencyUtiles.ts` and `DateUtils.ts`. Updated manager catch blocks to include the `err` object. Added logging to `LanguageManager.getLabel` catch. | `CurrencyUtiles.ts`, `DateUtils.ts`, `ConfigurationManager.ts`, `LanguageManager.ts` |
451
+ | SEC-012 | LOW | No Content-Type validation on fetch responses | Split JSON/blob branches. Added `Content-Type` header check for `application/json` before calling `.json()`. Non-JSON responses now throw a descriptive error instead of a generic `SyntaxError`. | `fetchWrapper.ts` |
452
+ | SEC-013 | INFO | TypeScript strict mode disabled | Changed `strict: false` to `strict: true` in `tsconfig.json`. Fixed 4 compilation errors: typed array literals, guarded optional param, typed uninitialized variable. All strict checks now active. | `tsconfig.json`, `AppPagination.tsx`, `FileUtils.ts`, `UrlUtils.ts`, `fetchWrapper.ts` |
453
+ | SEC-016 | INFO | Unchecked DOM null references in Spin | Replaced all `as HTMLElement` casts with null guards. `createSpinner`, `showSpinner`, `hideSpinner` only called when element exists. | `Spin.tsx` |
454
+
455
+ **Overall progress**: 9/16 fixed, 2/16 accepted risk, 5/16 open (2/2 HIGH fixed, 3/5 MEDIUM fixed, 2/5 LOW fixed, 2/4 INFO fixed, 2/4 INFO accepted). All HIGH-severity issues resolved. Remaining 5 open findings require architectural decisions (credentials mode, SessionToken migration, input validation, URL handling).
456
+
457
+ ---
458
+
459
+ ## Changes from Previous Report (2026-02-02)
460
+
461
+ - **Restructured findings** from 18 to 16 by consolidating related issues (e.g., path traversal across multiple loaders, JSON deserialization across multiple dispatchers)
462
+ - **Downgraded ReDoS** (SEC-009) from HIGH to LOW after analysis showing `/` and `.` delimiters constrain the attack surface
463
+ - **Added SEC-003** as a standalone finding -- `credentials: 'include'` was previously embedded in another finding
464
+ - **Added SEC-007** -- unvalidated JSON to Redux store was previously split across multiple findings
465
+ - **Clarified SEC-006** -- documented the specific redirect-based token leakage mechanism for custom headers
466
+ - **Noted lodash dependency classification** -- lodash is in devDependencies, meaning consumers control the runtime version
467
+ - **Added positive finding #8** -- FetchWrapper does validate response status correctly (the gap is only in the manager utilities)
468
+ - **Ran `npm audit`** -- confirmed zero known dependency vulnerabilities
package/docs/SPIN.md ADDED
@@ -0,0 +1,45 @@
1
+ # Spin
2
+
3
+ Loading spinner overlay using Syncfusion's spinner utilities. This is a headless component (renders no visible DOM) -- it controls a spinner attached to a target DOM element.
4
+
5
+ ## Import
6
+
7
+ ```javascript
8
+ import { Components } from '@firedesktop/react-base';
9
+ const { Spin } = Components;
10
+ ```
11
+
12
+ ## Props
13
+
14
+ | Prop | Type | Required | Default | Description |
15
+ |------|------|----------|---------|-------------|
16
+ | `spinning` | `boolean` | Yes | -- | Show (`true`) or hide (`false`) the spinner |
17
+ | `targetId` | `string` | No | `'root'` | DOM element ID where the spinner is attached |
18
+
19
+ ## Usage
20
+
21
+ ```jsx
22
+ const [loading, setLoading] = useState(false);
23
+
24
+ <Spin spinning={loading} />
25
+ ```
26
+
27
+ ### On a specific container
28
+
29
+ ```jsx
30
+ <div id="my-panel">
31
+ <Spin spinning={loading} targetId="my-panel" />
32
+ {/* panel content */}
33
+ </div>
34
+ ```
35
+
36
+ ## Behavior
37
+
38
+ - Creates the Syncfusion spinner on the target element when the component mounts
39
+ - Shows/hides the spinner based on the `spinning` prop
40
+ - If the target element does not exist in the DOM, the component does nothing (null-safe)
41
+ - Returns an empty `<React.Fragment />` -- it does not render any visible output
42
+
43
+ ## Dependencies
44
+
45
+ Uses `createSpinner`, `showSpinner`, `hideSpinner` from `@syncfusion/ej2-popups` (bundled dependency).
@@ -0,0 +1,75 @@
1
+ # Toaster
2
+
3
+ Toast notification component using Syncfusion's ToastComponent. Displays styled notifications for errors, info, success, and warnings.
4
+
5
+ ## Import
6
+
7
+ ```javascript
8
+ import { Components } from '@firedesktop/react-base';
9
+ const { Toaster, Toaster_Types } = Components;
10
+ ```
11
+
12
+ ## Types
13
+
14
+ ```typescript
15
+ type Toaster_Type = 'Error' | 'Information' | 'Success' | 'Warning';
16
+
17
+ type Toaster_Prop_Type = {
18
+ content?: string;
19
+ title: string;
20
+ type: Toaster_Type;
21
+ };
22
+ ```
23
+
24
+ ## Props
25
+
26
+ | Prop | Type | Required | Description |
27
+ |------|------|----------|-------------|
28
+ | `propertiesObject` | `Toaster_Prop_Type` | Yes | Toast configuration object |
29
+
30
+ ### `propertiesObject` fields
31
+
32
+ | Field | Type | Required | Description |
33
+ |-------|------|----------|-------------|
34
+ | `title` | `string` | Yes | Toast title |
35
+ | `content` | `string` | No | Toast body message |
36
+ | `type` | `Toaster_Type` | Yes | Notification type |
37
+
38
+ ## Usage
39
+
40
+ ```jsx
41
+ const [toast, setToast] = useState(null);
42
+
43
+ {toast && <Toaster propertiesObject={toast} />}
44
+ ```
45
+
46
+ ### Showing different notification types
47
+
48
+ ```jsx
49
+ // Success
50
+ setToast({ title: 'Saved', content: 'Record saved.', type: 'Success' });
51
+
52
+ // Error
53
+ setToast({ title: 'Error', content: 'Failed to save.', type: 'Error' });
54
+
55
+ // Warning
56
+ setToast({ title: 'Warning', content: 'Unsaved changes.', type: 'Warning' });
57
+
58
+ // Information
59
+ setToast({ title: 'Info', content: 'New version available.', type: 'Information' });
60
+ ```
61
+
62
+ ## Behavior
63
+
64
+ - Displays a toast notification positioned to the right
65
+ - Each type has a distinct CSS class and icon:
66
+ - `Error` -- `e-toast-danger`, error icon, 4s timeout
67
+ - `Information` -- `e-toast-info`, info icon
68
+ - `Success` -- `e-toast-success`, success icon
69
+ - `Warning` -- `e-toast-warning`, warning icon
70
+ - Shows a progress bar and close button
71
+ - Title and content are HTML-escaped before rendering (XSS protection)
72
+
73
+ ## Dependencies
74
+
75
+ Uses `ToastComponent` from `@syncfusion/ej2-react-notifications` (bundled dependency). Requires the Syncfusion Bootstrap4 theme CSS for styling.