hadadrjt commited on
Commit
3044058
·
1 Parent(s): c34c995

image: Add additional security layer.

Browse files

* Although Pollinations already implements server-side
sanitization, this enhancement adds an extra layer of
security to further strengthen input validation and
protection for safe and responsible usage.

Signed-off-by: Hadad <hadad@linuxmail.org>

Files changed (4) hide show
  1. assets/plugins/webLoader.js +106 -104
  2. config.js +2 -2
  3. package.json +1 -1
  4. public/webViewer.ejs +6 -0
assets/plugins/webLoader.js CHANGED
@@ -6,12 +6,43 @@
6
  (function () {
7
  'use strict';
8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  var noop = function () {};
10
  var methods = [
11
- 'log', 'error', 'warn', 'info', 'debug', 'trace',
12
- 'dir', 'table', 'time', 'timeEnd', 'timeLog', 'group',
13
- 'groupEnd', 'assert', 'count', 'countReset', 'profile',
14
- 'profileEnd', 'clear', 'dirxml', 'exception',
15
  'groupCollapsed'
16
  ];
17
 
@@ -35,21 +66,15 @@
35
 
36
  var InitWelcomeModal = function () {
37
  try {
38
- var hasShown = localStorage.getItem(
39
- 'welcomeModalShown'
40
- );
41
  if (!hasShown) {
42
- var modal = document.getElementById(
43
- 'welcomeModal'
44
- );
45
  if (modal) {
46
  modal.style.display = 'flex';
47
  }
48
  }
49
  } catch (e) {
50
- var modal = document.getElementById(
51
- 'welcomeModal'
52
- );
53
  if (modal) {
54
  modal.style.display = 'flex';
55
  }
@@ -61,10 +86,7 @@
61
  if (modal) {
62
  modal.style.display = 'none';
63
  try {
64
- localStorage.setItem(
65
- 'welcomeModalShown',
66
- 'true'
67
- );
68
  } catch (e) {}
69
  }
70
  };
@@ -89,34 +111,32 @@
89
 
90
  var modelValue = model.value;
91
  var sizeValue = size.value;
92
- var promptValue = prompt.value
93
- .trim()
94
- .replace(/\s+/g, ' ')
95
- .replace(/[\t\n\r]/g, '');
96
 
97
  var isValid = !!modelValue &&
98
  !!sizeValue &&
99
  !!promptValue &&
100
  promptValue.length > 0;
101
 
 
 
 
 
 
102
  submitBtn.disabled = !isValid;
103
  };
104
 
105
  var triggerExample = function (prompt, model, size) {
106
- var modelSelect = document.getElementById(
107
- 'modelSelect'
108
- );
109
- var sizeSelect = document.getElementById(
110
- 'sizeSelect'
111
- );
112
- var promptInput = document.getElementById(
113
- 'promptInput'
114
- );
115
  var form = document.getElementById('generateForm');
116
 
117
  if (modelSelect) modelSelect.value = model;
118
  if (sizeSelect) sizeSelect.value = size;
119
- if (promptInput) promptInput.value = prompt;
 
 
120
 
121
  validateInputs();
122
 
@@ -132,58 +152,54 @@
132
  var cancelGeneration = function () {
133
  var form = document.getElementById('generateForm');
134
  var fa = document.getElementById('formAction');
135
- var cancelBtn = event && event.target
136
- ? event.target.closest('button')
137
  : null;
138
-
139
  if (cancelBtn) {
140
  cancelBtn.disabled = true;
141
  cancelBtn.style.opacity = '0.6';
142
  cancelBtn.style.cursor = 'not-allowed';
 
 
 
143
 
144
- var btnText = cancelBtn.querySelector('span') ||
145
- cancelBtn.childNodes[
146
- cancelBtn.childNodes.length - 1
147
- ];
148
  if (btnText && btnText.nodeType === 3) {
149
  btnText.textContent = 'Cancelling...';
150
  } else if (!btnText) {
151
- var textNode = Array.from(
152
- cancelBtn.childNodes
153
- ).find(function(node) {
154
- return node.nodeType === 3 &&
155
- node.textContent.trim();
156
- });
157
  if (textNode) {
158
  textNode.textContent = 'Cancelling...';
159
  }
160
  }
161
  }
162
-
163
  if (fa) {
164
  fa.value = 'cancel';
165
-
166
  if (form) {
167
- var hiddenCancel = document.createElement(
168
- 'input'
169
- );
170
  hiddenCancel.type = 'hidden';
171
  hiddenCancel.name = 'forceCancel';
172
  hiddenCancel.value = 'true';
173
  form.appendChild(hiddenCancel);
174
-
175
  var timestamp = document.createElement('input');
176
  timestamp.type = 'hidden';
177
  timestamp.name = 'cancelTime';
178
  timestamp.value = Date.now();
179
  form.appendChild(timestamp);
180
-
181
  try {
182
  form.submit();
183
  } catch (e) {
184
  form.requestSubmit && form.requestSubmit();
185
  }
186
-
187
  setTimeout(function() {
188
  if (form && fa) {
189
  fa.value = 'cancel';
@@ -194,7 +210,7 @@
194
  }
195
  }
196
  }, 500);
197
-
198
  setTimeout(function() {
199
  window.location.reload();
200
  }, 3000);
@@ -206,7 +222,7 @@
206
  newFA.name = 'action';
207
  newFA.value = 'cancel';
208
  form.appendChild(newFA);
209
-
210
  try {
211
  form.submit();
212
  } catch (e) {
@@ -215,8 +231,10 @@
215
  } else {
216
  var xhr = new XMLHttpRequest();
217
  xhr.open('POST', window.location.href, true);
218
- xhr.setRequestHeader('Content-Type',
219
- 'application/x-www-form-urlencoded');
 
 
220
  xhr.onload = function() {
221
  window.location.reload();
222
  };
@@ -225,9 +243,9 @@
225
  };
226
  xhr.send('action=cancel&forceCancel=true');
227
  }
228
-
229
  window.cancelRequested = true;
230
-
231
  if (window.EventSource) {
232
  try {
233
  var sources = window.eventSources || [];
@@ -238,14 +256,13 @@
238
  });
239
  } catch (e) {}
240
  }
241
-
242
- if (window.AbortController &&
243
- window.abortController) {
244
  try {
245
  window.abortController.abort();
246
  } catch (e) {}
247
  }
248
-
249
  setTimeout(function() {
250
  if (window.cancelRequested) {
251
  window.stop && window.stop();
@@ -255,54 +272,41 @@
255
  }, 100);
256
  };
257
 
258
- var modelSelect = document.getElementById(
259
- 'modelSelect'
260
- );
261
- var sizeSelect = document.getElementById(
262
- 'sizeSelect'
263
- );
264
- var promptInput = document.getElementById(
265
- 'promptInput'
266
- );
267
 
268
  if (modelSelect) {
269
- modelSelect.addEventListener(
270
- 'change',
271
- validateInputs
272
- );
273
  }
 
274
  if (sizeSelect) {
275
- sizeSelect.addEventListener(
276
- 'change',
277
- validateInputs
278
- );
279
  }
280
 
281
  if (promptInput) {
282
- promptInput.addEventListener(
283
- 'input',
284
- validateInputs
285
- );
 
286
  promptInput.addEventListener('paste', function () {
287
- setTimeout(validateInputs, 10);
 
 
 
288
  });
289
- promptInput.addEventListener(
290
- 'keydown',
291
- function (e) {
292
- if (e.key === 'Enter' && e.ctrlKey) {
293
- e.preventDefault();
294
- var submitBtn = document.getElementById(
295
- 'submitBtn'
296
- );
297
- if (submitBtn && !submitBtn.disabled) {
298
- var form = document.getElementById(
299
- 'generateForm'
300
- );
301
- if (form) form.submit();
302
- }
303
  }
304
  }
305
- );
306
  }
307
 
308
  window.closeWelcomeModal = closeWelcomeModal;
@@ -323,11 +327,9 @@
323
  });
324
  });
325
 
326
- document.addEventListener('DOMContentLoaded',
327
- function() {
328
- InitWelcomeModal();
329
- }
330
- );
331
 
332
  if (document.readyState === 'complete' ||
333
  document.readyState === 'interactive') {
 
6
  (function () {
7
  'use strict';
8
 
9
+ var sanitizeInput = function (raw, encode) {
10
+ if (!raw) return '';
11
+ var clean = raw;
12
+
13
+ if (typeof DOMPurify !== 'undefined') {
14
+ clean = DOMPurify.sanitize(clean, {
15
+ ALLOWED_TAGS: [],
16
+ ALLOWED_ATTR: []
17
+ });
18
+ }
19
+
20
+ if (typeof filterXSS !== 'undefined') {
21
+ clean = filterXSS(clean, {
22
+ whiteList: {}
23
+ });
24
+ }
25
+
26
+ if (encode === false) {
27
+ return clean;
28
+ }
29
+
30
+ if (typeof he !== 'undefined') {
31
+ clean = he.encode(clean, {
32
+ useNamedReferences: true,
33
+ allowUnsafeSymbols: false
34
+ });
35
+ }
36
+
37
+ return clean;
38
+ };
39
+
40
  var noop = function () {};
41
  var methods = [
42
+ 'log', 'error', 'warn', 'info', 'debug', 'trace',
43
+ 'dir', 'table', 'time', 'timeEnd', 'timeLog', 'group',
44
+ 'groupEnd', 'assert', 'count', 'countReset', 'profile',
45
+ 'profileEnd', 'clear', 'dirxml', 'exception',
46
  'groupCollapsed'
47
  ];
48
 
 
66
 
67
  var InitWelcomeModal = function () {
68
  try {
69
+ var hasShown = localStorage.getItem('welcomeModalShown');
 
 
70
  if (!hasShown) {
71
+ var modal = document.getElementById('welcomeModal');
 
 
72
  if (modal) {
73
  modal.style.display = 'flex';
74
  }
75
  }
76
  } catch (e) {
77
+ var modal = document.getElementById('welcomeModal');
 
 
78
  if (modal) {
79
  modal.style.display = 'flex';
80
  }
 
86
  if (modal) {
87
  modal.style.display = 'none';
88
  try {
89
+ localStorage.setItem('welcomeModalShown', 'true');
 
 
 
90
  } catch (e) {}
91
  }
92
  };
 
111
 
112
  var modelValue = model.value;
113
  var sizeValue = size.value;
114
+ var promptValue = sanitizeInput(prompt.value);
 
 
 
115
 
116
  var isValid = !!modelValue &&
117
  !!sizeValue &&
118
  !!promptValue &&
119
  promptValue.length > 0;
120
 
121
+ if (typeof validator !== 'undefined') {
122
+ isValid = isValid &&
123
+ validator.isLength(promptValue, { min: 1 });
124
+ }
125
+
126
  submitBtn.disabled = !isValid;
127
  };
128
 
129
  var triggerExample = function (prompt, model, size) {
130
+ var modelSelect = document.getElementById('modelSelect');
131
+ var sizeSelect = document.getElementById('sizeSelect');
132
+ var promptInput = document.getElementById('promptInput');
 
 
 
 
 
 
133
  var form = document.getElementById('generateForm');
134
 
135
  if (modelSelect) modelSelect.value = model;
136
  if (sizeSelect) sizeSelect.value = size;
137
+ if (promptInput) {
138
+ promptInput.value = sanitizeInput(prompt);
139
+ }
140
 
141
  validateInputs();
142
 
 
152
  var cancelGeneration = function () {
153
  var form = document.getElementById('generateForm');
154
  var fa = document.getElementById('formAction');
155
+ var cancelBtn = event && event.target
156
+ ? event.target.closest('button')
157
  : null;
158
+
159
  if (cancelBtn) {
160
  cancelBtn.disabled = true;
161
  cancelBtn.style.opacity = '0.6';
162
  cancelBtn.style.cursor = 'not-allowed';
163
+
164
+ var btnText = cancelBtn.querySelector('span') ||
165
+ cancelBtn.childNodes[cancelBtn.childNodes.length - 1];
166
 
 
 
 
 
167
  if (btnText && btnText.nodeType === 3) {
168
  btnText.textContent = 'Cancelling...';
169
  } else if (!btnText) {
170
+ var textNode = Array.from(cancelBtn.childNodes)
171
+ .find(function(node) {
172
+ return node.nodeType === 3 &&
173
+ node.textContent.trim();
174
+ });
 
175
  if (textNode) {
176
  textNode.textContent = 'Cancelling...';
177
  }
178
  }
179
  }
180
+
181
  if (fa) {
182
  fa.value = 'cancel';
183
+
184
  if (form) {
185
+ var hiddenCancel = document.createElement('input');
 
 
186
  hiddenCancel.type = 'hidden';
187
  hiddenCancel.name = 'forceCancel';
188
  hiddenCancel.value = 'true';
189
  form.appendChild(hiddenCancel);
190
+
191
  var timestamp = document.createElement('input');
192
  timestamp.type = 'hidden';
193
  timestamp.name = 'cancelTime';
194
  timestamp.value = Date.now();
195
  form.appendChild(timestamp);
196
+
197
  try {
198
  form.submit();
199
  } catch (e) {
200
  form.requestSubmit && form.requestSubmit();
201
  }
202
+
203
  setTimeout(function() {
204
  if (form && fa) {
205
  fa.value = 'cancel';
 
210
  }
211
  }
212
  }, 500);
213
+
214
  setTimeout(function() {
215
  window.location.reload();
216
  }, 3000);
 
222
  newFA.name = 'action';
223
  newFA.value = 'cancel';
224
  form.appendChild(newFA);
225
+
226
  try {
227
  form.submit();
228
  } catch (e) {
 
231
  } else {
232
  var xhr = new XMLHttpRequest();
233
  xhr.open('POST', window.location.href, true);
234
+ xhr.setRequestHeader(
235
+ 'Content-Type',
236
+ 'application/x-www-form-urlencoded'
237
+ );
238
  xhr.onload = function() {
239
  window.location.reload();
240
  };
 
243
  };
244
  xhr.send('action=cancel&forceCancel=true');
245
  }
246
+
247
  window.cancelRequested = true;
248
+
249
  if (window.EventSource) {
250
  try {
251
  var sources = window.eventSources || [];
 
256
  });
257
  } catch (e) {}
258
  }
259
+
260
+ if (window.AbortController && window.abortController) {
 
261
  try {
262
  window.abortController.abort();
263
  } catch (e) {}
264
  }
265
+
266
  setTimeout(function() {
267
  if (window.cancelRequested) {
268
  window.stop && window.stop();
 
272
  }, 100);
273
  };
274
 
275
+ var modelSelect = document.getElementById('modelSelect');
276
+ var sizeSelect = document.getElementById('sizeSelect');
277
+ var promptInput = document.getElementById('promptInput');
 
 
 
 
 
 
278
 
279
  if (modelSelect) {
280
+ modelSelect.addEventListener('change', validateInputs);
 
 
 
281
  }
282
+
283
  if (sizeSelect) {
284
+ sizeSelect.addEventListener('change', validateInputs);
 
 
 
285
  }
286
 
287
  if (promptInput) {
288
+ promptInput.addEventListener('input', function () {
289
+ promptInput.value = sanitizeInput(promptInput.value);
290
+ validateInputs();
291
+ });
292
+
293
  promptInput.addEventListener('paste', function () {
294
+ setTimeout(function () {
295
+ promptInput.value = sanitizeInput(promptInput.value);
296
+ validateInputs();
297
+ }, 10);
298
  });
299
+
300
+ promptInput.addEventListener('keydown', function (e) {
301
+ if (e.key === 'Enter' && e.ctrlKey) {
302
+ e.preventDefault();
303
+ var submitBtn = document.getElementById('submitBtn');
304
+ if (submitBtn && !submitBtn.disabled) {
305
+ var form = document.getElementById('generateForm');
306
+ if (form) form.submit();
 
 
 
 
 
 
307
  }
308
  }
309
+ });
310
  }
311
 
312
  window.closeWelcomeModal = closeWelcomeModal;
 
327
  });
328
  });
329
 
330
+ document.addEventListener('DOMContentLoaded', function() {
331
+ InitWelcomeModal();
332
+ });
 
 
333
 
334
  if (document.readyState === 'complete' ||
335
  document.readyState === 'interactive') {
config.js CHANGED
@@ -19,12 +19,12 @@ export default {
19
  },
20
  limits: {
21
  bodySize: '2mb',
22
- maxContentLength: Infinity
23
  },
24
  generation: {
25
  progressInterval: 800,
26
  startDelay: 100,
27
- maxProgress: 90
28
  },
29
  paths: {
30
  views: 'public',
 
19
  },
20
  limits: {
21
  bodySize: '2mb',
22
+ maxContentLength: 2097152
23
  },
24
  generation: {
25
  progressInterval: 800,
26
  startDelay: 100,
27
+ maxProgress: 99
28
  },
29
  paths: {
30
  views: 'public',
package.json CHANGED
@@ -1,6 +1,6 @@
1
  {
2
  "name": "Image Generation Playground",
3
- "version": "0.0.1",
4
  "description": "Part of the UltimaX Intelligence ecosystem",
5
  "type": "module",
6
  "main": "server.js",
 
1
  {
2
  "name": "Image Generation Playground",
3
+ "version": "0.0.2",
4
  "description": "Part of the UltimaX Intelligence ecosystem",
5
  "type": "module",
6
  "main": "server.js",
public/webViewer.ejs CHANGED
@@ -77,6 +77,11 @@
77
 
78
  <link rel="stylesheet" href="/__public__/assets/css/styles.css" />
79
  <link rel="stylesheet" href="/__public__/assets/css/webLoader.css" />
 
 
 
 
 
80
  </head>
81
  <body data-is-generating="<%= !!isGenerating %>"
82
  data-request-id="<%= requestId || '' %>">
@@ -283,6 +288,7 @@
283
  <textarea name="prompt" id="promptInput" required
284
  placeholder="Describe the image you want to generate..."
285
  class="input-field textarea-field"
 
286
  <%= isGenerating ? 'disabled' : '' %>></textarea>
287
  </div>
288
 
 
77
 
78
  <link rel="stylesheet" href="/__public__/assets/css/styles.css" />
79
  <link rel="stylesheet" href="/__public__/assets/css/webLoader.css" />
80
+
81
+ <script src="https://cdn.jsdelivr.net/npm/dompurify@3.2.7/dist/purify.js"></script>
82
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/validator/13.15.15/validator.js"></script>
83
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/he/1.2.0/he.js"></script>
84
+ <script src="https://cdn.jsdelivr.net/npm/xss@1.0.15/dist/xss.js"></script>
85
  </head>
86
  <body data-is-generating="<%= !!isGenerating %>"
87
  data-request-id="<%= requestId || '' %>">
 
288
  <textarea name="prompt" id="promptInput" required
289
  placeholder="Describe the image you want to generate..."
290
  class="input-field textarea-field"
291
+ maxlength="300"
292
  <%= isGenerating ? 'disabled' : '' %>></textarea>
293
  </div>
294