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/infoutils.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
 * Information and debugging functions
4
 *
5
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6
 * @author     Andreas Gohr <[email protected]>
7
 */
8
9
use dokuwiki\HTTP\DokuHTTPClient;
10
use dokuwiki\Logger;
11
12
if(!defined('DOKU_MESSAGEURL')){
13
    if(in_array('ssl', stream_get_transports())) {
14
        define('DOKU_MESSAGEURL','https://update.dokuwiki.org/check/');
15
    }else{
16
        define('DOKU_MESSAGEURL','http://update.dokuwiki.org/check/');
17
    }
18
}
19
20
/**
21
 * Check for new messages from upstream
22
 *
23
 * @author Andreas Gohr <[email protected]>
24
 */
25
function checkUpdateMessages(){
26
    global $conf;
27
    global $INFO;
28
    global $updateVersion;
29
    if(!$conf['updatecheck']) return;
30
    if($conf['useacl'] && !$INFO['ismanager']) return;
31
32
    $cf = getCacheName($updateVersion, '.updmsg');
33
    $lm = @filemtime($cf);
34
    $is_http = substr(DOKU_MESSAGEURL, 0, 5) != 'https';
35
36
    // check if new messages needs to be fetched
37
    if($lm < time()-(60*60*24) || $lm < @filemtime(DOKU_INC.DOKU_SCRIPT)){
38
        @touch($cf);
39
        Logger::debug("checkUpdateMessages(): downloading messages to ".$cf.($is_http?' (without SSL)':' (with SSL)'));
40
        $http = new DokuHTTPClient();
41
        $http->timeout = 12;
42
        $resp = $http->get(DOKU_MESSAGEURL.$updateVersion);
43
        if(is_string($resp) && ($resp == "" || substr(trim($resp), -1) == '%')) {
44
            // basic sanity check that this is either an empty string response (ie "no messages")
45
            // or it looks like one of our messages, not WiFi login or other interposed response
46
            io_saveFile($cf,$resp);
47
        } else {
48
            Logger::debug("checkUpdateMessages(): unexpected HTTP response received", $http->error);
49
        }
50
    }else{
51
        Logger::debug("checkUpdateMessages(): messages up to date");
52
    }
53
54
    $data = io_readFile($cf);
55
    // show messages through the usual message mechanism
56
    $msgs = explode("\n%\n",$data);
57
    foreach($msgs as $msg){
58
        if($msg) msg($msg,2);
59
    }
60
}
61
62
63
/**
64
 * Return DokuWiki's version (split up in date and type)
65
 *
66
 * @author Andreas Gohr <[email protected]>
67
 */
68
function getVersionData(){
69
    $version = array();
70
    //import version string
71
    if(file_exists(DOKU_INC.'VERSION')){
72
        //official release
73
        $version['date'] = trim(io_readFile(DOKU_INC.'VERSION'));
74
        $version['type'] = 'Release';
75
    }elseif(is_dir(DOKU_INC.'.git')){
76
        $version['type'] = 'Git';
77
        $version['date'] = 'unknown';
78
79
        if ($date = shell_exec("git log -1 --pretty=format:'%cd' --date=short")) {
80
            $version['date'] = hsc($date);
81
        } else if (file_exists(DOKU_INC . '.git/HEAD')) {
82
            // we cannot use git on the shell -- let's do it manually!
83
            $headCommit = trim(file_get_contents(DOKU_INC . '.git/HEAD'));
84
            if (strpos($headCommit, 'ref: ') === 0) {
85
                // it is something like `ref: refs/heads/master`
86
                $pathToHead = substr($headCommit, 5);
87
                $headCommit = trim(file_get_contents(DOKU_INC . '.git/' . $pathToHead));
88
            }
89
            $subDir = substr($headCommit, 0, 2);
90
            $fileName = substr($headCommit, 2);
91
            $gitCommitObject = DOKU_INC . ".git/objects/$subDir/$fileName";
92
            if (file_exists($gitCommitObject) && method_exists(zlib_decode)) {
93
                $commit = zlib_decode(file_get_contents($gitCommitObject));
94
                $committerLine = explode("\n", $commit)[3];
95
                $committerData = explode(' ', $committerLine);
96
                end($committerData);
97
                $ts = prev($committerData);
98
                if ($ts && $date = date('Y-m-d', $ts)) {
99
                    $version['date'] = $date;
100
                }
101
            }
102
        }
103
    }else{
104
        global $updateVersion;
105
        $version['date'] = 'update version '.$updateVersion;
106
        $version['type'] = 'snapshot?';
107
    }
108
    return $version;
109
}
110
111
/**
112
 * Return DokuWiki's version (as a string)
113
 *
114
 * @author Anika Henke <[email protected]>
115
 */
116
function getVersion(){
117
    $version = getVersionData();
118
    return $version['type'].' '.$version['date'];
119
}
120
121
/**
122
 * Run a few sanity checks
123
 *
124
 * @author Andreas Gohr <[email protected]>
125
 */
126
function check(){
127
    global $conf;
128
    global $INFO;
129
    /* @var Input $INPUT */
130
    global $INPUT;
131
132
    if ($INFO['isadmin'] || $INFO['ismanager']){
133
        msg('DokuWiki version: '.getVersion(),1);
134
135
        if(version_compare(phpversion(),'7.2.0','<')){
136
            msg('Your PHP version is too old ('.phpversion().' vs. 7.2+ needed)',-1);
137
        }else{
138
            msg('PHP version '.phpversion(),1);
139
        }
140
    } else {
141
        if(version_compare(phpversion(),'7.2.0','<')){
142
            msg('Your PHP version is too old',-1);
143
        }
144
    }
145
146
    $mem = (int) php_to_byte(ini_get('memory_limit'));
147
    if($mem){
148
        if ($mem === -1) {
149
            msg('PHP memory is unlimited', 1);
150
        } else if ($mem < 16777216) {
151
            msg('PHP is limited to less than 16MB RAM (' . filesize_h($mem) . ').
152
            Increase memory_limit in php.ini', -1);
153
        } else if ($mem < 20971520) {
154
            msg('PHP is limited to less than 20MB RAM (' . filesize_h($mem) . '),
155
                you might encounter problems with bigger pages. Increase memory_limit in php.ini', -1);
156
        } else if ($mem < 33554432) {
157
            msg('PHP is limited to less than 32MB RAM (' . filesize_h($mem) . '),
158
                but that should be enough in most cases. If not, increase memory_limit in php.ini', 0);
159
        } else {
160
            msg('More than 32MB RAM (' . filesize_h($mem) . ') available.', 1);
161
        }
162
    }
163
164
    if(is_writable($conf['changelog'])){
165
        msg('Changelog is writable',1);
166
    }else{
167
        if (file_exists($conf['changelog'])) {
168
            msg('Changelog is not writable',-1);
169
        }
170
    }
171
172
    if (isset($conf['changelog_old']) && file_exists($conf['changelog_old'])) {
173
        msg('Old changelog exists', 0);
174
    }
175
176
    if (file_exists($conf['changelog'].'_failed')) {
177
        msg('Importing old changelog failed', -1);
178
    } else if (file_exists($conf['changelog'].'_importing')) {
179
        msg('Importing old changelog now.', 0);
180
    } else if (file_exists($conf['changelog'].'_import_ok')) {
181
        msg('Old changelog imported', 1);
182
        if (!plugin_isdisabled('importoldchangelog')) {
183
            msg('Importoldchangelog plugin not disabled after import', -1);
184
        }
185
    }
186
187
    if(is_writable(DOKU_CONF)){
188
        msg('conf directory is writable',1);
189
    }else{
190
        msg('conf directory is not writable',-1);
191
    }
192
193
    if($conf['authtype'] == 'plain'){
194
        global $config_cascade;
195
        if(is_writable($config_cascade['plainauth.users']['default'])){
196
            msg('conf/users.auth.php is writable',1);
197
        }else{
198
            msg('conf/users.auth.php is not writable',0);
199
        }
200
    }
201
202
    if(function_exists('mb_strpos')){
203
        if(defined('UTF8_NOMBSTRING')){
204
            msg('mb_string extension is available but will not be used',0);
205
        }else{
206
            msg('mb_string extension is available and will be used',1);
207
            if(ini_get('mbstring.func_overload') != 0){
208
                msg('mb_string function overloading is enabled, this will cause problems and should be disabled',-1);
209
            }
210
        }
211
    }else{
212
        msg('mb_string extension not available - PHP only replacements will be used',0);
213
    }
214
215
    if (!UTF8_PREGSUPPORT) {
216
        msg('PHP is missing UTF-8 support in Perl-Compatible Regular Expressions (PCRE)', -1);
217
    }
218
    if (!UTF8_PROPERTYSUPPORT) {
219
        msg('PHP is missing Unicode properties support in Perl-Compatible Regular Expressions (PCRE)', -1);
220
    }
221
222
    $loc = setlocale(LC_ALL, 0);
223
    if(!$loc){
224
        msg('No valid locale is set for your PHP setup. You should fix this',-1);
225
    }elseif(stripos($loc,'utf') === false){
226
        msg('Your locale <code>'.hsc($loc).'</code> seems not to be a UTF-8 locale,
227
             you should fix this if you encounter problems.',0);
228
    }else{
229
        msg('Valid locale '.hsc($loc).' found.', 1);
230
    }
231
232
    if($conf['allowdebug']){
233
        msg('Debugging support is enabled. If you don\'t need it you should set $conf[\'allowdebug\'] = 0',-1);
234
    }else{
235
        msg('Debugging support is disabled',1);
236
    }
237
238
    if($INFO['userinfo']['name']){
239
        msg('You are currently logged in as '.$INPUT->server->str('REMOTE_USER').' ('.$INFO['userinfo']['name'].')',0);
240
        msg('You are part of the groups '.implode(', ', $INFO['userinfo']['grps']),0);
241
    }else{
242
        msg('You are currently not logged in',0);
243
    }
244
245
    msg('Your current permission for this page is '.$INFO['perm'],0);
246
247
    if (file_exists($INFO['filepath']) && is_writable($INFO['filepath'])) {
248
        msg('The current page is writable by the webserver', 1);
249
    } elseif (!file_exists($INFO['filepath']) && is_writable(dirname($INFO['filepath']))) {
250
        msg('The current page can be created by the webserver', 1);
251
    } else {
252
        msg('The current page is not writable by the webserver', -1);
253
    }
254
255
    if ($INFO['writable']) {
256
        msg('The current page is writable by you', 1);
257
    } else {
258
        msg('The current page is not writable by you', -1);
259
    }
260
261
    // Check for corrupted search index
262
    $lengths = idx_listIndexLengths();
263
    $index_corrupted = false;
264
    foreach ($lengths as $length) {
265
        if (count(idx_getIndex('w', $length)) != count(idx_getIndex('i', $length))) {
266
            $index_corrupted = true;
267
            break;
268
        }
269
    }
270
271
    foreach (idx_getIndex('metadata', '') as $index) {
272
        if (count(idx_getIndex($index.'_w', '')) != count(idx_getIndex($index.'_i', ''))) {
273
            $index_corrupted = true;
274
            break;
275
        }
276
    }
277
278
    if($index_corrupted) {
279
        msg(
280
            'The search index is corrupted. It might produce wrong results and most
281
                probably needs to be rebuilt. See
282
                <a href="http://www.dokuwiki.org/faq:searchindex">faq:searchindex</a>
283
                for ways to rebuild the search index.', -1
284
        );
285
    } elseif(!empty($lengths)) {
286
        msg('The search index seems to be working', 1);
287
    } else {
288
        msg(
289
            'The search index is empty. See
290
                <a href="http://www.dokuwiki.org/faq:searchindex">faq:searchindex</a>
291
                for help on how to fix the search index. If the default indexer
292
                isn\'t used or the wiki is actually empty this is normal.'
293
        );
294
    }
295
296
    // rough time check
297
    $http = new DokuHTTPClient();
298
    $http->max_redirect = 0;
299
    $http->timeout = 3;
300
    $http->sendRequest('http://www.dokuwiki.org', '', 'HEAD');
301
    $now = time();
302
    if(isset($http->resp_headers['date'])) {
303
        $time = strtotime($http->resp_headers['date']);
304
        $diff = $time - $now;
305
306
        if(abs($diff) < 4) {
307
            msg("Server time seems to be okay. Diff: {$diff}s", 1);
308
        } else {
309
            msg("Your server's clock seems to be out of sync!
310
                 Consider configuring a sync with a NTP server.  Diff: {$diff}s");
311
        }
312
    }
313
314
}
315
316
/**
317
 * Display a message to the user
318
 *
319
 * If HTTP headers were not sent yet the message is added
320
 * to the global message array else it's printed directly
321
 * using html_msgarea()
322
 *
323
 * Triggers INFOUTIL_MSG_SHOW
324
 *
325
 * @see    html_msgarea()
326
 * @param string $message
327
 * @param int    $lvl   -1 = error, 0 = info, 1 = success, 2 = notify
328
 * @param string $line  line number
329
 * @param string $file  file number
330
 * @param int    $allow who's allowed to see the message, see MSG_* constants
331
 */
332
function msg($message,$lvl=0,$line='',$file='',$allow=MSG_PUBLIC){
333
    global $MSG, $MSG_shown;
334
    static $errors = [
335
        -1 => 'error',
336
        0 => 'info',
337
        1 => 'success',
338
        2 => 'notify',
339
    ];
340
341
    $msgdata = [
342
        'msg' => $message,
343
        'lvl' => $errors[$lvl],
344
        'allow' => $allow,
345
        'line' => $line,
346
        'file' => $file,
347
    ];
348
349
    $evt = new \dokuwiki\Extension\Event('INFOUTIL_MSG_SHOW', $msgdata);
350
    if ($evt->advise_before()) {
351
        /* Show msg normally - event could suppress message show */
352
        if($msgdata['line'] || $msgdata['file']) {
353
            $basename = \dokuwiki\Utf8\PhpString::basename($msgdata['file']);
354
            $msgdata['msg'] .=' ['.$basename.':'.$msgdata['line'].']';
355
        }
356
357
        if(!isset($MSG)) $MSG = array();
358
        $MSG[] = $msgdata;
359
        if(isset($MSG_shown) || headers_sent()){
360
            if(function_exists('html_msgarea')){
361
                html_msgarea();
362
            }else{
363
                print "ERROR(".$msgdata['lvl'].") ".$msgdata['msg']."\n";
364
            }
365
            unset($GLOBALS['MSG']);
366
        }
367
    }
368
    $evt->advise_after();
369
    unset($evt);
370
}
371
/**
372
 * Determine whether the current user is allowed to view the message
373
 * in the $msg data structure
374
 *
375
 * @param  $msg   array    dokuwiki msg structure
376
 *                         msg   => string, the message
377
 *                         lvl   => int, level of the message (see msg() function)
378
 *                         allow => int, flag used to determine who is allowed to see the message
379
 *                                       see MSG_* constants
380
 * @return bool
381
 */
382
function info_msg_allowed($msg){
383
    global $INFO, $auth;
384
385
    // is the message public? - everyone and anyone can see it
386
    if (empty($msg['allow']) || ($msg['allow'] == MSG_PUBLIC)) return true;
387
388
    // restricted msg, but no authentication
389
    if (empty($auth)) return false;
390
391
    switch ($msg['allow']){
392
        case MSG_USERS_ONLY:
393
            return !empty($INFO['userinfo']);
394
395
        case MSG_MANAGERS_ONLY:
396
            return $INFO['ismanager'];
397
398
        case MSG_ADMINS_ONLY:
399
            return $INFO['isadmin'];
400
401
        default:
402
            trigger_error('invalid msg allow restriction.  msg="'.$msg['msg'].'" allow='.$msg['allow'].'"',
403
                          E_USER_WARNING);
404
            return $INFO['isadmin'];
405
    }
406
407
    return false;
0 ignored issues
show
return false; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
408
}
409
410
/**
411
 * print debug messages
412
 *
413
 * little function to print the content of a var
414
 *
415
 * @author Andreas Gohr <[email protected]>
416
 *
417
 * @param string $msg
418
 * @param bool $hidden
419
 */
420
function dbg($msg,$hidden=false){
421
    if($hidden){
422
        echo "<!--\n";
423
        print_r($msg);
424
        echo "\n-->";
425
    }else{
426
        echo '<pre class="dbg">';
427
        echo hsc(print_r($msg,true));
428
        echo '</pre>';
429
    }
430
}
431
432
/**
433
 * Print info to debug log file
434
 *
435
 * @author Andreas Gohr <[email protected]>
436
 * @deprecated 2020-08-13
437
 * @param string $msg
438
 * @param string $header
439
 */
440
function dbglog($msg,$header=''){
441
    dbg_deprecated('\\dokuwiki\\Logger');
442
443
    // was the msg as single line string? use it as header
444
    if($header === '' && is_string($msg) && strpos($msg, "\n") === false) {
445
        $header = $msg;
446
        $msg = '';
447
    }
448
449
    Logger::getInstance(Logger::LOG_DEBUG)->log(
450
        $header, $msg
451
    );
452
}
453
454
/**
455
 * Log accesses to deprecated fucntions to the debug log
456
 *
457
 * @param string $alternative The function or method that should be used instead
458
 * @triggers INFO_DEPRECATION_LOG
459
 */
460
function dbg_deprecated($alternative = '') {
461
    \dokuwiki\Debug\DebugHelper::dbgDeprecatedFunction($alternative, 2);
462
}
463
464
/**
465
 * Print a reversed, prettyprinted backtrace
466
 *
467
 * @author Gary Owen <[email protected]>
468
 */
469
function dbg_backtrace(){
470
    // Get backtrace
471
    $backtrace = debug_backtrace();
472
473
    // Unset call to debug_print_backtrace
474
    array_shift($backtrace);
475
476
    // Iterate backtrace
477
    $calls = array();
478
    $depth = count($backtrace) - 1;
479
    foreach ($backtrace as $i => $call) {
480
        $location = $call['file'] . ':' . $call['line'];
481
        $function = (isset($call['class'])) ?
482
            $call['class'] . $call['type'] . $call['function'] : $call['function'];
483
484
        $params = array();
485
        if (isset($call['args'])){
486
            foreach($call['args'] as $arg){
487
                if(is_object($arg)){
488
                    $params[] = '[Object '.get_class($arg).']';
489
                }elseif(is_array($arg)){
490
                    $params[] = '[Array]';
491
                }elseif(is_null($arg)){
492
                    $params[] = '[NULL]';
493
                }else{
494
                    $params[] = (string) '"'.$arg.'"';
495
                }
496
            }
497
        }
498
        $params = implode(', ',$params);
499
500
        $calls[$depth - $i] = sprintf('%s(%s) called at %s',
501
                $function,
502
                str_replace("\n", '\n', $params),
503
                $location);
504
    }
505
    ksort($calls);
506
507
    return implode("\n", $calls);
508
}
509
510
/**
511
 * Remove all data from an array where the key seems to point to sensitive data
512
 *
513
 * This is used to remove passwords, mail addresses and similar data from the
514
 * debug output
515
 *
516
 * @author Andreas Gohr <[email protected]>
517
 *
518
 * @param array $data
519
 */
520
function debug_guard(&$data){
521
    foreach($data as $key => $value){
522
        if(preg_match('/(notify|pass|auth|secret|ftp|userinfo|token|buid|mail|proxy)/i',$key)){
523
            $data[$key] = '***';
524
            continue;
525
        }
526
        if(is_array($value)) debug_guard($data[$key]);
527
    }
528
}
529