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/vobject/vcard.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
 * ownCloud - VCard component
4
 *
5
 * This component represents the BEGIN:VCARD and END:VCARD found in every
6
 * vcard.
7
 *
8
 * @author Thomas Tanghus
9
 * @author Evert Pot (http://www.rooftopsolutions.nl/)
10
 * @copyright 2013-2014 Thomas Tanghus ([email protected])
11
 *
12
 * This library is free software; you can redistribute it and/or
13
 * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
14
 * License as published by the Free Software Foundation; either
15
 * version 3 of the License, or any later version.
16
 *
17
 * This library is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public
23
 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
24
 *
25
 */
26
27
namespace OCA\Contacts\VObject;
28
29
use OCA\Contacts\Utils;
30
use Sabre\VObject;
31
32
/**
33
 * This class overrides \Sabre\VObject\Component\VCard::validate() to be add
34
 * to import partially invalid vCards by ignoring invalid lines and to
35
 * validate and upgrade using ....
36
 *
37
 * Satisfy PHP Analyzer:
38
 * @property \OC\VObject\CompoundProperty N
39
 * @property \OC\VObject\CompoundProperty ORG
40
 * @property \OC\VObject\CompoundProperty ADR
41
 * @property \OCA\Contacts\VObject\GroupProperty CATEGORIES
42
 * @property \Sabre\VObject\Property\Text FN
43
 * @property \Sabre\VObject\Property\Text EMAIL
44
 * @property \Sabre\VObject\Property\Text VERSION
45
 * @property \Sabre\VObject\Property\Text BDAY
46
 * @property \Sabre\VObject\Property\Text UID
47
 * @property \Sabre\VObject\Property\Text REV
48
 * @property \Sabre\VObject\Property\Binary PHOTO
49
 * @property \Sabre\VObject\Property\Binary LOGO
50
 * @property \Sabre\VObject\Property\FlatText PRODID
51
*/
52
class VCard extends VObject\Component\VCard {
53
54
	/**
55
	* The following constants are used by the validate() method.
56
	*/
57
    const REPAIR = 1;
0 ignored issues
show
Tabs must be used to indent lines; spaces are not allowed
Loading history...
58
	const UPGRADE = 2;
59
60
	/**
61
	 * The groups in the contained properties
62
	 *
63
	 * @var array
64
	 */
65
	protected $groups = array();
66
67
	/**
68
	* VCards with version 2.1, 3.0 and 4.0 are found.
69
	*
70
	* If the VCARD doesn't know its version, 3.0 is assumed and if
71
	* option UPGRADE is given it will be upgraded to version 3.0.
72
	*/
73
	const DEFAULT_VERSION = '3.0';
74
75
	/**
76
	* The vCard 2.1 specification allows parameter values without a name.
77
	* The parameter name is then determined from the unique parameter value.
78
	* In version 2.1 e.g. a phone can be formatted like: TEL;HOME;CELL:123456789
79
	* This has to be changed to either TEL;TYPE=HOME,CELL:123456789 or TEL;TYPE=HOME;TYPE=CELL:123456789 - both are valid.
80
	*
81
	* From: https://github.com/barnabywalters/vcard/blob/master/barnabywalters/VCard/VCard.php
82
	*
83
	* @param string value
84
	* @return string
85
	*/
86
	protected function paramName($value) {
87
		static $types = array (
88
				'DOM', 'INTL', 'POSTAL', 'PARCEL','HOME', 'WORK',
89
				'PREF', 'VOICE', 'FAX', 'MSG', 'CELL', 'PAGER',
90
				'BBS', 'MODEM', 'CAR', 'ISDN', 'VIDEO',
91
				'AOL', 'APPLELINK', 'ATTMAIL', 'CIS', 'EWORLD',
92
				'INTERNET', 'IBMMAIL', 'MCIMAIL',
93
				'POWERSHARE', 'PRODIGY', 'TLX', 'X400',
94
				'GIF', 'CGM', 'WMF', 'BMP', 'MET', 'PMB', 'DIB',
95
				'PICT', 'TIFF', 'PDF', 'PS', 'JPEG', 'QTIME',
96
				'MPEG', 'MPEG2', 'AVI',
97
				'WAVE', 'AIFF', 'PCM',
98
				'X509', 'PGP');
99
		static $values = array (
100
				'INLINE', 'URL', 'CID');
101
		static $encodings = array (
102
				'7BIT', 'QUOTED-PRINTABLE', 'BASE64');
103
		$name = 'UNKNOWN';
104
		if (in_array($value, $types)) {
105
			$name = 'TYPE';
106
		} elseif (in_array($value, $values)) {
107
			$name = 'VALUE';
108
		} elseif (in_array($value, $encodings)) {
109
			$name = 'ENCODING';
110
		}
111
		return $name;
112
	}
113
114
	/**
115
	* Decode properties for upgrading from v. 2.1
116
	*
117
	* @param \Sabre\VObject\Property $property Reference to a \Sabre\VObject\Property.
118
	* The only encoding allowed in version 3.0 is 'b' for binary. All encoded strings
119
	* must therefore be decoded and the parameters removed.
120
	*/
121
	protected function decodeProperty(&$property) {
122
		foreach($property->parameters as $key=>&$parameter) {
123
			// Check for values without names which Sabre interprets
124
			// as names without values.
125
			if(trim($parameter->getValue()) === '') {
126
				$parameter->setValue($parameter->name);
127
				$parameter->name = $this->paramName($parameter->name);
128
			}
129
			// Check out for encoded string and decode them :-[
130
			if(strtoupper($parameter->name) == 'ENCODING') {
131
				if(strtoupper($parameter->getValue()) == 'QUOTED-PRINTABLE') {
132
					$property->setValue(str_replace(
133
						"\r\n", "\n",
134
						VObject\StringUtil::convertToUTF8(
135
							quoted_printable_decode($property->getValue())
0 ignored issues
show
The method getValue() does not seem to exist on object<Sabre\VObject\Property>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
136
						)
137
					));
138
					unset($property->parameters[$key]);
139
				} elseif(strtoupper($parameter->getValue()) == 'BASE64') {
140
					$parameter->setValue('b');
141
				}
142
			} elseif(strtoupper($parameter->name) == 'CHARSET') {
143
				unset($property->parameters[$key]);
144
			}
145
		}
146
	}
147
148
	/**
149
	* Work around issue in older VObject sersions
150
	* https://github.com/fruux/sabre-vobject/issues/24
151
	*
152
	* @param \Sabre\VObject\Property $property Reference to a \Sabre\VObject\Property.
153
	*/
154
	public function fixPropertyParameters(&$property) {
155
		// Work around issue in older VObject sersions
156
		// https://github.com/fruux/sabre-vobject/issues/24
157
		foreach($property->parameters as $key=>$parameter) {
158
			if(strpos($parameter->getValue(), ',') === false) {
159
				continue;
160
			}
161
			$values = explode(',', $parameter->getValue());
162
			$values = array_map('trim', $values);
163
			$parameter->setValue(array_shift($values));
164
			foreach($values as $value) {
165
				$property->add($parameter->name, $value);
166
			}
167
		}
168
	}
169
170
	/**
171
	* Validates the node for correctness.
172
	*
173
	* The following options are supported:
174
	*   - VCard::REPAIR - If something is broken, and automatic repair may
175
	*                    be attempted.
176
	*   - VCard::UPGRADE - If needed the vCard will be upgraded to version 3.0.
177
	*
178
	* An array is returned with warnings.
179
	*
180
	* Every item in the array has the following properties:
181
	*    * level - (number between 1 and 3 with severity information)
182
	*    * message - (human readable message)
183
	*    * node - (reference to the offending node)
184
	*
185
	* @param int $options
186
	* @return array
187
	*/
188 4
	public function validate($options = 0) {
189
190 4
		$warnings = array();
191 4
		$repaired = false;
192
193 4
		$version = $this->select('VERSION');
194 4
		if (count($version) !== 1) {
195
			$warnings[] = array(
196
				'level' => 1,
197
				'message' => 'The VERSION property must appear in the VCARD component exactly 1 time',
198
				'node' => $this,
199
			);
200
			if ($options & self::REPAIR) {
201
				if (count($version) > 1) {
202
					$version = (string) current($version);
203
					$this->remove('VERSION');
0 ignored issues
show
The method remove() does not exist on OCA\Contacts\VObject\VCard. Did you maybe mean removeFromGroup()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
204
					$this->VERSION = $version;
205
				} else {
206
					$this->VERSION = self::DEFAULT_VERSION;
207
				}
208
				$repaired = true;
209
			}
210
		} 
211
212 4
		$version = (string)$this->VERSION;
213 4
		if ($version!=='2.1' && $version!=='3.0' && $version!=='4.0') {
214
			$warnings[] = array(
215
				'level' => 1,
216 1
				'message' => 'Only vcard version 4.0 (RFC6350), version 3.0 (RFC2426) or version 2.1 (icm-vcard-2.1) are supported.',
217
				'node' => $this,
218
			);
219 4
			if ($options & self::REPAIR) {
220
				$this->VERSION = self::DEFAULT_VERSION;
221
				$repaired = true;
222
			}
223
		}
224
225
		# upgrade 2.1 cards to 3.0 cards.
226 4
		if ($options & self::UPGRADE && $version === '2.1') {
227
			$this->VERSION = self::DEFAULT_VERSION;
228
			$repaired = true;
229
			foreach($this->children as $idx => &$property) {
230
231
				$this->decodeProperty($property);
232
				$this->fixPropertyParameters($property);
233
				/* What exactly was I thinking here?
234
				switch((string)$property->name) {
235
					case 'LOGO':
236
					case 'SOUND':
237
					case 'PHOTO':
238
						if(isset($property['TYPE']) && strpos((string)$property['TYPE'], '/') === false) {
239
							$property['TYPE'] = 'image/' . strtolower($property['TYPE']);
240
						}
241
				}*/
242
			}
243
		}
244
245 4
		$fn = $this->select('FN');
246 4
		if (count($fn) !== 1 || trim((string)$this->FN) === '') {
247
			$warnings[] = array(
248
				'level' => 1,
249
				'message' => 'The FN property must appear in the VCARD component exactly 1 time',
250
				'node' => $this,
251
			);
252
			if ($options & self::REPAIR) {
253
				// We're going to try to see if we can use the contents of the
254
				// N property.
255
				$repaired = true;
256
				if (isset($this->N)
257
					&& substr((string)$this->N, 2) !== ';;'
258
					&& (string)$this->N !== ''
259
				) {
260
					$value = explode(';', (string)$this->N);
261
					if (isset($value[1]) && $value[1]) {
262
						$this->FN = $value[1] . ' ' . $value[0];
263
					} else {
264
						$this->FN = $value[0];
265
					}
266
				// Otherwise, the ORG property may work
267
				} elseif (isset($this->ORG)) {
268
					$this->FN = (string)$this->ORG;
269
				} elseif (isset($this->EMAIL)) {
270
					$this->FN = (string)$this->EMAIL;
271
				}
272
273
			}
274
		}
275
276 4
		if(isset($this->BDAY)) {
277
			if ($options & self::REPAIR) {
278
				// If the BDAY has a format of e.g. 19960401
279
				$bday = (string)$this->BDAY;
280
				if(strlen($bday) >= 8
281
					&& is_int(substr($bday, 0, 4))
282
					&& is_int(substr($bday, 4, 2))
283
					&& is_int(substr($bday, 6, 2))) {
284
					$this->BDAY = substr($bday, 0, 4).'-'.substr($bday, 4, 2).'-'.substr($bday, 6, 2);
285
					$this->BDAY->VALUE = 'DATE';
286
					$repaired = true;
287
				} else if($bday[5] !== '-' || $bday[7] !== '-') {
288
					try {
289
						// Skype exports as e.g. Jan 14, 1996
290
						$date = new \DateTime($bday);
291
						$this->BDAY = $date->format('Y-m-d');
292
						$this->BDAY->VALUE = 'DATE';
293
						$repaired = true;
294
					} catch(\Exception $e) {
295
						\OCP\Util::writeLog('contacts', __METHOD__.' Removing invalid BDAY: ' . $bday, \OCP\Util::DEBUG);
296
						unset($this->BDAY);
297
					}
298
				}
299
			}
300
		}
301
302 4
		$n = $this->select('N');
303 4
		if (count($n) !== 1) {
304 4
			$warnings[] = array(
305 4
				'level' => 1,
306 4
				'message' => 'The N property must appear in the VCARD component exactly 1 time',
307 4
				'node' => $this,
308
			);
309
			// TODO: Make a better effort parsing FN.
310 4
			if (($options & self::REPAIR) && count($n) === 0) {
311
				// Take 2 first name parts of 'FN' and reverse.
312 4
				$slice = array_reverse(array_slice(explode(' ', (string)$this->FN), 0, 2));
313 4
				if(count($slice) < 2) { // If not enought, add one more...
314
					$slice[] = "";
315
				}
316 4
				$this->N = $slice;
317 4
				$repaired = true;
318 4
			}
319 4
		}
320
321 4
		if (!isset($this->UID) || trim((string)$this->UID) === '') {
322 1
			$warnings[] = array(
323 1
				'level' => 1,
324 1
				'message' => 'Every vCard must have a UID',
325 1
				'node' => $this,
326
			);
327 1
			if ($options & self::REPAIR) {
328 1
				$this->UID = Utils\Properties::generateUID();
329 1
				$repaired = true;
330 1
			}
331 1
		}
332
333 4
		if ($repaired) {
334 4
			$now = new \DateTime;
335 4
			$this->REV = $now->format(\DateTime::W3C);
336
337 4
			if (count($this->select('PRODID')) > 1) {
338
				$this->remove('PRODID');
0 ignored issues
show
The method remove() does not exist on OCA\Contacts\VObject\VCard. Did you maybe mean removeFromGroup()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
339
			}
340 4
			$appInfo = \OCP\App::getAppInfo('contacts');
341 4
			$appVersion = \OCP\App::getAppVersion('contacts');
342 4
			$this->PRODID = '-//ownCloud//NONSGML '.$appInfo['name'].' '.$appVersion.'//EN';
343 4
		}
344
345 4
		return array_merge(
346 4
			parent::validate($options),
347
			$warnings
348 4
		);
349
350
	}
351
352
	/**
353
	 * Get all group names in the vCards properties
354
	 *
355
	 * NOTE: Not to confuse with CATEGORIES groups
356
	 *
357
	 * @return array
358
	 */
359
	public function propertyGroups() {
360
		foreach($this->children as $property) {
361
			if($property->group && !isset($this->groups[$property->group])) {
362
				$this->groups[] = $property->group;
363
			}
364
		}
365
		if(count($this->groups) > 1) {
366
			sort($this->groups);
367
		}
368
		return $this->groups;
369
	}
370
371
	/**
372
	* Test if vcard has group (CATEGORIES) $name
373
	*
374
	* @param string $name
375
	* @return bool
376
	*/
377
	public function inGroup($name) {
378
		if(!isset($this->CATEGORIES)) {
379
			return false;
380
		}
381
382
		return $this->CATEGORIES->hasGroup($name);
383
	}
384
385
	/**
386
	* Add group (CATEGORIES) $name to vcard
387
	*
388
	* Return true if contact wasn't already in group
389
	*
390
	* @param string $name
391
	* @return bool
392
	*/
393
	public function addToGroup($name) {
394
		if(!isset($this->CATEGORIES)) {
395
			$this->add('CATEGORIES');
396
		}
397
398
		return $this->CATEGORIES->addGroup($name);
399
	}
400
401
	/**
402
	* Remove group (CATEGORIES) $name from vcard
403
	*
404
	* Return true if vcard has been updated.
405
	*
406
	* @param string $name
407
	* @return bool
408
	*/
409
	public function removeFromGroup($name) {
410
411
		if(!isset($this->CATEGORIES)) {
412
			return false;
413
		}
414
415
		$updated = $this->CATEGORIES->removeGroup($name);
416
		// getParts() returns an array with an empty element if
417
		// CATEGORIES is empty
418
		$groups = $this->CATEGORIES->getParts();
419
		// Remove empty elements
420
		$groups = array_filter($groups, 'strlen');
421
		if(count($groups) === 0) {
422
			unset($this->{'CATEGORIES'});
423
			$updated = true;
424
		}
425
426
		return $updated;
427
	}
428
429
430
}
431