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

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
 * Classes used to send headers and cookies back to the user
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
 * Allow programs to request this object from WebRequest::response()
25
 * and handle all outputting (or lack of outputting) via it.
26
 * @ingroup HTTP
27
 */
28
class WebResponse {
29
30
	/** @var array Used to record set cookies, because PHP's setcookie() will
31
	 * happily send an identical Set-Cookie to the client.
32
	 */
33
	protected static $setCookies = [];
34
35
	/**
36
	 * Output an HTTP header, wrapper for PHP's header()
37
	 * @param string $string Header to output
38
	 * @param bool $replace Replace current similar header
39
	 * @param null|int $http_response_code Forces the HTTP response code to the specified value.
40
	 */
41
	public function header( $string, $replace = true, $http_response_code = null ) {
42
		if ( $http_response_code ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $http_response_code of type null|integer is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
43
			header( $string, $replace, $http_response_code );
44
		} else {
45
			header( $string, $replace );
46
		}
47
	}
48
49
	/**
50
	 * Get a response header
51
	 * @param string $key The name of the header to get (case insensitive).
52
	 * @return string|null The header value (if set); null otherwise.
53
	 * @since 1.25
54
	 */
55
	public function getHeader( $key ) {
56
		foreach ( headers_list() as $header ) {
57
			list( $name, $val ) = explode( ':', $header, 2 );
58
			if ( !strcasecmp( $name, $key ) ) {
59
				return trim( $val );
60
			}
61
		}
62
		return null;
63
	}
64
65
	/**
66
	 * Output an HTTP status code header
67
	 * @since 1.26
68
	 * @param int $code Status code
69
	 */
70
	public function statusHeader( $code ) {
71
		HttpStatus::header( $code );
72
	}
73
74
	/**
75
	 * Test if headers have been sent
76
	 * @since 1.27
77
	 * @return bool
78
	 */
79
	public function headersSent() {
80
		return headers_sent();
81
	}
82
83
	/**
84
	 * Set the browser cookie
85
	 * @param string $name The name of the cookie.
86
	 * @param string $value The value to be stored in the cookie.
87
	 * @param int|null $expire Unix timestamp (in seconds) when the cookie should expire.
88
	 *        0 (the default) causes it to expire $wgCookieExpiration seconds from now.
89
	 *        null causes it to be a session cookie.
90
	 * @param array $options Assoc of additional cookie options:
91
	 *     prefix: string, name prefix ($wgCookiePrefix)
92
	 *     domain: string, cookie domain ($wgCookieDomain)
93
	 *     path: string, cookie path ($wgCookiePath)
94
	 *     secure: bool, secure attribute ($wgCookieSecure)
95
	 *     httpOnly: bool, httpOnly attribute ($wgCookieHttpOnly)
96
	 * @since 1.22 Replaced $prefix, $domain, and $forceSecure with $options
97
	 */
98
	public function setCookie( $name, $value, $expire = 0, $options = [] ) {
99
		global $wgCookiePath, $wgCookiePrefix, $wgCookieDomain;
100
		global $wgCookieSecure, $wgCookieExpiration, $wgCookieHttpOnly;
101
102
		$options = array_filter( $options, function ( $a ) {
103
			return $a !== null;
104
		} ) + [
105
			'prefix' => $wgCookiePrefix,
106
			'domain' => $wgCookieDomain,
107
			'path' => $wgCookiePath,
108
			'secure' => $wgCookieSecure,
109
			'httpOnly' => $wgCookieHttpOnly,
110
			'raw' => false,
111
		];
112
113 View Code Duplication
		if ( $expire === null ) {
114
			$expire = 0; // Session cookie
115
		} elseif ( $expire == 0 && $wgCookieExpiration != 0 ) {
116
			$expire = time() + $wgCookieExpiration;
117
		}
118
119
		$func = $options['raw'] ? 'setrawcookie' : 'setcookie';
120
121
		if ( Hooks::run( 'WebResponseSetCookie', [ &$name, &$value, &$expire, &$options ] ) ) {
122
			$cookie = $options['prefix'] . $name;
123
			$data = [
124
				'name' => (string)$cookie,
125
				'value' => (string)$value,
126
				'expire' => (int)$expire,
127
				'path' => (string)$options['path'],
128
				'domain' => (string)$options['domain'],
129
				'secure' => (bool)$options['secure'],
130
				'httpOnly' => (bool)$options['httpOnly'],
131
			];
132
133
			// Per RFC 6265, key is name + domain + path
134
			$key = "{$data['name']}\n{$data['domain']}\n{$data['path']}";
135
136
			// If this cookie name was in the request, fake an entry in
137
			// self::$setCookies for it so the deleting check works right.
138
			if ( isset( $_COOKIE[$cookie] ) && !array_key_exists( $key, self::$setCookies ) ) {
139
				self::$setCookies[$key] = [];
140
			}
141
142
			// PHP deletes if value is the empty string; also, a past expiry is deleting
143
			$deleting = ( $data['value'] === '' || $data['expire'] > 0 && $data['expire'] <= time() );
144
145
			if ( $deleting && !isset( self::$setCookies[$key] ) ) { // isset( null ) is false
146
				wfDebugLog( 'cookie', 'already deleted ' . $func . ': "' . implode( '", "', $data ) . '"' );
147
			} elseif ( !$deleting && isset( self::$setCookies[$key] ) &&
148
				self::$setCookies[$key] === [ $func, $data ]
149
			) {
150
				wfDebugLog( 'cookie', 'already set ' . $func . ': "' . implode( '", "', $data ) . '"' );
151
			} else {
152
				wfDebugLog( 'cookie', $func . ': "' . implode( '", "', $data ) . '"' );
153
				if ( call_user_func_array( $func, array_values( $data ) ) ) {
154
					self::$setCookies[$key] = $deleting ? null : [ $func, $data ];
155
				}
156
			}
157
		}
158
	}
159
160
	/**
161
	 * Unset a browser cookie.
162
	 * This sets the cookie with an empty value and an expiry set to a time in the past,
163
	 * which will cause the browser to remove any cookie with the given name, domain and
164
	 * path from its cookie store. Options other than these (and prefix) have no effect.
165
	 * @param string $name Cookie name
166
	 * @param array $options Cookie options, see {@link setCookie()}
167
	 * @since 1.27
168
	 */
169
	public function clearCookie( $name, $options = [] ) {
170
		$this->setCookie( $name, '', time() - 31536000 /* 1 year */, $options );
171
	}
172
173
	/**
174
	 * Checks whether this request is performing cookie operations
175
	 *
176
	 * @return bool
177
	 * @since 1.27
178
	 */
179
	public function hasCookies() {
180
		return (bool)self::$setCookies;
181
	}
182
}
183
184
/**
185
 * @ingroup HTTP
186
 */
187
class FauxResponse extends WebResponse {
188
	private $headers;
189
	private $cookies = [];
190
	private $code;
191
192
	/**
193
	 * Stores a HTTP header
194
	 * @param string $string Header to output
195
	 * @param bool $replace Replace current similar header
196
	 * @param null|int $http_response_code Forces the HTTP response code to the specified value.
197
	 */
198
	public function header( $string, $replace = true, $http_response_code = null ) {
199
		if ( substr( $string, 0, 5 ) == 'HTTP/' ) {
200
			$parts = explode( ' ', $string, 3 );
201
			$this->code = intval( $parts[1] );
202
		} else {
203
			list( $key, $val ) = array_map( 'trim', explode( ":", $string, 2 ) );
204
205
			$key = strtoupper( $key );
206
207
			if ( $replace || !isset( $this->headers[$key] ) ) {
208
				$this->headers[$key] = $val;
209
			}
210
		}
211
212
		if ( $http_response_code !== null ) {
213
			$this->code = intval( $http_response_code );
214
		}
215
	}
216
217
	/**
218
	 * @since 1.26
219
	 * @param int $code Status code
220
	 */
221
	public function statusHeader( $code ) {
222
		$this->code = intval( $code );
223
	}
224
225
	public function headersSent() {
226
		return false;
227
	}
228
229
	/**
230
	 * @param string $key The name of the header to get (case insensitive).
231
	 * @return string|null The header value (if set); null otherwise.
232
	 */
233
	public function getHeader( $key ) {
234
		$key = strtoupper( $key );
235
236
		if ( isset( $this->headers[$key] ) ) {
237
			return $this->headers[$key];
238
		}
239
		return null;
240
	}
241
242
	/**
243
	 * Get the HTTP response code, null if not set
244
	 *
245
	 * @return int|null
246
	 */
247
	public function getStatusCode() {
248
		return $this->code;
249
	}
250
251
	/**
252
	 * @param string $name The name of the cookie.
253
	 * @param string $value The value to be stored in the cookie.
254
	 * @param int|null $expire Ignored in this faux subclass.
255
	 * @param array $options Ignored in this faux subclass.
256
	 */
257
	public function setCookie( $name, $value, $expire = 0, $options = [] ) {
258
		global $wgCookiePath, $wgCookiePrefix, $wgCookieDomain;
259
		global $wgCookieSecure, $wgCookieExpiration, $wgCookieHttpOnly;
260
261
		$options = array_filter( $options, function ( $a ) {
262
			return $a !== null;
263
		} ) + [
264
			'prefix' => $wgCookiePrefix,
265
			'domain' => $wgCookieDomain,
266
			'path' => $wgCookiePath,
267
			'secure' => $wgCookieSecure,
268
			'httpOnly' => $wgCookieHttpOnly,
269
			'raw' => false,
270
		];
271
272 View Code Duplication
		if ( $expire === null ) {
273
			$expire = 0; // Session cookie
274
		} elseif ( $expire == 0 && $wgCookieExpiration != 0 ) {
275
			$expire = time() + $wgCookieExpiration;
276
		}
277
278
		$this->cookies[$options['prefix'] . $name] = [
279
			'value' => (string)$value,
280
			'expire' => (int)$expire,
281
			'path' => (string)$options['path'],
282
			'domain' => (string)$options['domain'],
283
			'secure' => (bool)$options['secure'],
284
			'httpOnly' => (bool)$options['httpOnly'],
285
			'raw' => (bool)$options['raw'],
286
		];
287
	}
288
289
	/**
290
	 * @param string $name
291
	 * @return string|null
292
	 */
293
	public function getCookie( $name ) {
294
		if ( isset( $this->cookies[$name] ) ) {
295
			return $this->cookies[$name]['value'];
296
		}
297
		return null;
298
	}
299
300
	/**
301
	 * @param string $name
302
	 * @return array|null
303
	 */
304
	public function getCookieData( $name ) {
305
		if ( isset( $this->cookies[$name] ) ) {
306
			return $this->cookies[$name];
307
		}
308
		return null;
309
	}
310
311
	/**
312
	 * @return array
313
	 */
314
	public function getCookies() {
315
		return $this->cookies;
316
	}
317
}
318