Issues (61)

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 (2 issues)

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