Issues (299)

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/Answer.php (4 issues)

1
<?php declare(strict_types=1);
2
3
namespace XoopsModules\Smartfaq;
4
5
/**
6
 * Module: SmartFAQ
7
 * Author: The SmartFactory <www.smartfactory.ca>
8
 * Licence: GNU
9
 */
10
11
use XoopsModules\Smartfaq;
12
13
/**
14
 * Class Answer
15
 */
16
class Answer extends \XoopsObject
17
{
18
    public $attachment_array = [];
19
    public $db;
20
21
    /**
22
     * constructor
23
     * @param int|null|array $id
24
     */
25
    public function __construct($id = null)
26
    {
27
        $this->db = \XoopsDatabaseFactory::getDatabaseConnection();
28
        $this->initVar('answerid', \XOBJ_DTYPE_INT, null, false);
29
        $this->initVar('status', \XOBJ_DTYPE_INT, -1, false);
30
        $this->initVar('faqid', \XOBJ_DTYPE_INT, null, false);
31
        $this->initVar('answer', \XOBJ_DTYPE_TXTAREA, null, true);
32
        $this->initVar('uid', \XOBJ_DTYPE_INT, 0, false);
33
        $this->initVar('datesub', \XOBJ_DTYPE_INT, null, false);
34
        $this->initVar('notifypub', \XOBJ_DTYPE_INT, 0, false);
35
36
        $this->initVar('attachment', \XOBJ_DTYPE_TXTAREA, '');
37
38
        $this->initVar('dohtml', \XOBJ_DTYPE_INT, 1, false);
39
        $this->initVar('doxcode', \XOBJ_DTYPE_INT, 1, false);
40
        $this->initVar('dosmiley', \XOBJ_DTYPE_INT, 1, false);
41
        $this->initVar('doimage', \XOBJ_DTYPE_INT, 0, false);
42
        $this->initVar('dobr', \XOBJ_DTYPE_INT, 1, false);
43
44
        // for backward compatibility
45
        if (isset($id)) {
46
            if (\is_array($id)) {
47
                $this->assignVars($id);
48
            } else {
49
                $answerHandler = new AnswerHandler($this->db);
50
                $answer        = $answerHandler->get($id);
51
                foreach ($answer->vars as $k => $v) {
52
                    $this->assignVar($k, $v['value']);
53
                }
54
            }
55
        }
56
    }
57
58
    // ////////////////////////////////////////////////////////////////////////////////////
59
    // attachment functions    TODO: there should be a file/attachment management class
60
61
    /**
62
     * @return array|mixed|null
63
     */
64
    public function getAttachment()
65
    {
66
        if (\count($this->attachment_array)) {
67
            return $this->attachment_array;
68
        }
69
        $attachment = $this->getVar('attachment');
70
        if (empty($attachment)) {
71
            $this->attachment_array = null;
72
        } else {
73
            $this->attachment_array = @\unserialize(\base64_decode($attachment, true));
74
        }
75
76
        return $this->attachment_array;
77
    }
78
79
    /**
80
     * @param $attach_key
81
     * @return bool
82
     */
83
    public function incrementDownload($attach_key)
84
    {
85
        if (!$attach_key) {
86
            return false;
87
        }
88
        $this->attachment_array[(string)$attach_key]['num_download']++;
89
90
        return $this->attachment_array[(string)$attach_key]['num_download'];
91
    }
92
93
    /**
94
     * @return bool
95
     */
96
    public function saveAttachment()
97
    {
98
        $attachment_save = '';
99
        if ($this->attachment_array && \is_array($this->attachment_array)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->attachment_array 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...
100
            $attachment_save = \base64_encode(\serialize($this->attachment_array));
101
        }
102
        $this->setVar('attachment', $attachment_save);
103
        $sql = 'UPDATE ' . $GLOBALS['xoopsDB']->prefix('smartfaq_answers') . ' SET attachment=' . $GLOBALS['xoopsDB']->quoteString($attachment_save) . ' WHERE post_id = ' . $this->getVar('answerid');
104
        if (!$result = $GLOBALS['xoopsDB']->queryF($sql)) {
105
            //xoops_error($GLOBALS["xoopsDB"]->error());
106
            return false;
107
        }
108
109
        return true;
110
    }
111
112
    /**
113
     * @param null $attach_array
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $attach_array is correct as it would always require null to be passed?
Loading history...
114
     * @return bool
115
     */
116
    public function deleteAttachment($attach_array = null)
117
    {
118
        /** @var Smartfaq\Helper $helper */
119
        $helper = Smartfaq\Helper::getInstance();
120
121
        $attach_old = $this->getAttachment();
122
        if (!\is_array($attach_old) || \count($attach_old) < 1) {
123
            return true;
124
        }
125
        $this->attachment_array = [];
126
127
        if (null === $attach_array) {
128
            $attach_array = \array_keys($attach_old);
129
        } // to delete all!
130
        if (!\is_array($attach_array)) {
131
            $attach_array = [$attach_array];
132
        }
133
134
        foreach ($attach_old as $key => $attach) {
135
            if (\in_array($key, $attach_array, true)) {
136
                @\unlink(XOOPS_ROOT_PATH . '/' . $helper->getConfig('dir_attachments') . '/' . $attach['name_saved']);
137
                @\unlink(XOOPS_ROOT_PATH . '/' . $helper->getConfig('dir_attachments') . '/thumbs/' . $attach['name_saved']); // delete thumbnails
138
                continue;
139
            }
140
            $this->attachment_array[$key] = $attach;
141
        }
142
        $attachment_save = '';
143
        if ($this->attachment_array && \is_array($this->attachment_array)) {
144
            $attachment_save = \base64_encode(\serialize($this->attachment_array));
145
        }
146
        $this->setVar('attachment', $attachment_save);
147
148
        return true;
149
    }
150
151
    /**
152
     * @param string $name_saved
153
     * @param string $name_display
154
     * @param string $mimetype
155
     * @param int    $num_download
156
     * @return bool
157
     */
158
    public function setAttachment($name_saved = '', $name_display = '', $mimetype = '', $num_download = 0)
159
    {
160
        static $counter = 0;
161
        $this->attachment_array = $this->getAttachment();
162
        if ($name_saved) {
163
            $key                          = (string)(\time() + ($counter++));
164
            $this->attachment_array[$key] = [
165
                'name_saved'   => $name_saved,
166
                'name_display' => $name_display ?? $name_saved,
167
                'mimetype'     => $mimetype,
168
                'num_download' => isset($num_download) ? (int)$num_download : 0,
169
            ];
170
        }
171
        $attachment_save = null;
172
        if (\is_array($this->attachment_array)) {
173
            $attachment_save = \base64_encode(\serialize($this->attachment_array));
174
        }
175
        $this->setVar('attachment', $attachment_save);
176
177
        return true;
178
    }
179
180
    /**
181
     * TODO: refactor
182
     * @param bool $asSource
183
     * @return string
184
     */
185
    public function displayAttachment($asSource = false)
186
    {
187
        global $xoopsModule;
188
        /** @var Smartfaq\Helper $helper */
189
        $helper = Smartfaq\Helper::getInstance();
190
191
        $post_attachment = '';
192
        $attachments     = $this->getAttachment();
193
        if ($attachments && \is_array($attachments)) {
194
            $iconHandler = \sf_getIconHandler();
195
            $mime_path   = $iconHandler->getPath('mime');
196
            require_once XOOPS_ROOT_PATH . '/modules/' . $xoopsModule->getVar('dirname', 'n') . '/include/functions.image.php';
197
            $image_extensions = ['jpg', 'jpeg', 'gif', 'png', 'bmp']; // need improve !!!
198
            $post_attachment  .= '<br><strong>' . _MD_ATTACHMENT . '</strong>:';
199
            $post_attachment  .= '<br><hr size="1" noshade="noshade"><br>';
200
            foreach ($attachments as $key => $att) {
201
                $file_extension = \ltrim(mb_strrchr($att['name_saved'], '.'), '.');
202
                $filetype       = $file_extension;
203
                if (\file_exists(XOOPS_ROOT_PATH . '/' . $mime_path . '/' . $filetype . '.gif')) {
204
                    $icon_filetype = XOOPS_URL . '/' . $mime_path . '/' . $filetype . '.gif';
205
                } else {
206
                    $icon_filetype = XOOPS_URL . '/' . $mime_path . '/unknown.gif';
207
                }
208
                $file_size = @\filesize(XOOPS_ROOT_PATH . '/' . $helper->getConfig('dir_attachments') . '/' . $att['name_saved']);
209
                $file_size = \number_format($file_size / 1024, 2) . ' KB';
210
                if ($helper->getConfig('media_allowed') && \in_array(mb_strtolower($file_extension), $image_extensions, true)) {
211
                    $post_attachment .= '<br><img src="' . $icon_filetype . '" alt="' . $filetype . '"><strong>&nbsp; ' . $att['name_display'] . '</strong> <small>(' . $file_size . ')</small>';
212
                    $post_attachment .= '<br>' . \sf_attachmentImage($att['name_saved']);
213
                    $isDisplayed     = true;
214
                } else {
215
                    global $xoopsUser;
216
                    if (empty($helper->getConfig('show_userattach'))) {
217
                        $post_attachment .= '<a href="'
218
                                            . XOOPS_URL
219
                                            . '/modules/'
220
                                            . $xoopsModule->getVar('dirname', 'n')
221
                                            . '/dl_attachment.php?attachid='
222
                                            . $key
223
                                            . '&amp;post_id='
224
                                            . $this->getVar('post_id')
225
                                            . '"> <img src="'
226
                                            . $icon_filetype
227
                                            . '" alt="'
228
                                            . $filetype
229
                                            . '"> '
230
                                            . $att['name_display']
231
                                            . '</a> '
232
                                            . _MD_FILESIZE
233
                                            . ': '
234
                                            . $file_size
235
                                            . '; '
236
                                            . _MD_HITS
237
                                            . ': '
238
                                            . $att['num_download'];
239
                    } elseif ($xoopsUser && $xoopsUser->uid() > 0 && $xoopsUser->isActive()) {
240
                        $post_attachment .= '<a href="'
241
                                            . XOOPS_URL
242
                                            . '/modules/'
243
                                            . $xoopsModule->getVar('dirname', 'n')
244
                                            . '/dl_attachment.php?attachid='
245
                                            . $key
246
                                            . '&amp;post_id='
247
                                            . $this->getVar('post_id')
248
                                            . '"> <img src="'
249
                                            . $icon_filetype
250
                                            . '" alt="'
251
                                            . $filetype
252
                                            . '"> '
253
                                            . $att['name_display']
254
                                            . '</a> '
255
                                            . _MD_FILESIZE
256
                                            . ': '
257
                                            . $file_size
258
                                            . '; '
259
                                            . _MD_HITS
260
                                            . ': '
261
                                            . $att['num_download'];
262
                    } else {
263
                        $post_attachment .= \_MD_NEWBB_SEENOTGUEST;
0 ignored issues
show
The constant _MD_NEWBB_SEENOTGUEST was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
264
                    }
265
                }
266
                $post_attachment .= '<br>';
267
            }
268
        }
269
270
        return $post_attachment;
271
    }
272
273
    // attachment functions
274
    // ////////////////////////////////////////////////////////////////////////////////////
275
276
    /**
277
     * @param bool $force
278
     * @return bool
279
     */
280
    public function store($force = true)
281
    {
282
        $answerHandler = new AnswerHandler($this->db);
283
284
        if (Constants::SF_AN_STATUS_APPROVED == $this->status()) {
285
            $criteria = new \CriteriaCompo(new \Criteria('faqid', $this->faqid()));
0 ignored issues
show
It seems like $this->faqid() can also be of type array and array; however, parameter $value of Criteria::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

285
            $criteria = new \CriteriaCompo(new \Criteria('faqid', /** @scrutinizer ignore-type */ $this->faqid()));
Loading history...
286
            $answerHandler->updateAll('status', Constants::SF_AN_STATUS_REJECTED, $criteria);
287
        }
288
289
        return $answerHandler->insert($this, $force);
290
    }
291
292
    /**
293
     * @return mixed
294
     */
295
    public function answerid()
296
    {
297
        return $this->getVar('answerid');
298
    }
299
300
    /**
301
     * @return mixed
302
     */
303
    public function faqid()
304
    {
305
        return $this->getVar('faqid');
306
    }
307
308
    /**
309
     * @param string $format
310
     * @return mixed
311
     */
312
    public function answer($format = 'S')
313
    {
314
        return $this->getVar('answer', $format);
315
    }
316
317
    /**
318
     * @return mixed
319
     */
320
    public function uid()
321
    {
322
        return $this->getVar('uid');
323
    }
324
325
    /**
326
     * @param string $dateFormat
327
     * @param string $format
328
     * @return string
329
     */
330
    public function datesub($dateFormat = 'none', $format = 'S')
331
    {
332
        if ('none' === $dateFormat) {
333
            /** @var Smartfaq\Helper $helper */
334
            $helper            = Smartfaq\Helper::getInstance();
335
            $smartModuleConfig = $helper->getConfig();
336
            $dateFormat        = $smartModuleConfig['dateformat'];
337
        }
338
339
        return \formatTimestamp($this->getVar('datesub', $format), $dateFormat);
340
    }
341
342
    /**
343
     * @return mixed
344
     */
345
    public function status()
346
    {
347
        return $this->getVar('status');
348
    }
349
350
    /**
351
     * @return bool
352
     */
353
    public function notLoaded()
354
    {
355
        return (-1 == $this->getVar('answerid'));
356
    }
357
358
    /**
359
     * @param array $notifications
360
     */
361
    public function sendNotifications($notifications = []): void
362
    {
363
        $smartModule = Smartfaq\Utility::getModuleInfo();
364
365
        $myts = \MyTextSanitizer::getInstance();
366
        /** @var \XoopsNotificationHandler $notificationHandler */
367
        $notificationHandler = \xoops_getHandler('notification');
368
369
        $faqObj = new Smartfaq\Faq($this->faqid());
370
371
        $tags                  = [];
372
        $tags['MODULE_NAME']   = $myts->displayTarea($smartModule->getVar('name'));
373
        $tags['FAQ_NAME']      = $faqObj->question();
374
        $tags['FAQ_URL']       = XOOPS_URL . '/modules/' . $smartModule->getVar('dirname') . '/faq.php?faqid=' . $faqObj->faqid();
375
        $tags['CATEGORY_NAME'] = $faqObj->getCategoryName();
376
        $tags['CATEGORY_URL']  = XOOPS_URL . '/modules/' . $smartModule->getVar('dirname') . '/category.php?categoryid=' . $faqObj->categoryid();
377
        $tags['FAQ_QUESTION']  = $faqObj->question();
378
379
        // TODO : Not sure about the 'formpreview' ...
380
        $tags['FAQ_ANSWER'] = $this->answer('formpreview');
381
        $tags['DATESUB']    = $this->datesub();
382
383
        foreach ($notifications as $notification) {
384
            switch ($notification) {
385
                case Constants::SF_NOT_ANSWER_APPROVED:
386
                    // This notification is not working for PM, but is for email... and I don't understand why???
387
                    $notificationHandler->triggerEvent('faq', $this->answerid(), 'answer_approved', $tags);
388
                    break;
389
                case -1:
390
                default:
391
                    break;
392
            }
393
        }
394
    }
395
}
396