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/galleryalbum.js (1 issue)

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 Handlebars, Gallery, Thumbnails, GalleryImage */
2
(function ($, Gallery) {
3
	"use strict";
4
5
	var TEMPLATE =
6
		'<a class="row-element" style="width: {{targetWidth}}px; height: {{targetHeight}}px;" ' +
7
		'data-width="{{targetWidth}}" data-height="{{targetHeight}}"' +
8
		'href="{{targetPath}}" data-dir="{{dir}}" data-path="{{path}}"' +
9
		'data-permissions="{{permissions}}" data-freespace="{{freeSpace}}"' +
10
		'>' +
11
		'	<div class="album-loader loading"></div>' +
12
		'	<span class="album-label">' +
13
		'		<span class="title">{{label}}</span>' +
14
		'	</span>' +
15
		'	<div class="album container" style="width: {{targetWidth}}px; height: {{targetHeight}}px;" >' +
16
		'	</div>' +
17
		'</a>';
18
19
	/**
20
	 * Creates a new album object to store information about an album
21
	 *
22
	 * @param {string} path
23
	 * @param {Array<Album|GalleryImage>} subAlbums
24
	 * @param {Array<Album|GalleryImage>} images
25
	 * @param {string} name
26
	 * @param {number} fileId
27
	 * @param {number} mTime
28
	 * @param {string} etag
29
	 * @param {number} size
30
	 * @param {Boolean} sharedWithUser
31
	 * @param {string} owner
32
	 * @param {number} freeSpace
33
	 * @param {number} permissions
34
	 * @constructor
35
	 */
36
	var Album = function (path, subAlbums, images, name, fileId, mTime, etag, size, sharedWithUser,
0 ignored issues
show
This function has too many parameters. (12)
Loading history...
37
						  owner, freeSpace, permissions) {
38
		this.path = path;
39
		this.subAlbums = subAlbums;
40
		this.images = images;
41
		this.viewedItems = 0;
42
		this.name = name;
43
		this.fileId = fileId;
44
		this.mTime = mTime;
45
		this.etag = etag;
46
		this.size = size;
47
		this.sharedWithUser = sharedWithUser;
48
		this.owner = owner;
49
		this.freeSpace = freeSpace;
50
		this.permissions = permissions;
51
		this.domDef = null;
52
		this.loader = null;
53
		this.preloadOffset = 0;
54
	};
55
56
	Album.prototype = {
57
		requestId: null,
58
		droppableOptions: {
59
			accept: '#gallery > .row > a',
60
			activeClass: 'album-droppable',
61
			hoverClass: 'album-droppable-hover',
62
			tolerance: 'pointer'
63
		},
64
65
		/**
66
		 * Processes UI elements dropped on the album
67
		 *
68
		 * @param event
69
		 * @param ui
70
		 */
71
		onDrop: function (event, ui) {
72
			var $item = ui.draggable;
73
			var $clone = ui.helper;
74
			var $target = $(event.target);
75
			var targetPath = $target.data('dir').toString();
76
			var filePath = $item.data('path').toString();
77
			var fileName = OC.basename(filePath);
78
79
			this.loader.show();
80
81
			$clone.fadeOut("normal", function () {
82
				Gallery.move($item, fileName, filePath, $target, targetPath);
83
			});
84
		},
85
86
		/**
87
		 * Returns a new album row
88
		 *
89
		 * @param {number} width
90
		 *
91
		 * @returns {Gallery.Row}
92
		 */
93
		getRow: function (width) {
94
			return new Gallery.Row(width);
95
		},
96
97
		/**
98
		 * Creates the DOM element for the album and return it immediately so as to not block the
99
		 * rendering of the rest of the interface
100
		 *
101
		 *    * Each album also contains a link to open that folder
102
		 *    * An album has a natural size of 200x200 and is comprised of 4 thumbnails which have a
103
		 *        natural size of 200x200
104
		 *    * Thumbnails are checked first in order to make sure that we have something to show
105
		 *
106
		 * @param {number} targetHeight Each row has a specific height
107
		 *
108
		 * @return {$} The album to be placed on the row
109
		 */
110
		getDom: function (targetHeight) {
111
			if (this.domDef === null) {
112
				var template = Handlebars.compile(TEMPLATE);
113
				var albumElement = template({
114
					targetHeight: targetHeight,
115
					targetWidth: targetHeight,
116
					dir: this.path,
117
					path: this.path,
118
					permissions: this.permissions,
119
					freeSpace: this.freeSpace,
120
					label: this.name,
121
					targetPath: '#' + encodeURIComponent(this.path)
122
				});
123
				this.domDef = $(albumElement);
124
				this.loader = this.domDef.children('.album-loader');
125
				this.loader.hide();
126
				this.domDef.click(this._openAlbum.bind(this));
127
128
				this.droppableOptions.drop = this.onDrop.bind(this);
129
				this.domDef.droppable(this.droppableOptions);
130
131
				// Define a if you don't want to set the style in the template
132
				//a.width(targetHeight);
133
				//a.height(targetHeight);
134
135
				this._fillSubAlbum(targetHeight);
136
			} else {
137
				this.loader.hide();
138
			}
139
140
			return this.domDef;
141
		},
142
143
		/**
144
		 * Fills the row with albums and images
145
		 *
146
		 * @param {Gallery.Row} row The row to append elements to
147
		 *
148
		 * @returns {$.Deferred<Gallery.Row>}
149
		 */
150
		fillNextRow: function (row) {
151
			var def = new $.Deferred();
152
			var numberOfThumbnailsToPreload = 6;
153
			var buffer = 5;
154
155
			/**
156
			 * Add images to the row until it's full
157
			 *
158
			 * @todo The number of images to preload should be a user setting
159
			 *
160
			 * @param {Album} album
161
			 * @param {Row} row
162
			 * @param {Array<Album|GalleryImage>} images
163
			 *
164
			 * @returns {$.Deferred<Gallery.Row>}
165
			 */
166
			var addRowElements = function (album, row, images) {
167
				if ((album.viewedItems + buffer) > album.preloadOffset &&
168
					(album.preloadOffset < images.length)) {
169
					album._preload(numberOfThumbnailsToPreload);
170
				}
171
172
				var image = images[album.viewedItems];
173
				return row.addElement(image).then(function (more) {
174
					album.viewedItems++;
175
					if (more && album.viewedItems < images.length) {
176
						return addRowElements(album, row, images);
177
					}
178
					row.fit();
179
					def.resolve(row);
180
				});
181
			};
182
			var items = this.subAlbums.concat(this.images);
183
			addRowElements(this, row, items);
184
			return def.promise();
185
		},
186
187
		/**
188
		 * Returns IDs of thumbnails belonging to the album
189
		 *
190
		 * @param {number} count
191
		 *
192
		 * @return number[]
193
		 */
194
		getThumbnailIds: function (count) {
195
			var ids = [];
196
			var items = this.images.concat(this.subAlbums);
197
			for (var i = 0; i < items.length && i < count; i++) {
198
				ids = ids.concat(items[i].getThumbnailIds(count));
199
			}
200
201
			return ids;
202
		},
203
204
		/**
205
		 * Call when the album is clicked on.
206
		 *
207
		 * @param event
208
		 * @private
209
		 */
210
		_openAlbum: function (event) {
211
			event.stopPropagation();
212
			// show loading animation
213
			this.loader.show();
214
			if(!_.isUndefined(Gallery.Share)){
215
				Gallery.Share.hideDropDown();
216
			}
217
		},
218
219
		/**
220
		 * Retrieves a thumbnail and adds it to the album representation
221
		 *
222
		 * Only attaches valid thumbnails to the album
223
		 *
224
		 * @param {GalleryImage} image
225
		 * @param {number} targetHeight Each row has a specific height
226
		 * @param {number} calcWidth Album width
227
		 * @param {jQuery} imageHolder
228
		 *
229
		 * @returns {$.Deferred<Thumbnail>}
230
		 * @private
231
		 */
232
		_getOneImage: function (image, targetHeight, calcWidth, imageHolder) {
233
			var backgroundHeight, backgroundWidth;
234
235
			backgroundHeight = (targetHeight / 2);
236
			backgroundWidth = calcWidth - 2.01;
237
238
			// Adjust the size because of the margins around pictures
239
			backgroundHeight -= 2;
240
241
			imageHolder.css("height", backgroundHeight)
242
				.css("width", backgroundWidth);
243
			var spinner = $('<div class="icon-loading">');
244
			imageHolder.append(spinner);
245
246
			// img is a Thumbnail.image, true means square thumbnails
247
			return image.getThumbnail(true).then(function (img) {
248
				if (image.thumbnail.valid) {
249
					img.alt = '';
250
					spinner.remove();
251
					imageHolder.css("background-image", "url('" + img.src + "')")
252
						.css('opacity', 1);
253
				}
254
			});
255
		},
256
257
		/**
258
		 * Builds the album representation by placing 1 to 4 images on a grid
259
		 *
260
		 * @param {Array<GalleryImage>} images
261
		 * @param {number} targetHeight Each row has a specific height
262
		 * @param {object} a
263
		 *
264
		 * @returns {$.Deferred<Array>}
265
		 * @private
266
		 */
267
		_getFourImages: function (images, targetHeight, a) {
268
			var calcWidth = targetHeight;
269
			var targetWidth;
270
			var imagesCount = images.length;
271
			var def = new $.Deferred();
272
			var validImages = [];
273
			var fail = false;
274
			var thumbsArray = [];
275
276
			for (var i = 0; i < imagesCount; i++) {
277
				targetWidth = calcWidth;
278
				// One picture filling the album
279
				if (imagesCount === 1) {
280
					targetHeight = 2 * targetHeight;
281
				}
282
				// 2 bottom pictures out of 3, or 4 pictures have the size of a quarter of the album
283
				if ((imagesCount === 3 && i !== 0) || imagesCount === 4) {
284
					targetWidth = calcWidth / 2;
285
				}
286
287
				// Append the div first in order to not lose the order of images
288
				var imageHolder = $('<div class="cropped">');
289
				a.append(imageHolder);
290
				thumbsArray.push(
291
					this._getOneImage(images[i], targetHeight, targetWidth, imageHolder));
292
			}
293
294
			// This technique allows us to wait for all objects to be resolved before making a
295
			// decision
296
			$.when.apply($, thumbsArray).done(function () {
297
				for (var i = 0; i < imagesCount; i++) {
298
					// Collect all valid images, just in case
299
					if (images[i].thumbnail.valid) {
300
						validImages.push(images[i]);
301
					} else {
302
						fail = true;
303
					}
304
				}
305
306
				// At least one thumbnail could not be retrieved
307
				if (fail) {
308
					// Clean up the album
309
					a.children().remove();
310
					// Send back the list of images which have thumbnails
311
					def.reject(validImages);
312
				}
313
			});
314
315
			return def.promise();
316
		},
317
318
		/**
319
		 * Fills the album representation with images we've received
320
		 *
321
		 *    * Each album includes between 1 and 4 images
322
		 *    * Each album is also a link to open that folder
323
		 *    * An album has a natural size of 200x200 and is comprised of 4 thumbnails which have a
324
		 * natural size of 200x200 The whole thing gets resized to match the targetHeight
325
		 *
326
		 * @param {number} targetHeight
327
		 * @private
328
		 */
329
		_fillSubAlbum: function (targetHeight) {
330
			var album = this;
331
			var subAlbum = this.domDef.children('.album');
332
333
			if (this.images.length >= 1) {
334
				this._getFourImages(this.images, targetHeight, subAlbum).fail(
335
					function (validImages) {
336
						album.images = validImages;
337
						album._fillSubAlbum(targetHeight, subAlbum);
338
					});
339
			} else {
340
				var imageHolder = $('<div class="cropped">');
341
				subAlbum.append(imageHolder);
342
				this._showFolder(targetHeight, imageHolder);
343
			}
344
		},
345
346
		/**
347
		 * Shows a folder icon in the album since we couldn't get any proper thumbnail
348
		 *
349
		 * @param {number} targetHeight
350
		 * @param imageHolder
351
		 * @private
352
		 */
353
		_showFolder: function (targetHeight, imageHolder) {
354
			var image = new GalleryImage('Generic folder', 'Generic folder', -1, 'image/svg+xml',
355
				null, null);
356
			var thumb = Thumbnails.getStandardIcon(-1);
357
			image.thumbnail = thumb;
358
			this.images.push(image);
359
			thumb.loadingDeferred.done(function (img) {
360
				img.height = (targetHeight - 2);
361
				img.width = (targetHeight) - 2;
362
				imageHolder.append(img);
363
				imageHolder.css('opacity', 1);
364
			});
365
		},
366
367
		/**
368
		 * Preloads the first $count thumbnails
369
		 *
370
		 * @param {number} count
371
		 * @private
372
		 */
373
		_preload: function (count) {
374
			var items = this.subAlbums.concat(this.images);
375
			var realCounter = 0;
376
			var maxThumbs = 0;
377
			var fileIds = [];
378
			var squareFileIds = [];
379
			for (var i = this.preloadOffset; i < this.preloadOffset + count &&
380
			i < items.length; i++) {
381
				if (items[i].subAlbums) {
382
					maxThumbs = 4;
383
					var imagesLength = items[i].images.length;
384
					if (imagesLength > 0 && imagesLength < 4) {
385
						maxThumbs = imagesLength;
386
					}
387
					var squareFileId = items[i].getThumbnailIds(maxThumbs);
388
					squareFileIds = squareFileIds.concat(squareFileId);
389
					realCounter = realCounter + maxThumbs;
390
				} else {
391
					var fileId = items[i].getThumbnailIds();
392
					fileIds = fileIds.concat(fileId);
393
					realCounter++;
394
				}
395
				if (realCounter >= count) {
396
					i++;
397
					break;
398
				}
399
			}
400
401
			this.preloadOffset = i;
402
			Thumbnails.loadBatch(fileIds, false);
403
			Thumbnails.loadBatch(squareFileIds, true);
404
		}
405
	};
406
407
	window.Album = Album;
408
})(jQuery, Gallery);
409