Issues (380)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  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.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  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.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  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.
  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.
  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.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
  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.
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  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.
  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.
  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.
  Header Injection
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.

class/OnlineHandler.php (6 issues)

1
<?php
2
3
namespace XoopsModules\Newbb;
4
5
/**
6
 * NewBB 5.0x,  the forum module for XOOPS project
7
 *
8
 * @copyright      XOOPS Project (https://xoops.org)
9
 * @license        GNU GPL 2 or later (https://www.gnu.org/licenses/gpl-2.0.html)
10
 * @author         Taiwen Jiang (phppp or D.J.) <[email protected]>
11
 * @since          4.00
12
 * @package        module::newbb
13
 */
14
15
use Xmf\IPAddress;
16
use XoopsModules\Newbb;
17
18
19
20
require_once \dirname(__DIR__) . '/include/functions.config.php';
21
22
/**
23
 * Class OnlineHandler
24
 */
25
class OnlineHandler
26
{
27
    public $db;
28
    public $forum_id;
29
    public $forumObject;
30
    public $topic_id;
31
    public $user_ids = [];
32
33
    /**
34
     * OnlineHandler constructor.
35
     * @param \XoopsDatabase|null $db
36
     */
37
    public function __construct(\XoopsDatabase $db = null)
38
    {
39
        $this->db = $db;
40
    }
41
42
    /**
43
     * @param null|Newbb\Forum $forum
44
     * @param null|Topic       $forumtopic
45
     */
46
    public function init($forum = null, $forumtopic = null)
47
    {
48
        if (\is_object($forum)) {
49
            $this->forum_id    = $forum->getVar('forum_id');
50
            $this->forumObject = $forum;
51
        } else {
52
            $this->forum_id    = (int)$forum;
53
            $this->forumObject = $forum;
54
        }
55
        if (\is_object($forumtopic)) {
56
            $this->topic_id = $forumtopic->getVar('topic_id');
57
            if (empty($this->forum_id)) {
58
                $this->forum_id = $forumtopic->getVar('forum_id');
59
            }
60
        } else {
61
            $this->topic_id = (int)$forumtopic;
62
        }
63
64
        $this->update();
65
    }
66
67
    public function update()
68
    {
69
        global $xoopsModule;
70
71
        // set gc probabillity to 10% for now..
72
        if (\mt_rand(1, 100) < 60) {
73
            $this->gc(150);
74
        }
75
        if (\is_object($GLOBALS['xoopsUser'])) {
76
            $uid   = $GLOBALS['xoopsUser']->getVar('uid');
77
            $uname = $GLOBALS['xoopsUser']->getVar('uname');
78
            $name  = $GLOBALS['xoopsUser']->getVar('name');
79
        } else {
80
            $uid   = 0;
81
            $uname = '';
82
            $name  = '';
83
        }
84
85
        $xoops_onlineHandler = \xoops_getHandler('online');
86
        $xoopsupdate         = $xoops_onlineHandler->write($uid, $uname, \time(), $xoopsModule->getVar('mid'), \Xmf\IPAddress::fromRequest()->asReadable());
0 ignored issues
show
The method write() does not exist on XoopsObjectHandler. It seems like you code against a sub-type of XoopsObjectHandler such as XoopsPersistableObjectHandler. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

86
        /** @scrutinizer ignore-call */ 
87
        $xoopsupdate         = $xoops_onlineHandler->write($uid, $uname, \time(), $xoopsModule->getVar('mid'), \Xmf\IPAddress::fromRequest()->asReadable());
Loading history...
87
        if (!$xoopsupdate) {
88
            //xoops_error("newbb online upate error");
89
        }
90
91
        $uname = (empty($GLOBALS['xoopsModuleConfig']['show_realname']) || empty($name)) ? $uname : $name;
92
        $this->write($uid, $uname, \time(), $this->forum_id, IPAddress::fromRequest()->asReadable(), $this->topic_id);
93
    }
94
95
    /**
96
     * @param $xoopsTpl
97
     */
98
    public function render(\Smarty $xoopsTpl)
99
    {
100
        require_once \dirname(__DIR__) . '/include/functions.render.php';
101
        require_once \dirname(__DIR__) . '/include/functions.user.php';
102
        $criteria = null;
103
        if ($this->topic_id) {
104
            $criteria = new \Criteria('online_topic', $this->topic_id);
105
        } elseif ($this->forum_id) {
106
            $criteria = new \Criteria('online_forum', $this->forum_id);
107
        }
108
        $users     = $this->getAll($criteria);
109
        $num_total = \count($users);
110
111
        $num_user     = 0;
112
        $users_id     = [];
113
        $users_online = [];
114
        for ($i = 0; $i < $num_total; ++$i) {
115
            if (empty($users[$i]['online_uid'])) {
116
                continue;
117
            }
118
            $users_id[]                             = $users[$i]['online_uid'];
119
            $users_online[$users[$i]['online_uid']] = [
120
                'link'  => XOOPS_URL . '/userinfo.php?uid=' . $users[$i]['online_uid'],
121
                'uname' => $users[$i]['online_uname'],
122
            ];
123
            ++$num_user;
124
        }
125
        $num_anonymous           = $num_total - $num_user;
126
        $online                  = [];
127
        $online['image']         = \newbbDisplayImage('whosonline');
128
        $online['num_total']     = $num_total;
129
        $online['num_user']      = $num_user;
130
        $online['num_anonymous'] = $num_anonymous;
131
        $administrator_list      = \newbbIsModuleAdministrators($users_id);
132
        $moderator_list          = [];
133
        $member_list             = \array_diff(\array_keys($administrator_list), $users_id);
134
        if ($member_list) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $member_list of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
135
            if (\is_object($this->forumObject)) {
136
                $moderator_list = $this->forumObject->getVar('forum_moderator');
137
            } else {
138
                $moderator_list = \newbbIsForumModerators($member_list);
139
            }
140
        }
141
        foreach ($users_online as $uid => $user) {
142
            if (!empty($administrator_list[$uid])) {
143
                $user['level'] = 2;
144
            } elseif (!empty($moderator_list[$uid])) {
145
                $user['level'] = 1;
146
            } else {
147
                $user['level'] = 0;
148
            }
149
            $online['users'][] = $user;
150
        }
151
152
        $xoopsTpl->assign_by_ref('online', $online);
153
    }
154
155
    /**
156
     * Deprecated
157
     */
158
    public function showOnline()
159
    {
160
        require_once \dirname(__DIR__) . '/include/functions.render.php';
161
        require_once \dirname(__DIR__) . '/include/functions.user.php';
162
        $criteria = null;
163
        if ($this->topic_id) {
164
            $criteria = new \Criteria('online_topic', $this->topic_id);
165
        } elseif ($this->forum_id) {
166
            $criteria = new \Criteria('online_forum', $this->forum_id);
167
        }
168
        $users     = $this->getAll($criteria);
169
        $num_total = \count($users);
170
171
        $num_user     = 0;
172
        $users_id     = [];
173
        $users_online = [];
174
        for ($i = 0; $i < $num_total; ++$i) {
175
            if (empty($users[$i]['online_uid'])) {
176
                continue;
177
            }
178
            $users_id[]                             = $users[$i]['online_uid'];
179
            $users_online[$users[$i]['online_uid']] = [
180
                'link'  => XOOPS_URL . '/userinfo.php?uid=' . $users[$i]['online_uid'],
181
                'uname' => $users[$i]['online_uname'],
182
            ];
183
            ++$num_user;
184
        }
185
        $num_anonymous           = $num_total - $num_user;
186
        $online                  = [];
187
        $online['image']         = \newbbDisplayImage('whosonline');
188
        $online['statistik']     = \newbbDisplayImage('statistik');
189
        $online['num_total']     = $num_total;
190
        $online['num_user']      = $num_user;
191
        $online['num_anonymous'] = $num_anonymous;
192
        $administrator_list      = \newbbIsModuleAdministrators($users_id);
193
        $moderator_list          = [];
194
        $member_list             = \array_diff($users_id, \array_keys($administrator_list));
195
        if ($member_list) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $member_list of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
196
            if (\is_object($this->forumObject)) {
197
                $moderator_list = $this->forumObject->getVar('forum_moderator');
198
            } else {
199
                $moderator_list = \newbbIsForumModerators($member_list);
200
            }
201
        }
202
203
        foreach ($users_online as $uid => $user) {
204
            if (\in_array($uid, $administrator_list)) {
205
                $user['level'] = 2;
206
            } elseif (\in_array($uid, $moderator_list)) {
207
                $user['level'] = 1;
208
            } else {
209
                $user['level'] = 0;
210
            }
211
            $online['users'][] = $user;
212
        }
213
214
        return $online;
215
    }
216
217
    /**
218
     * Write online information to the database
219
     *
220
     * @param int     $uid      UID of the active user
221
     * @param string  $uname    Username
222
     * @param         $time
223
     * @param string  $forum_id Current forum_id
224
     * @param string  $ip       User's IP adress
225
     * @param         $topic_id
226
     * @return bool   TRUE on success
227
     * @internal param string $timestamp
228
     */
229
    public function write($uid, $uname, $time, $forum_id, $ip, $topic_id)
230
    {
231
        global $xoopsModule, $xoopsDB;
232
233
        $uid = (int)$uid;
234
        if ($uid > 0) {
235
            $sql = 'SELECT COUNT(*) FROM ' . $this->db->prefix('newbb_online') . ' WHERE online_uid=' . $uid;
0 ignored issues
show
The method prefix() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

235
            $sql = 'SELECT COUNT(*) FROM ' . $this->db->/** @scrutinizer ignore-call */ prefix('newbb_online') . ' WHERE online_uid=' . $uid;

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
236
        } else {
237
            $sql = 'SELECT COUNT(*) FROM ' . $this->db->prefix('newbb_online') . ' WHERE online_uid=' . $uid . " AND online_ip='" . $ip . "'";
238
        }
239
        [$count] = $this->db->fetchRow($this->db->queryF($sql));
240
        if ($count > 0) {
241
            $sql = 'UPDATE ' . $this->db->prefix('newbb_online') . " SET online_updated= '" . $time . "', online_forum = '" . $forum_id . "', online_topic = '" . $topic_id . "' WHERE online_uid = " . $uid;
242
            if (0 == $uid) {
243
                $sql .= " AND online_ip='" . $ip . "'";
244
            }
245
        } else {
246
            $sql = \sprintf('INSERT INTO `%s` (online_uid, online_uname, online_updated, online_ip, online_forum, online_topic) VALUES (%u, %s, %u, %s, %u, %u)', $this->db->prefix('newbb_online'), $uid, $this->db->quote($uname), $time, $this->db->quote($ip), $forum_id, $topic_id);
247
        }
248
        if (!$this->db->queryF($sql)) {
249
            //xoops_error($this->db->error());
250
            return false;
251
        }
252
253
        /** @var \XoopsOnlineHandler $xoops_onlineHandler */
254
        $xoops_onlineHandler = \xoops_getHandler('online');
255
        $xoopsOnlineTable    = $xoops_onlineHandler->table;
256
257
        $sql = 'DELETE FROM '
258
               . $this->db->prefix('newbb_online')
259
               . ' WHERE'
260
               . ' ( online_uid > 0 AND online_uid NOT IN ( SELECT online_uid FROM '
261
               . $xoopsOnlineTable
262
               . ' WHERE online_module ='
263
               . $xoopsModule->getVar('mid')
264
               . ' ) )'
265
               . ' OR ( online_uid = 0 AND online_ip NOT IN ( SELECT online_ip FROM '
266
               . $xoopsOnlineTable
267
               . ' WHERE online_module ='
268
               . $xoopsModule->getVar('mid')
269
               . ' AND online_uid = 0 ) )';
270
271
        $result = $this->db->queryF($sql);
272
        if ($result) {
273
            return true;
274
        }
275
        //xoops_error($this->db->error());
276
        return false;
277
    }
278
279
    /**
280
     * Garbage Collection
281
     *
282
     * Delete all online information that has not been updated for a certain time
283
     *
284
     * @param int $expire Expiration time in seconds
285
     */
286
    public function gc($expire)
287
    {
288
        global $xoopsModule;
289
        $sql = 'DELETE FROM ' . $this->db->prefix('newbb_online') . ' WHERE online_updated < ' . (\time() - (int)$expire);
290
        $this->db->queryF($sql);
291
292
        $xoops_onlineHandler = \xoops_getHandler('online');
293
        $xoops_onlineHandler->gc($expire);
0 ignored issues
show
The method gc() does not exist on XoopsObjectHandler. It seems like you code against a sub-type of XoopsObjectHandler such as XoopsPersistableObjectHandler. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

293
        $xoops_onlineHandler->/** @scrutinizer ignore-call */ 
294
                              gc($expire);
Loading history...
294
    }
295
296
    /**
297
     * Get an array of online information
298
     *
299
     * @param \CriteriaElement $criteria {@link \CriteriaElement}
300
     * @return array           Array of associative arrays of online information
301
     */
302
    public function getAll(\CriteriaElement $criteria = null)
303
    {
304
        $ret   = [];
305
        $limit = $start = 0;
306
        $sql   = 'SELECT * FROM ' . $this->db->prefix('newbb_online');
307
        if (\is_object($criteria) && $criteria instanceof \CriteriaElement) {
308
            $sql   .= ' ' . $criteria->renderWhere();
0 ignored issues
show
The method renderWhere() does not exist on CriteriaElement. Did you maybe mean render()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

308
            $sql   .= ' ' . $criteria->/** @scrutinizer ignore-call */ renderWhere();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
309
            $limit = $criteria->getLimit();
310
            $start = $criteria->getStart();
311
        }
312
        $result = $this->db->query($sql, $limit, $start);
313
        if (!$result) {
314
            return $ret;
315
        }
316
        while (false !== ($myrow = $this->db->fetchArray($result))) {
317
            $ret[] = $myrow;
318
            if ($myrow['online_uid'] > 0) {
319
                $this->user_ids[] = $myrow['online_uid'];
320
            }
321
            unset($myrow);
322
        }
323
        $this->user_ids = \array_unique($this->user_ids);
324
325
        return $ret;
326
    }
327
328
    /**
329
     * @param $uids
330
     * @return array
331
     */
332
    public function checkStatus($uids)
333
    {
334
        $online_users = [];
335
        $ret          = [];
336
        if (!empty($this->user_ids)) {
337
            $online_users = $this->user_ids;
338
        } else {
339
            $sql = 'SELECT online_uid FROM ' . $this->db->prefix('newbb_online');
340
            if (!empty($uids)) {
341
                $sql .= ' WHERE online_uid IN (' . \implode(', ', \array_map('\intval', $uids)) . ')';
342
            }
343
344
            $result = $this->db->query($sql);
345
            if (!$result) {
346
                return $ret;
347
            }
348
            while (list($uid) = $this->db->fetchRow($result)) {
349
                $online_users[] = $uid;
350
            }
351
        }
352
        foreach ($uids as $uid) {
353
            if (\in_array($uid, $online_users)) {
354
                $ret[$uid] = 1;
355
            }
356
        }
357
358
        return $ret;
359
    }
360
361
    /**
362
     * Count the number of online users
363
     *
364
     * @param \CriteriaElement $criteria {@link CriteriaElement}
365
     * @return bool
366
     */
367
    public function getCount(\CriteriaElement $criteria = null)
368
    {
369
        $sql = 'SELECT COUNT(*) FROM ' . $this->db->prefix('newbb_online');
370
        if (\is_object($criteria) && $criteria instanceof \CriteriaElement) {
371
            $sql .= ' ' . $criteria->renderWhere();
372
        }
373
        if (!$result = $this->db->query($sql)) {
374
            return false;
375
        }
376
        [$ret] = $this->db->fetchRow($result);
377
378
        return $ret;
379
    }
380
}
381