mastercontroller 1.3.1 → 1.3.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.
- package/.claude/settings.local.json +6 -1
- package/MasterAction.js +158 -27
- package/MasterActionFilters.js +213 -93
- package/MasterControl.js +289 -43
- package/MasterCors.js +1 -2
- package/MasterHtml.js +243 -145
- package/MasterPipeline.js +2 -3
- package/MasterRequest.js +203 -26
- package/MasterRouter.js +1 -2
- package/MasterSocket.js +7 -3
- package/MasterTemp.js +1 -2
- package/MasterTimeout.js +2 -4
- package/MasterTools.js +388 -0
- package/README.md +2288 -369
- package/TemplateOverwrite.js +1 -1
- package/docs/SECURITY-AUDIT-ACTION-SYSTEM.md +1374 -0
- package/docs/SECURITY-AUDIT-HTTPS.md +1056 -0
- package/docs/SECURITY-QUICKSTART.md +375 -0
- package/docs/timeout-and-error-handling.md +8 -6
- package/error/MasterError.js +1 -2
- package/error/MasterErrorRenderer.js +1 -2
- package/package.json +1 -1
- package/security/SecurityEnforcement.js +241 -0
- package/security/SessionSecurity.js +6 -5
- package/test/security/filters.test.js +276 -0
- package/test/security/https.test.js +214 -0
- package/test/security/path-traversal.test.js +222 -0
- package/test/security/xss.test.js +190 -0
- package/MasterSession.js +0 -208
- package/docs/server-setup-hostname-binding.md +0 -24
- package/docs/server-setup-http.md +0 -32
- package/docs/server-setup-https-credentials.md +0 -32
- package/docs/server-setup-https-env-tls-sni.md +0 -62
- package/docs/server-setup-nginx-reverse-proxy.md +0 -46
package/MasterHtml.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// version 0.0.25
|
|
2
2
|
|
|
3
|
-
var master = require('./MasterControl');
|
|
4
3
|
var fs = require('fs');
|
|
5
4
|
var tempClass = require('./MasterTemplate');
|
|
6
5
|
var toolClass = require('./MasterTools');
|
|
@@ -17,15 +16,41 @@ const { sanitizeTemplateHTML, sanitizeUserHTML, escapeHTML } = require('./securi
|
|
|
17
16
|
|
|
18
17
|
class html {
|
|
19
18
|
|
|
19
|
+
// Lazy-load master to avoid circular dependency (Google-style lazy initialization)
|
|
20
|
+
get _master() {
|
|
21
|
+
if (!this.__masterCache) {
|
|
22
|
+
this.__masterCache = require('./MasterControl');
|
|
23
|
+
}
|
|
24
|
+
return this.__masterCache;
|
|
25
|
+
}
|
|
26
|
+
|
|
20
27
|
javaScriptSerializer(name, obj){
|
|
28
|
+
// SECURITY: Escape closing script tags and dangerous characters
|
|
29
|
+
const jsonStr = JSON.stringify(obj)
|
|
30
|
+
.replace(/</g, '\\u003c')
|
|
31
|
+
.replace(/>/g, '\\u003e')
|
|
32
|
+
.replace(/&/g, '\\u0026')
|
|
33
|
+
.replace(/\u2028/g, '\\u2028')
|
|
34
|
+
.replace(/\u2029/g, '\\u2029');
|
|
35
|
+
|
|
21
36
|
return `<script type="text/javascript">
|
|
22
|
-
${name} = ${
|
|
37
|
+
${escapeHTML(name)} = ${jsonStr}
|
|
23
38
|
</script>`;
|
|
24
39
|
}
|
|
25
40
|
|
|
26
41
|
// render partial views
|
|
27
42
|
renderPartial(path, data){
|
|
28
43
|
try {
|
|
44
|
+
// SECURITY: Validate path to prevent traversal attacks
|
|
45
|
+
if (!path || path.includes('..') || path.includes('~') || path.startsWith('/')) {
|
|
46
|
+
logger.warn({
|
|
47
|
+
code: 'MC_SECURITY_PATH_TRAVERSAL',
|
|
48
|
+
message: 'Path traversal attempt blocked in renderPartial',
|
|
49
|
+
path: path
|
|
50
|
+
});
|
|
51
|
+
return '<!-- Invalid path -->';
|
|
52
|
+
}
|
|
53
|
+
|
|
29
54
|
var partialViewUrl = `/app/views/${path}`;
|
|
30
55
|
var fullPath = master.router.currentRoute.root + partialViewUrl;
|
|
31
56
|
|
|
@@ -64,6 +89,16 @@ class html {
|
|
|
64
89
|
|
|
65
90
|
// render all your link tags styles given the folder location
|
|
66
91
|
renderStyles(folderName, typeArray){
|
|
92
|
+
// SECURITY: Validate folder name to prevent path traversal
|
|
93
|
+
if (folderName && (folderName.includes('..') || folderName.includes('~') || folderName.startsWith('/'))) {
|
|
94
|
+
logger.warn({
|
|
95
|
+
code: 'MC_SECURITY_PATH_TRAVERSAL',
|
|
96
|
+
message: 'Path traversal attempt blocked in renderStyles',
|
|
97
|
+
folderName: folderName
|
|
98
|
+
});
|
|
99
|
+
return '';
|
|
100
|
+
}
|
|
101
|
+
|
|
67
102
|
var styles = [];
|
|
68
103
|
var styleFolder = `/app/assets/stylesheets/`;
|
|
69
104
|
var rootLocation = master.router.currentRoute.root;
|
|
@@ -108,6 +143,15 @@ class html {
|
|
|
108
143
|
|
|
109
144
|
// renders all scripts in main folder or folder location inside of javascript also its type specific if you provide type
|
|
110
145
|
renderScripts(folderName, typeArray){
|
|
146
|
+
// SECURITY: Validate folder name to prevent path traversal
|
|
147
|
+
if (folderName && (folderName.includes('..') || folderName.includes('~') || folderName.startsWith('/'))) {
|
|
148
|
+
logger.warn({
|
|
149
|
+
code: 'MC_SECURITY_PATH_TRAVERSAL',
|
|
150
|
+
message: 'Path traversal attempt blocked in renderScripts',
|
|
151
|
+
folderName: folderName
|
|
152
|
+
});
|
|
153
|
+
return '';
|
|
154
|
+
}
|
|
111
155
|
|
|
112
156
|
var scripts = [];
|
|
113
157
|
var jsFolder =`/app/assets/javascripts/`;
|
|
@@ -193,38 +237,46 @@ class html {
|
|
|
193
237
|
|
|
194
238
|
// return link tag
|
|
195
239
|
linkTo(name, location){
|
|
196
|
-
|
|
240
|
+
const safeName = escapeHTML(String(name));
|
|
241
|
+
const safeLocation = escapeHTML(String(location));
|
|
242
|
+
return `<a href="${safeLocation}">${safeName}</a>`;
|
|
197
243
|
}
|
|
198
244
|
|
|
199
245
|
// return image tag
|
|
200
246
|
imgTag(alt, location){
|
|
201
|
-
|
|
247
|
+
const safeAlt = escapeHTML(String(alt));
|
|
248
|
+
const safeLocation = escapeHTML(String(location));
|
|
249
|
+
return `<img src="${safeLocation}" alt="${safeAlt}">`;
|
|
202
250
|
}
|
|
203
251
|
|
|
204
252
|
// return text are tag
|
|
205
253
|
textAreaTag(name, message, obj){
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
254
|
+
const safeName = escapeHTML(String(name));
|
|
255
|
+
const safeMessage = escapeHTML(String(message));
|
|
256
|
+
|
|
257
|
+
let textArea = `<textarea name="${safeName}"`;
|
|
258
|
+
|
|
259
|
+
for (const [key, value] of Object.entries(obj || {})) {
|
|
260
|
+
const safeKey = escapeHTML(String(key));
|
|
261
|
+
const safeValue = escapeHTML(String(value));
|
|
262
|
+
textArea += ` ${safeKey}="${safeValue}"`;
|
|
263
|
+
}
|
|
213
264
|
|
|
214
|
-
textArea
|
|
265
|
+
textArea += `>${safeMessage}</textarea>`;
|
|
215
266
|
|
|
216
267
|
return textArea;
|
|
217
268
|
}
|
|
218
269
|
|
|
219
270
|
// form element builder starter
|
|
220
271
|
formTag(location, obj){
|
|
221
|
-
|
|
272
|
+
const safeLocation = escapeHTML(String(location));
|
|
273
|
+
let form = `<form action="${safeLocation}"`;
|
|
222
274
|
|
|
223
|
-
for (
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
}
|
|
227
|
-
}
|
|
275
|
+
for (const [key, value] of Object.entries(obj || {})) {
|
|
276
|
+
const safeKey = escapeHTML(String(key));
|
|
277
|
+
const safeValue = escapeHTML(String(value));
|
|
278
|
+
form += ` ${safeKey}="${safeValue}"`;
|
|
279
|
+
}
|
|
228
280
|
|
|
229
281
|
return form + ">";
|
|
230
282
|
}
|
|
@@ -235,90 +287,102 @@ class html {
|
|
|
235
287
|
}
|
|
236
288
|
// return text tag
|
|
237
289
|
passwordFieldTag(name, obj){
|
|
238
|
-
|
|
290
|
+
const safeName = escapeHTML(String(name));
|
|
291
|
+
let passwordField = `<input type="password" name="${safeName}"`;
|
|
239
292
|
|
|
240
|
-
for (
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
293
|
+
for (const [key, value] of Object.entries(obj || {})) {
|
|
294
|
+
const safeKey = escapeHTML(String(key));
|
|
295
|
+
const safeValue = escapeHTML(String(value));
|
|
296
|
+
passwordField += ` ${safeKey}="${safeValue}"`;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
passwordField += '/>';
|
|
246
300
|
|
|
247
301
|
return passwordField;
|
|
248
302
|
}
|
|
249
|
-
|
|
303
|
+
|
|
250
304
|
// return password field tag
|
|
251
305
|
textFieldTag(name, obj){
|
|
252
|
-
|
|
306
|
+
const safeName = escapeHTML(String(name));
|
|
307
|
+
let textField = `<input type="text" name="${safeName}"`;
|
|
253
308
|
|
|
254
|
-
for (
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
309
|
+
for (const [key, value] of Object.entries(obj || {})) {
|
|
310
|
+
const safeKey = escapeHTML(String(key));
|
|
311
|
+
const safeValue = escapeHTML(String(value));
|
|
312
|
+
textField += ` ${safeKey}="${safeValue}"`;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
textField += '/>';
|
|
260
316
|
return textField;
|
|
261
317
|
};
|
|
262
318
|
|
|
263
319
|
// hidden field tag
|
|
264
320
|
hiddenFieldTag(name, value, obj){
|
|
265
|
-
|
|
266
|
-
|
|
321
|
+
const safeName = escapeHTML(String(name));
|
|
322
|
+
const safeValue = escapeHTML(String(value));
|
|
323
|
+
|
|
324
|
+
let hiddenField = `<input type="hidden" name="${safeName}" value="${safeValue}"`;
|
|
325
|
+
|
|
326
|
+
for (const [key, val] of Object.entries(obj || {})) {
|
|
327
|
+
const safeKey = escapeHTML(String(key));
|
|
328
|
+
const safeVal = escapeHTML(String(val));
|
|
329
|
+
hiddenField += ` ${safeKey}="${safeVal}"`;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
hiddenField += '/>';
|
|
267
333
|
|
|
268
|
-
for (var key in obj) {
|
|
269
|
-
if (obj.hasOwnProperty(key)) {
|
|
270
|
-
hiddenField = hiddenField + " " + key + "=" + "'" + obj[key] + "'";
|
|
271
|
-
}
|
|
272
|
-
};
|
|
273
|
-
hiddenField = hiddenField + '/>';
|
|
274
|
-
|
|
275
334
|
return hiddenField;
|
|
276
335
|
|
|
277
336
|
}
|
|
278
337
|
|
|
279
338
|
// subit tag
|
|
280
339
|
submitButton(name, obj){
|
|
281
|
-
|
|
282
|
-
var submitButton = "<button type='submit' name='" + name + "'";
|
|
340
|
+
const safeName = escapeHTML(String(name));
|
|
283
341
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
342
|
+
let submitButton = `<button type="submit" name="${safeName}"`;
|
|
343
|
+
|
|
344
|
+
for (const [key, value] of Object.entries(obj || {})) {
|
|
345
|
+
const safeKey = escapeHTML(String(key));
|
|
346
|
+
const safeValue = escapeHTML(String(value));
|
|
347
|
+
submitButton += ` ${safeKey}="${safeValue}"`;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
submitButton += `>${safeName}</button>`;
|
|
289
351
|
|
|
290
|
-
submitButton = submitButton + ">" + name +'</button>';
|
|
291
|
-
|
|
292
352
|
return submitButton;
|
|
293
353
|
|
|
294
354
|
}
|
|
295
355
|
|
|
296
356
|
// search tag
|
|
297
357
|
searchField(name, obj){
|
|
298
|
-
|
|
299
|
-
|
|
358
|
+
const safeName = escapeHTML(String(name));
|
|
359
|
+
|
|
360
|
+
let searchField = `<input type="search" name="${safeName}"`;
|
|
361
|
+
|
|
362
|
+
for (const [key, value] of Object.entries(obj || {})) {
|
|
363
|
+
const safeKey = escapeHTML(String(key));
|
|
364
|
+
const safeValue = escapeHTML(String(value));
|
|
365
|
+
searchField += ` ${safeKey}="${safeValue}"`;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
searchField += '/>';
|
|
300
369
|
|
|
301
|
-
for (var key in obj) {
|
|
302
|
-
if (obj.hasOwnProperty(key)) {
|
|
303
|
-
searchField = searchField + " " + key + "=" + "'" + obj[key] + "'";
|
|
304
|
-
}
|
|
305
|
-
};
|
|
306
|
-
searchField = searchField + '/>';
|
|
307
|
-
|
|
308
370
|
return searchField;
|
|
309
371
|
}
|
|
310
372
|
|
|
311
373
|
// telephone field tag
|
|
312
374
|
telephoneField(name, obj){
|
|
375
|
+
const safeName = escapeHTML(String(name));
|
|
313
376
|
|
|
314
|
-
|
|
377
|
+
let telephoneField = `<input type="tel" name="${safeName}"`;
|
|
315
378
|
|
|
316
|
-
for (
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
|
|
379
|
+
for (const [key, value] of Object.entries(obj || {})) {
|
|
380
|
+
const safeKey = escapeHTML(String(key));
|
|
381
|
+
const safeValue = escapeHTML(String(value));
|
|
382
|
+
telephoneField += ` ${safeKey}="${safeValue}"`;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
telephoneField += '/>';
|
|
322
386
|
|
|
323
387
|
return telephoneField;
|
|
324
388
|
|
|
@@ -326,75 +390,85 @@ class html {
|
|
|
326
390
|
|
|
327
391
|
// date field tag
|
|
328
392
|
dateField(name, obj){
|
|
393
|
+
const safeName = escapeHTML(String(name));
|
|
329
394
|
|
|
330
|
-
|
|
395
|
+
let dateField = `<input type="date" name="${safeName}"`;
|
|
331
396
|
|
|
332
|
-
for (
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
397
|
+
for (const [key, value] of Object.entries(obj || {})) {
|
|
398
|
+
const safeKey = escapeHTML(String(key));
|
|
399
|
+
const safeValue = escapeHTML(String(value));
|
|
400
|
+
dateField += ` ${safeKey}="${safeValue}"`;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
dateField += '/>';
|
|
338
404
|
|
|
339
405
|
return dateField;
|
|
340
406
|
}
|
|
341
407
|
|
|
342
408
|
// date time local field tag
|
|
343
409
|
datetimeLocalField(name, obj){
|
|
410
|
+
const safeName = escapeHTML(String(name));
|
|
344
411
|
|
|
345
|
-
|
|
412
|
+
let datetimeLocalField = `<input type="datetime-local" name="${safeName}"`;
|
|
346
413
|
|
|
347
|
-
for (
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
414
|
+
for (const [key, value] of Object.entries(obj || {})) {
|
|
415
|
+
const safeKey = escapeHTML(String(key));
|
|
416
|
+
const safeValue = escapeHTML(String(value));
|
|
417
|
+
datetimeLocalField += ` ${safeKey}="${safeValue}"`;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
datetimeLocalField += '/>';
|
|
353
421
|
|
|
354
422
|
return datetimeLocalField;
|
|
355
423
|
}
|
|
356
424
|
|
|
357
425
|
// date month field tag
|
|
358
426
|
monthField(name, obj){
|
|
427
|
+
const safeName = escapeHTML(String(name));
|
|
359
428
|
|
|
360
|
-
|
|
429
|
+
let monthField = `<input type="month" name="${safeName}"`;
|
|
361
430
|
|
|
362
|
-
for (
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
|
|
431
|
+
for (const [key, value] of Object.entries(obj || {})) {
|
|
432
|
+
const safeKey = escapeHTML(String(key));
|
|
433
|
+
const safeValue = escapeHTML(String(value));
|
|
434
|
+
monthField += ` ${safeKey}="${safeValue}"`;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
monthField += '/>';
|
|
368
438
|
|
|
369
439
|
return monthField;
|
|
370
440
|
}
|
|
371
441
|
|
|
372
442
|
// date week field tag
|
|
373
443
|
weekField(name, obj){
|
|
444
|
+
const safeName = escapeHTML(String(name));
|
|
374
445
|
|
|
375
|
-
|
|
446
|
+
let weekField = `<input type="week" name="${safeName}"`;
|
|
447
|
+
|
|
448
|
+
for (const [key, value] of Object.entries(obj || {})) {
|
|
449
|
+
const safeKey = escapeHTML(String(key));
|
|
450
|
+
const safeValue = escapeHTML(String(value));
|
|
451
|
+
weekField += ` ${safeKey}="${safeValue}"`;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
weekField += '/>';
|
|
376
455
|
|
|
377
|
-
for (var key in obj) {
|
|
378
|
-
if (obj.hasOwnProperty(key)) {
|
|
379
|
-
weekField = weekField + " " + key + "=" + "'" + obj[key] + "'";
|
|
380
|
-
}
|
|
381
|
-
};
|
|
382
|
-
weekField = weekField + '/>';
|
|
383
|
-
|
|
384
456
|
return weekField;
|
|
385
457
|
}
|
|
386
458
|
|
|
387
459
|
// date url field tag
|
|
388
460
|
urlField(name, obj){
|
|
389
|
-
|
|
390
|
-
var urlField = "<input type='url' name='" + name + "' ";
|
|
461
|
+
const safeName = escapeHTML(String(name));
|
|
391
462
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
463
|
+
let urlField = `<input type="url" name="${safeName}"`;
|
|
464
|
+
|
|
465
|
+
for (const [key, value] of Object.entries(obj || {})) {
|
|
466
|
+
const safeKey = escapeHTML(String(key));
|
|
467
|
+
const safeValue = escapeHTML(String(value));
|
|
468
|
+
urlField += ` ${safeKey}="${safeValue}"`;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
urlField += '/>';
|
|
398
472
|
|
|
399
473
|
return urlField;
|
|
400
474
|
}
|
|
@@ -402,76 +476,92 @@ class html {
|
|
|
402
476
|
|
|
403
477
|
// date email field tag
|
|
404
478
|
emailField(name, obj){
|
|
405
|
-
|
|
406
|
-
var emailField = "<input type='email' name='" + name + "' ";
|
|
479
|
+
const safeName = escapeHTML(String(name));
|
|
407
480
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
481
|
+
let emailField = `<input type="email" name="${safeName}"`;
|
|
482
|
+
|
|
483
|
+
for (const [key, value] of Object.entries(obj || {})) {
|
|
484
|
+
const safeKey = escapeHTML(String(key));
|
|
485
|
+
const safeValue = escapeHTML(String(value));
|
|
486
|
+
emailField += ` ${safeKey}="${safeValue}"`;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
emailField += '/>';
|
|
414
490
|
|
|
415
491
|
return emailField;
|
|
416
492
|
}
|
|
417
493
|
|
|
418
494
|
// date color field tag
|
|
419
495
|
colorField(name, color, obj){
|
|
420
|
-
|
|
421
|
-
|
|
496
|
+
const safeName = escapeHTML(String(name));
|
|
497
|
+
const safeColor = escapeHTML(String(color));
|
|
422
498
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
499
|
+
let colorField = `<input type="color" name="${safeName}" value="${safeColor}"`;
|
|
500
|
+
|
|
501
|
+
for (const [key, value] of Object.entries(obj || {})) {
|
|
502
|
+
const safeKey = escapeHTML(String(key));
|
|
503
|
+
const safeValue = escapeHTML(String(value));
|
|
504
|
+
colorField += ` ${safeKey}="${safeValue}"`;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
colorField += '/>';
|
|
429
508
|
|
|
430
509
|
return colorField;
|
|
431
510
|
}
|
|
432
511
|
|
|
433
512
|
// date time field tag
|
|
434
513
|
timeField(name, obj){
|
|
435
|
-
|
|
436
|
-
var timeField = "<input type='time' name='" + name + "' ";
|
|
514
|
+
const safeName = escapeHTML(String(name));
|
|
437
515
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
516
|
+
let timeField = `<input type="time" name="${safeName}"`;
|
|
517
|
+
|
|
518
|
+
for (const [key, value] of Object.entries(obj || {})) {
|
|
519
|
+
const safeKey = escapeHTML(String(key));
|
|
520
|
+
const safeValue = escapeHTML(String(value));
|
|
521
|
+
timeField += ` ${safeKey}="${safeValue}"`;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
timeField += '/>';
|
|
444
525
|
|
|
445
526
|
return timeField;
|
|
446
527
|
}
|
|
447
528
|
|
|
448
529
|
// date number field tag
|
|
449
530
|
numberField(name, min, max, step, obj){
|
|
450
|
-
|
|
451
|
-
|
|
531
|
+
const safeName = escapeHTML(String(name));
|
|
532
|
+
const safeMin = escapeHTML(String(min));
|
|
533
|
+
const safeMax = escapeHTML(String(max));
|
|
534
|
+
const safeStep = escapeHTML(String(step));
|
|
452
535
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
536
|
+
let numberField = `<input type="number" name="${safeName}" min="${safeMin}" max="${safeMax}" step="${safeStep}"`;
|
|
537
|
+
|
|
538
|
+
for (const [key, value] of Object.entries(obj || {})) {
|
|
539
|
+
const safeKey = escapeHTML(String(key));
|
|
540
|
+
const safeValue = escapeHTML(String(value));
|
|
541
|
+
numberField += ` ${safeKey}="${safeValue}"`;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
numberField += '/>';
|
|
459
545
|
|
|
460
546
|
return numberField;
|
|
461
547
|
}
|
|
462
548
|
|
|
463
549
|
// date range field tag
|
|
464
550
|
rangeField(name, min, max, obj){
|
|
551
|
+
const safeName = escapeHTML(String(name));
|
|
552
|
+
const safeMin = escapeHTML(String(min));
|
|
553
|
+
const safeMax = escapeHTML(String(max));
|
|
465
554
|
|
|
466
|
-
|
|
555
|
+
let rangeField = `<input type="range" name="${safeName}" min="${safeMin}" max="${safeMax}"`;
|
|
556
|
+
|
|
557
|
+
for (const [key, value] of Object.entries(obj || {})) {
|
|
558
|
+
const safeKey = escapeHTML(String(key));
|
|
559
|
+
const safeValue = escapeHTML(String(value));
|
|
560
|
+
rangeField += ` ${safeKey}="${safeValue}"`;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
rangeField += '/>';
|
|
467
564
|
|
|
468
|
-
for (var key in obj) {
|
|
469
|
-
if (obj.hasOwnProperty(key)) {
|
|
470
|
-
rangeField = rangeField + " " + key + "=" + "'" + obj[key] + "'";
|
|
471
|
-
}
|
|
472
|
-
};
|
|
473
|
-
rangeField = rangeField + '/>';
|
|
474
|
-
|
|
475
565
|
return rangeField;
|
|
476
566
|
}
|
|
477
567
|
|
|
@@ -547,5 +637,13 @@ class html {
|
|
|
547
637
|
|
|
548
638
|
}
|
|
549
639
|
|
|
550
|
-
|
|
640
|
+
// Export and lazy register (prevents circular dependency - Spring/Angular pattern)
|
|
641
|
+
module.exports = html;
|
|
642
|
+
|
|
643
|
+
setImmediate(() => {
|
|
644
|
+
const master = require('./MasterControl');
|
|
645
|
+
if (master && master.extendView) {
|
|
646
|
+
master.extendView("html", html);
|
|
647
|
+
}
|
|
648
|
+
});
|
|
551
649
|
|
package/MasterPipeline.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// MasterPipeline - Middleware Pipeline System
|
|
2
2
|
// version 1.0
|
|
3
3
|
|
|
4
|
-
var master = require('./MasterControl');
|
|
5
4
|
const { logger } = require('./error/MasterErrorLogger');
|
|
6
5
|
|
|
7
6
|
class MasterPipeline {
|
|
@@ -340,5 +339,5 @@ class MasterPipeline {
|
|
|
340
339
|
}
|
|
341
340
|
}
|
|
342
341
|
|
|
343
|
-
//
|
|
344
|
-
|
|
342
|
+
// Export for MasterControl to register (prevents circular dependency)
|
|
343
|
+
module.exports = { MasterPipeline };
|