Issues (36)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  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.
  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.
  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.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  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.
  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.
  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.
  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.
  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.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  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.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
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/Files.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 *
4
 * This file is part of the Apix Project.
5
 *
6
 * (c) Franck Cassedanne <franck at ouarz.net>
7
 *
8
 * @license     http://opensource.org/licenses/BSD-3-Clause  New BSD License
9
 *
10
 */
11
12
namespace Apix\Cache;
13
14
/**
15
 * Class Files
16
 * In files cache wrapper.
17
 * Expiration time and tags are stored in the cache file
18
 *
19
 * @package Apix\Cache
20
 * @author  MacFJA
21
 */
22
class Files extends AbstractCache
23
{
24
    /**
25
     * Constructor.
26
     *
27
     * @param array  $options Array of options.
28
     */
29 306
    public function __construct(array $options = array())
30
    {
31
        $options += array(
32 306
            'directory' => sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'apix-cache',
33
            'locking' => true
34 306
        );
35 306
        parent::__construct(null, $options);
36 306
        if (!file_exists($this->getOption('directory')) || !is_dir($this->getOption('directory'))) {
37 272
            mkdir($this->getOption('directory'), 0755, true);
38 272
        }
39 306
    }
40
41
    /**
42
     * Retrieves the cache content for the given key.
43
     *
44
     * @param  string $key The cache key to retrieve.
45
     * @return mixed|null Returns the cached data or null.
46
     */
47 153
    public function loadKey($key)
48
    {
49 153
        $key = $this->mapKey($key);
50 153
        $path = $this->getOption('directory') . DIRECTORY_SEPARATOR . base64_encode($key);
51
52 153
        if (!file_exists($path) || !is_file($path)) {
53 85
            return null;
54
        }
55
56 102
        $data = $this->readFile($path);
57
58 102
        if ('' === $data) {
59 17
            unlink($path);
60 17
            return null;
61
        }
62 102
        $pos = strpos($data, PHP_EOL, 0);
63 102
        if (false === $pos) {// Un-complete file
64 102
            unlink($path);
65 17
            return null;
66 17
        }
67
68
        $eolLen = strlen(PHP_EOL);
69 85
        $pos = strpos($data, PHP_EOL, $pos+$eolLen);
70 85
        if (false === $pos) {// Un-complete file
71
            unlink($path);
72
            return null;
73
        }
74
75
        $serialized = substr($data, $pos+$eolLen);
76
        return unserialize($serialized);
77
    }
78
79 119
    /**
80
     * Retrieves the cache keys for the given tag.
81 119
     *
82 17
     * @param  string $tag The cache tag to retrieve.
83
     * @return array|null Returns an array of cache keys or null.
84
     */
85 102
    public function loadTag($tag)
86 102
    {
87 102
        if (!$this->getOption('tag_enable')) {
88 102
            return null;
89 102
        }
90 102
91
        $encoded = base64_encode($this->mapTag($tag));
92 85
        $found = array();
93 85
        $files = scandir($this->getOption('directory'));
94
        foreach ($files as $file) {
95 85
            if (substr($file, 0, 1) === '.') {
96 85
                continue;
97 85
            }
98 102
            $path = $this->getOption('directory') . DIRECTORY_SEPARATOR . $file;
99
            $fileTags = explode(' ', $this->readFile($path, 1));
100 102
101 51
            if (in_array($encoded, $fileTags, true)) {
102
                $found[] = base64_decode($file);
103 85
            }
104
        }
105
106
        if (0 === count($found)) {
107
            return null;
108
        }
109
        return $found;
110
    }
111
112
    /**
113
     * Get the file data.
114 187
     * If enable, lock file to preserve atomicity
115
     *
116 187
     * @param string $path The file path
117 187
     * @param int $line The line to read. If -1 read the whole file
118 187
     * @return string
119 187
     */
120
    protected function readFile($path, $line = -1)
121 187
    {
122 102
        $handle = fopen($path, 'r');
123 102
        if ($this->getOption('locking')) {
124 119
            flock($handle, LOCK_SH);
125 34
        }
126 34
127 119
        if (-1 === $line) {
128
            $data = stream_get_contents($handle);
129
        } else {
130 187
            for ($read = 1; $read < $line; $read++) {
131 187
                fgets($handle);
132 187
            }
133 187
            $data = rtrim(fgets($handle), PHP_EOL);
134
        }
135 187
136
        if ($this->getOption('locking')) {
137
            flock($handle, LOCK_UN);
138
        }
139
        fclose($handle);
140
141
        return $data;
142
    }
143
144
    /**
145
     * Saves data to the cache.
146
     *
147
     * @param  mixed $data The data to cache.
148 221
     * @param  string $key The cache id to save.
149
     * @param  array $tags The cache tags for this cache entry.
150 221
     * @param  int $ttl The time-to-live in seconds, if set to null the
151 221
     *                       cache is valid forever.
152
     * @return boolean Returns True on success or False on failure.
153 221
     */
154 221
    public function save($data, $key, array $tags = null, $ttl = null)
155 102
    {
156 102
        $key = $this->mapKey($key);
157 102
        $expire = (null === $ttl) ? 0 : time() + $ttl;
158 102
159 102
        $tag = '';
160 102
        if (null !== $tags) {
161
            $baseTags = $tags;
162 221
            array_walk($baseTags, function (&$item, $key, Files $cache) {
163 221
                $item = base64_encode($cache->mapTag($item));
164 221
            }, $this);
165 221
            $tag = implode(' ', $baseTags);
166 221
        }
167 221
168 221
        $path = $this->getOption('directory') . DIRECTORY_SEPARATOR . base64_encode($key);
169
        file_put_contents(
170
            $path,
171
            $tag . PHP_EOL . $expire . PHP_EOL . serialize($data),
172
            $this->getOption('locking') ? LOCK_EX : null
173
        );
174
        return true;
175
    }
176
177 68
    /**
178
     * Deletes the specified cache record.
179 68
     *
180 68
     * @param  string $key The cache id to remove.
181 68
     * @return boolean Returns True on success or False on failure.
182 34
     */
183
    public function delete($key)
184
    {
185 51
        $key = $this->mapKey($key);
186
        $path = $this->getOption('directory') . DIRECTORY_SEPARATOR . base64_encode($key);
187
        if (!file_exists($path)) {
188
            return false;
189
        }
190
191
        return unlink($path);
192
    }
193
194 17
    /**
195
     * Removes all the cached entries associated with the given tag names.
196 17
     *
197 17
     * @param  array $tags The array of tags to remove.
198 17
     * @return boolean Returns True on success or False on failure.
199 17
     */
200 17
    public function clean(array $tags)
201
    {
202 17
        $toRemove = array();
203 17
        $cleaned = false;
204 17
205
        foreach ($tags as $tag) {
206 17
            $keys = $this->loadTag($tag);
207 17
            if (null === $keys) {
208 17
                continue;
209
            }
210 17
            $cleaned = true;
211
            $toRemove = array_merge($toRemove, $keys);
212
        }
213
        $toRemove = array_unique($toRemove);
214
215
        foreach ($toRemove as $key) {
216
            $this->delete($this->removePrefixKey($key));
217
        }
218
219
        return $cleaned;
220 272
    }
221
222 272
    /**
223 272
     * Flush all the cached entries.
224 272
     *
225 272
     * @param  boolean $all Wether to flush the whole database, or (preferably)
226
     *                      the entries prefixed with prefix_key and prefix_tag.
227 204
     * @return boolean Returns True on success or False on failure.
228 204
     */
229 204
    public function flush($all = false)
230
    {
231 204
        $files = scandir($this->getOption('directory'));
232 204
        foreach ($files as $file) {
233 204
            if ('.' === substr($file, 0, 1)) {
234 272
                continue;
235 272
            }
236
            $path = $this->getOption('directory') . DIRECTORY_SEPARATOR . $file;
237
            $fullKey = base64_decode($file);
238
            $key = $this->removePrefixKey($fullKey);
239
240
            if ($all || (!$all && ($key !== $fullKey || '' === $this->options['prefix_key']))) {
241
                unlink($path);
242
            }
243
        }
244 34
    }
245
246 34
    /**
247 34
     * Returns the time-to-live (in seconds) for the given key.
248 34
     *
249 34
     * @param  string $key The name of the key.
250
     * @return int|false Returns the number of seconds left, 0 if valid
251
     *                       forever or False if the key is non-existant.
252 34
     */
253
    public function getTtl($key)
254 34
    {
255 17
        $key = $this->mapKey($key);
256
        $path = $this->getOption('directory') . DIRECTORY_SEPARATOR . base64_encode($key);
257
        if (!file_exists($path) || !is_file($path)) {
258 34
            return false;
259
        }
260
261
        $expire = $this->readFile($path, 2);
262
263
        if (0 === (int) $expire) {
264
            return 0;
265
        }
266
267
        return $expire - time();
0 ignored issues
show
Bug Compatibility introduced by
The expression $expire - time(); of type integer|double adds the type double to the return on line 267 which is incompatible with the return type declared by the interface Apix\Cache\Adapter::getTtl of type integer|false.
Loading history...
268
    }
269
}
270