Issues (557)

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.

src/Phing/PropertyHelper.php (4 issues)

Severity
1
<?php
2
3
/**
4
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
5
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
6
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
7
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
8
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
9
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
10
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
11
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
12
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
13
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
14
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
15
 *
16
 * This software consists of voluntary contributions made by many individuals
17
 * and is licensed under the LGPL. For more information please see
18
 * <http://phing.info>.
19
 */
20
21
namespace Phing;
22
23
use Phing\Exception\BuildException;
24
use Phing\Type\PropertyValue;
25
use Phing\Util\StringHelper;
26
27
/**
28
 * Component creation and configuration.
29
 *
30
 * @author Siad Ardroumli <[email protected]>
31
 */
32
class PropertyHelper
33
{
34
    /**
35
     * @var Project
36
     */
37
    private $project;
38
39
    /**
40
     * @var null|PropertyHelper
41
     */
42
    private $next;
43
44
    /**
45
     * Project properties map (usually String to String).
46
     */
47
    private $properties = [];
48
49
    /**
50
     * Map of "user" properties (as created in the Ant task, for example).
51
     * Note that these key/value pairs are also always put into the
52
     * project properties, so only the project properties need to be queried.
53
     * Mapping is String to String.
54
     */
55
    private $userProperties = [];
56
57
    /**
58
     * Map of inherited "user" properties - that are those "user"
59
     * properties that have been created by tasks and not been set
60
     * from the command line or a GUI tool.
61
     * Mapping is String to String.
62
     */
63
    private $inheritedProperties = [];
64
65
    /**
66
     * There are 2 ways to hook into property handling:
67
     *  - you can replace the main PropertyHelper. The replacement is required
68
     * to support the same semantics (of course :-).
69
     *
70
     *  - you can chain a property helper capable of storing some properties.
71
     *  Again, you are required to respect the immutability semantics (at
72
     *  least for non-dynamic properties)
73
     *
74
     * @param PropertyHelper $next the next property helper in the chain
75
     */
76
    public function setNext(PropertyHelper $next): void
77
    {
78
        $this->next = $next;
79
    }
80
81
    /**
82
     * Get the next property helper in the chain.
83
     *
84
     * @return null|PropertyHelper the next property helper
85
     */
86 926
    public function getNext(): ?PropertyHelper
87
    {
88 926
        return $this->next;
89
    }
90
91
    /**
92
     * Factory method to create a property processor.
93
     * Users can provide their own or replace it using "ant.PropertyHelper"
94
     * reference. User tasks can also add themselves to the chain, and provide
95
     * dynamic properties.
96
     *
97
     * @param Project $project the project fro which the property helper is required
98
     *
99
     * @return PropertyHelper the project's property helper
100
     */
101 928
    public static function getPropertyHelper(Project $project): PropertyHelper
102
    {
103
        /**
104
         * @var PropertyHelper $helper
105
         */
106 928
        $helper = $project->getReference('phing.PropertyHelper');
107 928
        if (null !== $helper) {
108 907
            return $helper;
109
        }
110 928
        $helper = new self();
111 928
        $helper->setProject($project);
112
113 928
        $project->addReference('phing.PropertyHelper', $helper);
114
115 928
        return $helper;
116
    }
117
118
    // --------------------  Methods to override  --------------------
119
120
    /**
121
     * Sets a property. Any existing property of the same name
122
     * is overwritten, unless it is a user property. Will be called
123
     * from setProperty().
124
     *
125
     * If all helpers return false, the property will be saved in
126
     * the default properties table by setProperty.
127
     *
128
     * @param string $ns        the namespace that the property is in (currently
129
     *                          not used
130
     * @param string $name      The name of property to set.
131
     *                          Must not be
132
     *                          <code>null</code>.
133
     * @param string $value     The new value of the property.
134
     *                          Must not be <code>null</code>.
135
     * @param bool   $inherited true if this property is inherited (an [sub]ant[call] property)
136
     * @param bool   $user      true if this property is a user property
137
     * @param bool   $isNew     true is this is a new property
138
     *
139
     * @return bool true if this helper has stored the property, false if it
140
     *              couldn't. Each helper should delegate to the next one (unless it
141
     *              has a good reason not to).
142
     */
143 925
    public function setPropertyHook($ns, $name, $value, $inherited, $user, $isNew): bool
144
    {
145 925
        return null !== $this->getNext()
146 925
            && $this->getNext()->setPropertyHook($ns, $name, $value, $inherited, $user, $isNew);
147
    }
148
149
    /**
150
     * Get a property. If all hooks return null, the default
151
     * tables will be used.
152
     *
153
     * @param string $ns   namespace of the sought property
154
     * @param string $name name of the sought property
155
     * @param bool   $user true if this is a user property
156
     *
157
     * @return null|string the property, if returned by a hook, or null if none
158
     */
159 907
    public function getPropertyHook($ns, $name, $user): ?string
160
    {
161 907
        if (null !== $this->getNext()) {
162
            $o = $this->getNext()->getPropertyHook($ns, $name, $user);
163
            if (null !== $o) {
164
                return $o;
165
            }
166
        }
167
168 907
        if (null !== $this->project && StringHelper::startsWith('toString:', $name)) {
169 1
            $name = StringHelper::substring($name, strlen('toString:'));
170 1
            $v = $this->project->getReference($name);
171
172 1
            return (null === $v) ? null : (string) $v;
173
        }
174
175 907
        return null;
176
    }
177
178
    // -------------------- Optional methods   --------------------
179
    // You can override those methods if you want to optimize or
180
    // do advanced things (like support a special syntax).
181
    // The methods do not chain - you should use them when embedding ant
182
    // (by replacing the main helper)
183
184
    /**
185
     * Replaces <code>${xxx}</code> style constructions in the given value
186
     * with the string value of the corresponding data types.
187
     *
188
     * @param string   $value The string to be scanned for property references.
189
     *                        May be <code>null</code>, in which case this
190
     *                        method returns immediately with no effect.
191
     * @param string[] $keys  Mapping (String to String) of property names to their
192
     *                        values. If <code>null</code>, only project properties
193
     *                        will be used.
194
     *
195
     * @throws BuildException if the string contains an opening
196
     *                        <code>${</code> without a closing
197
     *                        <code>}</code>
198
     *
199
     * @return string the original string with the properties replaced, or
200
     *                <code>null</code> if the original string is <code>null</code>
201
     */
202 764
    public function replaceProperties(?string $value, ?array $keys): ?string
203
    {
204 764
        if (null === $value) {
205
            return null;
206
        }
207 764
        if (null === $keys) {
0 ignored issues
show
The condition null === $keys is always false.
Loading history...
208 1
            $keys = $this->project->getProperties();
209
        }
210
        // Because we're not doing anything special (like multiple passes),
211
        // regex is the simplest / fastest.  PropertyTask, though, uses
212
        // the old parsePropertyString() method, since it has more stringent
213
        // requirements.
214
215 764
        $sb = $value;
216 764
        $iteration = 0;
217
        // loop to recursively replace tokens
218 764
        while (false !== strpos($sb, '${')) {
219 463
            $sb = preg_replace_callback(
220 463
                '/\$\{([^\$}]+)\}/',
221 463
                function ($matches) use ($keys) {
222 463
                    $propertyName = $matches[1];
223
224 463
                    $replacement = null;
225 463
                    if (array_key_exists($propertyName, $keys)) {
226 456
                        $replacement = $keys[$propertyName];
227
                    }
228
229 463
                    if (null === $replacement) {
230 11
                        $replacement = $this->getProperty(null, $propertyName);
231
                    }
232
233 463
                    if (null === $replacement) {
234 10
                        $this->project->log(
235 10
                            'Property ${' . $propertyName . '} has not been set.',
236 10
                            Project::MSG_VERBOSE
237 10
                        );
238
239 10
                        return $matches[0];
240
                    }
241
242 457
                    $this->project->log(
243 457
                        'Property ${' . $propertyName . '} => ' . (string) $replacement,
244 457
                        Project::MSG_VERBOSE
245 457
                    );
246
247 457
                    return $replacement;
248 463
                },
249 463
                $sb
250 463
            );
251
252
            // keep track of iterations so we can break out of otherwise infinite loops.
253 463
            ++$iteration;
254 463
            if (5 === $iteration) {
255 10
                return $sb;
256
            }
257
        }
258
259 763
        return $sb;
260
    }
261
262
    // -------------------- Default implementation  --------------------
263
    // Methods used to support the default behavior and provide backward
264
    // compatibility. Some will be deprecated, you should avoid calling them.
265
266
    /**
267
     * Default implementation of setProperty. Will be called from Project.
268
     *  This is the original 1.5 implementation, with calls to the hook
269
     *  added.
270
     *
271
     * @param string $ns      the namespace for the property (currently not used)
272
     * @param string $name    the name of the property
273
     * @param string $value   the value to set the property to
274
     * @param bool   $verbose if this is true output extra log messages
275
     *
276
     * @return bool true if the property is set
277
     */
278 925
    public function setProperty($ns, $name, $value, $verbose): bool
279
    {
280
        // user (CLI) properties take precedence
281 925
        if (isset($this->userProperties[$name])) {
282 25
            if ($verbose) {
283
                $this->project->log('Override ignored for user property ' . $name, Project::MSG_VERBOSE);
284
            }
285
286 25
            return false;
287
        }
288
289 925
        $done = $this->setPropertyHook($ns, $name, $value, false, false, false);
290 925
        if ($done) {
291
            return true;
292
        }
293
294 925
        if ($verbose && isset($this->properties[$name])) {
295 4
            $this->project->log(
296 4
                'Overriding previous definition of property ' . $name,
297 4
                Project::MSG_VERBOSE
298 4
            );
299
        }
300
301 925
        if ($verbose) {
302 260
            $this->project->log(
303 260
                'Setting project property: ' . $name . ' -> '
304 260
                . $value,
305 260
                Project::MSG_DEBUG
306 260
            );
307
        }
308 925
        $this->properties[$name] = $value;
309 925
        $this->project->addReference($name, new PropertyValue($value));
310
311 925
        return true;
312
    }
313
314
    /**
315
     * Sets a property if no value currently exists. If the property
316
     * exists already, a message is logged and the method returns with
317
     * no other effect.
318
     *
319
     * @param string $ns    the namespace for the property (currently not used)
320
     * @param string $name  the name of property to set
321
     * @param string $value the new value of the property
322
     */
323 556
    public function setNewProperty($ns, string $name, string $value): void
324
    {
325 556
        if (isset($this->properties[$name])) {
326 6
            $this->project->log('Override ignored for property ' . $name, Project::MSG_VERBOSE);
327
328 6
            return;
329
        }
330
331 554
        $done = $this->setPropertyHook($ns, $name, $value, false, false, true);
332 554
        if ($done) {
333
            return;
334
        }
335
336 554
        $this->project->log('Setting project property: ' . $name . ' -> ' . $value, Project::MSG_DEBUG);
337 554
        if (null !== $name && null !== $value) {
0 ignored issues
show
The condition null !== $value is always true.
Loading history...
338 554
            $this->properties[$name] = $value;
339 554
            $this->project->addReference($name, new PropertyValue($value));
340
        }
341
    }
342
343
    /**
344
     * Sets a user property, which cannot be overwritten by
345
     * set/unset property calls. Any previous value is overwritten.
346
     *
347
     * @param string $ns    the namespace for the property (currently not used)
348
     * @param string $name  the name of property to set
349
     * @param string $value the new value of the property
350
     */
351 906
    public function setUserProperty($ns, string $name, string $value): void
352
    {
353 906
        if (null === $name || null === $value) {
0 ignored issues
show
The condition null === $value is always false.
Loading history...
354
            return;
355
        }
356 906
        $this->project->log('Setting ro project property: ' . $name . ' -> ' . $value, Project::MSG_DEBUG);
357 906
        $this->userProperties[$name] = $value;
358
359 906
        $done = $this->setPropertyHook($ns, $name, $value, false, true, false);
360 906
        if ($done) {
361
            return;
362
        }
363 906
        $this->properties[$name] = $value;
364 906
        $this->project->addReference($name, new PropertyValue($value));
365
    }
366
367
    /**
368
     * Sets an inherited user property, which cannot be overwritten by set/unset
369
     * property calls. Any previous value is overwritten. Also marks
370
     * these properties as properties that have not come from the
371
     * command line.
372
     *
373
     * @param string $ns    the namespace for the property (currently not used)
374
     * @param string $name  the name of property to set
375
     * @param string $value the new value of the property
376
     */
377 24
    public function setInheritedProperty($ns, string $name, string $value): void
378
    {
379 24
        if (null === $name || null === $value) {
0 ignored issues
show
The condition null === $value is always false.
Loading history...
380
            return;
381
        }
382 24
        $this->inheritedProperties[$name] = $value;
383
384 24
        $this->project->log(
385 24
            'Setting ro project property: ' . $name . ' -> '
386 24
            . $value,
387 24
            Project::MSG_DEBUG
388 24
        );
389 24
        $this->userProperties[$name] = $value;
390
391 24
        $done = $this->setPropertyHook($ns, $name, $value, true, false, false);
392 24
        if ($done) {
393
            return;
394
        }
395 24
        $this->properties[$name] = $value;
396 24
        $this->project->addReference($name, new PropertyValue($value));
397
    }
398
399
    // -------------------- Getting properties  --------------------
400
401
    /**
402
     * Returns the value of a property, if it is set.  You can override
403
     * this method in order to plug your own storage.
404
     *
405
     * @param string      $ns   the namespace for the property (currently not used)
406
     * @param null|string $name The name of the property.
407
     *                          May be <code>null</code>, in which case
408
     *                          the return value is also <code>null</code>.
409
     *
410
     * @return mixed the property value, or <code>null</code> for no match
411
     *               or if a <code>null</code> name is provided
412
     */
413 907
    public function getProperty($ns, ?string $name)
414
    {
415 907
        if (null === $name) {
416
            return null;
417
        }
418 907
        $o = $this->getPropertyHook($ns, $name, false);
419 907
        if (null !== $o) {
420 1
            return $o;
421
        }
422
423 907
        $found = $this->properties[$name] ?? null;
424
        // check to see if there are unresolved property references
425 907
        if (null !== $found && false !== strpos($found, '${')) {
426
            // attempt to resolve properties
427 1
            $found = $this->replaceProperties($found, null);
428 1
            if (StringHelper::startsWith('${', $found) && StringHelper::endsWith('}', $found)) {
429 1
                $found = null;
430
            }
431
            // save resolved value
432 1
            $this->properties[$name] = $found;
433
        }
434
435 907
        return $found;
436
    }
437
438
    /**
439
     * Returns the value of a user property, if it is set.
440
     *
441
     * @param string      $ns   the namespace for the property (currently not used)
442
     * @param null|string $name The name of the property.
443
     *                          May be <code>null</code>, in which case
444
     *                          the return value is also <code>null</code>.
445
     *
446
     * @return null|string the property value, or <code>null</code> for no match
447
     *                     or if a <code>null</code> name is provided
448
     */
449 13
    public function getUserProperty($ns, ?string $name): ?string
450
    {
451 13
        if (null === $name) {
452
            return null;
453
        }
454 13
        $o = $this->getPropertyHook($ns, $name, true);
455 13
        if (null !== $o) {
456
            return $o;
457
        }
458
459 13
        return $this->userProperties[$name] ?? null;
460
    }
461
462
    // -------------------- Access to property tables  --------------------
463
    // This is used to support ant call and similar tasks. It should be
464
    // deprecated, it is possible to use a better (more efficient)
465
    // mechanism to preserve the context.
466
467
    /**
468
     * Returns a copy of the properties table.
469
     *
470
     * @return array a hashtable containing all properties
471
     *               (including user properties)
472
     */
473 907
    public function getProperties(): array
474
    {
475 907
        return $this->properties;
476
    }
477
478
    /**
479
     * Returns a copy of the user property hashtable.
480
     *
481
     * @return array a hashtable containing just the user properties
482
     */
483
    public function getUserProperties(): array
484
    {
485
        return $this->userProperties;
486
    }
487
488
    public function getInheritedProperties(): array
489
    {
490
        return $this->inheritedProperties;
491
    }
492
493
    /**
494
     * Copies all user properties that have not been set on the
495
     * command line or a GUI tool from this instance to the Project
496
     * instance given as the argument.
497
     *
498
     * <p>To copy all "user" properties, you will also have to call
499
     * {@link #copyUserProperties copyUserProperties}.</p>
500
     *
501
     * @param Project $other the project to copy the properties to.  Must not be null.
502
     */
503
    public function copyInheritedProperties(Project $other): void
504
    {
505
        foreach ($this->inheritedProperties as $arg => $value) {
506
            if (null === $other->getUserProperty($arg)) {
507
                $other->setInheritedProperty($arg, (string) $this->inheritedProperties[$arg]);
508
            }
509
        }
510
    }
511
512
    /**
513
     * Copies all user properties that have been set on the command
514
     * line or a GUI tool from this instance to the Project instance
515
     * given as the argument.
516
     *
517
     * <p>To copy all "user" properties, you will also have to call
518
     * {@link #copyInheritedProperties copyInheritedProperties}.</p>
519
     *
520
     * @param Project $other the project to copy the properties to
521
     */
522 37
    public function copyUserProperties(Project $other): void
523
    {
524 37
        foreach ($this->userProperties as $arg => $value) {
525 37
            if (!isset($this->inheritedProperties[$arg])) {
526 37
                $other->setUserProperty($arg, $value);
527
            }
528
        }
529
    }
530
531
    /**
532
     * Parses a string containing <code>${xxx}</code> style property
533
     * references into two lists. The first list is a collection
534
     * of text fragments, while the other is a set of string property names.
535
     * <code>null</code> entries in the first list indicate a property
536
     * reference from the second list.
537
     *
538
     * It can be overridden with a more efficient or customized version.
539
     *
540
     * @param string $value        text to parse
541
     * @param array  $fragments    list to add text fragments to
542
     * @param array  $propertyRefs list to add property names to
543
     *
544
     * @throws BuildException if the string contains an opening
545
     *                        <code>${</code> without a closing
546
     *                        <code>}</code>
547
     */
548 21
    public function parsePropertyString(string $value, array &$fragments, array &$propertyRefs): void
549
    {
550 21
        $prev = 0;
551
552 21
        while (($pos = strpos($value, '$', $prev)) !== false) {
553 9
            if ($pos > $prev) {
554 7
                $fragments[] = StringHelper::substring($value, $prev, $pos - 1);
555
            }
556 9
            if ($pos === (strlen($value) - 1)) {
557
                $fragments[] = '$';
558
                $prev = $pos + 1;
559 9
            } elseif ('{' !== $value[$pos + 1]) {
560
                // the string positions were changed to value-1 to correct
561
                // a fatal error coming from function substring()
562
                $fragments[] = StringHelper::substring($value, $pos, $pos + 1);
563
                $prev = $pos + 2;
564
            } else {
565 9
                $endName = strpos($value, '}', $pos);
566 9
                if (false === $endName) {
567
                    throw new BuildException("Syntax error in property: {$value}");
568
                }
569 9
                $propertyName = StringHelper::substring($value, $pos + 2, $endName - 1);
570 9
                $fragments[] = null;
571 9
                $propertyRefs[] = $propertyName;
572 9
                $prev = $endName + 1;
573
            }
574
        }
575
576 21
        if ($prev < strlen($value)) {
577 21
            $fragments[] = StringHelper::substring($value, $prev);
578
        }
579
    }
580
581
    // --------------------  Hook management  --------------------
582
583
    /**
584
     * Set the project for which this helper is performing property resolution.
585
     *
586
     * @param Project $p the project instance
587
     */
588 928
    private function setProject(Project $p): void
589
    {
590 928
        $this->project = $p;
591
    }
592
}
593