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/gallery.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 Album, GalleryImage */
2
(function ($, OC, t) {
3
	"use strict";
4
	var Gallery = {
5
		currentAlbum: null,
6
		currentEtag: null,
7
		config: {},
8
		/** Map of the whole gallery, built as we navigate through folders */
9
		albumMap: {},
10
		/** Used to pick an image based on the URL */
11
		imageMap: {},
12
		appName: 'gallery',
13
		token: undefined,
14
		activeSlideShow: null,
15
		buttonsWidth: 600,
16
		browserToolbarHeight: 150,
17
		filesClient: null,
18
19
		/**
20
		 * Refreshes the view and starts the slideshow if required
21
		 *
22
		 * @param {string} path
23
		 * @param {string} albumPath
24
		 */
25
		refresh: function (path, albumPath) {
26
			if (Gallery.currentAlbum !== albumPath) {
27
				Gallery.view.init(albumPath, null);
28
			}
29
30
			// If the path is mapped, that means that it's an albumPath
31
			if (Gallery.albumMap[path]) {
32
				if (Gallery.activeSlideShow) {
33
					Gallery.activeSlideShow.stop();
34
				}
35
			} else if (Gallery.imageMap[path] && Gallery.activeSlideShow.active === false) {
36
				Gallery.view.startSlideshow(path, albumPath);
37
			}
38
		},
39
40
		/**
41
		 * Retrieves information about all the images and albums located in the current folder
42
		 *
43
		 * @param {string} currentLocation
44
		 *
45
		 * @returns {*}
46
		 */
47
		getFiles: function (currentLocation) {
48
			// Cache the sorting order of the current album before loading new files
49
			if (!$.isEmptyObject(Gallery.albumMap)) {
50
				Gallery.albumMap[Gallery.currentAlbum].sorting = Gallery.config.albumSorting;
51
			}
52
			// Checks if we've visited this location before ands saves the etag to use for
53
			// comparison later
54
			var albumEtag;
55
			var albumCache = Gallery.albumMap[decodeURIComponent(currentLocation)];
56
			if (!$.isEmptyObject(albumCache)) {
57
				albumEtag = albumCache.etag;
58
			}
59
60
			// Sends the request to the server
61
			var params = {
62
				location: currentLocation,
63
				mediatypes: Gallery.config.getMediaTypes(),
64
				features: Gallery.config.getFeatures(),
65
				etag: albumEtag
66
			};
67
			// Only use the folder as a GET parameter and not as part of the URL
68
			var url = Gallery.utility.buildGalleryUrl('files', '/list', params);
69
			return $.getJSON(url).then(
70
				function (/**@type{{
71
					* files:Array,
72
					* albums:Array,
73
					* albumconfig:Object,
74
					* albumpath:String,
75
					* updated:Boolean}}*/
76
						  data) {
77
					var albumpath = data.albumpath;
78
					var updated = data.updated;
79
					// FIXME albumConfig should be cached as well
80
					/**@type {{design,information,sorting,error: string}}*/
81
					var albumConfig = data.albumconfig;
82
					//Gallery.config.setAlbumPermissions(currentAlbum);
83
					Gallery.config.setAlbumConfig(albumConfig, albumpath);
84
					// Both the folder and the etag have to match
85
					if ((decodeURIComponent(currentLocation) === albumpath) &&
86
						(updated === false)) {
87
						Gallery.imageMap = albumCache.imageMap;
88
					} else {
89
						Gallery._mapFiles(data);
90
					}
91
92
					// Restore the previous sorting order for this album
93
					if (!$.isEmptyObject(Gallery.albumMap[albumpath].sorting)) {
94
						Gallery.config.updateAlbumSorting(
95
							Gallery.albumMap[albumpath].sorting);
96
					}
97
98
				}, function (xhr) {
99
					var result = xhr.responseJSON;
100
					var albumPath = decodeURIComponent(currentLocation);
101
					var message;
102
					if (result === null) {
103
						message = t('gallery', 'There was a problem reading files from this album');
104
					} else {
105
						message = result.message;
106
					}
107
					Gallery.view.init(albumPath, message);
108
					Gallery._mapStructure(albumPath);
109
				});
110
		},
111
112
		/**
113
		 * Sorts albums and images based on user preferences
114
		 */
115
		sorter: function () {
116
			var sortType = 'name';
117
			var sortOrder = 'asc';
118
			var albumSortType = 'name';
119
			var albumSortOrder = 'asc';
120
			if (this.id === 'sort-date-button') {
121
				sortType = 'date';
122
123
			}
124
			var currentSort = Gallery.config.albumSorting;
125
			if (currentSort.type === sortType && currentSort.order === sortOrder) {
126
				sortOrder = 'des';
127
			}
128
129
			// Update the controls
130
			Gallery.view.sortControlsSetup(sortType, sortOrder);
131
132
			// We can't currently sort by album creation time
133
			if (sortType === 'name') {
134
				albumSortOrder = sortOrder;
135
			}
136
137
			// FIXME Rendering is still happening while we're sorting...
138
139
			// Clear before sorting
140
			Gallery.view.clear();
141
142
			// Sort the images
143
			Gallery.albumMap[Gallery.currentAlbum].images.sort(Gallery.utility.sortBy(sortType,
144
				sortOrder));
145
			Gallery.albumMap[Gallery.currentAlbum].subAlbums.sort(
146
				Gallery.utility.sortBy(albumSortType,
147
					albumSortOrder));
148
149
			// Save the new settings
150
			var sortConfig = {
151
				type: sortType,
152
				order: sortOrder,
153
				albumOrder: albumSortOrder
154
			};
155
			Gallery.config.updateAlbumSorting(sortConfig);
156
157
			// Refresh the view
158
			Gallery.view.viewAlbum(Gallery.currentAlbum);
159
		},
160
161
		/**
162
		 * Switches to the Files view
163
		 *
164
		 * @param event
165
		 */
166
		switchToFilesView: function (event) {
167
			event.stopPropagation();
168
169
			var subUrl = '';
170
			var params = {path: '/' + Gallery.currentAlbum};
171
			if (Gallery.token) {
172
				params.token = Gallery.token;
173
				subUrl = 's/{token}?path={path}';
174
			} else {
175
				subUrl = 'apps/files?dir={path}';
176
			}
177
178
			var button = $('#filelist-button');
179
			button.children('#button-loading').addClass('loading');
180
			OC.redirect(OC.generateUrl(subUrl, params));
181
		},
182
183
		/**
184
		 * Populates the share dialog with the needed information
185
		 *
186
		 * @param event
187
		 */
188
		share: function (event) {
189
			// Clicking on share button does not trigger automatic slide-up
190
			$('.album-info-container').slideUp();
191
192
			if (!Gallery.Share.droppedDown) {
193
				event.preventDefault();
194
				event.stopPropagation();
195
196
				var currentAlbum = Gallery.albumMap[Gallery.currentAlbum];
197
				$('a.share').data('path', currentAlbum.path)
198
					.data('link', true)
199
					.data('item-source', currentAlbum.fileId)
200
					.data('possible-permissions', currentAlbum.permissions)
201
					.click();
202
				if (!$('#linkCheckbox').is(':checked')) {
203
					$('#linkText').hide();
204
				}
205
			}
206
		},
207
208
		/**
209
		 * Sends an archive of the current folder to the browser
210
		 *
211
		 * @param event
212
		 */
213
		download: function (event) {
214
			event.preventDefault();
215
216
			var path = $('#content').data('albumname');
217
			var files = Gallery.currentAlbum;
218
			var downloadUrl = Gallery.utility.buildFilesUrl(path, files);
219
220
			OC.redirect(downloadUrl);
221
		},
222
223
		/**
224
		 * Shows an information box to the user
225
		 *
226
		 * @param event
227
		 */
228
		showInfo: function (event) {
229
			event.stopPropagation();
230
			Gallery.infoBox.showInfo();
231
		},
232
233
		/**
234
		 * Lets the user add the shared files to his cloud
235
		 */
236
		showSaveForm: function () {
237
			$(this).hide();
238
			$('.save-form').css('display', 'inline');
239
			$('#remote_address').focus();
240
		},
241
242
		/**
243
		 * Sends the shared files to the viewer's cloud
244
		 *
245
		 * @param event
246
		 */
247
		saveForm: function (event) {
248
			event.preventDefault();
249
250
			var saveElement = $('#save');
251
			var remote = $(this).find('input[type="text"]').val();
252
			var owner = saveElement.data('owner');
253
			var name = saveElement.data('name');
254
			var isProtected = saveElement.data('protected');
255
			Gallery._saveToServer(remote, Gallery.token, owner, name, isProtected);
256
		},
257
258
		/**
259
		 * Creates a new slideshow using the images found in the current folder
260
		 *
261
		 * @param {Array} images
262
		 * @param {string} startImage
263
		 * @param {boolean} autoPlay
264
		 *
265
		 * @returns {boolean}
266
		 */
267
		slideShow: function (images, startImage, autoPlay) {
268
			if (startImage === undefined) {
269
				OC.Notification.showTemporary(t('gallery',
270
					'Aborting preview. Could not find the file'));
271
				return false;
272
			}
273
			var start = images.indexOf(startImage);
274
			images = images.filter(function (image, index) {
275
				// If the slideshow is loaded before we get a thumbnail, we have to accept all
276
				// images
277
				if (!image.thumbnail) {
278
					return image;
279
				} else {
280
					if (image.thumbnail.valid) {
281
						return image;
282
					} else if (index < images.indexOf(startImage)) {
283
						start--;
284
					}
285
				}
286
			}).map(function (image) {
287
				var name = OC.basename(image.path);
288
				var previewUrl = Gallery.utility.getPreviewUrl(image.fileId, image.etag);
289
				var params = {
290
					c: image.etag,
291
					requesttoken: oc_requesttoken
0 ignored issues
show
Identifier 'oc_requesttoken' is not in camel case.
Loading history...
oc_requesttoken does not seem to be defined.
Loading history...
292
				};
293
				var downloadUrl = Gallery.utility.buildGalleryUrl('files',
294
					'/download/' + image.fileId,
295
					params);
296
297
				return {
298
					name: name,
299
					path: image.path,
300
					file: image.fileId,
301
					mimeType: image.mimeType,
302
					permissions: image.permissions,
303
					url: previewUrl,
304
					downloadUrl: downloadUrl
305
				};
306
			});
307
			Gallery.activeSlideShow.setImages(images, autoPlay);
308
			Gallery.activeSlideShow.onStop = function () {
309
				$('#content').show();
310
				Gallery.view.removeLoading();
311
				if (Gallery.currentAlbum !== '') {
312
					// Only modern browsers can manipulate history
313
					if (history && history.replaceState) {
314
						history.replaceState('', '',
315
							'#' + encodeURIComponent(Gallery.currentAlbum));
316
					} else {
317
						location.hash = '#' + encodeURIComponent(Gallery.currentAlbum);
318
					}
319
				} else {
320
					// Only modern browsers can manipulate history
321
					if (history && history.replaceState) {
322
						history.replaceState('', '', '#');
323
					} else {
324
						location.hash = '#';
325
					}
326
				}
327
			};
328
			Gallery.activeSlideShow.show(start);
329
			if(!_.isUndefined(Gallery.Share)){
330
				Gallery.Share.hideDropDown();
331
			}
332
			$('.album-info-container').slideUp();
333
			// Resets the last focused element
334
			document.activeElement.blur();
335
		},
336
337
		/**
338
		 * Moves files and albums to a new location
339
		 *
340
		 * @param {jQuery} $item
341
		 * @param {string} fileName
342
		 * @param {string} filePath
343
		 * @param {jQuery} $target
344
		 * @param {string} targetPath
345
		 */
346
		move: function ($item, fileName, filePath, $target, targetPath) {
347
			var self = this;
348
			var dir = Gallery.currentAlbum;
349
350
			if (targetPath.charAt(targetPath.length - 1) !== '/') {
351
				// make sure we move the files into the target dir,
352
				// not overwrite it
353
				targetPath = targetPath + '/';
354
			}
355
			self.filesClient.move(dir + '/' + fileName, targetPath + fileName)
356
				.done(function () {
357
					self._removeElement(dir, filePath, $item);
358
				})
359
				.fail(function (status) {
360
					if (status === 412) {
361
						// TODO: some day here we should invoke the conflict dialog
362
						OC.Notification.showTemporary(
363
							t('gallery', 'Could not move "{file}", target exists', {file: fileName})
364
						);
365
					} else {
366
						OC.Notification.showTemporary(
367
							t('gallery', 'Could not move "{file}"', {file: fileName})
368
						);
369
					}
370
					$item.fadeTo("normal", 1);
371
					$target.children('.album-loader').hide();
372
				})
373
				.always(function () {
374
					// Nothing?
375
				});
376
		},
377
378
		/**
379
		 * Builds the album's model
380
		 *
381
		 * @param {{
382
		 * 	files:Array,
383
		 * 	albums:Array,
384
		 * 	albumconfig:Object,
385
		 * 	albumpath:String,
386
		 *	updated:Boolean
387
		 * 	}} data
388
		 * @private
389
		 */
390
		_mapFiles: function (data) {
391
			Gallery.imageMap = {};
392
			var image = null;
393
			var path = null;
394
			var fileId = null;
395
			var mimeType = null;
396
			var mTime = null;
397
			var etag = null;
398
			var size = null;
399
			var sharedWithUser = null;
400
			var owner = null;
401
			var permissions = 0;
402
			var currentLocation = data.albumpath;
403
			// This adds a new node to the map for each parent album
404
			Gallery._mapStructure(currentLocation);
405
			var files = data.files;
406
			if (files.length > 0) {
407
				var subAlbumCache = {};
408
				var albumCache = Gallery.albumMap[currentLocation]
409
					= new Album(
410
					currentLocation,
411
					[],
412
					[],
413
					OC.basename(currentLocation),
414
					data.albums[currentLocation].nodeid,
415
					data.albums[currentLocation].mtime,
416
					data.albums[currentLocation].etag,
417
					data.albums[currentLocation].size,
418
					data.albums[currentLocation].sharedwithuser,
419
					data.albums[currentLocation].owner,
420
					data.albums[currentLocation].freespace,
421
					data.albums[currentLocation].permissions
422
				);
423
				for (var i = 0; i < files.length; i++) {
424
					path = files[i].path;
425
					fileId = files[i].nodeid;
426
					mimeType = files[i].mimetype;
427
					mTime = files[i].mtime;
428
					etag = files[i].etag;
429
					size = files[i].size;
430
					sharedWithUser = files[i].sharedwithuser;
431
					owner = files[i].owner;
432
					permissions = files[i].permissions;
433
434
					image =
435
						new GalleryImage(
436
							path, path, fileId, mimeType, mTime, etag, size, sharedWithUser, owner, permissions
437
						);
438
439
					// Determines the folder name for the image
440
					var dir = OC.dirname(path);
441
					if (dir === path) {
442
						dir = '';
443
					}
444
					if (dir === currentLocation) {
445
						// The image belongs to the current album, so we can add it directly
446
						albumCache.images.push(image);
447
					} else {
448
						// The image belongs to a sub-album, so we create a sub-album cache if it
449
						// doesn't exist and add images to it
450
						if (!subAlbumCache[dir]) {
451
							subAlbumCache[dir] = new Album(
452
								dir,
453
								[],
454
								[],
455
								OC.basename(dir),
456
								data.albums[dir].nodeid,
457
								data.albums[dir].mtime,
458
								data.albums[dir].etag,
459
								data.albums[dir].size,
460
								data.albums[dir].sharedwithuser,
461
								data.albums[currentLocation].owner,
462
								data.albums[currentLocation].freespace,
463
								data.albums[dir].permissions);
464
						}
465
						subAlbumCache[dir].images.push(image);
466
						// The sub-album also has to be added to the global map
467
						if (!Gallery.albumMap[dir]) {
468
							Gallery.albumMap[dir] = {};
469
						}
470
					}
471
					Gallery.imageMap[image.path] = image;
472
				}
473
				// Adds the sub-albums to the current album
474
				Gallery._mapAlbums(albumCache, subAlbumCache);
475
476
				// Caches the information which is not already cached
477
				albumCache.etag = data.albums[currentLocation].etag;
478
				albumCache.imageMap = Gallery.imageMap;
479
			}
480
		},
481
482
		/**
483
		 * Adds every album leading to the current folder to a global album map
484
		 *
485
		 * Per example, if you have Root/Folder1/Folder2/CurrentFolder then the map will contain:
486
		 *    * Root
487
		 *    * Folder1
488
		 *    * Folder2
489
		 *    * CurrentFolder
490
		 *
491
		 *  Every time a new location is loaded, the map is completed
492
		 *
493
		 *
494
		 * @param {string} path
495
		 *
496
		 * @returns {Album}
497
		 * @private
498
		 */
499
		_mapStructure: function (path) {
500
			if (!Gallery.albumMap[path]) {
501
				Gallery.albumMap[path] = {};
502
				// Builds relationships between albums
503
				if (path !== '') {
504
					var parent = OC.dirname(path);
505
					if (parent === path) {
506
						parent = '';
507
					}
508
					Gallery._mapStructure(parent);
509
				}
510
			}
511
			return Gallery.albumMap[path];
512
		},
513
514
		/**
515
		 * Adds the sub-albums to the current album
516
		 *
517
		 * @param {Album} albumCache
518
		 * @param {{Album}} subAlbumCache
519
		 * @private
520
		 */
521
		_mapAlbums: function (albumCache, subAlbumCache) {
522
			for (var j = 0, keys = Object.keys(subAlbumCache); j <
523
			keys.length; j++) {
524
				albumCache.subAlbums.push(subAlbumCache[keys[j]]);
525
			}
526
		},
527
528
		/**
529
		 * Saves the folder to a remote server
530
		 *
531
		 * Our location is the remote for the other server
532
		 *
533
		 * @param {string} remote
534
		 * @param {string}token
535
		 * @param {string}owner
536
		 * @param {string}name
537
		 * @param {boolean} isProtected
538
		 * @private
539
		 */
540
		_saveToServer: function (remote, token, owner, name, isProtected) {
541
			var location = window.location.protocol + '//' + window.location.host + OC.webroot;
542
			var isProtectedInt = (isProtected) ? 1 : 0;
543
			var url = remote + '/index.php/apps/files#' + 'remote=' + encodeURIComponent(location)
544
				+ "&token=" + encodeURIComponent(token) + "&owner=" + encodeURIComponent(owner) +
545
				"&name=" +
546
				encodeURIComponent(name) + "&protected=" + isProtectedInt;
547
548
			if (remote.indexOf('://') > 0) {
549
				OC.redirect(url);
550
			} else {
551
				// if no protocol is specified, we automatically detect it by testing https and
552
				// http
553
				// this check needs to happen on the server due to the Content Security Policy
554
				// directive
555
				$.get(OC.generateUrl('apps/files_sharing/testremote'),
556
					{remote: remote}).then(function (protocol) {
557
					if (protocol !== 'http' && protocol !== 'https') {
558
						OC.dialogs.alert(t('files_sharing',
559
							'No compatible server found at {remote}',
560
							{remote: remote}),
561
							t('files_sharing', 'Invalid server url'));
562
					} else {
563
						OC.redirect(protocol + '://' + url);
564
					}
565
				});
566
			}
567
		},
568
569
		/**
570
		 * Removes the moved element from the UI and refreshes the view
571
		 *
572
		 * @param {string} dir
573
		 * @param {string}filePath
574
		 * @param {jQuery} $item
575
		 * @private
576
		 */
577
		_removeElement: function (dir, filePath, $item) {
578
			var images = Gallery.albumMap[Gallery.currentAlbum].images;
579
			var albums = Gallery.albumMap[Gallery.currentAlbum].subAlbums;
580
			// if still viewing the same directory
581
			if (Gallery.currentAlbum === dir) {
582
				var removed = false;
583
				// We try to see if an image was removed
584
				var movedImage = _(images).findIndex({path: filePath});
585
				if (movedImage >= 0) {
586
					images.splice(movedImage, 1);
587
					removed = true;
588
				} else {
589
					// It wasn't an image, so try to remove an album
590
					var movedAlbum = _(albums).findIndex({path: filePath});
591
					if (movedAlbum >= 0) {
592
						albums.splice(movedAlbum, 1);
593
						removed = true;
594
					}
595
				}
596
597
				if (removed) {
598
					$item.remove();
599
					// Refresh the photowall without checking if new files have arrived in the
600
					// current album
601
					// TODO On the next visit this album is going to be reloaded, unless we can get
602
					// an etag back from the move endpoint
603
					Gallery.view.init(Gallery.currentAlbum);
604
				}
605
			}
606
		}
607
	};
608
	window.Gallery = Gallery;
609
})(jQuery, OC, t);
610