Completed
Push — V6 ( bb5c65...622987 )
by Georges
02:29
created

DriverBaseTrait::writefile()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 31
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 17
nc 2
nop 3
dl 0
loc 31
rs 8.8571
c 0
b 0
f 0
1
<?php
2
/**
3
 *
4
 * This file is part of phpFastCache.
5
 *
6
 * @license MIT License (MIT)
7
 *
8
 * For full copyright and license information, please see the docs/CREDITS.txt file.
9
 *
10
 * @author Khoa Bui (khoaofgod)  <[email protected]> http://www.phpfastcache.com
11
 * @author Georges.L (Geolim4)  <[email protected]>
12
 *
13
 */
14
namespace phpFastCache\Core\Pool;
15
16
use phpFastCache\Exceptions\phpFastCacheDriverException;
17
use phpFastCache\Core\Item\ExtendedCacheItemInterface;
18
19
20
/**
21
 * Class DriverBaseTrait
22
 * @package phpFastCache\Cache
23
 */
24
trait DriverBaseTrait
25
{
26
    use ExtendedCacheItemPoolTrait;
27
28
    /**
29
     * @var array default options, this will be merge to Driver's Options
30
     */
31
    protected $config = [];
32
33
    /**
34
     * @var bool
35
     */
36
    protected $fallback = false;
37
38
    /**
39
     * @var mixed Instance of driver service
40
     */
41
    protected $instance;
42
    
43
    /**
44
     * @param $config_name
45
     * @param string $value
46
     */
47
    public function setup($config_name, $value = '')
48
    {
49
        /**
50
         * Config for class
51
         */
52
        if (is_array($config_name)) {
53
            $this->config = array_merge($this->config, $config_name);
54
        } else {
55
            $this->config[ $config_name ] = $value;
56
        }
57
    }
58
59
    /**
60
     * @return array
61
     */
62
    public function getConfig()
63
    {
64
        return $this->config;
65
    }
66
67
    /**
68
     * @param $file
69
     * @return string
70
     * @throws phpFastCacheDriverException
71
     */
72
    protected function readfile($file)
73
    {
74
        if (function_exists('file_get_contents')) {
75
            return file_get_contents($file);
0 ignored issues
show
Security File Exposure introduced by
$file can contain request data and is used in file inclusion context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Fetching key HTTP_HOST from $_SERVER, and $_SERVER['HTTP_HOST'] is passed through str_replace(), and str_replace(':', '_', $_SERVER['HTTP_HOST']) is passed through strtolower(), and strtolower(str_replace(':', '_', $_SERVER['HTTP_HOST'])) is passed through preg_replace(), and $securityKey is assigned
    in src/phpFastCache/Core/Pool/IO/PathSeekerTrait.php on line 60
  2. Data is passed through trim(), and Data is passed through preg_replace()
    in vendor/src/phpFastCache/Core/Pool/IO/PathSeekerTrait.php on line 194
  3. $securityKey is assigned
    in src/phpFastCache/Core/Pool/IO/PathSeekerTrait.php on line 70
  4. $full_path is assigned
    in src/phpFastCache/Core/Pool/IO/PathSeekerTrait.php on line 87
  5. $full_path is passed through realpath()
    in src/phpFastCache/Core/Pool/IO/PathSeekerTrait.php on line 104
  6. PathSeekerTrait::getPath() returns tainted data
    in src/phpFastCache/Core/Pool/IO/PathSeekerTrait.php on line 133
  7. PathSeekerTrait::getFileDir() returns tainted data, and $path is assigned
    in src/phpFastCache/Core/Pool/IO/PathSeekerTrait.php on line 144
  8. PathSeekerTrait::getFilePath() returns tainted data, and $file_path is assigned
    in src/phpFastCache/Drivers/Files/Driver.php on line 113
  9. $file_path is passed to DriverBaseTrait::readfile()
    in src/phpFastCache/Drivers/Files/Driver.php on line 118

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
76
        } else {
77
            $string = '';
78
79
            $file_handle = @fopen($file, 'r');
80
            if (!$file_handle) {
81
                throw new phpFastCacheDriverException("Cannot read file located at: {$file}");
82
            }
83
            while (!feof($file_handle)) {
84
                $line = fgets($file_handle);
85
                $string .= $line;
86
            }
87
            fclose($file_handle);
88
89
            return $string;
90
        }
91
    }
92
93
    /**
94
     * @param string $file
95
     * @param string $data
96
     * @param bool $secureFileManipulation
97
     * @return bool
98
     */
99
    protected function writefile($file, $data, $secureFileManipulation = false)
100
    {
101
        /**
102
         * @eventName CacheWriteFileOnDisk
103
         * @param ExtendedCacheItemPoolInterface $this
104
         * @param string $file
105
         * @param bool $secureFileManipulation
106
         *
107
         */
108
        $this->eventManager->dispatch('CacheWriteFileOnDisk', $this, $file, $secureFileManipulation);
109
110
        if($secureFileManipulation){
111
            $tmpFilename = realpath(dirname($file) . '/tmp_' . md5(
112
                str_shuffle(uniqid($this->getDriverName(), false))
113
                . str_shuffle(uniqid($this->getDriverName(), false))
114
              ));
115
116
            $f = fopen($tmpFilename, 'w+');
117
            flock($f, LOCK_EX);
118
            $octetWritten = fwrite($f, $data);
119
            flock($f, LOCK_UN);
120
            fclose($f);
121
            rename($tmpFilename, $file);
0 ignored issues
show
Security File Manipulation introduced by
$tmpFilename can contain request data and is used in file manipulation context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Fetching key HTTP_HOST from $_SERVER, and $_SERVER['HTTP_HOST'] is passed through str_replace(), and str_replace(':', '_', $_SERVER['HTTP_HOST']) is passed through strtolower(), and strtolower(str_replace(':', '_', $_SERVER['HTTP_HOST'])) is passed through preg_replace(), and $securityKey is assigned
    in src/phpFastCache/Core/Pool/IO/PathSeekerTrait.php on line 60
  2. Data is passed through trim(), and Data is passed through preg_replace()
    in vendor/src/phpFastCache/Core/Pool/IO/PathSeekerTrait.php on line 194
  3. $securityKey is assigned
    in src/phpFastCache/Core/Pool/IO/PathSeekerTrait.php on line 70
  4. $full_path is assigned
    in src/phpFastCache/Core/Pool/IO/PathSeekerTrait.php on line 87
  5. $full_path is passed through realpath()
    in src/phpFastCache/Core/Pool/IO/PathSeekerTrait.php on line 104
  6. PathSeekerTrait::getPath() returns tainted data
    in src/phpFastCache/Core/Pool/IO/PathSeekerTrait.php on line 133
  7. PathSeekerTrait::getFileDir() returns tainted data, and $path is assigned
    in src/phpFastCache/Core/Pool/IO/PathSeekerTrait.php on line 144
  8. PathSeekerTrait::getFilePath() returns tainted data, and $file_path is assigned
    in src/phpFastCache/Drivers/Files/Driver.php on line 72
  9. $file_path is passed to DriverBaseTrait::writefile()
    in src/phpFastCache/Drivers/Files/Driver.php on line 94
  10. $file is passed through dirname(), and dirname($file) . '/tmp_' . md5(str_shuffle(uniqid($this->getDriverName(), false)) . str_shuffle(uniqid($this->getDriverName(), false))) is passed through realpath(), and $tmpFilename is assigned
    in src/phpFastCache/Core/Pool/DriverBaseTrait.php on line 111

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
Security File Manipulation introduced by
$file can contain request data and is used in file manipulation context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Fetching key HTTP_HOST from $_SERVER, and $_SERVER['HTTP_HOST'] is passed through str_replace(), and str_replace(':', '_', $_SERVER['HTTP_HOST']) is passed through strtolower(), and strtolower(str_replace(':', '_', $_SERVER['HTTP_HOST'])) is passed through preg_replace(), and $securityKey is assigned
    in src/phpFastCache/Core/Pool/IO/PathSeekerTrait.php on line 60
  2. Data is passed through trim(), and Data is passed through preg_replace()
    in vendor/src/phpFastCache/Core/Pool/IO/PathSeekerTrait.php on line 194
  3. $securityKey is assigned
    in src/phpFastCache/Core/Pool/IO/PathSeekerTrait.php on line 70
  4. $full_path is assigned
    in src/phpFastCache/Core/Pool/IO/PathSeekerTrait.php on line 87
  5. $full_path is passed through realpath()
    in src/phpFastCache/Core/Pool/IO/PathSeekerTrait.php on line 104
  6. PathSeekerTrait::getPath() returns tainted data
    in src/phpFastCache/Core/Pool/IO/PathSeekerTrait.php on line 133
  7. PathSeekerTrait::getFileDir() returns tainted data, and $path is assigned
    in src/phpFastCache/Core/Pool/IO/PathSeekerTrait.php on line 144
  8. PathSeekerTrait::getFilePath() returns tainted data, and $file_path is assigned
    in src/phpFastCache/Drivers/Files/Driver.php on line 72
  9. $file_path is passed to DriverBaseTrait::writefile()
    in src/phpFastCache/Drivers/Files/Driver.php on line 94

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
122
        }else{
123
            $f = fopen($file, 'w+');
124
            $octetWritten = fwrite($f, $data);
125
            fclose($f);
126
        }
127
128
        return $octetWritten !== false;
129
    }
130
131
    /**
132
     * Encode data types such as object/array
133
     * for driver that does not support
134
     * non-scalar value
135
     * @param $data
136
     * @return string
137
     */
138
    protected function encode($data)
139
    {
140
        return serialize($data);
141
    }
142
143
    /**
144
     * Decode data types such as object/array
145
     * for driver that does not support
146
     * non-scalar value
147
     * @param $value
148
     * @return mixed
149
     */
150
    protected function decode($value)
151
    {
152
        return @unserialize($value);
0 ignored issues
show
Security Object Injection introduced by
$value can contain request data and is used in unserialized context(s) leading to a potential security vulnerability.

2 paths for user data to reach this point

  1. Path: Read from $_COOKIE, and $_COOKIE[$keyword] is decoded by json_decode(), and json_decode($_COOKIE[$keyword], true) is passed to DriverBaseTrait::decode() in src/phpFastCache/Drivers/Cookie/Driver.php on line 104
  1. Read from $_COOKIE, and $_COOKIE[$keyword] is decoded by json_decode(), and json_decode($_COOKIE[$keyword], true) is passed to DriverBaseTrait::decode()
    in src/phpFastCache/Drivers/Cookie/Driver.php on line 104
  2. Path: Fetching key HTTP_HOST from $_SERVER, and $_SERVER['HTTP_HOST'] is passed through str_replace(), and str_replace(':', '_', $_SERVER['HTTP_HOST']) is passed through strtolower(), and strtolower(str_replace(':', '_', $_SERVER['HTTP_HOST'])) is passed through preg_replace(), and $securityKey is assigned in src/phpFastCache/Core/Pool/IO/PathSeekerTrait.php on line 60
  1. Fetching key HTTP_HOST from $_SERVER, and $_SERVER['HTTP_HOST'] is passed through str_replace(), and str_replace(':', '_', $_SERVER['HTTP_HOST']) is passed through strtolower(), and strtolower(str_replace(':', '_', $_SERVER['HTTP_HOST'])) is passed through preg_replace(), and $securityKey is assigned
    in src/phpFastCache/Core/Pool/IO/PathSeekerTrait.php on line 60
  2. Data is passed through trim(), and Data is passed through preg_replace()
    in vendor/src/phpFastCache/Core/Pool/IO/PathSeekerTrait.php on line 194
  3. $securityKey is assigned
    in src/phpFastCache/Core/Pool/IO/PathSeekerTrait.php on line 70
  4. $full_path is assigned
    in src/phpFastCache/Core/Pool/IO/PathSeekerTrait.php on line 87
  5. $full_path is passed through realpath()
    in src/phpFastCache/Core/Pool/IO/PathSeekerTrait.php on line 104
  6. PathSeekerTrait::getPath() returns tainted data
    in src/phpFastCache/Core/Pool/IO/PathSeekerTrait.php on line 133
  7. PathSeekerTrait::getFileDir() returns tainted data, and $path is assigned
    in src/phpFastCache/Core/Pool/IO/PathSeekerTrait.php on line 144
  8. PathSeekerTrait::getFilePath() returns tainted data, and $file_path is assigned
    in src/phpFastCache/Drivers/Files/Driver.php on line 113
  9. Data is passed through file_get_contents()
    in vendor/src/phpFastCache/Core/Pool/DriverBaseTrait.php on line 75
  10. $content is assigned
    in src/phpFastCache/Drivers/Files/Driver.php on line 118
  11. $content is passed to DriverBaseTrait::decode()
    in src/phpFastCache/Drivers/Files/Driver.php on line 120

Preventing Object Injection Attacks

If you pass raw user-data to unserialize() for example, this can be used to create an object of any class that is available in your local filesystem. For an attacker, classes that have magic methods like __destruct or __wakeup are particularly interesting in such a case, as they can be exploited very easily.

We recommend to not pass user data to such a function. In case of unserialize, better use JSON to transfer data.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
153
    }
154
155
    /**
156
     * Check phpModules or CGI
157
     * @return bool
158
     */
159
    protected function isPHPModule()
160
    {
161
        if (PHP_SAPI === 'apache2handler') {
162
            return true;
163
        } else {
164
            if (strpos(PHP_SAPI, 'handler') !== false) {
165
                return true;
166
            }
167
        }
168
169
        return false;
170
    }
171
172
173
    /**
174
     * @param $class
175
     * @return bool
176
     */
177
    protected function isExistingDriver($class)
178
    {
179
        return class_exists("\\phpFastCache\\Drivers\\{$class}");
180
    }
181
182
183
    /**
184
     * @param $tag
185
     * @return string
186
     */
187
    protected function _getTagName($tag)
188
    {
189
        return "__tag__" . $tag;
190
    }
191
192
    /**
193
     * @param \phpFastCache\Core\Item\ExtendedCacheItemInterface $item
194
     * @return array
195
     */
196
    public function driverPreWrap(ExtendedCacheItemInterface $item)
197
    {
198
        $wrap = [
199
          self::DRIVER_DATA_WRAPPER_INDEX => $item->get(),
200
          self::DRIVER_TAGS_WRAPPER_INDEX => $item->getTags(),
201
          self::DRIVER_EDATE_WRAPPER_INDEX => $item->getExpirationDate(),
202
        ];
203
204
        if($this->config['itemDetailedDate']){
205
            $wrap[ self::DRIVER_MDATE_WRAPPER_INDEX ] = new \DateTime();
206
            /**
207
             * If the creation date exists
208
             * reuse it else set a new Date
209
             */
210
            $wrap[ self::DRIVER_CDATE_WRAPPER_INDEX ] = $item->getCreationDate() ?: new \DateTime();
211
        }else{
212
            $wrap[ self::DRIVER_MDATE_WRAPPER_INDEX ] = null;
213
            $wrap[ self::DRIVER_CDATE_WRAPPER_INDEX ] = null;
214
        }
215
216
        return $wrap;
217
    }
218
219
    /**
220
     * @param array $wrapper
221
     * @return mixed
222
     */
223
    public function driverUnwrapData(array $wrapper)
224
    {
225
        return $wrapper[ self::DRIVER_DATA_WRAPPER_INDEX ];
226
    }
227
228
    /**
229
     * @param array $wrapper
230
     * @return mixed
231
     */
232
    public function driverUnwrapTags(array $wrapper)
233
    {
234
        return $wrapper[ self::DRIVER_TAGS_WRAPPER_INDEX ];
235
    }
236
237
238
    /**
239
     * @param array $wrapper
240
     * @return \DateTime
241
     */
242
    public function driverUnwrapEdate(array $wrapper)
243
    {
244
        return $wrapper[ self::DRIVER_EDATE_WRAPPER_INDEX ];
245
    }
246
247
    /**
248
     * @param array $wrapper
249
     * @return \DateTime
250
     */
251
    public function driverUnwrapCdate(array $wrapper)
252
    {
253
        return $wrapper[ self::DRIVER_CDATE_WRAPPER_INDEX ];
254
    }
255
256
257
    /**
258
     * @param array $wrapper
259
     * @return \DateTime
260
     */
261
    public function driverUnwrapMdate(array $wrapper)
262
    {
263
        return $wrapper[ self::DRIVER_MDATE_WRAPPER_INDEX ];
264
    }
265
266
    /**
267
     * @return string
268
     */
269
    public function getDriverName()
270
    {
271
        static $driverName;
272
273
        return ($driverName ?: $driverName = ucfirst(substr(strrchr((new \ReflectionObject($this))->getNamespaceName(), '\\'), 1)));
274
    }
275
276
    /**
277
     * @param \phpFastCache\Core\Item\ExtendedCacheItemInterface $item
278
     * @return bool
279
     * @throws \LogicException
280
     */
281
    public function driverWriteTags(ExtendedCacheItemInterface $item)
282
    {
283
        /**
284
         * Do not attempt to write tags
285
         * on tags item, it can leads
286
         * to an infinite recursive calls
287
         */
288
        if(strpos($item->getKey(), self::DRIVER_TAGS_KEY_PREFIX ) === 0){
289
            throw new \LogicException('Trying to set tag(s) to an Tag item index: ' . $item->getKey());
290
        }
291
292
        /**
293
         * @var $tagsItems ExtendedCacheItemInterface[]
294
         */
295
        $tagsItems = $this->getItems($this->getTagKeys($item->getTags()));
296
297
        foreach ($tagsItems as $tagsItem) {
298
            $data = $tagsItem->get();
299
            $expTimestamp = $item->getExpirationDate()->getTimestamp();
300
301
            /**
302
             * Using the key will
303
             * avoid to use array_unique
304
             * that has slow performances
305
             */
306
307
            $tagsItem->set(array_merge((array) $data, [$item->getKey() => $expTimestamp]));
308
309
            /**
310
             * Set the expiration date
311
             * of the $tagsItem based
312
             * on the older $item
313
             * expiration date
314
             */
315
            if ($expTimestamp > $tagsItem->getExpirationDate()->getTimestamp()) {
316
                $tagsItem->expiresAt($item->getExpirationDate());
0 ignored issues
show
Bug introduced by
It seems like $item->getExpirationDate() can be null; however, expiresAt() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
317
            }
318
            $this->driverWrite($tagsItem);
319
            $tagsItem->setHit(true);
320
        }
321
322
        /**
323
         * Also update removed tags to
324
         * keep the index up to date
325
         */
326
        $tagsItems = $this->getItems($this->getTagKeys($item->getRemovedTags()));
327
328
        foreach ($tagsItems as $tagsItem) {
329
            $data = (array) $tagsItem->get();
330
331
            unset($data[ $item->getKey() ]);
332
            $tagsItem->set($data);
333
334
            /**
335
             * Recalculate the expiration date
336
             *
337
             * If the $tagsItem does not have
338
             * any cache item references left
339
             * then remove it from tagsItems index
340
             */
341
            if (count($data)) {
342
                $tagsItem->expiresAt(max($data));
343
                $this->driverWrite($tagsItem);
344
                $tagsItem->setHit(true);
345
            } else {
346
                $this->deleteItem($tagsItem);
0 ignored issues
show
Documentation introduced by
$tagsItem is of type object<Psr\Cache\CacheItemInterface>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
347
            }
348
        }
349
350
        return true;
351
    }
352
353
    /**
354
     * @param $key
355
     * @return string
356
     */
357
    public function getTagKey($key)
358
    {
359
        return self::DRIVER_TAGS_KEY_PREFIX . $key;
360
    }
361
362
    /**
363
     * @param $key
364
     * @return string
365
     */
366
    public function getTagKeys(array $keys)
367
    {
368
        foreach ($keys as &$key) {
369
            $key = $this->getTagKey($key);
370
        }
371
372
        return $keys;
373
    }
374
375
    /**
376
     * @param string $optionName
377
     * @param mixed $optionValue
378
     * @return bool
379
     * @throws \InvalidArgumentException
380
     */
381
    public static function isValidOption($optionName, $optionValue)
1 ignored issue
show
Unused Code introduced by
The parameter $optionValue is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
382
    {
383
        if (!is_string($optionName)) {
384
            throw new \InvalidArgumentException('$optionName must be a string');
385
        }
386
387
        return true;
388
    }
389
390
    /**
391
     * @return array
392
     */
393
    public static function getRequiredOptions()
394
    {
395
        return [];
396
    }
397
398
    /**
399
     * @return array
400
     */
401
    public static function getValidOptions()
402
    {
403
        return [];
404
    }
405
}