Issues (807)

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/ZervWizard.php (10 issues)

1
<?php
2
3
namespace XoopsModules\Pedigree;
4
5
/**
6
 *  Copyright 2005 Zervaas Enterprises (www.zervaas.com.au)
7
 *
8
 *  Licensed under the Apache License, Version 2.0 (the "License");
9
 *  you may not use this file except in compliance with the License.
10
 *  You may obtain a copy of the License at
11
 *
12
 *      http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 *  Unless required by applicable law or agreed to in writing, software
15
 *  distributed under the License is distributed on an "AS IS" BASIS,
16
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 *  See the License for the specific language governing permissions and
18
 *  limitations under the License.
19
 */
20
21
/**
22
 * ZervWizard
23
 *
24
 * A class to manage multi-step forms or wizards. This involves managing
25
 * the various steps, storing its values and switching between each
26
 * step
27
 *
28
 * @author  Quentin Zervaas
29
 */
30
class ZervWizard
31
{
32
    // whether or not all steps of the form are complete
33
    public $_complete = false;
34
    // internal array to store the various steps
35
    public $_steps = [];
36
    // the current step
37
    public $_currentStep = null;
38
    // the prefix of the container key where form values are stored
39
    public $_containerPrefix = '__wiz_';
40
    // an array of any errors that have occurred
41
    public $_errors = [];
42
    // key in container where step status is stored
43
    public $_step_status_key = '__step_complete';
44
    // key in container where expected action is stored
45
    public $_step_expected_key = '__expected_action';
46
    // options to use for the wizard
47
    public $options = ['redirectAfterPost' => true];
48
    // action that resets the container
49
    public $resetAction = '__reset';
50
51
    /**
52
     * ZervWizard
53
     *
54
     * Constructor. Primarily sets up the container
55
     *
56
     * @param array  &$container Reference to container array
57
     * @param string  $name      A unique name for the wizard for container storage
58
     */
59
    public function __construct($container, $name)
60
    {
61
        if (!\is_array($container)) {
0 ignored issues
show
The condition is_array($container) is always true.
Loading history...
62
            $this->addError('container', 'Container not valid');
63
64
            return;
65
        }
66
67
        $containerKey = $this->_containerPrefix . $name;
68
        if (!\array_key_exists($containerKey, $container)) {
69
            $container[$containerKey] = [];
70
        }
71
72
        $this->container = &$container[$containerKey];
0 ignored issues
show
Bug Best Practice introduced by
The property container does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
73
74
        if (!\array_key_exists('_errors', $this->container)) {
75
            $this->container['_errors'] = [];
76
        }
77
        $this->_errors = &$this->container['_errors'];
78
    }
79
80
    /**
81
     * process
82
     *
83
     * Processes the form for the specified step. If the processed step
84
     * is complete, then the wizard is set to use the next step. If this
85
     * is the initial call to process, then the wizard is set to use the
86
     * first step. Once the next step is determined, the prepare method
87
     * is called for the step. This has the method name prepare_[step name]()
88
     *
89
     * @param string|null $action  The step being processed. This should correspond
90
     *                             to a step created in addStep()
91
     * @param array  &    $form    The unmodified form values to process
0 ignored issues
show
Documentation Bug introduced by
The doc comment & at position 0 could not be parsed: Unknown type name '&' at position 0 in &.
Loading history...
92
     * @param bool        $process True if the step is being processed, false if being prepared
93
     * @param string|null $action  The step being processed. This should correspond
94
     *                             to a step created in addStep()
95
     * @param array  &    $form    The unmodified form values to process
96
     * @param bool        $process True if the step is being processed, false if being prepared
97
     * @todo    Need a way to jump between steps, e.g. from step 2 to 4 and validating all data
98
     *
99
     */
100
    public function process($action, $form, $process = true)
101
    {
102
        if ($action == $this->resetAction) {
103
            $this->clearContainer();
104
            $this->setCurrentStep($this->getFirstIncompleteStep());
105
        } elseif (isset($form['reset'])) {
106
            $this->clearContainer();
107
            $this->setCurrentStep($this->getFirstIncompleteStep());
108
        } elseif (isset($form['previous']) && !$this->isFirstStep()) {
109
            // clear out errors
110
            $this->_errors = [];
111
112
            $this->setCurrentStep($this->getPreviousStep($action));
113
            $this->doRedirect();
114
        } elseif (isset($form['addvalue']) && !$this->isFirstStep()) {
115
            // clear out errors
116
            $this->_errors = [];
117
118
            // processing callback must exist and validate to proceed
119
            $callback = 'process' . $action;
120
            $complete = \method_exists($this, $callback) && $this->$callback($form);
121
122
            $this->container[$this->_step_status_key][$action] = $complete;
0 ignored issues
show
Bug Best Practice introduced by
The property container does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
123
            $this->setCurrentStep($action);
124
        } else {
125
            $proceed = false;
126
127
            // check if the step to be processed is valid
128
            if ('' === $action) {
129
                $action = $this->getExpectedStep();
130
            }
131
132
            if ($this->stepCanBeProcessed($action)) {
133
                if ($this->getStepNumber($action) <= $this->getStepNumber($this->getExpectedStep())) {
134
                    $proceed = true;
135
                } else {
136
                    $proceed = false;
137
                }
138
            }
139
140
            if ($proceed) {
141
                if ($process) {
142
                    // clear out errors
143
                    $this->_errors = [];
144
145
                    // processing callback must exist and validate to proceed
146
                    $callback = 'process' . $action;
147
                    $complete = \method_exists($this, $callback) && $this->$callback($form);
148
149
                    $this->container[$this->_step_status_key][$action] = $complete;
150
151
                    if ($complete) {
152
                        $this->setCurrentStep($this->getFollowingStep($action));
153
                    } // all ok, go to next step
154
                    else {
155
                        $this->setCurrentStep($action);
156
                    } // error occurred, redo step
157
158
                    // final processing once complete
159
                    if ($this->isComplete()) {
160
                        $this->completeCallback();
161
                    }
162
163
                    $this->doRedirect();
164
                } else {
165
                    $this->setCurrentStep($action);
166
                }
167
            } else {
168
                // when initally starting the wizard
169
170
                $this->setCurrentStep($this->getFirstIncompleteStep());
171
            }
172
        }
173
174
        // setup any required data for this step
175
        $callback = 'prepare' . $this->getStepName();
176
        if (\method_exists($this, $callback)) {
177
            $this->$callback();
178
        }
179
    }
180
181
    /**
182
     * completeCallback
183
     *
184
     * Function to run once the final step has been processed and is valid.
185
     * This should be overwritten in child classes
186
     */
187
    public function completeCallback()
188
    {
189
    }
190
191
    public function doRedirect()
192
    {
193
        if ($this->coalesce($this->options['redirectAfterPost'], false)) {
194
            $redir = $_SERVER['REQUEST_URI'];
195
            $redir = \preg_replace('/\?' . preg_quote($_SERVER['QUERY_STRING'], '/') . '$/', '', $redir);
196
            \header('Location: ' . $redir);
197
            exit;
198
        }
199
    }
200
201
    /**
202
     * isComplete
203
     *
204
     * Check if the form is complete. This can only be properly determined
205
     * after process() has been called.
206
     *
207
     * @return bool True if the form is complete and valid, false if not
208
     */
209
    public function isComplete()
210
    {
211
        return $this->_complete;
212
    }
213
214
    /**
215
     * setCurrentStep
216
     *
217
     * Sets the current step in the form. This should generally only be
218
     * called internally but you may have reason to change the current
219
     * step.
220
     *
221
     * @param string|null $step The step to set as current
222
     */
223
    public function setCurrentStep($step)
224
    {
225
        if (null === $step || !$this->stepExists($step)) {
226
            $this->_complete                            = true;
227
            $this->container[$this->_step_expected_key] = null;
0 ignored issues
show
Bug Best Practice introduced by
The property container does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
228
        } else {
229
            $this->_currentStep                         = $step;
230
            $this->container[$this->_step_expected_key] = $step;
231
        }
232
    }
233
234
    /**
235
     * @return mixed|null
236
     */
237
    public function getExpectedStep()
238
    {
239
        $step = $this->coalesce($this->container[$this->_step_expected_key], null);
240
        if ($this->stepExists($step)) {
241
            return $step;
242
        }
243
244
        return null;
245
    }
246
247
    /**
248
     * stepExists
249
     *
250
     * Check if the given step exists
251
     *
252
     * @param string $stepname The name of the step to check for
253
     *
254
     * @return bool True if the step exists, false if not
255
     */
256
    public function stepExists($stepname)
257
    {
258
        return \array_key_exists($stepname, $this->_steps);
259
    }
260
261
    /**
262
     * getStepName
263
     *
264
     * Get the name of the current step
265
     *
266
     * @return string The name of the current step
267
     */
268
    public function getStepName()
269
    {
270
        return $this->_currentStep;
271
    }
272
273
    /**
274
     * getStepNumber
275
     *
276
     * Gets the step number (from 1 to N where N is the number of steps
277
     * in the wizard) of the current step
278
     *
279
     * @param string $step Optional. The step to get the number for. If null then uses current step
280
     *
281
     * @return int The number of the step. 0 if something went wrong
282
     */
283
    public function getStepNumber($step = null)
284
    {
285
        $steps    = \array_keys($this->_steps);
286
        $numSteps = \count($steps);
287
288
        if ('' === $step) {
289
            $step = $this->getStepName();
290
        }
291
292
        $ret = 0;
293
        for ($n = 1; $n <= $numSteps && 0 == $ret; ++$n) {
294
            if ($step == $steps[$n - 1]) {
295
                $ret = $n;
296
            }
297
        }
298
299
        return $ret;
300
    }
301
302
    /**
303
     * @param $step
304
     *
305
     * @return bool
306
     */
307
    public function stepCanBeProcessed($step)
308
    {
309
        $steps    = \array_keys($this->_steps);
310
        $numSteps = \count($steps);
0 ignored issues
show
The assignment to $numSteps is dead and can be removed.
Loading history...
311
312
        foreach ($steps as $iValue) {
313
            $_step = $iValue;
314
            if ($_step == $step) {
315
                break;
316
            }
317
318
            if (!$this->container[$this->_step_status_key][$_step]) {
319
                return false;
320
            }
321
        }
322
323
        return true;
324
    }
325
326
    /**
327
     * getStepProperty
328
     *
329
     * Retrieve a property for a given step. At this stage, the only
330
     * property steps have is a title property.
331
     *
332
     * @param string $key     The key to get a property for
333
     * @param mixed  $default The value to return if the key isn't found
334
     *
335
     * @return mixed The property value or the default value
336
     */
337
    public function getStepProperty($key, $default = null)
338
    {
339
        $step = $this->getStepName();
340
        if (isset($this->_steps[$step][$key])) {
341
            return $this->_steps[$step][$key];
342
        }
343
344
        return $default;
345
    }
346
347
    /**
348
     * getFirstStep
349
     *
350
     * Get the step name of the first step
351
     *
352
     * @return string The name of the first step, or null if no steps
353
     */
354
    public function getFirstStep()
355
    {
356
        $steps = \array_keys($this->_steps);
357
358
        return \count($steps) > 0 ? $steps[0] : null;
359
    }
360
361
    /**
362
     * @return mixed|null
363
     */
364
    public function getFirstIncompleteStep()
365
    {
366
        $steps    = \array_keys($this->_steps);
367
        $numSteps = \count($steps);
0 ignored issues
show
The assignment to $numSteps is dead and can be removed.
Loading history...
368
369
        foreach ($steps as $iValue) {
370
            $_step = $iValue;
371
372
            if (!\array_key_exists($this->_step_status_key, $this->container)
373
                || !$this->container[$this->_step_status_key][$_step]) {
374
                return $_step;
375
            }
376
        }
377
378
        return null;
379
    }
380
381
    /**
382
     * getPreviousStep
383
     *
384
     * Gets the step name of the previous step. If the current
385
     * step is the first step, then null is returned
386
     *
387
     * @param $step
388
     *
389
     * @return string The name of the previous step, or null
390
     */
391
    public function getPreviousStep($step)
392
    {
393
        $ret   = null;
394
        $steps = \array_keys($this->_steps);
395
396
        $done = false;
0 ignored issues
show
The assignment to $done is dead and can be removed.
Loading history...
397
        foreach ($steps as $s) {
398
            if ($s == $step) {
399
                $done = true;
400
                break;
401
            }
402
            $ret = $s;
403
        }
404
405
        return $ret;
406
    }
407
408
    /**
409
     * getFollowingStep
410
     *
411
     * Get the step name of the next step. If the current
412
     * step is the last step, returns null
413
     *
414
     * @param $step
415
     *
416
     * @return string The name of the next step, or null
417
     */
418
    public function getFollowingStep($step)
419
    {
420
        $ret   = null;
421
        $steps = \array_keys($this->_steps);
422
423
        $ready = false;
424
        foreach ($steps as $s) {
425
            if ($s == $step) {
426
                $ready = true;
427
            } else {
428
                if ($ready) {
429
                    $ret = $s;
430
                    break;
431
                }
432
            }
433
        }
434
435
        return $ret;
436
    }
437
438
    /**
439
     * addStep
440
     *
441
     * Adds a step to the wizard
442
     *
443
     * @param string $stepname The name of the step
444
     * @param string $title    The title of the current step
445
     */
446
    public function addStep($stepname, $title)
447
    {
448
        if (\array_key_exists($stepname, $this->_steps)) {
449
            $this->addError('step', 'Step with name ' . $stepname . ' already exists');
450
451
            return;
452
        }
453
454
        $this->_steps[$stepname] = ['title' => $title];
455
456
        if (!\array_key_exists($this->_step_status_key, $this->container)) {
457
            $this->container[$this->_step_status_key] = [];
0 ignored issues
show
Bug Best Practice introduced by
The property container does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
458
        }
459
460
        if (!\array_key_exists($stepname, $this->container[$this->_step_status_key])) {
461
            $this->container[$this->_step_status_key][$stepname] = false;
462
        }
463
    }
464
465
    /**
466
     * isFirstStep
467
     *
468
     * Check if the current step is the first step
469
     *
470
     * @return bool True if the current step is the first step
471
     */
472
    public function isFirstStep()
473
    {
474
        $steps = \array_keys($this->_steps);
475
476
        return \count($steps) > 0 && $steps[0] == $this->getStepName();
477
    }
478
479
    /**
480
     * isLastStep
481
     *
482
     * Check if the current step is the last step
483
     *
484
     * @return bool True if the current step is the last step
485
     */
486
    public function isLastStep()
487
    {
488
        $steps = \array_keys($this->_steps);
489
490
        return \count($steps) > 0 && \array_pop($steps) == $this->getStepName();
491
    }
492
493
    /**
494
     * setValue
495
     *
496
     * Sets a value in the container
497
     *
498
     * @param string $key The key for the value to set
499
     * @param mixed  $val The value
500
     */
501
    public function setValue($key, $val)
502
    {
503
        $this->container[$key] = $val;
0 ignored issues
show
Bug Best Practice introduced by
The property container does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
504
    }
505
506
    /**
507
     * getValue
508
     *
509
     * Gets a value from the container
510
     *
511
     * @param string $key     The key for the value to get
512
     * @param mixed  $default The value to return if the key doesn't exist
513
     *
514
     * @return mixed Either the key's value or the default value
515
     */
516
    public function getValue($key, $default = null)
517
    {
518
        return $this->coalesce($this->container[$key], $default);
519
    }
520
521
    /**
522
     * clearContainer
523
     *
524
     * Removes all data from the container. This is primarily used
525
     * to reset the wizard data completely
526
     */
527
    public function clearContainer()
528
    {
529
        foreach ($this->container as $k => $v) {
530
            unset($this->container[$k]);
531
        }
532
    }
533
534
    /**
535
     * coalesce
536
     *
537
     * Initializes a variable, by returning either the variable
538
     * or a default value
539
     *
540
     * @param mixed &$var     The variable to fetch
541
     * @param mixed  $default The value to return if variable doesn't exist or is null
542
     *
543
     * @return mixed The variable value or the default value
544
     */
545
    public function coalesce($var, $default = null)
546
    {
547
        return (isset($var) && null !== $var) ? $var : $default;
548
    }
549
550
    /**
551
     * addError
552
     *
553
     * Add an error
554
     *
555
     * @param string $key An identifier for the error (e.g. the field name)
556
     * @param string $val An error message
557
     */
558
    public function addError($key, $val)
559
    {
560
        $this->_errors[$key] = $val;
561
    }
562
563
    /**
564
     * isError
565
     *
566
     * Check if an error has occurred
567
     *
568
     * @param string $key The field to check for error. If none specified checks for any error
569
     *
570
     * @return bool True if an error has occurred, false if not
571
     */
572
    public function isError($key = null)
573
    {
574
        if (null !== $key) {
575
            return \array_key_exists($key, $this->_errors);
576
        }
577
578
        return \count($this->_errors) > 0;
579
    }
580
581
    /**
582
     * @param $key
583
     * @return mixed|null
584
     */
585
    public function getError($key)
586
    {
587
        return \array_key_exists($key, $this->_errors) ? $this->_errors[$key] : null;
588
    }
589
}
590