Issues (108)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

js/slideshow.js (5 issues)

Languages
Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
/* global DOMPurify */
2
(function ($, OC, OCA, t) {
3
	"use strict";
4
	/**
5
	 * Slideshow featuring zooming
6
	 *
7
	 * @constructor
8
	 */
9
	var SlideShow = function () {
10
	};
11
12
	SlideShow.prototype = {
13
		slideshowTemplate: null,
14
		container: null,
15
		zoomablePreviewContainer: null,
16
		controls: null,
17
		imageCache: {},
18
		/** {Image} */
19
		currentImage: null,
20
		errorLoadingImage: false,
21
		onStop: null,
22
		zoomablePreview: null,
23
		active: false,
24
		backgroundToggle: false,
25
26
		/**
27
		 * Initialises the slideshow
28
		 *
29
		 * @param {boolean} autoPlay
30
		 * @param {number} interval
31
		 * @param {Array} features
32
		 */
33
		init: function (autoPlay, interval, features) {
34
			if (features.indexOf('background_colour_toggle') > -1) {
35
				this.backgroundToggle = true;
36
			}
37
38
			return $.when(this._getSlideshowTemplate()).then(function ($tmpl) {
39
				// Move the slideshow outside the content so we can hide the content
40
				$('body').append($tmpl);
41
				this.container = $('#slideshow');
42
				this.zoomablePreviewContainer = this.container.find('.bigshotContainer');
43
				this.zoomablePreview = new SlideShow.ZoomablePreview(this.container);
44
				this.controls =
45
					new SlideShow.Controls(
46
						this,
47
						this.container,
48
						this.zoomablePreview,
49
						interval,
50
						features);
51
				this.controls.init();
52
53
				this._initControlsAutoFader();
54
55
				// Only modern browsers can manipulate history
56
				if (history && history.pushState) {
57
					// Stop the slideshow when backing out.
58
					$(window).bind('popstate.slideshow', function () {
59
						if (this.active === true) {
60
							this.active = false;
61
							this.controls.stop();
62
						}
63
					}.bind(this));
64
				}
65
			}.bind(this)).fail(function () {
66
				OC.Notification.show(t('core', 'Error loading slideshow template'));
67
			});
68
		},
69
70
		/**
71
		 * Refreshes the slideshow's data
72
		 *
73
		 * @param {{name:string, url: string, path: string, fallBack: string}[]} images
74
		 * @param {boolean} autoPlay
75
		 */
76
		setImages: function (images, autoPlay) {
77
			this._hideImage();
78
			this.images = images;
79
			this.controls.update(images, autoPlay);
80
		},
81
82
		/**
83
		 * Launches the slideshow
84
		 *
85
		 * @param {number} index
86
		 *
87
		 * @returns {*}
88
		 */
89
		show: function (index) {
90
			this.hideErrorNotification();
91
			this.active = true;
92
			this.container.show();
93
			this.container.css('background-position', 'center');
94
			this._hideImage();
95
			var currentImageId = index;
96
			return this.loadImage(this.images[index]).then(function (img) {
97
				this.container.css('background-position', '-10000px 0');
98
99
				// check if we moved along while we were loading
100
				if (currentImageId === index) {
101
					var image = this.images[index];
102
					var transparent = this._isTransparent(image.mimeType);
103
					this.controls.showActionButtons(transparent, Gallery.token, image.permissions);
0 ignored issues
show
Gallery does not seem to be defined.
Loading history...
104
					this.errorLoadingImage = false;
105
					this.currentImage = img;
106
107
					var backgroundColour = '#000';
108
					if (transparent) {
109
						backgroundColour = '#fff';
110
					}
111
					img.setAttribute('alt', image.name);
112
					$(img).css('position', 'absolute');
113
					$(img).css('background-color', backgroundColour);
114
					if (transparent && this.backgroundToggle === true) {
115
						var $border = 30 / window.devicePixelRatio;
116
						$(img).css('outline', $border + 'px solid ' + backgroundColour);
117
					}
118
119
					this.zoomablePreview.startBigshot(img, this.currentImage, image.mimeType);
120
121
					this._setUrl(image.path);
122
					this.controls.show(currentImageId);
123
				}
124
			}.bind(this), function () {
125
				// Don't do anything if the user has moved along while we were loading as it would
126
				// mess up the index
127
				if (currentImageId === index) {
128
					this.errorLoadingImage = true;
129
					this.showErrorNotification(null);
130
					this._setUrl(this.images[index].path);
131
					this.images.splice(index, 1);
132
					this.controls.updateControls(this.images, this.errorLoadingImage);
133
				}
134
			}.bind(this));
135
		},
136
137
		/**
138
		 * Loads the image to show in the slideshow and preloads the next one
139
		 *
140
		 * @param {Object} preview
141
		 *
142
		 * @returns {*}
143
		 */
144
		loadImage: function (preview) {
145
			var url = preview.url;
146
			var mimeType = preview.mimeType;
147
148
			if (!this.imageCache[url]) {
149
				this.imageCache[url] = new $.Deferred();
150
				var image = new Image();
151
152
				image.onload = function () {
153
					if (this.imageCache[url]) {
154
						this.imageCache[url].resolve(image);
155
					}
156
				}.bind(this);
157
				image.onerror = function () {
158
					if (this.imageCache[url]) {
159
						this.imageCache[url].reject(url);
160
					}
161
				}.bind(this);
162
				if (mimeType === 'image/svg+xml') {
163
					image.src = this._getSVG(url);
164
				} else {
165
					image.src = url;
166
				}
167
			}
168
			return this.imageCache[url];
169
		},
170
171
		/**
172
		 * Shows a new image in the slideshow and preloads the next in the list
173
		 *
174
		 * @param {number} current
175
		 * @param {Object} next
176
		 */
177
		next: function (current, next) {
178
			this.show(current).then(function () {
179
				// Preloads the next image in the list
180
				this.loadImage(next);
181
			}.bind(this));
182
		},
183
184
		/**
185
		 * Stops the slideshow
186
		 */
187
		stop: function () {
188
			this.active = false;
189
			this.images = null;
190
			this._hideImage();
191
			if (this.onStop) {
192
				this.onStop();
193
			}
194
		},
195
196
		/**
197
		 * Sends the current image as a download
198
		 *
199
		 * @param {string} downloadUrl
200
		 *
201
		 * @returns {boolean}
202
		 */
203
		getImageDownload: function (downloadUrl) {
204
			OC.redirect(downloadUrl);
205
			return false;
206
		},
207
208
		/**
209
		 * Changes the colour of the background of the image
210
		 */
211
		toggleBackground: function () {
212
			var toHex = function (x) {
213
				return ("0" + parseInt(x).toString(16)).slice(-2);
214
			};
215
			var container = this.zoomablePreviewContainer.children('img');
216
			var rgb = container.css('background-color').match(/\d+/g);
217
			var hex = "#" + toHex(rgb[0]) + toHex(rgb[1]) + toHex(rgb[2]);
218
			var $border = 30 / window.devicePixelRatio;
219
220
			// Grey #363636
221
			if (hex === "#000000") {
222
				container.css('background-color', '#FFF');
223
				if (this.backgroundToggle === true) {
224
					container.css('outline', $border + 'px solid #FFF');
225
				}
226
			} else {
227
				container.css('background-color', '#000');
228
				if (this.backgroundToggle === true) {
229
					container.css('outline', $border + 'px solid #000');
230
				}
231
			}
232
		},
233
234
		/**
235
		 * Shows an error notification
236
		 *
237
		 * @param {string} message
238
		 */
239
		showErrorNotification: function (message) {
240
			if ($.isEmptyObject(message)) {
241
				message = t('gallery',
242
					'<strong>Error!</strong> Could not generate a preview of this file.<br>' +
243
					'Please go to the next slide while we remove this image from the slideshow');
244
			}
245
			this.container.find('.notification').html(message);
246
			this.container.find('.notification').show();
247
			this.controls.hideButton('.changeBackground');
248
		},
249
250
		/**
251
		 * Hides the error notification
252
		 */
253
		hideErrorNotification: function () {
254
			this.container.find('.notification').hide();
255
			this.container.find('.notification').html('');
256
		},
257
258
		/**
259
		 * Removes a specific button from the interface
260
		 *
261
		 * @param button
262
		 */
263
		removeButton: function (button) {
264
			this.controls.removeButton(button);
265
		},
266
267
		/**
268
		 * Deletes an image from the slideshow
269
		 *
270
		 * @param {object} image
271
		 * @param {number} currentIndex
272
		 */
273
		deleteImage: function (image, currentIndex) {
274
			// These are Gallery specific commands to be replaced
275
			// which should sit somewhere else
276
			if (!window.galleryFileAction) {
277
				delete Gallery.imageMap[image.path];
0 ignored issues
show
Gallery does not seem to be defined.
Loading history...
278
				delete Thumbnails.map[image.file];
0 ignored issues
show
Thumbnails does not seem to be defined.
Loading history...
279
				Gallery.albumMap[Gallery.currentAlbum].images.splice(currentIndex, 1);
0 ignored issues
show
Gallery does not seem to be defined.
Loading history...
280
				Gallery.view.init(Gallery.currentAlbum);
0 ignored issues
show
Gallery does not seem to be defined.
Loading history...
281
			}
282
283
			
284
		},
285
286
		
287
		/**
288
		 * Automatically fades the controls after 3 seconds
289
		 *
290
		 * @private
291
		 */
292
		_initControlsAutoFader: function () {
293
			var inactiveCallback = function () {
294
				this.container.addClass('inactive');
295
			}.bind(this);
296
			var inactiveTimeout = setTimeout(inactiveCallback, 3000);
297
298
			this.container.on('mousemove touchstart', function () {
299
				this.container.removeClass('inactive');
300
				clearTimeout(inactiveTimeout);
301
				inactiveTimeout = setTimeout(inactiveCallback, 3000);
302
			}.bind(this));
303
		},
304
305
		/**
306
		 * Simplest way to detect if image is transparent.
307
		 *
308
		 * That's very inaccurate since it doesn't include images which support transparency
309
		 *
310
		 * @param mimeType
311
		 * @returns {boolean}
312
		 * @private
313
		 */
314
		_isTransparent: function (mimeType) {
315
			return !(mimeType === 'image/jpeg'
316
				|| mimeType === 'image/x-dcraw'
317
				|| mimeType === 'application/font-sfnt'
318
				|| mimeType === 'application/x-font'
319
			);
320
		},
321
322
		/**
323
		 * Changes the browser Url, based on the current image
324
		 *
325
		 * @param {string} path
326
		 * @private
327
		 */
328
		_setUrl: function (path) {
329
			if (history && history.replaceState) {
330
				history.replaceState('', '', '#' + encodeURI(path));
331
			}
332
		},
333
334
		/**
335
		 * Hides the current image (before loading the next)
336
		 *
337
		 * @private
338
		 */
339
		_hideImage: function () {
340
			this.zoomablePreviewContainer.empty();
341
			this.controls.hideActionButtons();
342
		},
343
344
		/**
345
		 * Retrieves an SVG
346
		 *
347
		 * An SVG can't be simply attached to a src attribute like a bitmap image
348
		 *
349
		 * @param {string} source
350
		 *
351
		 * @returns {*}
352
		 * @private
353
		 */
354
		_getSVG: function (source) {
355
			var svgPreview = null;
356
			// DOMPurify only works with IE10+ and we load SVGs in the IMG tag
357
			if (window.btoa &&
358
				document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image",
359
					"1.1")) {
360
				var xmlHttp = new XMLHttpRequest();
361
				xmlHttp.open("GET", source, false);
362
				xmlHttp.send(null);
363
				if (xmlHttp.status === 200) {
364
					var pureSvg = DOMPurify.sanitize(xmlHttp.responseText, {ADD_TAGS: ['filter']});
365
					// Remove XML comment garbage left in the purified data
366
					var badTag = pureSvg.indexOf(']&gt;');
367
					var fixedPureSvg = pureSvg.substring(badTag < 0 ? 0 : 5, pureSvg.length);
368
					svgPreview = "data:image/svg+xml;base64," + window.btoa(fixedPureSvg);
369
				}
370
			}
371
372
			return svgPreview;
373
		},
374
375
		/**
376
		 * Retrieves the slideshow's template
377
		 *
378
		 * @returns {*}
379
		 * @private
380
		 */
381
		_getSlideshowTemplate: function () {
382
			var defer = $.Deferred();
383
			if (!this.$slideshowTemplate) {
384
				var self = this;
385
				var url = OC.generateUrl('apps/gallery/slideshow', null);
386
				$.get(url, function (tmpl) {
387
						var template = $(tmpl);
388
						var tmplButton;
389
						var buttonsArray = [
390
							{
391
								el: '.next',
392
								trans: t('gallery', 'Next')
393
							},
394
							{
395
								el: '.play',
396
								trans: t('gallery', 'Play')
397
							},
398
							{
399
								el: '.pause',
400
								trans: t('gallery', 'Pause')
401
							},
402
							{
403
								el: '.previous',
404
								trans: t('gallery', 'Previous')
405
							},
406
							{
407
								el: '.exit',
408
								trans: t('gallery', 'Close')
409
							},
410
							{
411
								el: '.downloadImage',
412
								trans: t('gallery', 'Download'),
413
								toolTip: true
414
							},
415
							{
416
								el: '.changeBackground',
417
								trans: t('gallery', 'Toggle background'),
418
								toolTip: true
419
							},
420
							{
421
								el: '.deleteImage',
422
								trans: t('gallery', 'Delete'),
423
								toolTip: true
424
							},
425
							{
426
								el: '.rotationCup',
427
								trans: t('gallery', 'rotation'),
428
								toolTip: true
429
							}
430
						];
431
						for (var i = 0; i < buttonsArray.length; i++) {
432
							var button = buttonsArray[i];
433
434
							tmplButton = template.find(button.el);
435
							tmplButton.val(button.trans);
436
							if (button.toolTip) {
437
								tmplButton.attr("title", button.trans);
438
							}
439
						}
440
						self.$slideshowTemplate = template;
441
						defer.resolve(self.$slideshowTemplate);
442
					})
443
					.fail(function () {
444
						defer.reject();
445
					});
446
			} else {
447
				defer.resolve(this.$slideshowTemplate);
448
			}
449
			return defer.promise();
450
		}
451
	};
452
453
	window.SlideShow = SlideShow;
454
})(jQuery, OC, OCA, t);
455