@reldens/server-utils 0.35.0 → 0.37.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/README.md +457 -395
- package/lib/app-server-factory/security-configurer.js +58 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,395 +1,457 @@
|
|
|
1
|
-
# Reldens - Server Utils
|
|
2
|
-
|
|
3
|
-
A Node.js server toolkit providing secure application server creation, file handling, encryption, and file upload capabilities for production-ready applications with modular security configurations.
|
|
4
|
-
|
|
5
|
-
[](https://github.com/damian-pastorini/reldens)
|
|
6
|
-
|
|
7
|
-
## Features
|
|
8
|
-
|
|
9
|
-
### AppServerFactory
|
|
10
|
-
- Complete Express.js server configuration with modular security
|
|
11
|
-
- HTTPS/HTTP server creation with SSL certificate management
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
- **
|
|
32
|
-
- **
|
|
33
|
-
- **
|
|
34
|
-
- **
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
- Password
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
-
|
|
59
|
-
-
|
|
60
|
-
-
|
|
61
|
-
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
-
|
|
66
|
-
-
|
|
67
|
-
-
|
|
68
|
-
-
|
|
69
|
-
-
|
|
70
|
-
-
|
|
71
|
-
-
|
|
72
|
-
-
|
|
73
|
-
-
|
|
74
|
-
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
let
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
let
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
'.
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
)
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
-
|
|
294
|
-
|
|
295
|
-
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
###
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
1
|
+
# Reldens - Server Utils
|
|
2
|
+
|
|
3
|
+
A Node.js server toolkit providing secure application server creation, file handling, encryption, and file upload capabilities for production-ready applications with modular security configurations.
|
|
4
|
+
|
|
5
|
+
[](https://github.com/damian-pastorini/reldens)
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
### AppServerFactory
|
|
10
|
+
- Complete Express.js server configuration with modular security
|
|
11
|
+
- HTTPS/HTTP server creation with SSL certificate management
|
|
12
|
+
- Optimized static asset caching for CSS, JS, fonts, and images
|
|
13
|
+
- SNI (Server Name Indication) support for multi-domain hosting
|
|
14
|
+
- Virtual host management with domain mapping
|
|
15
|
+
- Development mode detection with appropriate configurations
|
|
16
|
+
- CORS configuration with flexible origin management
|
|
17
|
+
- Rate limiting with customizable thresholds
|
|
18
|
+
- Security headers and XSS protection
|
|
19
|
+
- Helmet integration for enhanced security
|
|
20
|
+
- Protocol enforcement (HTTP to HTTPS redirection)
|
|
21
|
+
- Trusted proxy configuration
|
|
22
|
+
- Request parsing with size limits and validation
|
|
23
|
+
- Static file serving with security headers
|
|
24
|
+
- Compression middleware with smart filtering
|
|
25
|
+
- Input validation utilities
|
|
26
|
+
|
|
27
|
+
#### Modular Security Components
|
|
28
|
+
The AppServerFactory now uses specialized security configurers:
|
|
29
|
+
|
|
30
|
+
- **CorsConfigurer** - Dynamic CORS origin validation with development domain support
|
|
31
|
+
- **DevelopmentModeDetector** - Automatic development environment detection
|
|
32
|
+
- **ProtocolEnforcer** - Protocol redirection with development mode awareness
|
|
33
|
+
- **RateLimitConfigurer** - Global and endpoint-specific rate limiting
|
|
34
|
+
- **SecurityConfigurer** - Helmet integration with CSP management and XSS protection
|
|
35
|
+
|
|
36
|
+
### FileHandler
|
|
37
|
+
- Secure file system operations with path validation
|
|
38
|
+
- File and folder creation, copying, and removal
|
|
39
|
+
- JSON file parsing and validation
|
|
40
|
+
- File type detection based on magic numbers
|
|
41
|
+
- Secure filename generation
|
|
42
|
+
- Path sanitization and traversal protection
|
|
43
|
+
- File permissions checking
|
|
44
|
+
- Folder content listing and filtering
|
|
45
|
+
- Temporary file creation
|
|
46
|
+
- File quarantine functionality for security threats
|
|
47
|
+
- Binary file head reading for type detection
|
|
48
|
+
- Directory walking with callback processing
|
|
49
|
+
- File comparison and relative path calculations
|
|
50
|
+
- Comprehensive error handling with detailed context
|
|
51
|
+
|
|
52
|
+
### Encryptor
|
|
53
|
+
- Password hashing using PBKDF2 with configurable iterations
|
|
54
|
+
- Password validation against stored hashes
|
|
55
|
+
- AES-256-GCM data encryption and decryption
|
|
56
|
+
- Secure token generation with a customizable length
|
|
57
|
+
- TOTP (Time-based One-Time Password) generation
|
|
58
|
+
- Data hashing with multiple algorithms (SHA-256, SHA-512, MD5)
|
|
59
|
+
- HMAC generation and verification
|
|
60
|
+
- Constant-time string comparison for security
|
|
61
|
+
- Cryptographically secure random value generation
|
|
62
|
+
|
|
63
|
+
### UploaderFactory
|
|
64
|
+
- Multer-based file upload handling with security validation
|
|
65
|
+
- Multiple file upload support with field mapping
|
|
66
|
+
- File type validation using MIME types and extensions
|
|
67
|
+
- Filename security validation and sanitization
|
|
68
|
+
- File size limits and upload count restrictions
|
|
69
|
+
- Secure filename generation option
|
|
70
|
+
- File content validation based on magic numbers
|
|
71
|
+
- Dangerous file extension filtering
|
|
72
|
+
- Automatic file cleanup on validation failure
|
|
73
|
+
- Custom error response handling
|
|
74
|
+
- Upload destination mapping per field
|
|
75
|
+
|
|
76
|
+
## Installation
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npm install @reldens/server-utils
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Quick Start
|
|
83
|
+
|
|
84
|
+
### Basic Server Setup
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
const { AppServerFactory } = require('@reldens/server-utils');
|
|
88
|
+
|
|
89
|
+
let appServerFactory = new AppServerFactory();
|
|
90
|
+
let serverResult = appServerFactory.createAppServer({
|
|
91
|
+
port: 3000,
|
|
92
|
+
useHttps: false,
|
|
93
|
+
autoListen: true,
|
|
94
|
+
useCompression: true
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
if(serverResult){
|
|
98
|
+
let { app, appServer } = serverResult;
|
|
99
|
+
console.log('Server running on port 3000');
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### HTTPS Server with Optimized Caching
|
|
104
|
+
|
|
105
|
+
```javascript
|
|
106
|
+
let appServerFactory = new AppServerFactory();
|
|
107
|
+
let serverResult = appServerFactory.createAppServer({
|
|
108
|
+
port: 443,
|
|
109
|
+
useHttps: true,
|
|
110
|
+
keyPath: '/ssl/server.key',
|
|
111
|
+
certPath: '/ssl/server.crt',
|
|
112
|
+
autoListen: true
|
|
113
|
+
});
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Cache configuration is automatic with defaults:
|
|
117
|
+
- CSS/JS: 1 year
|
|
118
|
+
- Fonts: 1 year
|
|
119
|
+
- Images: 30 days
|
|
120
|
+
|
|
121
|
+
Override cache settings if needed:
|
|
122
|
+
|
|
123
|
+
```javascript
|
|
124
|
+
let appServerFactory = new AppServerFactory();
|
|
125
|
+
appServerFactory.cacheConfig = {
|
|
126
|
+
'.css': 86400,
|
|
127
|
+
'.js': 86400,
|
|
128
|
+
'.png': 604800
|
|
129
|
+
};
|
|
130
|
+
let serverResult = appServerFactory.createAppServer({
|
|
131
|
+
useHttps: true,
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### File Operations
|
|
136
|
+
|
|
137
|
+
```javascript
|
|
138
|
+
const { FileHandler } = require('@reldens/server-utils');
|
|
139
|
+
|
|
140
|
+
// Read a JSON configuration file
|
|
141
|
+
let config = FileHandler.fetchFileJson('/path/to/config.json');
|
|
142
|
+
if(config){
|
|
143
|
+
console.log('Configuration loaded:', config);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Create a folder securely
|
|
147
|
+
if(FileHandler.createFolder('/path/to/new/folder')){
|
|
148
|
+
console.log('Folder created successfully');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Generate a secure filename
|
|
152
|
+
let secureFilename = FileHandler.generateSecureFilename('user-upload.jpg');
|
|
153
|
+
console.log('Secure filename:', secureFilename);
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Password Encryption
|
|
157
|
+
|
|
158
|
+
```javascript
|
|
159
|
+
const { Encryptor } = require('@reldens/server-utils');
|
|
160
|
+
|
|
161
|
+
// Hash a password
|
|
162
|
+
let hashedPassword = Encryptor.encryptPassword('userPassword123');
|
|
163
|
+
if(hashedPassword){
|
|
164
|
+
console.log('Password hashed:', hashedPassword);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Validate password
|
|
168
|
+
let isValid = Encryptor.validatePassword('userPassword123', hashedPassword);
|
|
169
|
+
console.log('Password valid:', isValid);
|
|
170
|
+
|
|
171
|
+
// Generate secure token
|
|
172
|
+
let secureToken = Encryptor.generateSecureToken(32);
|
|
173
|
+
console.log('Secure token:', secureToken);
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### File Upload Configuration
|
|
177
|
+
|
|
178
|
+
```javascript
|
|
179
|
+
const { UploaderFactory } = require('@reldens/server-utils');
|
|
180
|
+
|
|
181
|
+
let uploaderFactory = new UploaderFactory({
|
|
182
|
+
maxFileSize: 10 * 1024 * 1024, // 10MB
|
|
183
|
+
mimeTypes: {
|
|
184
|
+
image: ['image/jpeg', 'image/png', 'image/gif'],
|
|
185
|
+
document: ['application/pdf', 'text/plain']
|
|
186
|
+
},
|
|
187
|
+
allowedExtensions: {
|
|
188
|
+
image: ['.jpg', '.jpeg', '.png', '.gif'],
|
|
189
|
+
document: ['.pdf', '.txt']
|
|
190
|
+
},
|
|
191
|
+
applySecureFileNames: true
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
let uploader = uploaderFactory.createUploader(
|
|
195
|
+
[{ name: 'avatar' }, { name: 'document' }],
|
|
196
|
+
{ avatar: '/uploads/avatars', document: '/uploads/docs' },
|
|
197
|
+
{ avatar: 'image', document: 'document' }
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
// Use with Express
|
|
201
|
+
app.post('/upload', uploader, (req, res) => {
|
|
202
|
+
console.log('Files uploaded:', req.files);
|
|
203
|
+
res.json({ success: true });
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Advanced Configuration
|
|
208
|
+
|
|
209
|
+
### HTTPS Server with Multiple Domains
|
|
210
|
+
|
|
211
|
+
```javascript
|
|
212
|
+
let appServerFactory = new AppServerFactory();
|
|
213
|
+
|
|
214
|
+
appServerFactory.addDomain({
|
|
215
|
+
hostname: 'example.com',
|
|
216
|
+
keyPath: '/ssl/example.com.key',
|
|
217
|
+
certPath: '/ssl/example.com.crt',
|
|
218
|
+
aliases: ['www.example.com']
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
appServerFactory.addDomain({
|
|
222
|
+
hostname: 'api.example.com',
|
|
223
|
+
keyPath: '/ssl/api.example.com.key',
|
|
224
|
+
certPath: '/ssl/api.example.com.crt'
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
let serverResult = appServerFactory.createAppServer({
|
|
228
|
+
useHttps: true,
|
|
229
|
+
useVirtualHosts: true,
|
|
230
|
+
keyPath: '/ssl/default.key',
|
|
231
|
+
certPath: '/ssl/default.crt',
|
|
232
|
+
port: 443,
|
|
233
|
+
enforceProtocol: true,
|
|
234
|
+
developmentMultiplier: 10
|
|
235
|
+
});
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Development Mode Configuration
|
|
239
|
+
|
|
240
|
+
```javascript
|
|
241
|
+
let appServerFactory = new AppServerFactory();
|
|
242
|
+
|
|
243
|
+
// Add development domains (automatically detected)
|
|
244
|
+
appServerFactory.addDevelopmentDomain('localhost');
|
|
245
|
+
appServerFactory.addDevelopmentDomain('dev.myapp.local');
|
|
246
|
+
|
|
247
|
+
let serverResult = appServerFactory.createAppServer({
|
|
248
|
+
port: 3000,
|
|
249
|
+
corsOrigin: ['http://localhost:3000', 'http://dev.myapp.local:3000'],
|
|
250
|
+
developmentMultiplier: 5, // More lenient rate limiting in dev
|
|
251
|
+
developmentPorts: [3000, 3001, 8080],
|
|
252
|
+
developmentExternalDomains: {
|
|
253
|
+
'script-src': ['https://cdn.example.com'],
|
|
254
|
+
'style-src': ['https://fonts.googleapis.com']
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Custom Security Configuration
|
|
260
|
+
|
|
261
|
+
```javascript
|
|
262
|
+
let appServerFactory = new AppServerFactory();
|
|
263
|
+
|
|
264
|
+
let serverResult = appServerFactory.createAppServer({
|
|
265
|
+
useHelmet: true,
|
|
266
|
+
helmetConfig: {
|
|
267
|
+
contentSecurityPolicy: {
|
|
268
|
+
directives: {
|
|
269
|
+
defaultSrc: ["'self'"],
|
|
270
|
+
styleSrc: ["'self'", "'unsafe-inline'"],
|
|
271
|
+
scriptSrc: ["'self'"]
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
globalRateLimit: 100, // requests per window
|
|
276
|
+
windowMs: 60000, // 1 minute
|
|
277
|
+
maxRequests: 30,
|
|
278
|
+
trustedProxy: '127.0.0.1',
|
|
279
|
+
useXssProtection: true,
|
|
280
|
+
sanitizeOptions: {allowedTags: [], allowedAttributes: {}}
|
|
281
|
+
});
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### External Domains Configuration
|
|
285
|
+
|
|
286
|
+
When using `developmentExternalDomains` to configure CSP policies, keys can be specified in either kebab-case or camelCase format. The system automatically converts kebab-case keys to the appropriate camelCase format:
|
|
287
|
+
|
|
288
|
+
```javascript
|
|
289
|
+
let serverResult = appServerFactory.createAppServer({
|
|
290
|
+
developmentExternalDomains: {
|
|
291
|
+
// Both formats work - choose whichever you prefer
|
|
292
|
+
'scriptSrc': ['https://cdn.example.com'], // camelCase
|
|
293
|
+
'script-src': ['https://platform-api.example.com'], // kebab-case (auto-converted)
|
|
294
|
+
'styleSrc': ['https://fonts.googleapis.com'], // camelCase
|
|
295
|
+
'font-src': ['https://fonts.gstatic.com'] // kebab-case (auto-converted)
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
The system automatically adds these domains to both the base directive and the corresponding `-elem` variant (e.g., `scriptSrc` and `scriptSrcElem`).
|
|
301
|
+
|
|
302
|
+
### Helmet CSP Configuration Options
|
|
303
|
+
|
|
304
|
+
The security configurer provides flexible CSP directive handling with merge or override behavior:
|
|
305
|
+
|
|
306
|
+
#### Merging Directives (Default)
|
|
307
|
+
|
|
308
|
+
By default, custom CSP directives are merged with the security defaults, allowing you to add additional sources without redefining all directives:
|
|
309
|
+
|
|
310
|
+
```javascript
|
|
311
|
+
let serverResult = appServerFactory.createAppServer({
|
|
312
|
+
useHelmet: true,
|
|
313
|
+
helmetConfig: {
|
|
314
|
+
contentSecurityPolicy: {
|
|
315
|
+
directives: {
|
|
316
|
+
// These will be ADDED to the default directives
|
|
317
|
+
scriptSrc: ['https://analytics.example.com'],
|
|
318
|
+
styleSrc: ['https://cdn.example.com']
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
// Result: default directives + your additional sources
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
#### Overriding Directives
|
|
328
|
+
|
|
329
|
+
Set `overrideDirectives: true` to completely replace the default directives with your custom configuration:
|
|
330
|
+
|
|
331
|
+
```javascript
|
|
332
|
+
let serverResult = appServerFactory.createAppServer({
|
|
333
|
+
useHelmet: true,
|
|
334
|
+
helmetConfig: {
|
|
335
|
+
contentSecurityPolicy: {
|
|
336
|
+
overrideDirectives: true, // Replace defaults entirely
|
|
337
|
+
directives: {
|
|
338
|
+
defaultSrc: ["'self'"],
|
|
339
|
+
scriptSrc: ["'self'", "https://trusted-cdn.com"],
|
|
340
|
+
styleSrc: ["'self'", "'unsafe-inline'"],
|
|
341
|
+
imgSrc: ["'self'", "data:", "https:"],
|
|
342
|
+
fontSrc: ["'self'"],
|
|
343
|
+
connectSrc: ["'self'"],
|
|
344
|
+
frameAncestors: ["'none'"],
|
|
345
|
+
baseUri: ["'self'"],
|
|
346
|
+
formAction: ["'self'"]
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
## API Reference
|
|
354
|
+
|
|
355
|
+
### AppServerFactory Methods
|
|
356
|
+
|
|
357
|
+
- `createAppServer(config)` - Creates and configures Express server
|
|
358
|
+
- `addDomain(domainConfig)` - Adds domain configuration for virtual hosting
|
|
359
|
+
- `addDevelopmentDomain(domain)` - Adds development domain pattern
|
|
360
|
+
- `setDomainMapping(mapping)` - Sets domain to configuration mapping
|
|
361
|
+
- `enableServeHome(app, callback)` - Enables homepage serving
|
|
362
|
+
- `serveStatics(app, staticPath)` - Serves static files
|
|
363
|
+
- `serveStaticsPath(app, route, staticPath)` - Serves static files on specific route
|
|
364
|
+
- `enableCSP(cspOptions)` - Enables Content Security Policy
|
|
365
|
+
- `listen(port)` - Starts server listening
|
|
366
|
+
- `close()` - Gracefully closes server
|
|
367
|
+
|
|
368
|
+
### FileHandler Methods
|
|
369
|
+
|
|
370
|
+
- `exists(path)` - Checks if file or folder exists
|
|
371
|
+
- `createFolder(path)` - Creates folder with a recursive option
|
|
372
|
+
- `remove(path)` - Removes file or folder recursively
|
|
373
|
+
- `removeMultiple(filePaths)` - Removes multiple files from an array of paths
|
|
374
|
+
- `copyFile(source, destination)` - Copies file to destination
|
|
375
|
+
- `copyFolderSync(source, destination)` - Copies folder recursively
|
|
376
|
+
- `readFile(path)` - Reads file contents as string
|
|
377
|
+
- `writeFile(path, content)` - Writes content to file
|
|
378
|
+
- `fetchFileJson(path)` - Reads and parses JSON file
|
|
379
|
+
- `fetchFileContents(path)` - Reads file with validation
|
|
380
|
+
- `updateFileContents(path, content)` - Updates existing file
|
|
381
|
+
- `isFile(path)` - Checks if a path is a file
|
|
382
|
+
- `isFolder(path)` - Checks if a path is folder
|
|
383
|
+
- `getFilesInFolder(path, extensions)` - Lists files with optional filtering
|
|
384
|
+
- `validateFileType(path, type, allowedTypes, maxSize)` - Validates file type and size
|
|
385
|
+
- `detectFileType(path)` - Detects MIME type from file signature
|
|
386
|
+
- `generateSecureFilename(originalName)` - Generates cryptographically secure filename
|
|
387
|
+
- `quarantineFile(path, reason)` - Moves file to quarantine folder
|
|
388
|
+
- `createTempFile(prefix, extension)` - Creates a temporary file path
|
|
389
|
+
- `moveFile(from, to)` - Moves file to new location
|
|
390
|
+
- `getFileSize(path)` - Gets file size in bytes
|
|
391
|
+
- `compareFiles(file1, file2)` - Compares file contents
|
|
392
|
+
- `getRelativePath(from, to)` - Calculates relative path
|
|
393
|
+
- `walkDirectory(path, callback)` - Recursively processes directory tree
|
|
394
|
+
- `getDirectorySize(path)` - Calculates total directory size
|
|
395
|
+
- `emptyDirectory(path)` - Removes all contents from directory
|
|
396
|
+
|
|
397
|
+
### Encryptor Methods
|
|
398
|
+
|
|
399
|
+
- `encryptPassword(password)` - Hashes password with salt
|
|
400
|
+
- `validatePassword(password, hash)` - Validates password against hash
|
|
401
|
+
- `generateSecretKey()` - Generates 256-bit secret key
|
|
402
|
+
- `encryptData(data, key)` - Encrypts data with AES-256-GCM
|
|
403
|
+
- `decryptData(encryptedData, key)` - Decrypts AES-256-GCM data
|
|
404
|
+
- `generateSecureToken(length)` - Generates base64url token
|
|
405
|
+
- `generateTOTP(secret, timeStep)` - Generates time-based OTP
|
|
406
|
+
- `hashData(data, algorithm)` - Hashes data with specified algorithm
|
|
407
|
+
- `generateHMAC(data, secret, algorithm)` - Generates HMAC signature
|
|
408
|
+
- `verifyHMAC(data, secret, signature, algorithm)` - Verifies HMAC signature
|
|
409
|
+
- `constantTimeCompare(a, b)` - Performs constant-time string comparison
|
|
410
|
+
|
|
411
|
+
### UploaderFactory Methods
|
|
412
|
+
|
|
413
|
+
- `createUploader(fields, buckets, allowedTypes)` - Creates multer upload middleware
|
|
414
|
+
- `validateFilenameSecurity(filename)` - Validates filename for security
|
|
415
|
+
- `validateFile(file, allowedType, callback)` - Validates a file during upload
|
|
416
|
+
- `validateFileContents(file, allowedType)` - Validates file content after upload
|
|
417
|
+
- `convertToRegex(key)` - Converts MIME type patterns to regex
|
|
418
|
+
|
|
419
|
+
## Security Features
|
|
420
|
+
|
|
421
|
+
### Path Traversal Protection
|
|
422
|
+
All file operations include comprehensive path validation to prevent directory traversal attacks and access to system files.
|
|
423
|
+
|
|
424
|
+
### Secure File Upload
|
|
425
|
+
File uploads are validated at multiple levels including filename, MIME type, file extension, file size, and content validation using magic number detection. Failed uploads are automatically cleaned up using efficient file removal.
|
|
426
|
+
|
|
427
|
+
### Rate Limiting
|
|
428
|
+
Configurable rate limiting with development mode detection for appropriate thresholds in different environments.
|
|
429
|
+
|
|
430
|
+
### HTTPS Support
|
|
431
|
+
Full SSL/TLS support with SNI for multi-domain hosting and automatic certificate management.
|
|
432
|
+
|
|
433
|
+
### Input Validation
|
|
434
|
+
Built-in validators for common input types including email, username, strong passwords, alphanumeric strings, and IP addresses.
|
|
435
|
+
|
|
436
|
+
### Cryptographic Security
|
|
437
|
+
Industry-standard encryption using PBKDF2 for passwords, AES-256-GCM for data encryption, and secure random generation for tokens.
|
|
438
|
+
|
|
439
|
+
## Error Handling
|
|
440
|
+
|
|
441
|
+
All methods include comprehensive error handling with detailed error objects containing context information. Errors are logged appropriately and never expose sensitive system information.
|
|
442
|
+
|
|
443
|
+
---
|
|
444
|
+
|
|
445
|
+
## Documentation
|
|
446
|
+
|
|
447
|
+
[https://www.reldens.com/documentation/utils/](https://www.reldens.com/documentation/utils/)
|
|
448
|
+
|
|
449
|
+
Need something specific?
|
|
450
|
+
|
|
451
|
+
[Request a feature here: https://www.reldens.com/features-request](https://www.reldens.com/features-request)
|
|
452
|
+
|
|
453
|
+
---
|
|
454
|
+
|
|
455
|
+
### [Reldens](https://github.com/damian-pastorini/reldens/ "Reldens")
|
|
456
|
+
|
|
457
|
+
##### [By DwDeveloper](https://www.dwdeveloper.com/ "DwDeveloper")
|
|
@@ -16,38 +16,36 @@ class SecurityConfigurer
|
|
|
16
16
|
this.useHelmet = true;
|
|
17
17
|
this.useXssProtection = true;
|
|
18
18
|
this.helmetConfig = false;
|
|
19
|
+
this.helmetOptions = {};
|
|
19
20
|
this.sanitizeOptions = {allowedTags: [], allowedAttributes: {}};
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
setupHelmet(app, config)
|
|
23
24
|
{
|
|
24
|
-
this.isDevelopmentMode = config.isDevelopmentMode || false;
|
|
25
25
|
this.useHelmet = config.useHelmet !== false;
|
|
26
|
-
this.helmetConfig = config.helmetConfig || false;
|
|
27
26
|
if(!this.useHelmet){
|
|
28
27
|
return;
|
|
29
28
|
}
|
|
30
|
-
|
|
29
|
+
app.use(helmet(this.mapHelmetOptions(config)));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
mapHelmetOptions(config)
|
|
33
|
+
{
|
|
34
|
+
this.isDevelopmentMode = config.isDevelopmentMode || false;
|
|
35
|
+
this.helmetConfig = config.helmetConfig || {};
|
|
36
|
+
this.helmetOptions = {
|
|
31
37
|
crossOriginEmbedderPolicy: false,
|
|
32
38
|
crossOriginOpenerPolicy: false,
|
|
33
39
|
crossOriginResourcePolicy: false,
|
|
34
40
|
originAgentCluster: false
|
|
35
|
-
}
|
|
36
|
-
if(this.helmetConfig){
|
|
37
|
-
Object.assign(helmetOptions, this.helmetConfig);
|
|
38
|
-
}
|
|
39
|
-
app.use(helmet(helmetOptions));
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
setModeOptions(helmetOptions, config)
|
|
43
|
-
{
|
|
41
|
+
};
|
|
44
42
|
if(this.isDevelopmentMode){
|
|
45
|
-
helmetOptions.contentSecurityPolicy = false;
|
|
46
|
-
helmetOptions.hsts = false;
|
|
47
|
-
helmetOptions.noSniff = false;
|
|
48
|
-
return helmetOptions;
|
|
43
|
+
this.helmetOptions.contentSecurityPolicy = false;
|
|
44
|
+
this.helmetOptions.hsts = false;
|
|
45
|
+
this.helmetOptions.noSniff = false;
|
|
46
|
+
return this.helmetOptions;
|
|
49
47
|
}
|
|
50
|
-
helmetOptions.contentSecurityPolicy = {
|
|
48
|
+
this.helmetOptions.contentSecurityPolicy = {
|
|
51
49
|
directives: {
|
|
52
50
|
defaultSrc: ["'self'"],
|
|
53
51
|
scriptSrc: ["'self'"],
|
|
@@ -62,13 +60,50 @@ class SecurityConfigurer
|
|
|
62
60
|
formAction: ["'self'"]
|
|
63
61
|
}
|
|
64
62
|
};
|
|
63
|
+
if(this.helmetConfig.contentSecurityPolicy){
|
|
64
|
+
if(this.helmetConfig.contentSecurityPolicy.overrideDirectives){
|
|
65
|
+
this.helmetOptions.contentSecurityPolicy.directives = this.helmetConfig.contentSecurityPolicy.directives;
|
|
66
|
+
}
|
|
67
|
+
if(
|
|
68
|
+
!this.helmetConfig.contentSecurityPolicy.overrideDirectives
|
|
69
|
+
&& this.helmetConfig.contentSecurityPolicy.directives
|
|
70
|
+
){
|
|
71
|
+
let configDirectivesKeys = Object.keys(this.helmetConfig.contentSecurityPolicy.directives);
|
|
72
|
+
for(let directiveKey of configDirectivesKeys){
|
|
73
|
+
let directiveValues = this.helmetConfig.contentSecurityPolicy.directives[directiveKey];
|
|
74
|
+
if(!Array.isArray(directiveValues)){
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if(!this.helmetOptions.contentSecurityPolicy.directives[directiveKey]){
|
|
78
|
+
this.helmetOptions.contentSecurityPolicy.directives[directiveKey] = [];
|
|
79
|
+
}
|
|
80
|
+
for(let value of directiveValues){
|
|
81
|
+
this.helmetOptions.contentSecurityPolicy.directives[directiveKey].push(value);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
let cspKeys = Object.keys(this.helmetConfig.contentSecurityPolicy);
|
|
86
|
+
for(let cspKey of cspKeys){
|
|
87
|
+
if('directives' === cspKey || 'overrideDirectives' === cspKey){
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
this.helmetOptions.contentSecurityPolicy[cspKey] = this.helmetConfig.contentSecurityPolicy[cspKey];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
let helmetConfigKeys = Object.keys(this.helmetConfig);
|
|
94
|
+
for(let configKey of helmetConfigKeys){
|
|
95
|
+
if('contentSecurityPolicy' === configKey){
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
this.helmetOptions[configKey] = this.helmetConfig[configKey];
|
|
99
|
+
}
|
|
65
100
|
if(config.developmentExternalDomains){
|
|
66
101
|
this.addExternalDomainsToCsp(
|
|
67
|
-
helmetOptions.contentSecurityPolicy.directives,
|
|
102
|
+
this.helmetOptions.contentSecurityPolicy.directives,
|
|
68
103
|
config.developmentExternalDomains
|
|
69
104
|
);
|
|
70
105
|
}
|
|
71
|
-
return helmetOptions;
|
|
106
|
+
return this.helmetOptions;
|
|
72
107
|
}
|
|
73
108
|
|
|
74
109
|
addExternalDomainsToCsp(directives, externalDomains)
|
|
@@ -79,11 +114,12 @@ class SecurityConfigurer
|
|
|
79
114
|
if(!Array.isArray(domains)){
|
|
80
115
|
continue;
|
|
81
116
|
}
|
|
117
|
+
let camelCaseKey = directiveKey.replace(/-([a-z])/g, (match, letter) => letter.toUpperCase());
|
|
82
118
|
for(let domain of domains){
|
|
83
|
-
if(directives[
|
|
84
|
-
directives[
|
|
119
|
+
if(directives[camelCaseKey]){
|
|
120
|
+
directives[camelCaseKey].push(domain);
|
|
85
121
|
}
|
|
86
|
-
let elemKey =
|
|
122
|
+
let elemKey = camelCaseKey+'Elem';
|
|
87
123
|
if(directives[elemKey]){
|
|
88
124
|
directives[elemKey].push(domain);
|
|
89
125
|
}
|