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/OutputHandler.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
 * Functions to be used with PHP's output buffer.
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
 * Standard output handler for use with ob_start
25
 *
26
 * @param string $s
27
 *
28
 * @return string
29
 */
30
function wfOutputHandler( $s ) {
31
	global $wgDisableOutputCompression, $wgValidateAllHtml, $wgMangleFlashPolicy;
32
	if ( $wgMangleFlashPolicy ) {
33
		$s = wfMangleFlashPolicy( $s );
34
	}
35
	if ( $wgValidateAllHtml ) {
36
		$headers = headers_list();
37
		$isHTML = false;
38
		foreach ( $headers as $header ) {
39
			$parts = explode( ':', $header, 2 );
40
			if ( count( $parts ) !== 2 ) {
41
				continue;
42
			}
43
			$name = strtolower( trim( $parts[0] ) );
44
			$value = trim( $parts[1] );
45
			if ( $name == 'content-type' && ( strpos( $value, 'text/html' ) === 0
46
				|| strpos( $value, 'application/xhtml+xml' ) === 0 )
47
			) {
48
				$isHTML = true;
49
				break;
50
			}
51
		}
52
		if ( $isHTML ) {
53
			$s = wfHtmlValidationHandler( $s );
54
		}
55
	}
56
	if ( !$wgDisableOutputCompression && !ini_get( 'zlib.output_compression' ) ) {
57
		if ( !defined( 'MW_NO_OUTPUT_COMPRESSION' ) ) {
58
			$s = wfGzipHandler( $s );
59
		}
60
		if ( !ini_get( 'output_handler' ) ) {
61
			wfDoContentLength( strlen( $s ) );
62
		}
63
	}
64
	return $s;
65
}
66
67
/**
68
 * Get the "file extension" that some client apps will estimate from
69
 * the currently-requested URL.
70
 * This isn't on WebRequest because we need it when things aren't initialized
71
 * @private
72
 *
73
 * @return string
74
 */
75
function wfRequestExtension() {
0 ignored issues
show
wfRequestExtension uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
76
	/// @todo FIXME: this sort of dupes some code in WebRequest::getRequestUrl()
77
	if ( isset( $_SERVER['REQUEST_URI'] ) ) {
78
		// Strip the query string...
79
		list( $path ) = explode( '?', $_SERVER['REQUEST_URI'], 2 );
80
	} elseif ( isset( $_SERVER['SCRIPT_NAME'] ) ) {
81
		// Probably IIS. QUERY_STRING appears separately.
82
		$path = $_SERVER['SCRIPT_NAME'];
83
	} else {
84
		// Can't get the path from the server? :(
85
		return '';
86
	}
87
88
	$period = strrpos( $path, '.' );
89
	if ( $period !== false ) {
90
		return strtolower( substr( $path, $period ) );
91
	}
92
	return '';
93
}
94
95
/**
96
 * Handler that compresses data with gzip if allowed by the Accept header.
97
 * Unlike ob_gzhandler, it works for HEAD requests too.
98
 *
99
 * @param string $s
100
 *
101
 * @return string
102
 */
103
function wfGzipHandler( $s ) {
104
	if ( !function_exists( 'gzencode' ) ) {
105
		wfDebug( __FUNCTION__ . "() skipping compression (gzencode unavailable)\n" );
106
		return $s;
107
	}
108
	if ( headers_sent() ) {
109
		wfDebug( __FUNCTION__ . "() skipping compression (headers already sent)\n" );
110
		return $s;
111
	}
112
113
	$ext = wfRequestExtension();
114
	if ( $ext == '.gz' || $ext == '.tgz' ) {
115
		// Don't do gzip compression if the URL path ends in .gz or .tgz
116
		// This confuses Safari and triggers a download of the page,
117
		// even though it's pretty clearly labeled as viewable HTML.
118
		// Bad Safari! Bad!
119
		return $s;
120
	}
121
122
	if ( wfClientAcceptsGzip() ) {
123
		wfDebug( __FUNCTION__ . "() is compressing output\n" );
124
		header( 'Content-Encoding: gzip' );
125
		$s = gzencode( $s, 6 );
126
	}
127
128
	// Set vary header if it hasn't been set already
129
	$headers = headers_list();
130
	$foundVary = false;
131
	foreach ( $headers as $header ) {
132
		$headerName = strtolower( substr( $header, 0, 5 ) );
133
		if ( $headerName == 'vary:' ) {
134
			$foundVary = true;
135
			break;
136
		}
137
	}
138
	if ( !$foundVary ) {
139
		header( 'Vary: Accept-Encoding' );
140
		global $wgUseKeyHeader;
141
		if ( $wgUseKeyHeader ) {
142
			header( 'Key: Accept-Encoding;match=gzip' );
143
		}
144
	}
145
	return $s;
146
}
147
148
/**
149
 * Mangle flash policy tags which open up the site to XSS attacks.
150
 *
151
 * @param string $s
152
 *
153
 * @return string
154
 */
155
function wfMangleFlashPolicy( $s ) {
156
	# Avoid weird excessive memory usage in PCRE on big articles
157
	if ( preg_match( '/\<\s*cross-domain-policy(?=\s|\>)/i', $s ) ) {
158
		return preg_replace( '/\<(\s*)(cross-domain-policy(?=\s|\>))/i', '<$1NOT-$2', $s );
159
	} else {
160
		return $s;
161
	}
162
}
163
164
/**
165
 * Add a Content-Length header if possible. This makes it cooperate with CDN better.
166
 *
167
 * @param int $length
168
 */
169
function wfDoContentLength( $length ) {
0 ignored issues
show
wfDoContentLength uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
170
	if ( !headers_sent()
171
		&& isset( $_SERVER['SERVER_PROTOCOL'] )
172
		&& $_SERVER['SERVER_PROTOCOL'] == 'HTTP/1.0'
173
	) {
174
		header( "Content-Length: $length" );
175
	}
176
}
177
178
/**
179
 * Replace the output with an error if the HTML is not valid
180
 *
181
 * @param string $s
182
 *
183
 * @return string
184
 */
185
function wfHtmlValidationHandler( $s ) {
186
187
	$errors = '';
188
	if ( MWTidy::checkErrors( $s, $errors ) ) {
189
		return $s;
190
	}
191
192
	header( 'Cache-Control: no-cache' );
193
194
	$out = Html::element( 'h1', null, 'HTML validation error' );
195
	$out .= Html::openElement( 'ul' );
196
197
	$error = strtok( $errors, "\n" );
198
	$badLines = [];
199
	while ( $error !== false ) {
200
		if ( preg_match( '/^line (\d+)/', $error, $m ) ) {
201
			$lineNum = intval( $m[1] );
202
			$badLines[$lineNum] = true;
203
			$out .= Html::rawElement( 'li', null,
204
				Html::element( 'a', [ 'href' => "#line-{$lineNum}" ], $error ) ) . "\n";
205
		}
206
		$error = strtok( "\n" );
207
	}
208
209
	$out .= Html::closeElement( 'ul' );
210
	$out .= Html::element( 'pre', null, $errors );
211
	$out .= Html::openElement( 'ol' ) . "\n";
212
	$line = strtok( $s, "\n" );
213
	$i = 1;
214
	while ( $line !== false ) {
215
		$attrs = [];
216
		if ( isset( $badLines[$i] ) ) {
217
			$attrs['class'] = 'highlight';
218
			$attrs['id'] = "line-$i";
219
		}
220
		$out .= Html::element( 'li', $attrs, $line ) . "\n";
221
		$line = strtok( "\n" );
222
		$i++;
223
	}
224
	$out .= Html::closeElement( 'ol' );
225
226
	$style = <<<CSS
227
.highlight { background-color: #ffc }
228
li { white-space: pre }
229
CSS;
230
231
	$out = Html::htmlHeader( [ 'lang' => 'en', 'dir' => 'ltr' ] ) .
232
		Html::rawElement( 'head', null,
233
			Html::element( 'title', null, 'HTML validation error' ) .
234
			Html::inlineStyle( $style ) ) .
235
		Html::rawElement( 'body', null, $out ) .
236
		Html::closeElement( 'html' );
237
238
	return $out;
239
}
240