Issues (4122)

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.

includes/RevisionList.php (2 issues)

Labels
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
<?php
2
/**
3
 * Holders of revision list for a single page
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License along
16
 * with this program; if not, write to the Free Software Foundation, Inc.,
17
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
 * http://www.gnu.org/copyleft/gpl.html
19
 *
20
 * @file
21
 */
22
23
/**
24
 * List for revision table items for a single page
25
 */
26
abstract class RevisionListBase extends ContextSource implements Iterator {
27
	/** @var Title */
28
	public $title;
29
30
	/** @var array */
31
	protected $ids;
32
33
	/** @var ResultWrapper|bool */
34
	protected $res;
35
36
	/** @var bool|object */
37
	protected $current;
38
39
	/**
40
	 * Construct a revision list for a given title
41
	 * @param IContextSource $context
42
	 * @param Title $title
43
	 */
44
	function __construct( IContextSource $context, Title $title ) {
45
		$this->setContext( $context );
46
		$this->title = $title;
47
	}
48
49
	/**
50
	 * Select items only where the ID is any of the specified values
51
	 * @param array $ids
52
	 */
53
	function filterByIds( array $ids ) {
54
		$this->ids = $ids;
55
	}
56
57
	/**
58
	 * Get the internal type name of this list. Equal to the table name.
59
	 * Override this function.
60
	 * @return null
61
	 */
62
	public function getType() {
63
		return null;
64
	}
65
66
	/**
67
	 * Initialise the current iteration pointer
68
	 */
69
	protected function initCurrent() {
70
		$row = $this->res->current();
71
		if ( $row ) {
72
			$this->current = $this->newItem( $row );
73
		} else {
74
			$this->current = false;
75
		}
76
	}
77
78
	/**
79
	 * Start iteration. This must be called before current() or next().
80
	 * @return Revision First list item
81
	 */
82
	public function reset() {
83
		if ( !$this->res ) {
84
			$this->res = $this->doQuery( wfGetDB( DB_REPLICA ) );
0 ignored issues
show
It seems like wfGetDB(DB_REPLICA) can be null; however, doQuery() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
85
		} else {
86
			$this->res->rewind();
87
		}
88
		$this->initCurrent();
89
		return $this->current;
90
	}
91
92
	public function rewind() {
93
		$this->reset();
94
	}
95
96
	/**
97
	 * Get the current list item, or false if we are at the end
98
	 * @return Revision
99
	 */
100
	public function current() {
101
		return $this->current;
102
	}
103
104
	/**
105
	 * Move the iteration pointer to the next list item, and return it.
106
	 * @return Revision
107
	 */
108
	public function next() {
109
		$this->res->next();
110
		$this->initCurrent();
111
		return $this->current;
112
	}
113
114
	public function key() {
115
		return $this->res ? $this->res->key(): 0;
116
	}
117
118
	public function valid() {
119
		return $this->res ? $this->res->valid() : false;
120
	}
121
122
	/**
123
	 * Get the number of items in the list.
124
	 * @return int
125
	 */
126
	public function length() {
127
		if ( !$this->res ) {
128
			return 0;
129
		} else {
130
			return $this->res->numRows();
131
		}
132
	}
133
134
	/**
135
	 * Do the DB query to iterate through the objects.
136
	 * @param IDatabase $db DB object to use for the query
137
	 */
138
	abstract public function doQuery( $db );
139
140
	/**
141
	 * Create an item object from a DB result row
142
	 * @param object $row
143
	 */
144
	abstract public function newItem( $row );
145
}
146
147
/**
148
 * Abstract base class for revision items
149
 */
150
abstract class RevisionItemBase {
151
	/** @var RevisionListBase The parent */
152
	protected $list;
153
154
	/** The database result row */
155
	protected $row;
156
157
	/**
158
	 * @param RevisionListBase $list
159
	 * @param object $row DB result row
160
	 */
161
	public function __construct( $list, $row ) {
162
		$this->list = $list;
163
		$this->row = $row;
164
	}
165
166
	/**
167
	 * Get the DB field name associated with the ID list.
168
	 * Override this function.
169
	 * @return null
170
	 */
171
	public function getIdField() {
172
		return null;
173
	}
174
175
	/**
176
	 * Get the DB field name storing timestamps.
177
	 * Override this function.
178
	 * @return bool
179
	 */
180
	public function getTimestampField() {
181
		return false;
182
	}
183
184
	/**
185
	 * Get the DB field name storing user ids.
186
	 * Override this function.
187
	 * @return bool
188
	 */
189
	public function getAuthorIdField() {
190
		return false;
191
	}
192
193
	/**
194
	 * Get the DB field name storing user names.
195
	 * Override this function.
196
	 * @return bool
197
	 */
198
	public function getAuthorNameField() {
199
		return false;
200
	}
201
202
	/**
203
	 * Get the ID, as it would appear in the ids URL parameter
204
	 * @return int
205
	 */
206
	public function getId() {
207
		$field = $this->getIdField();
0 ignored issues
show
Are you sure the assignment to $field is correct as $this->getIdField() (which targets RevisionItemBase::getIdField()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
208
		return $this->row->$field;
209
	}
210
211
	/**
212
	 * Get the date, formatted in user's language
213
	 * @return string
214
	 */
215
	public function formatDate() {
216
		return $this->list->getLanguage()->userDate( $this->getTimestamp(),
217
			$this->list->getUser() );
218
	}
219
220
	/**
221
	 * Get the time, formatted in user's language
222
	 * @return string
223
	 */
224
	public function formatTime() {
225
		return $this->list->getLanguage()->userTime( $this->getTimestamp(),
226
			$this->list->getUser() );
227
	}
228
229
	/**
230
	 * Get the timestamp in MW 14-char form
231
	 * @return mixed
232
	 */
233
	public function getTimestamp() {
234
		$field = $this->getTimestampField();
235
		return wfTimestamp( TS_MW, $this->row->$field );
236
	}
237
238
	/**
239
	 * Get the author user ID
240
	 * @return int
241
	 */
242
	public function getAuthorId() {
243
		$field = $this->getAuthorIdField();
244
		return intval( $this->row->$field );
245
	}
246
247
	/**
248
	 * Get the author user name
249
	 * @return string
250
	 */
251
	public function getAuthorName() {
252
		$field = $this->getAuthorNameField();
253
		return strval( $this->row->$field );
254
	}
255
256
	/**
257
	 * Returns true if the current user can view the item
258
	 */
259
	abstract public function canView();
260
261
	/**
262
	 * Returns true if the current user can view the item text/file
263
	 */
264
	abstract public function canViewContent();
265
266
	/**
267
	 * Get the HTML of the list item. Should be include "<li></li>" tags.
268
	 * This is used to show the list in HTML form, by the special page.
269
	 */
270
	abstract public function getHTML();
271
}
272
273
class RevisionList extends RevisionListBase {
274
	public function getType() {
275
		return 'revision';
276
	}
277
278
	/**
279
	 * @param IDatabase $db
280
	 * @return mixed
281
	 */
282
	public function doQuery( $db ) {
283
		$conds = [ 'rev_page' => $this->title->getArticleID() ];
284
		if ( $this->ids !== null ) {
285
			$conds['rev_id'] = array_map( 'intval', $this->ids );
286
		}
287
		return $db->select(
288
			[ 'revision', 'page', 'user' ],
289
			array_merge( Revision::selectFields(), Revision::selectUserFields() ),
290
			$conds,
291
			__METHOD__,
292
			[ 'ORDER BY' => 'rev_id DESC' ],
293
			[
294
				'page' => Revision::pageJoinCond(),
295
				'user' => Revision::userJoinCond() ]
296
		);
297
	}
298
299
	public function newItem( $row ) {
300
		return new RevisionItem( $this, $row );
301
	}
302
}
303
304
/**
305
 * Item class for a live revision table row
306
 */
307
class RevisionItem extends RevisionItemBase {
308
	/** @var Revision */
309
	protected $revision;
310
311
	/** @var RequestContext */
312
	protected $context;
313
314
	public function __construct( $list, $row ) {
315
		parent::__construct( $list, $row );
316
		$this->revision = new Revision( $row );
317
		$this->context = $list->getContext();
318
	}
319
320
	public function getIdField() {
321
		return 'rev_id';
322
	}
323
324
	public function getTimestampField() {
325
		return 'rev_timestamp';
326
	}
327
328
	public function getAuthorIdField() {
329
		return 'rev_user';
330
	}
331
332
	public function getAuthorNameField() {
333
		return 'rev_user_text';
334
	}
335
336
	public function canView() {
337
		return $this->revision->userCan( Revision::DELETED_RESTRICTED, $this->context->getUser() );
338
	}
339
340
	public function canViewContent() {
341
		return $this->revision->userCan( Revision::DELETED_TEXT, $this->context->getUser() );
342
	}
343
344
	public function isDeleted() {
345
		return $this->revision->isDeleted( Revision::DELETED_TEXT );
346
	}
347
348
	/**
349
	 * Get the HTML link to the revision text.
350
	 * @todo Essentially a copy of RevDelRevisionItem::getRevisionLink. That class
351
	 * should inherit from this one, and implement an appropriate interface instead
352
	 * of extending RevDelItem
353
	 * @return string
354
	 */
355 View Code Duplication
	protected function getRevisionLink() {
356
		$date = htmlspecialchars( $this->list->getLanguage()->userTimeAndDate(
357
			$this->revision->getTimestamp(), $this->list->getUser() ) );
358
359
		if ( $this->isDeleted() && !$this->canViewContent() ) {
360
			return $date;
361
		}
362
		return Linker::linkKnown(
363
			$this->list->title,
364
			$date,
365
			[],
366
			[
367
				'oldid' => $this->revision->getId(),
368
				'unhide' => 1
369
			]
370
		);
371
	}
372
373
	/**
374
	 * Get the HTML link to the diff.
375
	 * @todo Essentially a copy of RevDelRevisionItem::getDiffLink. That class
376
	 * should inherit from this one, and implement an appropriate interface instead
377
	 * of extending RevDelItem
378
	 * @return string
379
	 */
380 View Code Duplication
	protected function getDiffLink() {
381
		if ( $this->isDeleted() && !$this->canViewContent() ) {
382
			return $this->context->msg( 'diff' )->escaped();
383
		} else {
384
			return Linker::linkKnown(
385
					$this->list->title,
386
					$this->list->msg( 'diff' )->escaped(),
387
					[],
388
					[
389
						'diff' => $this->revision->getId(),
390
						'oldid' => 'prev',
391
						'unhide' => 1
392
					]
393
				);
394
		}
395
	}
396
397
	/**
398
	 * @todo Essentially a copy of RevDelRevisionItem::getHTML. That class
399
	 * should inherit from this one, and implement an appropriate interface instead
400
	 * of extending RevDelItem
401
	 * @return string
402
	 */
403
	public function getHTML() {
404
		$difflink = $this->context->msg( 'parentheses' )
405
			->rawParams( $this->getDiffLink() )->escaped();
406
		$revlink = $this->getRevisionLink();
407
		$userlink = Linker::revUserLink( $this->revision );
408
		$comment = Linker::revComment( $this->revision );
409
		if ( $this->isDeleted() ) {
410
			$revlink = "<span class=\"history-deleted\">$revlink</span>";
411
		}
412
		return "<li>$difflink $revlink $userlink $comment</li>";
413
	}
414
}
415