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/logging/DeleteLogFormatter.php (1 issue)

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
 * Formatter for delete log entries.
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
 * @author Niklas Laxström
22
 * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
23
 * @since 1.22
24
 */
25
26
/**
27
 * This class formats delete log entries.
28
 *
29
 * @since 1.19
30
 */
31
class DeleteLogFormatter extends LogFormatter {
32
	protected function getMessageKey() {
33
		$key = parent::getMessageKey();
34
		if ( in_array( $this->entry->getSubtype(), [ 'event', 'revision' ] ) ) {
35
			if ( count( $this->getMessageParameters() ) < 5 ) {
36
				// Messages: logentry-delete-event-legacy, logentry-delete-revision-legacy,
37
				// logentry-suppress-event-legacy, logentry-suppress-revision-legacy
38
				return "$key-legacy";
39
			}
40
		}
41
42
		return $key;
43
	}
44
45
	protected function getMessageParameters() {
46
		if ( isset( $this->parsedParametersDeleteLog ) ) {
47
			return $this->parsedParametersDeleteLog;
0 ignored issues
show
The property parsedParametersDeleteLog does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
48
		}
49
50
		$params = parent::getMessageParameters();
51
		$subtype = $this->entry->getSubtype();
52
		if ( in_array( $subtype, [ 'event', 'revision' ] ) ) {
53
			// $params[3] here is 'revision' or 'archive' for page revisions, 'oldimage' or
54
			// 'filearchive' for file versions, or a comma-separated list of log_ids for log
55
			// entries. $subtype here is 'revision' for page revisions and file
56
			// versions, or 'event' for log entries.
57
			if (
58
				( $subtype === 'event' && count( $params ) === 6 )
59
				|| (
60
					$subtype === 'revision' && isset( $params[3] )
61
					&& in_array( $params[3], [ 'revision', 'archive', 'oldimage', 'filearchive' ] )
62
				)
63
			) {
64
				// See RevDelList::getLogParams()/RevDelLogList::getLogParams()
65
				$paramStart = $subtype === 'revision' ? 4 : 3;
66
67
				$old = $this->parseBitField( $params[$paramStart + 1] );
68
				$new = $this->parseBitField( $params[$paramStart + 2] );
69
				list( $hid, $unhid, $extra ) = RevisionDeleter::getChanges( $new, $old );
70
				$changes = [];
71
				// messages used: revdelete-content-hid, revdelete-summary-hid, revdelete-uname-hid
72
				foreach ( $hid as $v ) {
73
					$changes[] = $this->msg( "$v-hid" )->plain();
74
				}
75
				// messages used: revdelete-content-unhid, revdelete-summary-unhid,
76
				// revdelete-uname-unhid
77
				foreach ( $unhid as $v ) {
78
					$changes[] = $this->msg( "$v-unhid" )->plain();
79
				}
80
				foreach ( $extra as $v ) {
81
					$changes[] = $this->msg( $v )->plain();
82
				}
83
				$changeText = $this->context->getLanguage()->listToText( $changes );
84
85
				$newParams = array_slice( $params, 0, 3 );
86
				$newParams[3] = $changeText;
87
				$ids = is_array( $params[$paramStart] )
88
					? $params[$paramStart]
89
					: explode( ',', $params[$paramStart] );
90
				$newParams[4] = $this->context->getLanguage()->formatNum( count( $ids ) );
91
92
				$this->parsedParametersDeleteLog = $newParams;
93
				return $this->parsedParametersDeleteLog;
94
			} else {
95
				$this->parsedParametersDeleteLog = array_slice( $params, 0, 3 );
96
				return $this->parsedParametersDeleteLog;
97
			}
98
		}
99
100
		$this->parsedParametersDeleteLog = $params;
101
		return $this->parsedParametersDeleteLog;
102
	}
103
104
	protected function parseBitField( $string ) {
105
		// Input is like ofield=2134 or just the number
106
		if ( strpos( $string, 'field=' ) === 1 ) {
107
			list( , $field ) = explode( '=', $string );
108
109
			return (int)$field;
110
		} else {
111
			return (int)$string;
112
		}
113
	}
114
115
	public function getActionLinks() {
116
		$user = $this->context->getUser();
117
		if ( !$user->isAllowed( 'deletedhistory' )
118
			|| $this->entry->isDeleted( LogPage::DELETED_ACTION )
119
		) {
120
			return '';
121
		}
122
123
		switch ( $this->entry->getSubtype() ) {
124
			case 'delete': // Show undelete link
125
				if ( $user->isAllowed( 'undelete' ) ) {
126
					$message = 'undeletelink';
127
				} else {
128
					$message = 'undeleteviewlink';
129
				}
130
				$revert = Linker::linkKnown(
131
					SpecialPage::getTitleFor( 'Undelete' ),
132
					$this->msg( $message )->escaped(),
133
					[],
134
					[ 'target' => $this->entry->getTarget()->getPrefixedDBkey() ]
135
				);
136
137
				return $this->msg( 'parentheses' )->rawParams( $revert )->escaped();
138
139
			case 'revision': // If an edit was hidden from a page give a review link to the history
140
				$params = $this->extractParameters();
141
				if ( !isset( $params[3] ) || !isset( $params[4] ) ) {
142
					return '';
143
				}
144
145
				// Different revision types use different URL params...
146
				$key = $params[3];
147
				// This is a array or CSV of the IDs
148
				$ids = is_array( $params[4] )
149
					? $params[4]
150
					: explode( ',', $params[4] );
151
152
				$links = [];
153
154
				// If there's only one item, we can show a diff link
155
				if ( count( $ids ) == 1 ) {
156
					// Live revision diffs...
157
					if ( $key == 'oldid' || $key == 'revision' ) {
158
						$links[] = Linker::linkKnown(
159
							$this->entry->getTarget(),
160
							$this->msg( 'diff' )->escaped(),
161
							[],
162
							[
163
								'diff' => intval( $ids[0] ),
164
								'unhide' => 1
165
							]
166
						);
167
						// Deleted revision diffs...
168
					} elseif ( $key == 'artimestamp' || $key == 'archive' ) {
169
						$links[] = Linker::linkKnown(
170
							SpecialPage::getTitleFor( 'Undelete' ),
171
							$this->msg( 'diff' )->escaped(),
172
							[],
173
							[
174
								'target' => $this->entry->getTarget()->getPrefixedDBkey(),
175
								'diff' => 'prev',
176
								'timestamp' => $ids[0]
177
							]
178
						);
179
					}
180
				}
181
182
				// View/modify link...
183
				$links[] = Linker::linkKnown(
184
					SpecialPage::getTitleFor( 'Revisiondelete' ),
185
					$this->msg( 'revdel-restore' )->escaped(),
186
					[],
187
					[
188
						'target' => $this->entry->getTarget()->getPrefixedText(),
189
						'type' => $key,
190
						'ids' => implode( ',', $ids ),
191
					]
192
				);
193
194
				return $this->msg( 'parentheses' )->rawParams(
195
					$this->context->getLanguage()->pipeList( $links ) )->escaped();
196
197
			case 'event': // Hidden log items, give review link
198
				$params = $this->extractParameters();
199
				if ( !isset( $params[3] ) ) {
200
					return '';
201
				}
202
				// This is a CSV of the IDs
203
				$query = $params[3];
204
				if ( is_array( $query ) ) {
205
					$query = implode( ',', $query );
206
				}
207
				// Link to each hidden object ID, $params[1] is the url param
208
				$revert = Linker::linkKnown(
209
					SpecialPage::getTitleFor( 'Revisiondelete' ),
210
					$this->msg( 'revdel-restore' )->escaped(),
211
					[],
212
					[
213
						'target' => $this->entry->getTarget()->getPrefixedText(),
214
						'type' => 'logging',
215
						'ids' => $query
216
					]
217
				);
218
219
				return $this->msg( 'parentheses' )->rawParams( $revert )->escaped();
220
			default:
221
				return '';
222
		}
223
	}
224
225
	protected function getParametersForApi() {
226
		$entry = $this->entry;
227
		$params = [];
228
229
		$subtype = $this->entry->getSubtype();
230
		if ( in_array( $subtype, [ 'event', 'revision' ] ) ) {
231
			$rawParams = $entry->getParameters();
232
			if ( $subtype === 'event' ) {
233
				array_unshift( $rawParams, 'logging' );
234
			}
235
236
			static $map = [
237
				'4::type',
238
				'5::ids',
239
				'6::ofield',
240
				'7::nfield',
241
				'4::ids' => '5::ids',
242
				'5::ofield' => '6::ofield',
243
				'6::nfield' => '7::nfield',
244
			];
245
			foreach ( $map as $index => $key ) {
246
				if ( isset( $rawParams[$index] ) ) {
247
					$rawParams[$key] = $rawParams[$index];
248
					unset( $rawParams[$index] );
249
				}
250
			}
251
252
			$old = $this->parseBitField( $rawParams['6::ofield'] );
253
			$new = $this->parseBitField( $rawParams['7::nfield'] );
254
			if ( !is_array( $rawParams['5::ids'] ) ) {
255
				$rawParams['5::ids'] = explode( ',', $rawParams['5::ids'] );
256
			}
257
258
			$params = [
259
				'::type' => $rawParams['4::type'],
260
				':array:ids' => $rawParams['5::ids'],
261
				':assoc:old' => [ 'bitmask' => $old ],
262
				':assoc:new' => [ 'bitmask' => $new ],
263
			];
264
265
			static $fields = [
266
				Revision::DELETED_TEXT => 'content',
267
				Revision::DELETED_COMMENT => 'comment',
268
				Revision::DELETED_USER => 'user',
269
				Revision::DELETED_RESTRICTED => 'restricted',
270
			];
271
			foreach ( $fields as $bit => $key ) {
272
				$params[':assoc:old'][$key] = (bool)( $old & $bit );
273
				$params[':assoc:new'][$key] = (bool)( $new & $bit );
274
			}
275
		}
276
277
		return $params;
278
	}
279
280 View Code Duplication
	public function formatParametersForApi() {
281
		$ret = parent::formatParametersForApi();
282
		if ( isset( $ret['ids'] ) ) {
283
			ApiResult::setIndexedTagName( $ret['ids'], 'id' );
284
		}
285
		return $ret;
286
	}
287
}
288