Issues (847)

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.

inc/httputils.php (4 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
 * Utilities for handling HTTP related tasks
4
 *
5
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6
 * @author     Andreas Gohr <[email protected]>
7
 */
8
9
define('HTTP_MULTIPART_BOUNDARY','D0KuW1K1B0uNDARY');
10
define('HTTP_HEADER_LF',"\r\n");
11
define('HTTP_CHUNK_SIZE',16*1024);
12
13
/**
14
 * Checks and sets HTTP headers for conditional HTTP requests
15
 *
16
 * @author   Simon Willison <[email protected]>
17
 * @link     http://simonwillison.net/2003/Apr/23/conditionalGet/
18
 *
19
 * @param    int $timestamp lastmodified time of the cache file
20
 * @returns  void or exits with previously header() commands executed
21
 */
22
function http_conditionalRequest($timestamp){
23
    // A PHP implementation of conditional get, see
24
    //   http://fishbowl.pastiche.org/2002/10/21/http_conditional_get_for_rss_hackers/
25
    $last_modified = substr(gmdate('r', $timestamp), 0, -5).'GMT';
26
    $etag = '"'.md5($last_modified).'"';
27
    // Send the headers
28
    header("Last-Modified: $last_modified");
29
    header("ETag: $etag");
30
    // See if the client has provided the required headers
31
    if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])){
32
        $if_modified_since = stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']);
33
    }else{
34
        $if_modified_since = false;
35
    }
36
37
    if (isset($_SERVER['HTTP_IF_NONE_MATCH'])){
38
        $if_none_match = stripslashes($_SERVER['HTTP_IF_NONE_MATCH']);
39
    }else{
40
        $if_none_match = false;
41
    }
42
43
    if (!$if_modified_since && !$if_none_match){
0 ignored issues
show
Bug Best Practice introduced by
The expression $if_modified_since of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

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

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
Bug Best Practice introduced by
The expression $if_none_match of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

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

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
44
        return;
45
    }
46
47
    // At least one of the headers is there - check them
48
    if ($if_none_match && $if_none_match != $etag) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $if_none_match of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

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

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
49
        return; // etag is there but doesn't match
50
    }
51
52
    if ($if_modified_since && $if_modified_since != $last_modified) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $if_modified_since of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

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

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
53
        return; // if-modified-since is there but doesn't match
54
    }
55
56
    // Nothing has changed since their last request - serve a 304 and exit
57
    header('HTTP/1.0 304 Not Modified');
58
59
    // don't produce output, even if compression is on
60
    @ob_end_clean();
61
    exit;
62
}
63
64
/**
65
 * Let the webserver send the given file via x-sendfile method
66
 *
67
 * @author Chris Smith <[email protected]>
68
 *
69
 * @param string $file absolute path of file to send
70
 * @returns  void or exits with previous header() commands executed
71
 */
72
function http_sendfile($file) {
73
    global $conf;
74
75
    //use x-sendfile header to pass the delivery to compatible web servers
76
    if($conf['xsendfile'] == 1){
77
        header("X-LIGHTTPD-send-file: $file");
78
        ob_end_clean();
79
        exit;
80
    }elseif($conf['xsendfile'] == 2){
81
        header("X-Sendfile: $file");
82
        ob_end_clean();
83
        exit;
84
    }elseif($conf['xsendfile'] == 3){
85
        // FS#2388 nginx just needs the relative path.
86
        $file = DOKU_REL.substr($file, strlen(fullpath(DOKU_INC)) + 1);
87
        header("X-Accel-Redirect: $file");
88
        ob_end_clean();
89
        exit;
90
    }
91
}
92
93
/**
94
 * Send file contents supporting rangeRequests
95
 *
96
 * This function exits the running script
97
 *
98
 * @param resource $fh - file handle for an already open file
99
 * @param int $size     - size of the whole file
100
 * @param int $mime     - MIME type of the file
101
 *
102
 * @author Andreas Gohr <[email protected]>
103
 */
104
function http_rangeRequest($fh,$size,$mime){
105
    $ranges  = array();
106
    $isrange = false;
107
108
    header('Accept-Ranges: bytes');
109
110
    if(!isset($_SERVER['HTTP_RANGE'])){
111
        // no range requested - send the whole file
112
        $ranges[] = array(0,$size,$size);
113
    }else{
114
        $t = explode('=', $_SERVER['HTTP_RANGE']);
115
        if (!$t[0]=='bytes') {
116
            // we only understand byte ranges - send the whole file
117
            $ranges[] = array(0,$size,$size);
118
        }else{
119
            $isrange = true;
120
            // handle multiple ranges
121
            $r = explode(',',$t[1]);
122
            foreach($r as $x){
123
                $p = explode('-', $x);
124
                $start = (int)$p[0];
125
                $end   = (int)$p[1];
126
                if (!$end) $end = $size - 1;
127
                if ($start > $end || $start > $size || $end > $size){
128
                    header('HTTP/1.1 416 Requested Range Not Satisfiable');
129
                    print 'Bad Range Request!';
130
                    exit;
131
                }
132
                $len = $end - $start + 1;
133
                $ranges[] = array($start,$end,$len);
134
            }
135
        }
136
    }
137
    $parts = count($ranges);
138
139
    // now send the type and length headers
140
    if(!$isrange){
141
        header("Content-Type: $mime",true);
142
    }else{
143
        header('HTTP/1.1 206 Partial Content');
144
        if($parts == 1){
145
            header("Content-Type: $mime",true);
146
        }else{
147
            header('Content-Type: multipart/byteranges; boundary='.HTTP_MULTIPART_BOUNDARY,true);
148
        }
149
    }
150
151
    // send all ranges
152
    for($i=0; $i<$parts; $i++){
153
        list($start,$end,$len) = $ranges[$i];
154
155
        // multipart or normal headers
156
        if($parts > 1){
157
            echo HTTP_HEADER_LF.'--'.HTTP_MULTIPART_BOUNDARY.HTTP_HEADER_LF;
158
            echo "Content-Type: $mime".HTTP_HEADER_LF;
159
            echo "Content-Range: bytes $start-$end/$size".HTTP_HEADER_LF;
160
            echo HTTP_HEADER_LF;
161
        }else{
162
            header("Content-Length: $len");
163
            if($isrange){
164
                header("Content-Range: bytes $start-$end/$size");
165
            }
166
        }
167
168
        // send file content
169
        fseek($fh,$start); //seek to start of range
170
        $chunk = ($len > HTTP_CHUNK_SIZE) ? HTTP_CHUNK_SIZE : $len;
171
        while (!feof($fh) && $chunk > 0) {
172
            @set_time_limit(30); // large files can take a lot of time
173
            print fread($fh, $chunk);
174
            flush();
175
            $len -= $chunk;
176
            $chunk = ($len > HTTP_CHUNK_SIZE) ? HTTP_CHUNK_SIZE : $len;
177
        }
178
    }
179
    if($parts > 1){
180
        echo HTTP_HEADER_LF.'--'.HTTP_MULTIPART_BOUNDARY.'--'.HTTP_HEADER_LF;
181
    }
182
183
    // everything should be done here, exit (or return if testing)
184
    if (defined('SIMPLE_TEST')) return;
185
    exit;
186
}
187
188
/**
189
 * Check for a gzipped version and create if necessary
190
 *
191
 * return true if there exists a gzip version of the uncompressed file
192
 * (samepath/samefilename.sameext.gz) created after the uncompressed file
193
 *
194
 * @author Chris Smith <[email protected]>
195
 *
196
 * @param string $uncompressed_file
197
 * @return bool
198
 */
199
function http_gzip_valid($uncompressed_file) {
200
    if(!DOKU_HAS_GZIP) return false;
201
202
    $gzip = $uncompressed_file.'.gz';
203
    if (filemtime($gzip) < filemtime($uncompressed_file)) {    // filemtime returns false (0) if file doesn't exist
204
        return copy($uncompressed_file, 'compress.zlib://'.$gzip);
205
    }
206
207
    return true;
208
}
209
210
/**
211
 * Set HTTP headers and echo cachefile, if useable
212
 *
213
 * This function handles output of cacheable resource files. It ses the needed
214
 * HTTP headers. If a useable cache is present, it is passed to the web server
215
 * and the script is terminated.
216
 *
217
 * @param string $cache cache file name
218
 * @param bool   $cache_ok    if cache can be used
219
 */
220
function http_cached($cache, $cache_ok) {
221
    global $conf;
222
223
    // check cache age & handle conditional request
224
    // since the resource files are timestamped, we can use a long max age: 1 year
225
    header('Cache-Control: public, max-age=31536000');
226
    header('Pragma: public');
227
    if($cache_ok){
228
        http_conditionalRequest(filemtime($cache));
229
        if($conf['allowdebug']) header("X-CacheUsed: $cache");
230
231
        // finally send output
232
        if ($conf['gzip_output'] && http_gzip_valid($cache)) {
233
            header('Vary: Accept-Encoding');
234
            header('Content-Encoding: gzip');
235
            readfile($cache.".gz");
236
        } else {
237
            http_sendfile($cache);
238
            readfile($cache);
239
        }
240
        exit;
241
    }
242
243
    http_conditionalRequest(time());
244
}
245
246
/**
247
 * Cache content and print it
248
 *
249
 * @param string $file file name
250
 * @param string $content
251
 */
252
function http_cached_finish($file, $content) {
253
    global $conf;
254
255
    // save cache file
256
    io_saveFile($file, $content);
257
    if(DOKU_HAS_GZIP) io_saveFile("$file.gz",$content);
258
259
    // finally send output
260
    if ($conf['gzip_output'] && DOKU_HAS_GZIP) {
261
        header('Vary: Accept-Encoding');
262
        header('Content-Encoding: gzip');
263
        print gzencode($content,9,FORCE_GZIP);
264
    } else {
265
        print $content;
266
    }
267
}
268
269
/**
270
 * Fetches raw, unparsed POST data
271
 *
272
 * @return string
273
 */
274
function http_get_raw_post_data() {
275
    static $postData = null;
276
    if ($postData === null) {
277
        $postData = file_get_contents('php://input');
278
    }
279
    return $postData;
280
}
281
282
/**
283
 * Set the HTTP response status and takes care of the used PHP SAPI
284
 *
285
 * Inspired by CodeIgniter's set_status_header function
286
 *
287
 * @param int    $code
288
 * @param string $text
289
 */
290
function http_status($code = 200, $text = '') {
291
    static $stati = array(
292
        200 => 'OK',
293
        201 => 'Created',
294
        202 => 'Accepted',
295
        203 => 'Non-Authoritative Information',
296
        204 => 'No Content',
297
        205 => 'Reset Content',
298
        206 => 'Partial Content',
299
300
        300 => 'Multiple Choices',
301
        301 => 'Moved Permanently',
302
        302 => 'Found',
303
        304 => 'Not Modified',
304
        305 => 'Use Proxy',
305
        307 => 'Temporary Redirect',
306
307
        400 => 'Bad Request',
308
        401 => 'Unauthorized',
309
        403 => 'Forbidden',
310
        404 => 'Not Found',
311
        405 => 'Method Not Allowed',
312
        406 => 'Not Acceptable',
313
        407 => 'Proxy Authentication Required',
314
        408 => 'Request Timeout',
315
        409 => 'Conflict',
316
        410 => 'Gone',
317
        411 => 'Length Required',
318
        412 => 'Precondition Failed',
319
        413 => 'Request Entity Too Large',
320
        414 => 'Request-URI Too Long',
321
        415 => 'Unsupported Media Type',
322
        416 => 'Requested Range Not Satisfiable',
323
        417 => 'Expectation Failed',
324
325
        500 => 'Internal Server Error',
326
        501 => 'Not Implemented',
327
        502 => 'Bad Gateway',
328
        503 => 'Service Unavailable',
329
        504 => 'Gateway Timeout',
330
        505 => 'HTTP Version Not Supported'
331
    );
332
333
    if($text == '' && isset($stati[$code])) {
334
        $text = $stati[$code];
335
    }
336
337
    $server_protocol = (isset($_SERVER['SERVER_PROTOCOL'])) ? $_SERVER['SERVER_PROTOCOL'] : false;
338
339
    if(substr(php_sapi_name(), 0, 3) == 'cgi' || defined('SIMPLE_TEST')) {
340
        header("Status: {$code} {$text}", true);
341
    } elseif($server_protocol == 'HTTP/1.1' OR $server_protocol == 'HTTP/1.0') {
342
        header($server_protocol." {$code} {$text}", true, $code);
343
    } else {
344
        header("HTTP/1.1 {$code} {$text}", true, $code);
345
    }
346
}
347