Issues (7)

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/Stream.php (1 issue)

Labels
Severity

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 Stream package.
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
declare(strict_types=1);
11
12
namespace Serafim\Stream;
13
14
use Serafim\Stream\Exception\NotFoundException;
15
use Serafim\Stream\Exception\NotReadableException;
16
use Serafim\Stream\Exception\StreamCreatingException;
17
use Serafim\Stream\Wrapper\ReadStreamWrapper;
18
19
class Stream implements StreamInterface
20
{
21
    /**
22
     * Default streaming class. Must implement StreamWrapperInterface.
23
     *
24
     * @var string
25
     */
26
    private const DEFAULT_STREAM_WRAPPER = ReadStreamWrapper::class;
27
28
    /**
29
     * Error message in the case of stream name conflicts.
30
     *
31
     * @var string
32
     */
33
    private const STREAM_DUPLICATION_EXCEPTION =
34
        'Could not create stream "%s", because stream ' .
35
        'with same name already has been registered.';
36
37
    /**
38
     * List of registered stream handlers.
39
     *
40
     * @var array<StreamInterface>
41
     */
42
    protected static array $streams = [];
0 ignored issues
show
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected T_ARRAY, expecting T_FUNCTION or T_CONST
Loading history...
43
44
    /**
45
     * Current stream name.
46
     *
47
     * @var string
48
     */
49
    private string $name;
50
51
    /**
52
     * List of handlers processing the source code of the stream.
53
     *
54
     * @var array<\Closure>
55
     */
56
    private array $readHandlers = [];
57
58
    /**
59
     * List of handlers processing file read attempts.
60
     *
61
     * @var array<\Closure>
62
     */
63
    private array $openHandlers = [];
64
65
    /**
66
     * @param string $name
67
     */
68
    private function __construct(string $name)
69
    {
70 25
        $this->name = $name;
71
    }
72 25
73 25
    /**
74
     * Returns a positive result if the handler is valid and allows processing
75
     * of the result.
76
     *
77
     * In order to remove the handler's registration, you need to call
78
     * <code>
79
     * Stream::unregister($stream->getName());
80
     * </code>
81
     *
82
     * @return bool
83
     */
84
    public function isRegistered(): bool
85
    {
86 1
        return static::exists($this->name);
87
    }
88 1
89
    /**
90
     * Creates a new arbitrary stream handler with a randomly generated name.
91
     *
92
     * @param positive-int $complexity Suffix length for the generated stream handler name.
93
     * @param string $wrapper A wrapper class where the read/write stream will be redirected.
94
     * @return Stream|static
95
     * @throws StreamCreatingException
96
     * @throws \Exception
97
     */
98
    public static function new(int $complexity = 8, string $wrapper = self::DEFAULT_STREAM_WRAPPER): self
99
    {
100 15
        assert($complexity > 0, 'Name complexity should be greater than 0');
101
102 15
        $name = 'stream' . \bin2hex(\random_bytes($complexity));
103
104 15
        return static::create($name, $wrapper);
105
    }
106 15
107
    /**
108
     * Creating and registering a new stream with the specified name.
109
     *
110
     * If the existing handler is a system handler (e.g. "php", "memory",
111
     * "phar", "http", etc.), an exception will be thrown. In the event that
112
     * the non-system handler already exists, the existing one will be returned.
113
     *
114
     * @param string $protocol The name of the protocol. May contain
115
     *      alphanumeric sequences, but must begin with a letter.
116
     * @param string $wrapper A wrapper class where the read/write stream
117
     *      will be redirected.
118
     * @return Stream|static
119
     * @throws StreamCreatingException
120
     */
121
    public static function create(string $protocol, string $wrapper = self::DEFAULT_STREAM_WRAPPER): self
122
    {
123 25
        if (isset(static::$streams[$protocol])) {
124
            return static::$streams[$protocol];
125 25
        }
126 1
127
        static::register($protocol, $stream = new static($protocol), $wrapper);
128
129 25
        return $stream;
130
    }
131 24
132
    /**
133
     * Registration of stream protocol handler.
134
     *
135
     * @param string $protocol The name of the protocol. May contain
136
     *      alphanumeric sequences, but must begin with a letter.
137
     * @param string $wrapper A wrapper class where the read/write stream
138
     *      will be redirected.
139
     * @param StreamInterface $stream Instance of StreamInterface where
140
     *      read/write stream will be redirected.
141
     * @return StreamInterface
142
     * @throws StreamCreatingException
143
     */
144
    public static function register(
145
        string $protocol,
146 25
        StreamInterface $stream,
147
        string $wrapper = self::DEFAULT_STREAM_WRAPPER
148
    ): StreamInterface {
149
        static::$streams[$protocol] = $stream;
150
151 25
        if (static::exists($protocol)) {
152
            throw new StreamCreatingException(\sprintf(self::STREAM_DUPLICATION_EXCEPTION, $protocol));
153 25
        }
154 2
155
        \stream_wrapper_register($protocol, $wrapper);
156
157 24
        return $stream;
158
    }
159 24
160
    /**
161
     * Cancels custom handler registration. Ignores built-in handlers, or
162
     * handlers that have been registered with other registrars
163
     * (other than this class).
164
     *
165
     * @param string $protocol The name of the protocol. May contain
166
     *      alphanumeric sequences, but must begin with a letter.
167
     * @return bool Returns true in the event that the deletion of registration
168
     *      was made successfully and false otherwise.
169
     */
170
    public static function unregister(string $protocol): bool
171
    {
172 2
        if (isset(static::$streams[$protocol])) {
173
            unset(static::$streams[$protocol]);
174 2
            \stream_wrapper_unregister($protocol);
175 1
176 1
            return true;
177
        }
178 1
179
        return false;
180
    }
181 1
182
    /**
183
     * Returns a handler by protocol name.
184
     *
185
     * @param string $protocol The name of the protocol. May contain
186
     *      alphanumeric sequences, but must begin with a letter.
187
     * @return StreamInterface
188
     * @throws StreamCreatingException
189
     */
190
    public static function get(string $protocol): StreamInterface
191
    {
192 8
        if (! isset(static::$streams[$protocol])) {
193
            $error = \sprintf('Protocol "%s://" should be registered', $protocol);
194 8
            throw new StreamCreatingException($error);
195 1
        }
196 1
197
        return static::$streams[$protocol];
198
    }
199 7
200
    /**
201
     * Adds a read attempt handler. If no such handler has been registered for
202
     * this stream handler, it will be opened by calling the file_get_contents
203
     * function.
204
     *
205
     * <code>
206
     *  $stream->tryRead(function (string $pathname): ?string {
207
     *      return \is_file($pathname) ? \file_get_contents($pathname) : null;
208
     *  });
209
     * </code>
210
     *
211
     * @param \Closure $then
212
     * @return Stream
213
     */
214
    public function tryRead(\Closure $then): self
215
    {
216 1
        $this->openHandlers[] = $then;
217
218 1
        return $this;
219
    }
220 1
221
    /**
222
     * Adds a source code handler. Each closure passed to this method takes
223
     * the source text as a string and must return a line with the new text.
224
     *
225
     * <code>
226
     *  $stream->onRead(function (string $sources): string {
227
     *      return $sources;
228
     *  });
229
     * </code>
230
     *
231
     * @param \Closure $then
232
     * @return Stream|$this
233
     */
234
    public function onRead(\Closure $then): self
235
    {
236 15
        $this->readHandlers[] = $then;
237
238 15
        return $this;
239
    }
240 15
241
    /**
242
     * Returns the full path with the protocol for the passed name.
243
     *
244
     * @param string $pathname The path to the file string.
245
     * @return string
246
     */
247
    public function pathname(string $pathname): string
248
    {
249 7
        return \sprintf('%s://%s', $this->getName(), $pathname);
250
    }
251 7
252
    /**
253
     * Returns the name of the current stream.
254
     *
255
     * @return string
256
     */
257
    public function getName(): string
258
    {
259 9
        return $this->name;
260
    }
261 9
262
    /**
263
     * Reads a file using the actual file path, using all the registered
264
     * read and processing handlers.
265
     *
266
     * <code>
267
     *  echo Stream::get('protocol')->read('path/to/file.txt');
268
     * </code>
269
     *
270
     * @param string $pathname The path to the file string.
271
     * @return string
272
     * @throws NotFoundException
273
     * @throws NotReadableException
274
     */
275
    public function read(string $pathname): string
276
    {
277 7
        return $this->handleRead($this->handleOpen($pathname));
278
    }
279 7
280
    /**
281
     * @param string $sources
282
     * @return string
283
     */
284
    private function handleRead(string $sources): string
285
    {
286 4
        foreach ($this->readHandlers as $handler) {
287
            $sources = $handler($sources);
288 4
        }
289 1
290
        return $sources;
291
    }
292 4
293
    /**
294
     * @param string $pathname
295
     * @return string
296
     * @throws NotFoundException
297
     * @throws NotReadableException
298
     */
299
    private function handleOpen(string $pathname): string
300
    {
301 7
        if (\count($this->openHandlers)) {
302
            foreach ($this->openHandlers as $handler) {
303 7
                if (\is_string($result = $handler($pathname))) {
304 1
                    return $result;
305 1
                }
306 1
            }
307
        }
308
309
        $this->assertIsFile($pathname);
310
        $this->assertIsReadable($pathname);
311 6
312 4
        return \file_get_contents($pathname);
313
    }
314 3
315
    /**
316
     * @param string $pathname
317
     * @return void
318
     * @throws NotFoundException
319
     */
320
    private function assertIsFile(string $pathname): void
321
    {
322 6
        if (! \is_file($pathname)) {
323
            $error = \sprintf('File %s not found', $pathname);
324 6
            throw new NotFoundException($error);
325 2
        }
326 2
    }
327
328 4
    /**
329
     * @param string $pathname
330
     * @return void
331
     * @throws NotReadableException
332
     */
333
    private function assertIsReadable(string $pathname): void
334
    {
335 4
        if (! \is_readable($pathname)) {
336
            $error = \sprintf('File %s not readable', $pathname);
337 4
            throw new NotReadableException($error);
338 1
        }
339 1
    }
340
341 3
    /**
342
     * Returns whether the transferred protocol is registered.
343
     *
344
     * @param string $protocol
345
     * @return bool
346
     */
347
    public static function exists(string $protocol): bool
348
    {
349 25
        return \in_array($protocol, \stream_get_wrappers(), true);
350
    }
351
}
352