Issues (2473)

Branch: master

Security Analysis    no vulnerabilities found

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.

engine/classes/Elgg/AttributeLoader.php (7 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
namespace Elgg;
3
4
/**
5
 * Loads \ElggEntity attributes from DB or validates those passed in via constructor
6
 *
7
 * @access private
8
 *
9
 * @package    Elgg.Core
10
 * @subpackage DataModel
11
 */
12
class AttributeLoader {
13
14
	/**
15
	 * @var array names of attributes in all entities
16
	 *
17
	 * @todo require this to be injected and get it from \ElggEntity
18
	 */
19
	protected static $primary_attr_names = array(
20
		'guid',
21
		'type',
22
		'subtype',
23
		'owner_guid',
24
		'container_guid',
25
		'site_guid',
26
		'access_id',
27
		'time_created',
28
		'time_updated',
29
		'last_action',
30
		'enabled',
31
	);
32
33
	/**
34
	 * @var array names of attributes in all entities that should be stored as integer values
35
	 */
36
	protected static $integer_attr_names = array(
37
		'guid',
38
		'owner_guid',
39
		'container_guid',
40
		'site_guid',
41
		'access_id',
42
		'time_created',
43
		'time_updated',
44
		'last_action',
45
		// \ElggUser
46
		'prev_last_action',
47
		'last_login',
48
		'prev_last_login'
49
	);
50
51
	/**
52
	 * @var array names of attributes in all entities that should be stored as null if empty
53
	 */
54
	protected static $null_attr_names = array(
55
		'name',
56
		'title',
57
		'description',
58
		'url',
59
	);
60
61
	/**
62
	 * @var array names of secondary attributes required for the entity
63
	 */
64
	protected $secondary_attr_names = array();
65
66
	/**
67
	 * @var string entity type (not class) required for fetched primaries
68
	 */
69
	protected $required_type;
70
71
	/**
72
	 * @var array
73
	 */
74
	protected $initialized_attributes;
75
76
	/**
77
	 * @var string class of object being loaded
78
	 */
79
	protected $class;
80
81
	/**
82
	 * @var bool should access control be considered when fetching entity?
83
	 */
84
	public $requires_access_control = true;
85
86
	/**
87
	 * @var callable function used to load attributes from {prefix}entities table
88
	 */
89
	public $primary_loader = 'get_entity_as_row';
90
91
	/**
92
	 * @var callable function used to load attributes from secondary table
93
	 */
94
	public $secondary_loader = '';
95
96
	/**
97
	 * @var callable function used to load all necessary attributes
98
	 */
99
	public $full_loader = '';
100
101
	/**
102
	 * @var array retrieved values that are not attributes
103
	 */
104
	protected $additional_select_values = array();
105
106
	/**
107
	 * Constructor
108
	 *
109
	 * @param string $class             class of object being loaded
110
	 * @param string $required_type     entity type this is being used to populate
111
	 * @param array  $initialized_attrs attributes after initializeAttributes() has been run
112
	 * @throws \InvalidArgumentException
113
	 */
114
	public function __construct($class, $required_type, array $initialized_attrs) {
115
		if (!is_string($class)) {
116
			throw new \InvalidArgumentException('$class must be a class name.');
117
		}
118
		$this->class = $class;
119
120
		if (!is_string($required_type)) {
121
			throw new \InvalidArgumentException('$requiredType must be a system entity type.');
122
		}
123
		$this->required_type = $required_type;
124
125
		$this->initialized_attributes = $initialized_attrs;
126
		$all_attr_names = array_keys($initialized_attrs);
127
		$this->secondary_attr_names = array_diff($all_attr_names, self::$primary_attr_names);
128
	}
129
130
	/**
131
	 * Get primary attributes missing that are missing
132
	 *
133
	 * @param \stdClass $row Database row
134
	 * @return array
135
	 */
136
	protected function isMissingPrimaries($row) {
137
		return array_diff(self::$primary_attr_names, array_keys($row)) !== array();
138
	}
139
140
	/**
141
	 * Get secondary attributes that are missing
142
	 *
143
	 * @param \stdClass $row Database row
144
	 * @return array
145
	 */
146
	protected function isMissingSecondaries($row) {
147
		return array_diff($this->secondary_attr_names, array_keys($row)) !== array();
148
	}
149
150
	/**
151
	 * Check that the type is correct
152
	 *
153
	 * @param \stdClass $row Database row
154
	 * @return void
155
	 * @throws \InvalidClassException
156
	 */
157
	protected function checkType($row) {
158
		if ($row['type'] !== $this->required_type) {
159
			$msg = "GUID:" . $row['guid'] . " is not a valid " . $this->class;
160
			throw new \InvalidClassException($msg);
161
		}
162
	}
163
164
	/**
165
	 * Get values selected from the database that are not attributes
166
	 *
167
	 * @return array
168
	 */
169
	public function getAdditionalSelectValues() {
170
		return $this->additional_select_values;
171
	}
172
	
173
	/**
174
	 * Get all required attributes for the entity, validating any that are passed in. Returns empty array
175
	 * if can't be loaded (Check $failure_reason).
176
	 *
177
	 * This function splits loading between "primary" attributes (those in {prefix}entities table) and
178
	 * "secondary" attributes (e.g. those in {prefix}objects_entity), but can load all at once if a
179
	 * combined loader is available.
180
	 *
181
	 * @param mixed $row a row loaded from DB (array or \stdClass) or a GUID
182
	 * @return array will be empty if failed to load all attributes (access control or entity doesn't exist)
183
	 *
184
	 * @throws \InvalidArgumentException|\LogicException|\IncompleteEntityException
185
	 */
186
	public function getRequiredAttributes($row) {
187
		if (!is_array($row) && !($row instanceof \stdClass)) {
188
			// assume row is the GUID
189
			$row = array('guid' => $row);
190
		}
191
		$row = (array) $row;
192
		if (empty($row['guid'])) {
193
			throw new \InvalidArgumentException('$row must be or contain a GUID');
194
		}
195
196
		$was_missing_primaries = $this->isMissingPrimaries($row);
0 ignored issues
show
$row is of type array<string,?,{"guid":"?"}>, but the function expects a object<stdClass>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
197
		$was_missing_secondaries = $this->isMissingSecondaries($row);
0 ignored issues
show
$row is of type array<string,?,{"guid":"?"}>, but the function expects a object<stdClass>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
198
199
		// some types have a function to load all attributes at once, it should be faster
200
		if (($was_missing_primaries || $was_missing_secondaries) && is_callable($this->full_loader)) {
201
			$fetched = (array) call_user_func($this->full_loader, $row['guid']);
202
			if (!$fetched) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $fetched of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
203
				return array();
204
			}
205
			$row = array_merge($row, $fetched);
206
			$this->checkType($row);
0 ignored issues
show
$row is of type array, but the function expects a object<stdClass>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
207
		} else {
208
			if ($was_missing_primaries) {
209
				if (!is_callable($this->primary_loader)) {
210
					throw new \LogicException('Primary attribute loader must be callable');
211
				}
212
				if ($this->requires_access_control) {
213
					$fetched = (array) call_user_func($this->primary_loader, $row['guid']);
214
				} else {
215
					$ignoring_access = elgg_set_ignore_access();
216
					$fetched = (array) call_user_func($this->primary_loader, $row['guid']);
217
					elgg_set_ignore_access($ignoring_access);
218
				}
219
				if (!$fetched) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $fetched of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
220
					return array();
221
				}
222
				$row = array_merge($row, $fetched);
223
			}
224
225
			// We must test type before trying to load the secondaries so that InvalidClassException
226
			// gets thrown. Otherwise the secondary loader will fail and return false.
227
			$this->checkType($row);
0 ignored issues
show
$row is of type array, but the function expects a object<stdClass>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
228
229
			if ($was_missing_secondaries) {
230
				if (!is_callable($this->secondary_loader)) {
231
					throw new \LogicException('Secondary attribute loader must be callable');
232
				}
233
				$fetched = (array) call_user_func($this->secondary_loader, $row['guid']);
234
				if (!$fetched) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $fetched of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
235
					throw new \IncompleteEntityException("Secondary loader failed to return row for {$row['guid']}");
236
				}
237
				$row = array_merge($row, $fetched);
238
			}
239
		}
240
241
		$row = $this->filterAddedColumns($row);
242
243
		$row['subtype'] = (int)$row['subtype'];
244
245
		// set to null when reading empty value, to match default empty value; See #5456
246
		foreach (self::$null_attr_names as $key) {
247
			if (isset($row[$key]) && !$row[$key]) {
248
				$row[$key] = null;
249
			}
250
		}
251
252
		// Note: If there are still missing attributes, we're running on a 1.7 or earlier schema. We let
253
		// this pass so the upgrades can run.
254
255
		// guid needs to be an int  https://github.com/elgg/elgg/issues/4111
256
		foreach (self::$integer_attr_names as $key) {
257
			if (isset($row[$key])) {
258
				$row[$key] = (int) $row[$key];
259
			}
260
		}
261
		return $row;
262
	}
263
264
	/**
265
	 * Filter non-attribute keys into $this->additional_select_values
266
	 *
267
	 * @param array $row All columns from the query
268
	 * @return array Columns acceptable for the entity's attributes
269
	 */
270
	protected function filterAddedColumns($row) {
271
		// make an array with keys as acceptable attribute names
272
		$acceptable_attrs = self::$primary_attr_names;
273
		array_splice($acceptable_attrs, count($acceptable_attrs), 0, $this->secondary_attr_names);
274
		$acceptable_attrs = array_combine($acceptable_attrs, $acceptable_attrs);
275
276
		foreach ($row as $key => $val) {
277
			if (!isset($acceptable_attrs[$key])) {
278
				$this->additional_select_values[$key] = $val;
279
				unset($row[$key]);
280
			}
281
		}
282
		return $row;
283
	}
284
}
285
286