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.

libs/virtualrest/RestbaseVirtualRESTService.php (2 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
 * Virtual HTTP service client for RESTBase
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
21
/**
22
 * Virtual REST service for RESTBase
23
 * @since 1.25
24
 */
25
class RestbaseVirtualRESTService extends VirtualRESTService {
26
	/**
27
	 * Example RESTBase v1 requests:
28
	 *  GET /local/v1/page/html/{title}{/revision}
29
	 *  POST /local/v1/transform/html/to/wikitext{/title}{/revision}
30
	 *   * body: array( 'html' => ... )
31
	 *  POST /local/v1/transform/wikitext/to/html{/title}{/revision}
32
	 *   * body: array( 'wikitext' => ... ) or array( 'wikitext' => ..., 'body_only' => true/false )
33
	 *
34
	 * @param array $params Key/value map
35
	 *   - url            : RESTBase server URL
36
	 *   - domain         : Wiki domain to use
37
	 *   - timeout        : request timeout in seconds (optional)
38
	 *   - forwardCookies : cookies to forward to RESTBase/Parsoid (as a Cookie
39
	 *                       header string) or false (optional)
40
	 *                       Note: forwardCookies will in the future be a boolean
41
	 *                       only, signifing request cookies should be forwarded
42
	 *                       to the service; the current state is due to the way
43
	 *                       VE handles this particular parameter
44
	 *   - HTTPProxy      : HTTP proxy to use (optional)
45
	 *   - parsoidCompat  : whether to parse URL as if they were meant for Parsoid
46
	 *                       boolean (optional)
47
	 *   - fixedUrl       : Do not append domain to the url. For example to use
48
	 *                       English Wikipedia restbase, you would this to true
49
	 *                       and url to https://en.wikipedia.org/api/rest_#version#
50
	 */
51
	public function __construct( array $params ) {
52
		// set up defaults and merge them with the given params
53
		$mparams = array_merge( [
54
			'name' => 'restbase',
55
			'url' => 'http://localhost:7231/',
56
			'domain' => 'localhost',
57
			'timeout' => 100,
58
			'forwardCookies' => false,
59
			'HTTPProxy' => null,
60
			'parsoidCompat' => false,
61
			'fixedUrl' => false,
62
		], $params );
63
		// Ensure that the url parameter has a trailing slash.
64
		$mparams['url'] = preg_replace(
65
			'#/?$#',
66
			'/',
67
			$mparams['url']
68
		);
69
		// Ensure the correct domain format: strip protocol, port,
70
		// and trailing slash if present.  This lets us use
71
		// $wgCanonicalServer as a default value, which is very convenient.
72
		$mparams['domain'] = preg_replace(
73
			'/^(https?:\/\/)?([^\/:]+?)(:\d+)?\/?$/',
74
			'$2',
75
			$mparams['domain']
76
		);
77
		parent::__construct( $mparams );
78
	}
79
80
	public function onRequests( array $reqs, Closure $idGenFunc ) {
81
		if ( $this->params['parsoidCompat'] ) {
82
			return $this->onParsoidRequests( $reqs, $idGenFunc );
83
		}
84
85
		$result = [];
86
		foreach ( $reqs as $key => $req ) {
87
			if ( $this->params['fixedUrl'] ) {
88
				$version = explode( '/', $req['url'] )[1];
89
				$req['url'] =
90
					str_replace( '#version#', $version, $this->params['url'] ) .
91
					preg_replace( '#^local/v./#', '', $req['url'] );
92
			} else {
93
				// replace /local/ with the current domain
94
				$req['url'] = preg_replace( '#^local/#', $this->params['domain'] . '/', $req['url'] );
95
				// and prefix it with the service URL
96
				$req['url'] = $this->params['url'] . $req['url'];
97
			}
98
99
			// set the appropriate proxy, timeout and headers
100
			if ( $this->params['HTTPProxy'] ) {
101
				$req['proxy'] = $this->params['HTTPProxy'];
102
			}
103
			if ( $this->params['timeout'] != null ) {
104
				$req['reqTimeout'] = $this->params['timeout'];
105
			}
106
			if ( $this->params['forwardCookies'] ) {
107
				$req['headers']['Cookie'] = $this->params['forwardCookies'];
108
			}
109
			$result[$key] = $req;
110
		}
111
112
		return $result;
113
	}
114
115
	/**
116
	 * Remaps Parsoid v1/v3 requests to RESTBase v1 requests.
117
	 */
118
	public function onParsoidRequests( array $reqs, Closure $idGeneratorFunc ) {
119
		$result = [];
120
		foreach ( $reqs as $key => $req ) {
121
			$version = explode( '/', $req['url'] )[1];
122
			if ( $version === 'v3' ) {
123
				$result[$key] = $this->onParsoid3Request( $req, $idGeneratorFunc );
124
			} elseif ( $version === 'v1' ) {
125
				$result[$key] = $this->onParsoid1Request( $req, $idGeneratorFunc );
126
			} else {
127
				throw new Exception( "Only v1 and v3 are supported." );
128
			}
129
		}
130
131
		return $result;
132
	}
133
134
	/**
135
	 * Remap a Parsoid v1 request to a RESTBase v1 request.
136
	 *
137
	 * Example Parsoid v1 requests:
138
	 *  GET /local/v1/page/$title/html/$oldid
139
	 *   * $oldid is optional
140
	 *  POST /local/v1/transform/html/to/wikitext/$title/$oldid
141
	 *   * body: array( 'html' => ... )
142
	 *   * $title and $oldid are optional
143
	 *  POST /local/v1/transform/wikitext/to/html/$title
144
	 *   * body: array( 'wikitext' => ... ) or array( 'wikitext' => ..., 'body' => true/false )
145
	 *   * $title is optional
146
	 *
147
	 * NOTE: the POST APIs aren't "real" Parsoid v1 APIs, they are just what
148
	 * Visual Editor "pretends" the V1 API is like.  (See
149
	 * ParsoidVirtualRESTService.)
150
	 */
151 View Code Duplication
	public function onParsoid1Request( array $req, Closure $idGeneratorFunc ) {
152
		$parts = explode( '/', $req['url'] );
153
		list(
154
			$targetWiki, // 'local'
155
			$version, // 'v1'
156
			$reqType // 'page' or 'transform'
157
		) = $parts;
158
		if ( $targetWiki !== 'local' ) {
159
			throw new Exception( "Only 'local' target wiki is currently supported" );
160
		} elseif ( $version !== 'v1' ) {
161
			throw new Exception( "Version mismatch: should not happen." );
162
		} elseif ( $reqType !== 'page' && $reqType !== 'transform' ) {
163
			throw new Exception( "Request type must be either 'page' or 'transform'" );
164
		}
165
		$req['url'] = $this->params['url'] . $this->params['domain'] . '/v1/' . $reqType . '/';
166
		if ( $reqType === 'page' ) {
167
			$title = $parts[3];
168
			if ( $parts[4] !== 'html' ) {
169
				throw new Exception( "Only 'html' output format is currently supported" );
170
			}
171
			$req['url'] .= 'html/' . $title;
172
			if ( isset( $parts[5] ) ) {
173
				$req['url'] .= '/' . $parts[5];
174
			} elseif ( isset( $req['query']['oldid'] ) && $req['query']['oldid'] ) {
175
				$req['url'] .= '/' . $req['query']['oldid'];
176
				unset( $req['query']['oldid'] );
177
			}
178
		} elseif ( $reqType === 'transform' ) {
179
			// from / to transform
180
			$req['url'] .= $parts[3] . '/to/' . $parts[5];
181
			// the title
182
			if ( isset( $parts[6] ) ) {
183
				$req['url'] .= '/' . $parts[6];
184
			}
185
			// revision id
186
			if ( isset( $parts[7] ) ) {
187
				$req['url'] .= '/' . $parts[7];
188
			} elseif ( isset( $req['body']['oldid'] ) && $req['body']['oldid'] ) {
189
				$req['url'] .= '/' . $req['body']['oldid'];
190
				unset( $req['body']['oldid'] );
191
			}
192
			if ( $parts[4] !== 'to' ) {
193
				throw new Exception( "Part index 4 is not 'to'" );
194
			}
195
			if ( $parts[3] === 'html' && $parts[5] === 'wikitext' ) {
196
				if ( !isset( $req['body']['html'] ) ) {
197
					throw new Exception( "You must set an 'html' body key for this request" );
198
				}
199
			} elseif ( $parts[3] == 'wikitext' && $parts[5] == 'html' ) {
200
				if ( !isset( $req['body']['wikitext'] ) ) {
201
					throw new Exception( "You must set a 'wikitext' body key for this request" );
202
				}
203
				if ( isset( $req['body']['body'] ) ) {
204
					$req['body']['body_only'] = $req['body']['body'];
205
					unset( $req['body']['body'] );
206
				}
207
			} else {
208
				throw new Exception( "Transformation unsupported" );
209
			}
210
		}
211
		// set the appropriate proxy, timeout and headers
212
		if ( $this->params['HTTPProxy'] ) {
213
			$req['proxy'] = $this->params['HTTPProxy'];
214
		}
215
		if ( $this->params['timeout'] != null ) {
216
			$req['reqTimeout'] = $this->params['timeout'];
217
		}
218
		if ( $this->params['forwardCookies'] ) {
219
			$req['headers']['Cookie'] = $this->params['forwardCookies'];
220
		}
221
222
		return $req;
223
	}
224
225
	/**
226
	 * Remap a Parsoid v3 request to a RESTBase v1 request.
227
	 *
228
	 * Example Parsoid v3 requests:
229
	 *  GET /local/v3/page/html/$title/{$revision}
230
	 *   * $revision is optional
231
	 *  POST /local/v3/transform/html/to/wikitext/{$title}{/$revision}
232
	 *   * body: array( 'html' => ... )
233
	 *   * $title and $revision are optional
234
	 *  POST /local/v3/transform/wikitext/to/html/{$title}{/$revision}
235
	 *   * body: array( 'wikitext' => ... ) or array( 'wikitext' => ..., 'body_only' => true/false )
236
	 *   * $title is optional
237
	 *   * $revision is optional
238
	 */
239
	public function onParsoid3Request( array $req, Closure $idGeneratorFunc ) {
240
		$parts = explode( '/', $req['url'] );
241
		list(
242
			$targetWiki, // 'local'
243
			$version, // 'v3'
244
			$action, // 'transform' or 'page'
0 ignored issues
show
The assignment to $action is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
245
			$format, // 'html' or 'wikitext'
0 ignored issues
show
The assignment to $format is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
246
			// $title, // optional
247
			// $revision, // optional
248
		) = $parts;
249
		if ( $targetWiki !== 'local' ) {
250
			throw new Exception( "Only 'local' target wiki is currently supported" );
251
		} elseif ( $version !== 'v3' ) {
252
			throw new Exception( "Version mismatch: should not happen." );
253
		}
254
		// replace /local/ with the current domain, change v3 to v1,
255
		$req['url'] = preg_replace( '#^local/v3/#', $this->params['domain'] . '/v1/', $req['url'] );
256
		// and prefix it with the service URL
257
		$req['url'] = $this->params['url'] . $req['url'];
258
		// set the appropriate proxy, timeout and headers
259
		if ( $this->params['HTTPProxy'] ) {
260
			$req['proxy'] = $this->params['HTTPProxy'];
261
		}
262
		if ( $this->params['timeout'] != null ) {
263
			$req['reqTimeout'] = $this->params['timeout'];
264
		}
265
		if ( $this->params['forwardCookies'] ) {
266
			$req['headers']['Cookie'] = $this->params['forwardCookies'];
267
		}
268
269
		return $req;
270
	}
271
272
}
273