Issues (493)

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.

lib/controller/addressbookcontroller.php (4 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
<?php
2
/**
3
 * @author Thomas Tanghus
4
 * @copyright 2013-2014 Thomas Tanghus ([email protected])
5
 * This file is licensed under the Affero General Public License version 3 or
6
 * later.
7
 * See the COPYING-README file.
8
 */
9
10
namespace OCA\Contacts\Controller;
11
12
use OCA\Contacts\App,
13
	OCA\Contacts\JSONResponse,
14
	OCA\Contacts\Utils\JSONSerializer,
15
	OCA\Contacts\Controller,
16
	OCP\AppFramework\Http,
17
	OCP\IRequest;
18
19
/**
20
 * Controller class For Address Books
21
 */
22
class AddressBookController extends Controller {
23
24
	/** @var string */
25
	protected $userId;
26
27
	public function __construct($appName, IRequest $request, App $app, $userId) {
28
		parent::__construct($appName, $request, $app);
29
		$this->userId = $userId;
30
	}
31
32
	/**
33
	 * @NoAdminRequired
34
	 * @NoCSRFRequired
35
	 */
36
	public function userAddressBooks() {
37
		$addressBooks = $this->app->getAddressBooksForUser();
38
		$result = array();
39
		$lastModified = 0;
40
41
		foreach($addressBooks as $addressBook) {
42
43
			$data = $addressBook->getMetaData();
44
			$result[] = $data;
45
46
			if (!is_null($data['lastmodified'])) {
47
				$lastModified = max($lastModified, $data['lastmodified']);
48
			}
49
50
		}
51
52
		// To avoid invalid cache deletion time is saved
53
		/*$lastModified = max(
54
			$lastModified,
55
			\OCP\Config::getUserValue($this->userId, 'contacts', 'last_address_book_deleted', 0)
56
		);*/
57
58
		$response = new JSONResponse(array(
59
			'addressbooks' => $result,
60
		));
61
62
		/** FIXME: Caching is currently disabled
63
		if($lastModified > 0) {
64
			$response->setLastModified(\DateTime::createFromFormat('U', $lastModified) ?: null);
65
			$response->setETag(md5($lastModified));
66
		}
67
		*/
68
69
		return $response;
70
	}
71
72
	/**
73
	 * @NoAdminRequired
74
	 * @NoCSRFRequired
75
	 */
76
	public function getAddressBook() {
77
		$params = $this->request->urlParams;
78
79
		$addressBook = $this->app->getAddressBook($params['backend'], $params['addressBookId']);
80
		$lastModified = $addressBook->lastModified();
81
		$response = new JSONResponse();
82
		$response->setData(array('data' => $addressBook->getMetaData()));
83
84 View Code Duplication
		if (!is_null($lastModified)) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
85
			$response->addHeader('Cache-Control', 'private, must-revalidate');
86
			$response->setLastModified(\DateTime::createFromFormat('U', $lastModified) ?: null);
87
			$etag = md5($lastModified);
88
			$response->setETag($etag);
89
		}
90
91
		return $response;
92
	}
93
94
	/**
95
	 * @NoAdminRequired
96
	 * @NoCSRFRequired
97
	 */
98
	public function getContacts() {
99
		$params = $this->request->urlParams;
100
101
		$addressBook = $this->app->getAddressBook($params['backend'], $params['addressBookId']);
102
		$lastModified = $addressBook->lastModified();
103
		$etag = null;
104
		$response = new JSONResponse();
105
106 View Code Duplication
		if (!is_null($lastModified)) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
107
			//$response->addHeader('Cache-Control', 'private, must-revalidate');
108
			$response->setLastModified(\DateTime::createFromFormat('U', $lastModified) ?: null);
109
			$etag = md5($lastModified);
110
			$response->setETag($etag);
111
		}
112
113
		if (!is_null($etag)
114
			&& $this->request->getHeader('If-None-Match') === '"'.$etag.'"')
115
		{
116
			return $response->setStatus(Http::STATUS_NOT_MODIFIED);
117
		} else {
118
			switch ($this->request->method) {
119
				case 'OPTIONS':
120
					$options = array('GET', 'HEAD', 'OPTIONS');
121
					if ($addressBook->hasPermission(\OCP\PERMISSION_DELETE)
122
						&& $addressBook->getBackend()->hasAddressBookMethodFor(\OCP\PERMISSION_DELETE))
123
					{
124
						$options[] = 'DELETE';
125
					}
126
					if ($addressBook->hasPermission(\OCP\PERMISSION_UPDATE)
127
						&& $addressBook->getBackend()->hasAddressBookMethodFor(\OCP\PERMISSION_UPDATE))
128
					{
129
						$options[] = 'POST';
130
					}
131
					$response->addHeader('Allow' , implode(',', $options));
132
					return $response;
133
				case 'HEAD':
134
					return $response;
135
				case 'GET':
136
					$contacts = array();
137
138
					foreach ($addressBook->getChildren() as $i => $contact) {
139
						$result = JSONSerializer::serializeContact($contact);
140
						if ($result !== null) {
141
							$contacts[] = $result;
142
						}
143
					}
144
145
					return $response->setData(array('contacts' => $contacts));
146
			}
147
		}
148
	}
149
150
	/**
151
	 * @NoAdminRequired
152
	 */
153
	public function addAddressBook() {
154
		$params = $this->request->urlParams;
155
156
		$response = new JSONResponse();
157
158
		$backend = $this->app->getBackend($params['backend']);
159
160
		if (!$backend->hasAddressBookMethodFor(\OCP\PERMISSION_CREATE)) {
161
			throw new \Exception('This backend does not support adding address books', 501);
162
		}
163
164
		try {
165
			$id = $backend->createAddressBook($this->request->post);
166
		} catch(\Exception $e) {
167
			return $response->bailOut($e->getMessage());
168
		}
169
170
		if ($id === false) {
171
			return $response->bailOut(App::$l10n->t('Error creating address book'));
172
		}
173
174
		return $response->setStatus('201')->setParams($backend->getAddressBook($id));
175
	}
176
177
	/**
178
	 * @NoAdminRequired
179
	 */
180 View Code Duplication
	public function updateAddressBook() {
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
181
		$params = $this->request->urlParams;
182
183
		$response = new JSONResponse();
184
185
		$addressBook = $this->app->getAddressBook($params['backend'], $params['addressBookId']);
186
		$addressBook->update($this->request['properties']);
187
188
		return $response->setParams($addressBook->getMetaData());
189
	}
190
191
	/**
192
	 * @NoAdminRequired
193
	 */
194
	public function deleteAddressBook() {
195
		$params = $this->request->urlParams;
196
197
		$response = new JSONResponse();
198
199
		$backend = $this->app->getBackend($params['backend']);
200
201
		if (!$backend->hasAddressBookMethodFor(\OCP\PERMISSION_DELETE)) {
202
			throw new \Exception(App::$l10n->t(
203
				'The "%s" backend does not support deleting address books', array($backend->name)
204
			), 501);
205
		}
206
207
		$addressBookInfo = $backend->getAddressBook($params['addressBookId']);
208
209
		if (!$addressBookInfo['permissions'] & \OCP\PERMISSION_DELETE) {
210
			throw new \Exception(App::$l10n->t(
211
				'You do not have permissions to delete the "%s" address book',
212
				array($addressBookInfo['displayname'])
213
			), 403);
214
		}
215
216
		if (!$backend->deleteAddressBook($params['addressBookId'])) {
217
			throw new \Exception(App::$l10n->t(
218
				'Error deleting address book'
219
			), 500);
220
		}
221
222
		\OCP\Config::setUserValue($this->userId, 'contacts', 'last_address_book_deleted', time());
223
		return $response;
224
	}
225
226
	/**
227
	 * @NoAdminRequired
228
	 */
229 View Code Duplication
	public function activateAddressBook() {
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
230
		$params = $this->request->urlParams;
231
232
		$response = new JSONResponse();
233
234
		$addressBook = $this->app->getAddressBook($params['backend'], $params['addressBookId']);
235
236
		$addressBook->setActive($this->request->post['state']);
237
238
		return $response;
239
	}
240
241
	/**
242
	 * @NoAdminRequired
243
	 */
244
	public function addChild() {
245
		$params = $this->request->urlParams;
246
247
		$response = new JSONResponse();
248
249
		$addressBook = $this->app->getAddressBook($params['backend'], $params['addressBookId']);
250
251
		try {
252
			$id = $addressBook->addChild();
253
		} catch(\Exception $e) {
254
			return $response->bailOut($e->getMessage());
255
		}
256
257
		if ($id === false) {
258
			return $response->bailOut(App::$l10n->t('Error creating contact.'));
259
		}
260
261
		$contact = $addressBook->getChild($id);
262
263
		$serialized = JSONSerializer::serializeContact($contact);
264
265
		if (is_null($serialized)) {
266
			throw new \Exception(App::$l10n->t(
267
				'Error creating contact'
268
			));
269
		}
270
271
		$response->setStatus('201')->setETag($contact->getETag());
272
		$response->addHeader('Location',
273
			\OCP\Util::linkToRoute(
274
				'contacts_contact_get',
275
				array(
276
					'backend' => $params['backend'],
277
					'addressBookId' => $params['addressBookId'],
278
					'contactId' => $id
279
				)
280
			)
281
		);
282
		return $response->setParams($serialized);
283
	}
284
285
	/**
286
	 * @NoAdminRequired
287
	 */
288
	public function deleteChild() {
289
		$params = $this->request->urlParams;
290
291
		$response = new JSONResponse();
292
293
		$addressBook = $this->app->getAddressBook($params['backend'], $params['addressBookId']);
294
295
		$result = $addressBook->deleteChild($params['contactId']);
296
297
		if ($result === false) {
298
			throw new \Exception(App::$l10n->t(
299
				'Error deleting contact'
300
			), 500);
301
		}
302
303
		return $response->setStatus('204');
304
	}
305
306
	/**
307
	 * @NoAdminRequired
308
	 */
309
	public function deleteChildren() {
310
		$params = $this->request->urlParams;
311
312
		$response = new JSONResponse();
313
314
		$addressBook = $this->app->getAddressBook($params['backend'], $params['addressBookId']);
315
		$contacts = $this->request->post['contacts'];
316
317
		$result = $addressBook->deleteChildren($contacts);
318
319
		return $response->setParams(array('result' => $result));
320
	}
321
322
	/**
323
	 * @NoAdminRequired
324
	 */
325
	public function moveChild() {
326
		$params = $this->request->urlParams;
327
		$targetInfo = $this->request->post['target'];
328
329
		$response = new JSONResponse();
330
331
		// TODO: Check if the backend supports move (is 'local' or 'shared') and use that operation instead.
332
		// If so, set status 204 and don't return the serialized contact.
333
		$fromAddressBook = $this->app->getAddressBook($params['backend'], $params['addressBookId']);
334
		$targetAddressBook = $this->app->getAddressBook($targetInfo['backend'], $targetInfo['id']);
335
		$contact = $fromAddressBook->getChild($params['contactId']);
336
337
		if (!$contact) {
338
			throw new \Exception(App::$l10n->t(
339
				'Error retrieving contact'
340
			), 500);
341
		}
342
343
		$contactId = $targetAddressBook->addChild($contact);
344
345
		// Retrieve the contact again to be sure it's in sync
346
		$contact = $targetAddressBook->getChild($contactId);
347
348
		if (!$contact) {
349
			throw new \Exception(App::$l10n->t(
350
				'Error saving contact'
351
			), 500);
352
		}
353
354
		if (!$fromAddressBook->deleteChild($params['contactId'])) {
355
			// Don't bail out because we have to return the contact
356
			return $response->debug(App::$l10n->t('Error removing contact from other address book.'));
357
		}
358
359
		$serialized = JSONSerializer::serializeContact($contact);
360
361
		if (is_null($serialized)) {
362
			throw new \Exception(App::$l10n->t(
363
				'Error getting moved contact'
364
			));
365
		}
366
367
		return $response->setParams($serialized);
368
	}
369
370
}
371
372