Completed
Push — V6 ( ddfc29...839d12 )
by Georges
03:14
created

IOHelperTrait::htaccessGen()   C

Complexity

Conditions 7
Paths 9

Size

Total Lines 30
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 17
nc 9
nop 2
dl 0
loc 30
rs 6.7272
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
15
namespace phpFastCache\Core\Pool\IO;
16
17
use phpFastCache\Core\Pool\ExtendedCacheItemPoolInterface;
18
use phpFastCache\Exceptions\phpFastCacheIOException;
19
use phpFastCache\Util\Directory;
20
21
trait IOHelperTrait
22
{
23
    /**
24
     * @var array
25
     */
26
    public $tmp = [];
27
28
    /**
29
     * @param bool $readonly
30
     * @return string
31
     * @throws phpFastCacheIOException
32
     */
33
    public function getPath($readonly = false)
34
    {
35
        /**
36
         * Get the base system temporary directory
37
         */
38
        $tmp_dir = rtrim(ini_get('upload_tmp_dir') ?: sys_get_temp_dir(), '\\/') . DIRECTORY_SEPARATOR . 'phpfastcache';
39
40
        /**
41
         * Calculate the security key
42
         */
43
        {
44
            $securityKey = array_key_exists('securityKey', $this->config) ? $this->config[ 'securityKey' ] : '';
0 ignored issues
show
Bug introduced by
The property config does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
45
            if (!$securityKey || $securityKey === 'auto') {
46
                if (isset($_SERVER[ 'HTTP_HOST' ])) {
47
                    $securityKey = preg_replace('/^www./', '', strtolower(str_replace(':', '_', $_SERVER[ 'HTTP_HOST' ])));
48
                } else {
49
                    $securityKey = ($this->isPHPModule() ? 'web' : 'cli');
50
                }
51
            }
52
53
            if ($securityKey !== '') {
54
                $securityKey .= '/';
55
            }
56
57
            $securityKey = static::cleanFileName($securityKey);
58
        }
59
60
        /**
61
         * Extends the temporary directory
62
         * with the security key and the driver name
63
         */
64
        $tmp_dir = rtrim($tmp_dir, '/') . DIRECTORY_SEPARATOR;
65
66
        if (empty($this->config[ 'path' ]) || !is_string($this->config[ 'path' ])) {
67
            if (self::isPHPModule()) {
68
                $path = $tmp_dir;
69
            } else {
70
                $document_root_path = rtrim($_SERVER[ 'DOCUMENT_ROOT' ], '/') . '/../';
71
                $path = isset($_SERVER[ 'DOCUMENT_ROOT' ]) && is_writable($document_root_path) ? $document_root_path : rtrim(__DIR__, '/') . DIRECTORY_SEPARATOR;
72
            }
73
74
            if ($this->config[ 'path' ] != '') {
75
                $path = $this->config[ 'path' ];
76
            }
77
78
        } else {
79
            $path = $this->config[ 'path' ];
80
        }
81
82
        $full_path = rtrim($path, '/')
83
          . DIRECTORY_SEPARATOR
84
          . $securityKey
85
          . DIRECTORY_SEPARATOR
86
          . $this->getDriverName();
87
        $full_path_hash = md5($full_path);
88
89
        /**
90
         * In readonly mode we only attempt
91
         * to verify if the directory exists
92
         * or not, if it does not then we
93
         * return the temp dir
94
         */
95
        if ($readonly === true) {
96
            if($this->config[ 'autoTmpFallback' ] && (@file_exists($full_path) || !@is_writable($full_path))){
97
                return $tmp_dir;
98
            }
99
            return $full_path;
100
        }else{
101
            if (!isset($this->tmp[ $full_path_hash ]) || (!@file_exists($full_path) || !@is_writable($full_path))) {
102
                if (!@file_exists($full_path)) {
103
                    @mkdir($full_path, $this->setChmodAuto(), true);
0 ignored issues
show
Security File Manipulation introduced by
$full_path 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/IOHelperTrait.php on line 47
  2. Data is passed through trim(), and Data is passed through preg_replace()
    in vendor/src/phpFastCache/Core/Pool/IO/IOHelperTrait.php on line 216
  3. $securityKey is assigned
    in src/phpFastCache/Core/Pool/IO/IOHelperTrait.php on line 57
  4. $full_path is assigned
    in src/phpFastCache/Core/Pool/IO/IOHelperTrait.php on line 82

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...
104
                }else if (!@is_writable($full_path)) {
105
                    @chmod($full_path, $this->setChmodAuto());
0 ignored issues
show
Security File Manipulation introduced by
$full_path 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/IOHelperTrait.php on line 47
  2. Data is passed through trim(), and Data is passed through preg_replace()
    in vendor/src/phpFastCache/Core/Pool/IO/IOHelperTrait.php on line 216
  3. $securityKey is assigned
    in src/phpFastCache/Core/Pool/IO/IOHelperTrait.php on line 57
  4. $full_path is assigned
    in src/phpFastCache/Core/Pool/IO/IOHelperTrait.php on line 82

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...
106
                }
107
108
                if ($this->config[ 'autoTmpFallback' ] && !@is_writable($full_path)) {
109
                    /**
110
                     * Switch back to tmp dir
111
                     * again if the path is not writable
112
                     */
113
                    $full_path = $tmp_dir;
114
                    if (!@file_exists($full_path)) {
115
                        @mkdir($full_path, $this->setChmodAuto(), true);
116
                    }
117
                }
118
119
                /**
120
                 * In case there is no directory
121
                 * writable including tye temporary
122
                 * one, we must throw an exception
123
                 */
124
                if (!@file_exists($full_path) || !@is_writable($full_path)) {
125
                    throw new phpFastCacheIOException('PLEASE CREATE OR CHMOD ' . $full_path . ' - 0777 OR ANY WRITABLE PERMISSION!');
126
                }
127
128
                $this->tmp[ $full_path_hash ] = $full_path;
129
                $this->htaccessGen($full_path, array_key_exists('htaccess', $this->config) ? $this->config[ 'htaccess' ] : false);
130
            }
131
        }
132
133
        return realpath($full_path);
134
    }
135
136
137
    /**
138
     * @param $keyword
139
     * @param bool $skip
140
     * @return string
141
     * @throws phpFastCacheIOException
142
     */
143
    private function getFilePath($keyword, $skip = false)
144
    {
145
        $path = $this->getPath();
146
147
        if ($keyword === false) {
148
            return $path;
149
        }
150
151
        $filename = $this->encodeFilename($keyword);
152
        $folder = substr($filename, 0, 2) . DIRECTORY_SEPARATOR . substr($filename, 2, 2);
153
        $path = rtrim($path, '/\\') . DIRECTORY_SEPARATOR . $folder;
154
155
        /**
156
         * Skip Create Sub Folders;
157
         */
158
        if ($skip == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
159
            if (!file_exists($path)) {
160
                if (@!mkdir($path, $this->setChmodAuto(), true)) {
161
                    throw new phpFastCacheIOException('PLEASE CHMOD ' . $path . ' - ' . $this->setChmodAuto() . ' OR ANY WRITABLE PERMISSION!');
162
                }
163
            }
164
        }
165
166
        return $path . '/' . $filename . '.txt';
167
    }
168
169
170
171
    /**
172
     * @param $keyword
173
     * @return string
174
     */
175
    protected function encodeFilename($keyword)
176
    {
177
        return md5($keyword);
178
    }
179
180
    /**
181
     * @return bool
182
     */
183
    public function isExpired()
184
    {
185
        trigger_error(__FUNCTION__ . '() is deprecated, use ExtendedCacheItemInterface::isExpired() instead.', E_USER_DEPRECATED);
186
187
        return true;
188
    }
189
190
    /**
191
     * @param $this ->config
192
     * @return int
193
     */
194
    public function setChmodAuto()
195
    {
196
        if (!isset($this->config[ 'default_chmod' ]) || $this->config[ 'default_chmod' ] == '' || is_null($this->config[ 'default_chmod' ])) {
197
            return 0777;
198
        } else {
199
            return $this->config[ 'default_chmod' ];
200
        }
201
    }
202
203
    /**
204
     * @param $filename
205
     * @return mixed
206
     */
207
    protected static function cleanFileName($filename)
208
    {
209
        $regex = [
210
          '/[\?\[\]\/\\\=\<\>\:\;\,\'\"\&\$\#\*\(\)\|\~\`\!\{\}]/',
211
          '/\.$/',
212
          '/^\./',
213
        ];
214
        $replace = ['-', '', ''];
215
216
        return trim(preg_replace($regex, $replace, trim($filename)), '-');
217
    }
218
219
    /**
220
     * @param $path
221
     * @param bool $create
222
     * @throws phpFastCacheIOException
223
     */
224
    protected function htaccessGen($path, $create = true)
225
    {
226
        if ($create === true) {
227
            if (!is_writable($path)) {
228
                try {
229
                    if(!chmod($path, 0777)){
230
                        throw new phpFastCacheIOException('Chmod failed on : ' . $path);
231
                    }
232
                } catch (phpFastCacheIOException $e) {
233
                    throw new phpFastCacheIOException('PLEASE CHMOD ' . $path . ' - 0777 OR ANY WRITABLE PERMISSION!', 0, $e);
234
                }
235
            }
236
237
            if (!file_exists($path . "/.htaccess")) {
238
                $content = <<<HTACCESS
239
### This .htaccess is auto-generated by PhpFastCache ###
240
order deny, allow
241
deny from all
242
allow from 127.0.0.1
243
HTACCESS;
244
245
                $file = @fopen($path . '/.htaccess', 'w+');
246
                if (!$file) {
247
                    throw new phpFastCacheIOException('PLEASE CHMOD ' . $path . ' - 0777 OR ANY WRITABLE PERMISSION!');
248
                }
249
                fwrite($file, $content);
250
                fclose($file);
251
            }
252
        }
253
    }
254
255
256
    /**
257
     * @param $file
258
     * @return string
259
     * @throws phpFastCacheIOException
260
     */
261
    protected function readfile($file)
262
    {
263
        if (function_exists('file_get_contents')) {
264
            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/IOHelperTrait.php on line 47
  2. Data is passed through trim(), and Data is passed through preg_replace()
    in vendor/src/phpFastCache/Core/Pool/IO/IOHelperTrait.php on line 216
  3. $securityKey is assigned
    in src/phpFastCache/Core/Pool/IO/IOHelperTrait.php on line 57
  4. $full_path is assigned
    in src/phpFastCache/Core/Pool/IO/IOHelperTrait.php on line 82
  5. IOHelperTrait::getPath() returns tainted data, and $path is assigned
    in src/phpFastCache/Core/Pool/IO/IOHelperTrait.php on line 145
  6. IOHelperTrait::getFilePath() returns tainted data, and $file_path is assigned
    in src/phpFastCache/Drivers/Files/Driver.php on line 98
  7. $file_path is passed to IOHelperTrait::readfile()
    in src/phpFastCache/Drivers/Files/Driver.php on line 103

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...
265
        } else {
266
            $string = '';
267
268
            $file_handle = @fopen($file, 'r');
269
            if (!$file_handle) {
270
                throw new phpFastCacheIOException("Cannot read file located at: {$file}");
271
            }
272
            while (!feof($file_handle)) {
273
                $line = fgets($file_handle);
274
                $string .= $line;
275
            }
276
            fclose($file_handle);
277
278
            return $string;
279
        }
280
    }
281
282
    /**
283
     * @param string $file
284
     * @param string $data
285
     * @param bool $secureFileManipulation
286
     * @return bool
287
     * @throws phpFastCacheIOException
288
     */
289
    protected function writefile($file, $data, $secureFileManipulation = false)
290
    {
291
        /**
292
         * @eventName CacheWriteFileOnDisk
293
         * @param ExtendedCacheItemPoolInterface $this
294
         * @param string $file
295
         * @param bool $secureFileManipulation
296
         *
297
         */
298
        $this->eventManager->dispatch('CacheWriteFileOnDisk', $this, $file, $secureFileManipulation);
0 ignored issues
show
Bug introduced by
The property eventManager does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
299
300
        if($secureFileManipulation){
301
            $tmpFilename = Directory::getAbsolutePath(dirname($file) . '/tmp_' . md5(
302
                str_shuffle(uniqid($this->getDriverName(), false))
303
                . str_shuffle(uniqid($this->getDriverName(), false))
304
              ));
305
306
            $f = fopen($tmpFilename, 'w+');
307
            flock($f, LOCK_EX);
308
            $octetWritten = fwrite($f, $data);
309
            flock($f, LOCK_UN);
310
            fclose($f);
311
312
            if(!rename($tmpFilename, $file)){
0 ignored issues
show
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/IOHelperTrait.php on line 47
  2. Data is passed through trim(), and Data is passed through preg_replace()
    in vendor/src/phpFastCache/Core/Pool/IO/IOHelperTrait.php on line 216
  3. $securityKey is assigned
    in src/phpFastCache/Core/Pool/IO/IOHelperTrait.php on line 57
  4. $full_path is assigned
    in src/phpFastCache/Core/Pool/IO/IOHelperTrait.php on line 82
  5. IOHelperTrait::getPath() returns tainted data, and $path is assigned
    in src/phpFastCache/Core/Pool/IO/IOHelperTrait.php on line 145
  6. IOHelperTrait::getFilePath() returns tainted data, and $file_path is assigned
    in src/phpFastCache/Drivers/Files/Driver.php on line 73
  7. $file_path is passed to IOHelperTrait::writefile()
    in src/phpFastCache/Drivers/Files/Driver.php on line 80

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...
313
                throw new phpFastCacheIOException(sprintf('Failed to rename %s to %s', $tmpFilename, $file));
314
            }
315
        }else{
316
            $f = fopen($file, 'w+');
317
            $octetWritten = fwrite($f, $data);
318
            fclose($f);
319
        }
320
321
        return $octetWritten !== false;
322
    }
323
}