Issues (992)

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/NestedTree.php (3 issues)

1
<?php
2
3
namespace XoopsModules\Extgallery;
4
5
/**
6
 * ExtGallery Class Manager
7
 *
8
 * You may not change or alter any portion of this comment or credits
9
 * of supporting developers from this source code or any supporting source code
10
 * which is considered copyrighted (c) material of the original comment or credit authors.
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
 *
15
 * @copyright   {@link https://xoops.org/ XOOPS Project}
16
 * @license     GNU GPL 2 (https://www.gnu.org/licenses/old-licenses/gpl-2.0.html)
17
 * @author      Zoullou (http://www.zoullou.net)
18
 * @package     ExtGallery
19
 */
20
21
use XoopsModules\Extgallery;
22
23
/**
24
 * Class NestedTree
25
 * @package XoopsModules\Extgallery
26
 */
27
class NestedTree
28
{
29
    public $db;
30
    public $table;
31
    public $fields;
32
33
    /**
34
     * Constructor. Set the database table name and necessary field names
35
     *
36
     * @param        $db
37
     * @param string $table       Name of the tree database table
38
     * @param string $idField     Name of the primary key ID field
39
     * @param string $parentField Name of the parent ID field
40
     * @param string $sortField   Name of the field to sort data.
41
     */
42
    public function __construct(\XoopsDatabase $db, $table, $idField, $parentField, $sortField)
43
    {
44
        $this->db     = $db;
45
        $this->table  = $db->prefix($table);
46
        $this->fields = [
47
            'id'     => $idField,
48
            'parent' => $parentField,
49
            'sort'   => $sortField,
50
        ];
51
    }
52
53
    /**
54
     * Fetch the node data for the node identified by $id
55
     *
56
     * @param int $id    The ID of the node to fetch
57
     * @return null|\XoopsObject An object containing the node's
58
     *                   data, or null if node not found
59
     */
60
    public function getNode($id)
61
    {
62
        $query = \sprintf('SELECT * FROM `%s` WHERE `%s` = %d', $this->table, $this->fields['id'], $id);
63
64
        $result = $this->db->query($query);
65
        $row    = $this->db->fetchArray($result);
66
        if ($row) {
67
            return $row;
68
        }
69
70
        return null;
71
    }
72
73
    /**
74
     * Fetch the descendants of a node, or if no node is specified, fetch the
75
     * entire tree. Optionally, only return child data instead of all descendant
76
     * data.
77
     *
78
     * @param int  $id             The ID of the node to fetch descendant data for.
79
     *                             Specify an invalid ID (e.g. 0) to retrieve all data.
80
     * @param bool $includeSelf    Whether or not to include the passed node in the
81
     *                             the results. This has no meaning if fetching entire tree.
82
     * @param bool $childrenOnly   True if only returning children data. False if
83
     *                             returning all descendant data
84
     * @return array The descendants of the passed now
85
     */
86
    public function getDescendants($id = 0, $includeSelf = false, $childrenOnly = false)
87
    {
88
        $idField = $this->fields['id'];
89
90
        $node = $this->getNode($id);
91
        if (null === $node) {
92
            $nleft     = 0;
93
            $nright    = 0;
94
            $parent_id = 0;
95
        } else {
96
            $nleft     = $node['nleft'];
97
            $nright    = $node['nright'];
98
            $parent_id = $node[$this->fields['id']];
99
        }
100
101
        if ($childrenOnly) {
102
            if ($includeSelf) {
103
                $query = \sprintf('SELECT * FROM `%s` WHERE `%s` = %d OR %s = %d ORDER BY nleft', $this->table, $this->fields['id'], $parent_id, $this->fields['parent'], $parent_id);
104
            } else {
105
                $query = \sprintf('SELECT * FROM `%s` WHERE `%s` = %d ORDER BY nleft', $this->table, $this->fields['parent'], $parent_id);
106
            }
107
        } else {
108
            if ($nleft > 0 && $includeSelf) {
109
                $query = \sprintf('SELECT * FROM `%s` WHERE nleft >= %d AND nright <= %d ORDER BY nleft', $this->table, $nleft, $nright);
110
            } else {
111
                if ($nleft > 0) {
112
                    $query = \sprintf('SELECT * FROM `%s` WHERE nleft > %d AND nright < %d ORDER BY nleft', $this->table, $nleft, $nright);
113
                } else {
114
                    $query = \sprintf('SELECT * FROM `%s` ORDER BY nleft', $this->table);
115
                }
116
            }
117
        }
118
119
        $result = $this->db->query($query);
120
121
        $arr = [];
122
        while (false !== ($row = $this->db->fetchArray($result))) {
123
            $arr[$row[$idField]] = $row;
124
        }
125
126
        return $arr;
127
    }
128
129
    /**
130
     * Fetch the children of a node, or if no node is specified, fetch the
131
     * top level items.
132
     *
133
     * @param int  $id            The ID of the node to fetch child data for.
134
     * @param bool $includeSelf   Whether or not to include the passed node in the
135
     *                            the results.
136
     * @return array The children of the passed node
137
     */
138
    public function getChildren($id = 0, $includeSelf = false)
139
    {
140
        return $this->getDescendants($id, $includeSelf, true);
141
    }
142
143
    /**
144
     * Fetch the path to a node. If an invalid node is passed, an empty array is returned.
145
     * If a top level node is passed, an array containing on that node is included (if
146
     * 'includeSelf' is set to true, otherwise an empty array)
147
     *
148
     * @param int  $id            The ID of the node to fetch child data for.
149
     * @param bool $includeSelf   Whether or not to include the passed node in the
150
     *                            the results.
151
     * @return array An array of each node to passed node
152
     */
153
    public function getPath($id = 0, $includeSelf = false)
154
    {
155
        $node = $this->getNode($id);
156
        if (null === $node) {
157
            return [];
158
        }
159
160
        if ($includeSelf) {
161
            $query = \sprintf('SELECT * FROM `%s` WHERE nleft <= %d AND nright >= %d ORDER BY nlevel', $this->table, $node['nleft'], $node['nright']);
162
        } else {
163
            $query = \sprintf('SELECT * FROM `%s` WHERE nleft < %d AND nright > %d ORDER BY nlevel', $this->table, $node['nleft'], $node['nright']);
164
        }
165
166
        $result = $this->db->query($query);
167
168
        $idField = $this->fields['id'];
169
        $arr     = [];
170
        while (false !== ($row = $this->db->fetchArray($result))) {
171
            $arr[$row[$idField]] = $row;
172
        }
173
174
        return $arr;
175
    }
176
177
    /**
178
     * Check if one node descends from another node. If either node is not
179
     * found, then false is returned.
180
     *
181
     * @param int $descendant_id The node that potentially descends
182
     * @param int $ancestor_id   The node that is potentially descended from
183
     * @return bool True if $descendant_id descends from $ancestor_id, false otherwise
184
     */
185
    public function isDescendantOf($descendant_id, $ancestor_id)
186
    {
187
        $node = $this->getNode($ancestor_id);
188
        if (null === $node) {
189
            return false;
190
        }
191
192
        $query = \sprintf(
193
            'SELECT COUNT(*) AS is_descendant
194
                                      FROM `%s`
195
                                      WHERE `%s` = %d
196
                                      AND nleft > %d
197
                                      AND nright < %d',
198
            $this->table,
199
            $this->fields['id'],
200
            $descendant_id,
201
            $node->nleft,
202
            $node->nright
203
        );
204
205
        $result = $this->db->query($query);
206
207
        $row = $this->db->fetchArray($result);
208
        if ($row) {
209
            return $row['is_descendant'] > 0;
210
        }
211
212
        return false;
213
    }
214
215
    /**
216
     * Check if one node is a child of another node. If either node is not
217
     * found, then false is returned.
218
     *
219
     * @param int $child_id  The node that is possibly a child
220
     * @param int $parent_id The node that is possibly a parent
221
     * @return bool True if $child_id is a child of $parent_id, false otherwise
222
     */
223
    public function isChildOf($child_id, $parent_id)
224
    {
225
        $query = \sprintf('SELECT COUNT(*) AS is_child FROM `%s` WHERE `%s` = %d AND %s = %d', $this->table, $this->fields['id'], $child_id, $this->fields['parent'], $parent_id);
226
227
        $result = $this->db->query($query);
228
229
        $row = $this->db->fetchArray($result);
230
        if ($row) {
231
            return $row['is_child'] > 0;
232
        }
233
234
        return false;
235
    }
236
237
    /**
238
     * Find the number of descendants a node has
239
     *
240
     * @param int $id The ID of the node to search for. Pass 0 to count all nodes in the tree.
241
     * @return int The number of descendants the node has, or -1 if the node isn't found.
242
     */
243
    public function numDescendants($id)
244
    {
245
        if (0 == $id) {
246
            $query  = \sprintf('SELECT COUNT(*) AS num_descendants FROM `%s`', $this->table);
247
            $result = $this->db->query($query);
248
            $row    = $this->db->fetchArray($result);
249
            if ($row) {
250
                return $row['num_descendants'];
251
            }
252
        } else {
253
            $node = $this->getNode($id);
254
            if (!null === $node) {
255
                return ($node['nright'] - $node['nleft'] - 1) / 2;
256
            }
257
        }
258
259
        return -1;
260
    }
261
262
    /**
263
     * Find the number of children a node has
264
     *
265
     * @param int $id The ID of the node to search for. Pass 0 to count the first level items
266
     * @return int The number of descendants the node has, or -1 if the node isn't found.
267
     */
268
    public function numChildren($id)
269
    {
270
        $query  = \sprintf('SELECT COUNT(*) AS num_children FROM `%s` WHERE `%s` = %d', $this->table, $this->fields['parent'], $id);
271
        $result = $this->db->query($query);
272
        $row    = $this->db->fetchArray($result);
273
        if ($row) {
274
            return $row['num_children'];
275
        }
276
277
        return -1;
278
    }
279
280
    /**
281
     * @param $id
282
     *
283
     * @return int
284
     */
285
    public function numLeef($id)
286
    {
287
        $query = \sprintf('SELECT COUNT(*) AS num_leef FROM `%s` WHERE nright - nleft = 1', $this->table);
288
        if (0 != $id) {
289
            $node  = $this->getNode($id);
290
            $query .= \sprintf(' AND nleft > %d AND nright < %d', $node['nleft'], $node['nright']);
291
        }
292
        $result = $this->db->query($query);
293
        $row    = $this->db->fetchArray($result);
294
        if ($row) {
295
            return $row['num_leef'];
296
        }
297
298
        return -1;
299
    }
300
301
    /**
302
     * Fetch the tree data, nesting within each node references to the node's children
303
     *
304
     * @return array The tree with the node's child data
305
     */
306
    public function getTreeWithChildren()
307
    {
308
        $idField     = $this->fields['id'];
0 ignored issues
show
The assignment to $idField is dead and can be removed.
Loading history...
309
        $parentField = $this->fields['parent'];
0 ignored issues
show
The assignment to $parentField is dead and can be removed.
Loading history...
310
311
        $query = \sprintf('SELECT * FROM `%s` ORDER BY %s', $this->table, $this->fields['sort']);
312
313
        $result = $this->db->query($query);
314
315
        // create a root node to hold child data about first level items
316
        $root                      = [];
317
        $root[$this->fields['id']] = 0;
318
        $root['children']          = [];
319
320
        $arr = [
321
            $root,
322
        ];
323
324
        // populate the array and create an empty children array
325
        while (false !== ($row = $this->db->fetchArray($result))) {
326
            $arr[$row[$this->fields['id']]]             = $row;
327
            $arr[$row[$this->fields['id']]]['children'] = [];
328
        }
329
330
        // now process the array and build the child data
331
        foreach ($arr as $id => $row) {
332
            if (isset($row[$this->fields['parent']])) {
333
                $arr[$row[$this->fields['parent']]]['children'][$id] = $id;
334
            }
335
        }
336
337
        return $arr;
338
    }
339
340
    /**
341
     * Rebuilds the tree data and saves it to the database
342
     */
343
    public function rebuild()
344
    {
345
        $data = $this->getTreeWithChildren();
346
347
        $n     = 0; // need a variable to hold the running n tally
348
        $level = 0; // need a variable to hold the running level tally
0 ignored issues
show
The assignment to $level is dead and can be removed.
Loading history...
349
350
        // invoke the recursive function. Start it processing
351
        // on the fake "root node" generated in getTreeWithChildren().
352
        // because this node doesn't really exist in the database, we
353
        // give it an initial nleft value of 0 and an nlevel of 0.
354
        $this->_generateTreeData($data, 0, 0, $n);
355
        //echo "<pre>";print_r($data);echo "</pre>";
356
        // at this point the the root node will have nleft of 0, nlevel of 0
357
        // and nright of (tree size * 2 + 1)
358
359
        foreach ($data as $id => $row) {
360
            // skip the root node
361
            if (0 == $id) {
362
                continue;
363
            }
364
365
            $query = \sprintf('UPDATE `%s` SET nlevel = %d, nleft = %d, nright = %d WHERE `%s` = %d', $this->table, $row['nlevel'], $row['nleft'], $row['nright'], $this->fields['id'], $id);
366
            $this->db->queryF($query);
367
        }
368
    }
369
370
    /**
371
     * Generate the tree data. A single call to this generates the n-values for
372
     * 1 node in the tree. This function assigns the passed in n value as the
373
     * node's nleft value. It then processes all the node's children (which
374
     * in turn recursively processes that node's children and so on), and when
375
     * it is finally done, it takes the update n-value and assigns it as its
376
     * nright value. Because it is passed as a reference, the subsequent changes
377
     * in subrequests are held over to when control is returned so the nright
378
     * can be assigned.
379
     *
380
     * @param array &$arr   A reference to the data array, since we need to
381
     *                      be able to update the data in it
382
     * @param int    $id    The ID of the current node to process
383
     * @param int    $level The nlevel to assign to the current node
384
     * @param int   &$n     A reference to the running tally for the n-value
385
     */
386
    public function _generateTreeData(&$arr, $id, $level, &$n)
387
    {
388
        $arr[$id]['nlevel'] = $level;
389
        $arr[$id]['nleft']  = ++$n;
390
391
        // loop over the node's children and process their data
392
        // before assigning the nright value
393
        foreach ($arr[$id]['children'] as $child_id) {
394
            $this->_generateTreeData($arr, $child_id, $level + 1, $n);
395
        }
396
        $arr[$id]['nright'] = ++$n;
397
    }
398
}
399