Issues (165)

Security Analysis    no request data  

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.

Plugins/RFA.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
/**
3
 * RfA Analysis Library, modified for use with Peachy
4
 * Copyright (C) 2006 Tangotango (tangotango.wp _at_ gmail _dot_ com)
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 2
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19
 */
20
21
/**
22
 * An RFA object contains the parsed information for an RFA
23
 */
24
class RFA {
25
26
	protected $pgUsername = false;
27
	protected $enddate = false;
28
	protected $support = array();
29
	protected $oppose = array();
30
	protected $neutral = array();
31
	protected $duplicates = array();
32
	protected $lasterror = '';
33
34
	/**
35
	 * Analyzes an RFA. Returns TRUE on success, FALSE on failure
36
	 * @param Wiki $wiki
37
	 * @param $page
38
	 * @param null $rawwikitext
39
	 */
40
	public function __construct( Wiki $wiki, $page, $rawwikitext = null ) {
41
		if( is_null( $rawwikitext ) ) $rawwikitext = $wiki->initPage( $page )->get_text();
42
43
		$split = preg_split(
44
			"/^(?:(?:'''|(?:<includeonly><noin<\/includeonly><includeonly>clude><\/includeonly>)?={4,5}(?:<includeonly><\/noin<\/includeonly><includeonly>clude><\/includeonly>''')?)"
45
			. "\s*?(Support|Oppose|Neutral|Comments)\s*?(?:'''|(?:'''<includeonly><noin<\/includeonly><includeonly>clude><\/includeonly>)?={4,5}(?:<includeonly><\/noin<\/includeonly><includeonly>clude><\/includeonly>)?)|;\s*(Support|Oppose|Neutral|Comments))\s*(?:<br>|<br \/>)?\s*$/im"
46
			, $rawwikitext, -1, PREG_SPLIT_DELIM_CAPTURE
47
		);
48
49
		$header = array_shift( $split );
50
51
		//=== Deal with the header ===//
52
		$header = str_ireplace( array( '<nowiki>', '</nowiki>' ), '', $header );
53
54
		if( preg_match( "/===\s*\[\[User:(.*?)\|.*?\]\]\s*===/", $header, $matches ) ) {
55
			$this->username = $matches[1];
0 ignored issues
show
The property username does not seem to exist. Did you mean pgUsername?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
56
		} elseif( preg_match( "/===\s*\[\[.*?\|(.*?)\]\]\s*===/", $header, $matches ) ) {
57
			$this->username = $matches[1];
0 ignored issues
show
The property username does not seem to exist. Did you mean pgUsername?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
58
		}
59
60
		$header = str_replace( array( '[[', ']]' ), '', $header );
61
62
		if( preg_match( "/end(?:ing|ed)?(?: no earlier than)? (.*?) \(UTC\)/i", $header, $matches ) ) {
63
			$this->enddate = $matches[1];
64
		}
65
		//=== End header stuff ===//
66
67
		//Now parse through each non-header section, figuring out what they are
68
		//Nothing expected = 0, Support = 1, Oppose = 2, Neutral = 3
69
		$nextsection = 0;
70
71
		foreach( $split as $splut ){
72
			$splut = trim( $splut );
73
			if( empty( $splut ) ) {
74
				continue;
75
			}
76
77
			if( strcasecmp( $splut, 'Support' ) == 0 ) {
78
				$nextsection = 1;
79
			} elseif( strcasecmp( $splut, 'Oppose' ) == 0 ) {
80
				$nextsection = 2;
81
			} elseif( strcasecmp( $splut, 'Neutral' ) == 0 ) {
82
				$nextsection = 3;
83
			} else {
84
				switch( $nextsection ){
85
					case 1:
86
						$support = $splut;
87
						break;
88
					case 2:
89
						$oppose = $splut;
90
						break;
91
					case 3:
92
						$neutral = $splut;
93
						break;
94
				}
95
				$nextsection = 0;
96
			}
97
		}
98
99
		if( !isset( $support ) ) {
100
			$this->lasterror = "Support section not found";
101
			return false;
0 ignored issues
show
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
102
		}
103
		if( !isset( $oppose ) ) {
104
			$this->lasterror = "Oppose section not found";
105
			return false;
0 ignored issues
show
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
106
		}
107
		if( !isset( $neutral ) ) {
108
			$this->lasterror = "Neutral section not found";
109
			return false;
0 ignored issues
show
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
110
		}
111
112
		$this->support = $this->analyzeSection( $support );
113
		$this->oppose = $this->analyzeSection( $oppose );
114
		$this->neutral = $this->analyzeSection( $neutral );
115
116
		//Merge all votes in one array and sort:
117
		$m = array();
118
		foreach( $this->support as $s ){
119
			if( isset( $s['name'] ) ) $m[] = $s['name'];
120
		}
121
		foreach( $this->oppose as $o ){
122
			if( isset( $o['name'] ) ) $m[] = $o['name'];
123
		}
124
		foreach( $this->neutral as $n ){
125
			if( isset( $n['name'] ) ) $m[] = $n['name'];
126
		}
127
		sort( $m );
128
		//Find duplicates:
129
		for( $i = 0; $i < count( $m ); $i++ ){
130
			if( $i != count( $m ) - 1 ) {
131
				if( $m[$i] == $m[$i + 1] ) {
132
					$this->duplicates[] = $m[$i];
133
				}
134
			}
135
		}
136
137
		return true;
0 ignored issues
show
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
138
	}
139
140
	public function get_username() {
141
		return $this->username;
0 ignored issues
show
The property username does not seem to exist. Did you mean pgUsername?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
142
	}
143
144
	public function get_enddate() {
145
		return $this->enddate;
146
	}
147
148
	public function get_support() {
149
		return $this->support;
150
	}
151
152
	public function get_oppose() {
153
		return $this->oppose;
154
	}
155
156
	public function get_neutral() {
157
		return $this->neutral;
158
	}
159
160
	public function get_duplicates() {
161
		return $this->duplicates;
162
	}
163
164
	public function get_lasterror() {
165
		return $this->lasterror;
166
	}
167
168
	/**
169
	 * Attempts to find a signature in $input using the default regex. Returns matches.
170
	 * @param $input
171
	 * @param $matches
172
	 *
173
	 * @return int
174
	 */
175
	protected function findSig( $input, &$matches ) {
176
		//Supports User: and User talk: wikilinks, {{fullurl}}, unsubsted {{unsigned}}, unsubsted {{unsigned2}}, anything that looks like a custom sig template
177
		return preg_match_all(
178
			"/\[\[[Uu]ser(?:[\s_][Tt]alk)?\:([^\]\|\/]*)(?:\|[^\]]*)?\]\]" //1: Normal [[User:XX]] and [[User talk:XX]]
179
			. "|\{\{(?:[Ff]ullurl\:[Uu]ser(?:[\s_][Tt]alk)?\:|[Uu]nsigned\|)([^\}\|]*)(?:|[\|\}]*)?\}\}" //2: {{fullurl}} and {{unsigned}} templates
180
			. "|(?:\{\{)[Uu]ser(?:[\s_][Tt]alk)?\:([^\}\/\|]*)" //3: {{User:XX/sig}} templates
181
			. "|\{\{[Uu]nsigned2\|[^\|]*\|([^\}]*)\}\}" //4: {{unsigned2|Date|XX}} templates
182
			. "|(?:\[\[)[Uu]ser\:([^\]\/\|]*)\/[Ss]ig[\|\]]/" //5: [[User:XX/sig]] links (compromise measure)
183
			, $input, $matches, PREG_OFFSET_CAPTURE
184
		);
185
	}
186
187
	/**
188
	 * Attempts to find a signature in $input using a different regex. Returns matches.
189
	 * @param $input
190
	 * @param $matches
191
	 *
192
	 * @return int
193
	 */
194
	protected function findSigAlt( $input, &$matches ) {
195
		return preg_match_all(
196
			"/\[\[[Uu]ser(?:[\s_][Tt]alk)?\:([^\]\/\|]*)" //5: "[[User:XX/PageAboutMe" links (notice no end tag)
197
			. "|\[\[[Ss]pecial\:[Cc]ontributions\/([^\|\]]*)/"
198
			, $input, $matches, PREG_OFFSET_CAPTURE
199
		);
200
	}
201
202
	/**
203
	 * Attempts to find a signature in $input. Returns the name of the user, false on failure.
204
	 * @param $input
205
	 * @param $iffy
206
	 *
207
	 * @return bool|string false if not found Signature, or the Signature if it is found
208
	 */
209
	protected function findSigInLine( $input, &$iffy ) {
210
		$iffy = 0;
211
212
		$parsee_array = explode( "\n", $input );
213
		for( $n = 0; $n < count( $parsee_array ); $n++ ){ //This for will terminate when a sig is found.
214
			$parsee = $parsee_array[$n];
215
			//Okay, let's try and remove "copied from above" messages. If the line has more than one timestamp, we'll disregard anything after the first.
216
			//Note: we're ignoring people who use custom timestamps - if these peoples' votes are moved, the mover's name will show up as having voted.
217
218
			//If more than one timestamp is found in the first portion of the vote:
219
			$tsmatches = array();
220
			$dummymatches = array();
221
			if( preg_match_all( '/' . "[0-2][0-9]\:[0-5][0-9], [1-3]?[0-9] (?:January|February|March|April|May|June|July|August|September|October|November|December) \d{4} \(UTC\)" . '/', $parsee, $tsmatches, PREG_OFFSET_CAPTURE ) > 1 ) {
222
				//Go through each timestamp-section, looking for a signature
223
				foreach( $tsmatches[0] as $minisection ){
224
					$temp = substr( $parsee, 0, $minisection[1] );
225
					//If a signature is found, stop and use it as voter
226
					if( $this->findSig( $temp, $dummymatches ) != 0 ) { //KNOWN ISSUE: Write description later
227
						$parsee = $temp;
228
						break;
229
					}
230
				}
231
			}
232
233
			//Start the main signature-finding:
234
			$matches = array();
235
			if( $this->findSig( $parsee, $matches ) == 0 ) {
236
				//Okay, signature not found. Let's try the backup regex
237
				if( $this->findSigAlt( $parsee, $matches ) == 0 ) {
238
					//Signature was not found in this iteration of the main loop :(
239
					continue; //Go on to next newline (may be iffy)
240
				} else {
241
					$merged = array_merge( $matches[1], $matches[2] );
242
				}
243
			} else {
244
				//Merge the match arrays:
245
				$merged = array_merge( $matches[5], $matches[1], $matches[3], $matches[2], $matches[4] );
246
			}
247
			//Remove blank values and arrays of the form ('',-1):
248
			foreach( $merged as $key => $value ){
249
				if( is_array( $value ) && ( $value[0] == '' ) && ( $value[1] == -1 ) ) {
250
					unset( $merged[$key] );
251
				} elseif( $value == "" ) {
252
					unset( $merged[$key] );
253
				}
254
			}
255
256
			//Let's find out the real signature
257
			$keys = array();
258
			$values = array();
259
			foreach( $merged as $mergee ){
260
				$keys[] = $mergee[0];
261
				$values[] = $mergee[1];
262
			}
263
			//Now sort:
264
			array_multisort( $values, SORT_DESC, SORT_NUMERIC, $keys );
265
			//Now we should have the most relevant match (i.e., the sig) at the top of $keys
266
			$i = 0;
267
			$foundsig = '';
268
			while( $foundsig == '' ){
269
				$foundsig = trim( $keys[$i++] );
270
				if( $i == count( $keys ) ) break; //If we can only find blank usernames in the sig, catch overflow
271
				//Also fires when the first sig is also the last sig, so not an error
272
			}
273
274
			//Set iffy flag (level 1) if went beyond first line
275
			if( $n > 0 ) {
276
				$iffy = 1;
277
			}
278
			return $foundsig;
279
		}
280
281
		return false;
282
	}
283
284
	/**
285
	 * Analyzes an RFA section. Returns an array of parsed signatures on success. Undefined behaviour on failure.
286
	 * @param string $input
287
	 *
288
	 * @return array
289
	 */
290
	private function analyzeSection( $input ) {
291
		//Remove trailing sharp, if any
292
		$input = preg_replace( '/#\s*$/', '', $input );
293
294
		//Old preg_split regex: "/(^|\n)\s*\#[^\#\:\*]/"
295
		$parsed = preg_split( "/(^|\n)\#/", $input );
296
		//Shift off first empty element:
297
		array_shift( $parsed );
298
299
		foreach( $parsed as &$parsee ){ //Foreach line
300
			//If the line is empty for some reason, ignore
301
			$parsee = trim( $parsee );
302
			if( empty( $parsee ) ) continue;
303
304
			//If the line has been indented (disabled), or is a comment, ignore
305
			if( ( $parsee[0] == ':' ) || ( $parsee[0] == '*' ) || ( $parsee[0] == '#' ) ) {
306
				$parsee = '///###///';
307
				continue;
308
			}; //struck-out vote or comment
309
310
			$parsedsig = $this->findSigInLine( $parsee, $iffy ); //Find signature
311
			$orgsig = $parsee;
312
			$parsee = array();
313
			$parsee['context'] = $orgsig;
314
			if( $parsedsig === false ) {
315
				$parsee['error'] = 'Signature not found';
316
			} else {
317
				$parsee['name'] = $parsedsig;
318
			}
319
			if( @$iffy == 1 ) {
320
				$parsee['iffy'] = '1';
321
			}
322
		} //Foreach line
323
324
		if( ( count( $parsed ) == 1 ) && ( @trim( $parsed[0]['name'] ) == '' ) ) { //filters out placeholder sharp sign used in empty sections
325
			$parsed = array();
326
		}
327
328
		//Delete struck-out keys "continued" in foreach
329
		foreach( $parsed as $key => $value ){
330
			if( $value == '///###///' ) {
331
				unset( $parsed[$key] );
332
			}
333
		}
334
335
		return $parsed;
336
	}
337
338
}
339