mastercontroller 1.3.9 → 1.3.12
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/.eslintrc.json +50 -0
- package/.github/workflows/ci.yml +317 -0
- package/.prettierrc +10 -0
- package/CHANGES.md +296 -0
- package/DEPLOYMENT.md +956 -0
- package/FIXES_APPLIED.md +378 -0
- package/FORTUNE_500_UPGRADE.md +863 -0
- package/MasterAction.js +10 -263
- package/MasterControl.js +226 -43
- package/MasterRequest.js +42 -1
- package/MasterRouter.js +42 -37
- package/PERFORMANCE_SECURITY_AUDIT.md +677 -0
- package/README.md +602 -71
- package/SENIOR_ENGINEER_AUDIT.md +2477 -0
- package/VERIFICATION_CHECKLIST.md +726 -0
- package/error/README.md +2452 -0
- package/monitoring/HealthCheck.js +347 -0
- package/monitoring/PrometheusExporter.js +416 -0
- package/monitoring/README.md +3112 -0
- package/package.json +64 -11
- package/security/MasterValidator.js +140 -10
- package/security/README.md +1805 -0
- package/security/adapters/RedisCSRFStore.js +428 -0
- package/security/adapters/RedisRateLimiter.js +462 -0
- package/security/adapters/RedisSessionStore.js +476 -0
- package/MasterCors.js.tmp +0 -0
- package/MasterHtml.js +0 -649
- package/MasterPipeline.js.tmp +0 -0
- package/MasterRequest.js.tmp +0 -0
- package/MasterRouter.js.tmp +0 -0
- package/MasterSocket.js.tmp +0 -0
- package/MasterTemp.js.tmp +0 -0
- package/MasterTemplate.js +0 -230
- package/MasterTimeout.js.tmp +0 -0
- package/TemplateOverwrite.js +0 -41
- package/TemplateOverwrite.js.tmp +0 -0
- package/error/ErrorBoundary.js +0 -353
- package/error/HydrationMismatch.js +0 -265
- package/error/MasterError.js +0 -240
- package/error/MasterError.js.tmp +0 -0
- package/error/MasterErrorRenderer.js +0 -536
- package/error/MasterErrorRenderer.js.tmp +0 -0
- package/error/SSRErrorHandler.js +0 -273
- package/ssr/hydration-client.js +0 -93
- package/ssr/runtime-ssr.cjs +0 -553
- package/ssr/ssr-shims.js +0 -73
package/MasterHtml.js
DELETED
|
@@ -1,649 +0,0 @@
|
|
|
1
|
-
// version 0.0.25
|
|
2
|
-
|
|
3
|
-
var fs = require('fs');
|
|
4
|
-
var tempClass = require('./MasterTemplate');
|
|
5
|
-
var toolClass = require('./MasterTools');
|
|
6
|
-
var temp = new tempClass();
|
|
7
|
-
var tools = new toolClass();
|
|
8
|
-
|
|
9
|
-
// Enhanced error handling
|
|
10
|
-
const { handleTemplateError } = require('./error/MasterBackendErrorHandler');
|
|
11
|
-
const { safeReadFile, safeFileExists } = require('./error/MasterErrorMiddleware');
|
|
12
|
-
const { logger } = require('./error/MasterErrorLogger');
|
|
13
|
-
|
|
14
|
-
// Security - Sanitization
|
|
15
|
-
const { sanitizeTemplateHTML, sanitizeUserHTML, escapeHTML } = require('./security/MasterSanitizer');
|
|
16
|
-
|
|
17
|
-
class html {
|
|
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
|
-
|
|
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
|
-
|
|
36
|
-
return `<script type="text/javascript">
|
|
37
|
-
${escapeHTML(name)} = ${jsonStr}
|
|
38
|
-
</script>`;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// render partial views
|
|
42
|
-
renderPartial(path, data){
|
|
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
|
-
|
|
54
|
-
var partialViewUrl = `/app/views/${path}`;
|
|
55
|
-
var fullPath = this._master.router.currentRoute.root + partialViewUrl;
|
|
56
|
-
|
|
57
|
-
const fileResult = safeReadFile(fs, fullPath);
|
|
58
|
-
|
|
59
|
-
if (!fileResult.success) {
|
|
60
|
-
logger.warn({
|
|
61
|
-
code: 'MC_ERR_VIEW_NOT_FOUND',
|
|
62
|
-
message: `Partial view not found: ${path}`,
|
|
63
|
-
file: fullPath
|
|
64
|
-
});
|
|
65
|
-
return `<!-- Partial view not found: ${path} -->`;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
var partialView = null;
|
|
69
|
-
if(this._master.overwrite.isTemplate){
|
|
70
|
-
partialView = this._master.overwrite.templateRender(data, "renderPartialView");
|
|
71
|
-
}
|
|
72
|
-
else{
|
|
73
|
-
partialView = temp.htmlBuilder(fileResult.content, data);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return partialView;
|
|
77
|
-
} catch (error) {
|
|
78
|
-
const mcError = handleTemplateError(error, path, data);
|
|
79
|
-
logger.error({
|
|
80
|
-
code: mcError.code,
|
|
81
|
-
message: mcError.message,
|
|
82
|
-
file: path,
|
|
83
|
-
originalError: error
|
|
84
|
-
});
|
|
85
|
-
return `<!-- Error rendering partial: ${path} -->`;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// render all your link tags styles given the folder location
|
|
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
|
-
|
|
102
|
-
var styles = [];
|
|
103
|
-
var styleFolder = `/app/assets/stylesheets/`;
|
|
104
|
-
var rootLocation = this._master.router.currentRoute.root;
|
|
105
|
-
var extention = "";
|
|
106
|
-
|
|
107
|
-
if(this._master.router.currentRoute.isComponent === true){
|
|
108
|
-
extention = tools.getBackSlashBySection(this._master.router.currentRoute.root, 2, "/");
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
var type = typeArray === undefined ? ["css"] : typeArray;
|
|
112
|
-
|
|
113
|
-
if(folderName){
|
|
114
|
-
styleFolder = `${styleFolder}${folderName}/`;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (fs.existsSync(`${rootLocation}${styleFolder}`)) {
|
|
118
|
-
fs.readdirSync(`${rootLocation}${styleFolder}`).forEach(function(file){
|
|
119
|
-
|
|
120
|
-
var fileExtension = file.replace(/^.*\./, '');
|
|
121
|
-
if(type.indexOf(fileExtension) >= 0){
|
|
122
|
-
var fileLocatoon = `${styleFolder}${file}`;
|
|
123
|
-
if(this._master.router.currentRoute.isComponent === true){
|
|
124
|
-
styles.push(`<link rel="stylesheet" type="text/${type}" href="/${extention}${fileLocatoon}">`);
|
|
125
|
-
}
|
|
126
|
-
else{
|
|
127
|
-
styles.push(`<link rel="stylesheet" type="text/${type}" href="${fileLocatoon}">`);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
var partialView = null;
|
|
133
|
-
|
|
134
|
-
if(this._master.overwrite.isTemplate){
|
|
135
|
-
partialView = this._master.overwrite.templateRender({}, "renderStyles");
|
|
136
|
-
}
|
|
137
|
-
else{
|
|
138
|
-
partialView = temp.htmlBuilder(styles.join(""),{});
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
return partialView;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// renders all scripts in main folder or folder location inside of javascript also its type specific if you provide type
|
|
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
|
-
}
|
|
155
|
-
|
|
156
|
-
var scripts = [];
|
|
157
|
-
var jsFolder =`/app/assets/javascripts/`;
|
|
158
|
-
var rootLocation = this._master.router.currentRoute.root;
|
|
159
|
-
var extention = "";
|
|
160
|
-
//components/auth/app/assets/javascripts/pages/changePassword.js
|
|
161
|
-
if(this._master.router.currentRoute.isComponent === true){
|
|
162
|
-
extention = tools.getBackSlashBySection(this._master.router.currentRoute.root, 2, "/");
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
var type = typeArray === undefined ? ["js"] : typeArray;
|
|
166
|
-
|
|
167
|
-
if(folderName){
|
|
168
|
-
jsFolder = `${jsFolder}${folderName}/`;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
if (fs.existsSync(`${rootLocation}${jsFolder}`)) {
|
|
172
|
-
fs.readdirSync(`${rootLocation}${jsFolder}`).forEach(function(file){
|
|
173
|
-
var fileExtension = file.replace(/^.*\./, '');
|
|
174
|
-
if(type.indexOf(fileExtension) >= 0){
|
|
175
|
-
var fileLocatoon = `${jsFolder}${file}`;
|
|
176
|
-
if(this._master.router.currentRoute.isComponent === true){
|
|
177
|
-
scripts.push(`<script src="/${extention}${fileLocatoon}"></script>`);
|
|
178
|
-
}
|
|
179
|
-
else{
|
|
180
|
-
scripts.push(`<script src="${fileLocatoon}"></script>`);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
var partialView = null;
|
|
187
|
-
|
|
188
|
-
if(this._master.overwrite.isTemplate){
|
|
189
|
-
partialView = this._master.overwrite.templateRender({}, "renderScripts");
|
|
190
|
-
}
|
|
191
|
-
else{
|
|
192
|
-
partialView = temp.htmlBuilder(scripts.join(""),{});
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
return partialView;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
// renders js using location
|
|
200
|
-
renderJS(folderName, name){
|
|
201
|
-
if(folderName === undefined && name === undefined){
|
|
202
|
-
return "";
|
|
203
|
-
}
|
|
204
|
-
else{
|
|
205
|
-
var rootLocation = this._master.router.currentRoute.root;
|
|
206
|
-
var jsFolder = `/app/assets/javascripts/`;
|
|
207
|
-
if(this._master.router.currentRoute.isComponent === true){
|
|
208
|
-
rootLocation = tools.getBackSlashBySection(this._master.router.currentRoute.root, 2, "/");
|
|
209
|
-
jsFolder = `${rootLocation}${jsFolder}`;
|
|
210
|
-
}
|
|
211
|
-
if(folderName){
|
|
212
|
-
jsFolder = `${jsFolder}${folderName}/${name}`;
|
|
213
|
-
}
|
|
214
|
-
return `<script type="text/javascript" src="/${jsFolder}"></script>`;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// render css directly on the page suing location name
|
|
219
|
-
renderCss(folderName, name){
|
|
220
|
-
if(folderName === undefined && name === undefined){
|
|
221
|
-
return "";
|
|
222
|
-
}
|
|
223
|
-
else{
|
|
224
|
-
var styleFolder = `/app/assets/stylesheets/`;
|
|
225
|
-
var rootLocation = this._master.router.currentRoute.root;
|
|
226
|
-
if(this._master.router.currentRoute.isComponent === true){
|
|
227
|
-
rootLocation = tools.getBackSlashBySection(this._master.router.currentRoute.root, 2, "/");
|
|
228
|
-
styleFolder = `${rootLocation}${styleFolder}`;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if(folderName){
|
|
232
|
-
styleFolder = `${styleFolder}${folderName}/${name}`;
|
|
233
|
-
}
|
|
234
|
-
return `<link rel="stylesheet" type="text/css" href="/${styleFolder}">`;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// return link tag
|
|
239
|
-
linkTo(name, location){
|
|
240
|
-
const safeName = escapeHTML(String(name));
|
|
241
|
-
const safeLocation = escapeHTML(String(location));
|
|
242
|
-
return `<a href="${safeLocation}">${safeName}</a>`;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// return image tag
|
|
246
|
-
imgTag(alt, location){
|
|
247
|
-
const safeAlt = escapeHTML(String(alt));
|
|
248
|
-
const safeLocation = escapeHTML(String(location));
|
|
249
|
-
return `<img src="${safeLocation}" alt="${safeAlt}">`;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// return text are tag
|
|
253
|
-
textAreaTag(name, message, obj){
|
|
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
|
-
}
|
|
264
|
-
|
|
265
|
-
textArea += `>${safeMessage}</textarea>`;
|
|
266
|
-
|
|
267
|
-
return textArea;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// form element builder starter
|
|
271
|
-
formTag(location, obj){
|
|
272
|
-
const safeLocation = escapeHTML(String(location));
|
|
273
|
-
let form = `<form action="${safeLocation}"`;
|
|
274
|
-
|
|
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
|
-
}
|
|
280
|
-
|
|
281
|
-
return form + ">";
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// form element builder ender
|
|
285
|
-
formTagEnd(){
|
|
286
|
-
return '</form>';
|
|
287
|
-
}
|
|
288
|
-
// return text tag
|
|
289
|
-
passwordFieldTag(name, obj){
|
|
290
|
-
const safeName = escapeHTML(String(name));
|
|
291
|
-
let passwordField = `<input type="password" name="${safeName}"`;
|
|
292
|
-
|
|
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 += '/>';
|
|
300
|
-
|
|
301
|
-
return passwordField;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// return password field tag
|
|
305
|
-
textFieldTag(name, obj){
|
|
306
|
-
const safeName = escapeHTML(String(name));
|
|
307
|
-
let textField = `<input type="text" name="${safeName}"`;
|
|
308
|
-
|
|
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 += '/>';
|
|
316
|
-
return textField;
|
|
317
|
-
};
|
|
318
|
-
|
|
319
|
-
// hidden field tag
|
|
320
|
-
hiddenFieldTag(name, value, obj){
|
|
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 += '/>';
|
|
333
|
-
|
|
334
|
-
return hiddenField;
|
|
335
|
-
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
// subit tag
|
|
339
|
-
submitButton(name, obj){
|
|
340
|
-
const safeName = escapeHTML(String(name));
|
|
341
|
-
|
|
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>`;
|
|
351
|
-
|
|
352
|
-
return submitButton;
|
|
353
|
-
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
// search tag
|
|
357
|
-
searchField(name, obj){
|
|
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 += '/>';
|
|
369
|
-
|
|
370
|
-
return searchField;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
// telephone field tag
|
|
374
|
-
telephoneField(name, obj){
|
|
375
|
-
const safeName = escapeHTML(String(name));
|
|
376
|
-
|
|
377
|
-
let telephoneField = `<input type="tel" name="${safeName}"`;
|
|
378
|
-
|
|
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 += '/>';
|
|
386
|
-
|
|
387
|
-
return telephoneField;
|
|
388
|
-
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
// date field tag
|
|
392
|
-
dateField(name, obj){
|
|
393
|
-
const safeName = escapeHTML(String(name));
|
|
394
|
-
|
|
395
|
-
let dateField = `<input type="date" name="${safeName}"`;
|
|
396
|
-
|
|
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 += '/>';
|
|
404
|
-
|
|
405
|
-
return dateField;
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
// date time local field tag
|
|
409
|
-
datetimeLocalField(name, obj){
|
|
410
|
-
const safeName = escapeHTML(String(name));
|
|
411
|
-
|
|
412
|
-
let datetimeLocalField = `<input type="datetime-local" name="${safeName}"`;
|
|
413
|
-
|
|
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 += '/>';
|
|
421
|
-
|
|
422
|
-
return datetimeLocalField;
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
// date month field tag
|
|
426
|
-
monthField(name, obj){
|
|
427
|
-
const safeName = escapeHTML(String(name));
|
|
428
|
-
|
|
429
|
-
let monthField = `<input type="month" name="${safeName}"`;
|
|
430
|
-
|
|
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 += '/>';
|
|
438
|
-
|
|
439
|
-
return monthField;
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
// date week field tag
|
|
443
|
-
weekField(name, obj){
|
|
444
|
-
const safeName = escapeHTML(String(name));
|
|
445
|
-
|
|
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 += '/>';
|
|
455
|
-
|
|
456
|
-
return weekField;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
// date url field tag
|
|
460
|
-
urlField(name, obj){
|
|
461
|
-
const safeName = escapeHTML(String(name));
|
|
462
|
-
|
|
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 += '/>';
|
|
472
|
-
|
|
473
|
-
return urlField;
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
// date email field tag
|
|
478
|
-
emailField(name, obj){
|
|
479
|
-
const safeName = escapeHTML(String(name));
|
|
480
|
-
|
|
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 += '/>';
|
|
490
|
-
|
|
491
|
-
return emailField;
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
// date color field tag
|
|
495
|
-
colorField(name, color, obj){
|
|
496
|
-
const safeName = escapeHTML(String(name));
|
|
497
|
-
const safeColor = escapeHTML(String(color));
|
|
498
|
-
|
|
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 += '/>';
|
|
508
|
-
|
|
509
|
-
return colorField;
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
// date time field tag
|
|
513
|
-
timeField(name, obj){
|
|
514
|
-
const safeName = escapeHTML(String(name));
|
|
515
|
-
|
|
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 += '/>';
|
|
525
|
-
|
|
526
|
-
return timeField;
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
// date number field tag
|
|
530
|
-
numberField(name, min, max, step, obj){
|
|
531
|
-
const safeName = escapeHTML(String(name));
|
|
532
|
-
const safeMin = escapeHTML(String(min));
|
|
533
|
-
const safeMax = escapeHTML(String(max));
|
|
534
|
-
const safeStep = escapeHTML(String(step));
|
|
535
|
-
|
|
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 += '/>';
|
|
545
|
-
|
|
546
|
-
return numberField;
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
// date range field tag
|
|
550
|
-
rangeField(name, min, max, obj){
|
|
551
|
-
const safeName = escapeHTML(String(name));
|
|
552
|
-
const safeMin = escapeHTML(String(min));
|
|
553
|
-
const safeMax = escapeHTML(String(max));
|
|
554
|
-
|
|
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 += '/>';
|
|
564
|
-
|
|
565
|
-
return rangeField;
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
// allows you to add data object to params
|
|
569
|
-
addDataToParams(data){
|
|
570
|
-
|
|
571
|
-
//loop through data and add it to new oobjects prototype
|
|
572
|
-
if(data){
|
|
573
|
-
var newObj = Object.create(data);
|
|
574
|
-
newObj.prototype = newObj.__proto__;
|
|
575
|
-
this._master.view.extend(newObj);
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
// ==================== Security Methods ====================
|
|
580
|
-
|
|
581
|
-
/**
|
|
582
|
-
* Sanitize user-generated HTML content
|
|
583
|
-
* Use this for any HTML that comes from user input
|
|
584
|
-
* @param {string} html - HTML content to sanitize
|
|
585
|
-
* @returns {string} - Sanitized HTML
|
|
586
|
-
*/
|
|
587
|
-
sanitizeHTML(html) {
|
|
588
|
-
return sanitizeUserHTML(html);
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
/**
|
|
592
|
-
* Escape HTML special characters
|
|
593
|
-
* Use this to display user input as text (not HTML)
|
|
594
|
-
* @param {string} text - Text to escape
|
|
595
|
-
* @returns {string} - Escaped text safe for display
|
|
596
|
-
*/
|
|
597
|
-
escapeHTML(text) {
|
|
598
|
-
return escapeHTML(text);
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
/**
|
|
602
|
-
* Render user content safely
|
|
603
|
-
* Sanitizes HTML and wraps in container
|
|
604
|
-
* @param {string} content - User-generated content
|
|
605
|
-
* @param {string} containerTag - HTML tag to wrap content (default: div)
|
|
606
|
-
* @param {object} attrs - Attributes for container
|
|
607
|
-
* @returns {string} - Safe HTML
|
|
608
|
-
*/
|
|
609
|
-
renderUserContent(content, containerTag = 'div', attrs = {}) {
|
|
610
|
-
const sanitized = sanitizeUserHTML(content);
|
|
611
|
-
|
|
612
|
-
let attrStr = '';
|
|
613
|
-
for (const [key, value] of Object.entries(attrs)) {
|
|
614
|
-
attrStr += ` ${key}="${escapeHTML(String(value))}"`;
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
return `<${containerTag}${attrStr}>${sanitized}</${containerTag}>`;
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
/**
|
|
621
|
-
* Create safe text node content
|
|
622
|
-
* @param {string} text - Text content
|
|
623
|
-
* @returns {string} - HTML-escaped text
|
|
624
|
-
*/
|
|
625
|
-
textNode(text) {
|
|
626
|
-
return escapeHTML(text);
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
/**
|
|
630
|
-
* Create safe attribute value
|
|
631
|
-
* @param {string} value - Attribute value
|
|
632
|
-
* @returns {string} - Escaped and quoted value
|
|
633
|
-
*/
|
|
634
|
-
safeAttr(value) {
|
|
635
|
-
return `"${escapeHTML(String(value))}"`;
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
}
|
|
639
|
-
|
|
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
|
-
});
|
|
649
|
-
|
package/MasterPipeline.js.tmp
DELETED
|
File without changes
|
package/MasterRequest.js.tmp
DELETED
|
File without changes
|
package/MasterRouter.js.tmp
DELETED
|
File without changes
|
package/MasterSocket.js.tmp
DELETED
|
File without changes
|
package/MasterTemp.js.tmp
DELETED
|
File without changes
|