Issues (20)

Security Analysis    1 potential vulnerability

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

  Cross-Site Scripting (1)
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/CRUDlex/FileHandler.php (6 issues)

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 CRUDlex package.
5
 *
6
 * (c) Philip Lehmann-Böhm <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace CRUDlex;
13
14
use League\Flysystem\FilesystemInterface;
15
use Symfony\Component\HttpFoundation\Request;
16
use Symfony\Component\HttpFoundation\StreamedResponse;
17
18
/**
19
 * Handles the files.
20
 */
21
class FileHandler
22
{
23
24
    /**
25
     * Brings the abstract access to the filesystem.
26
     * @var FilesystemInterface
27
     */
28
    protected $filesystem;
29
30
    /**
31
     * Holds the entity definition.
32
     * @var EntityDefinition
33
     */
34
    protected $entityDefinition;
35
36
    /**
37
     * Constructs a file system path for the given parameters for storing the
38
     * file of the file field.
39
     *
40
     * @param string $entityName
41
     * the entity name
42
     * @param Entity $entity
43
     * the entity
44
     * @param string $field
45
     * the file field in the entity
46
     *
47
     * @return string
48
     * the constructed path for storing the file of the file field
49
     */
50 7
    protected function getPath($entityName, Entity $entity, $field)
51
    {
52 7
        return $this->entityDefinition->getField($field, 'path').'/'.$entityName.'/'.$entity->get('id').'/'.$field;
53
    }
54
55
    /**
56
     * Executes a function for each file field of this entity.
57
     *
58
     * @param Entity $entity
59
     * the just created entity
60
     * @param string $entityName
61
     * the name of the entity as this class here is not aware of it
62
     * @param \Closure $function
63
     * the function to perform, takes $entity, $entityName and $field as parameter
64
     */
65 8
    protected function performOnFiles(Entity $entity, $entityName, $function)
66
    {
67 8
        $fields = $this->entityDefinition->getEditableFieldNames();
68 8
        foreach ($fields as $field) {
69 8
            if ($this->entityDefinition->getType($field) == 'file') {
70 8
                $function($entity, $entityName, $field);
71
            }
72
        }
73 8
    }
74
75
    /**
76
     * Writes the uploaded files.
77
     *
78
     * @param AbstractData $data
79
     * the AbstractData instance who should receive the events
80
     * @param Request $request
81
     * the HTTP request containing the file data
82
     * @param Entity $entity
83
     * the just manipulated entity
84
     * @param string $entityName
85
     * the name of the entity as this class here is not aware of it
86
     * @param string $action
87
     * the name of the performed action
88
     *
89
     * @return boolean
90
     * true if all before events passed
91
     */
92 6
    protected function shouldWriteFile(AbstractData $data, Request $request, Entity $entity, $entityName, $action)
93
    {
94 6
        $result = $data->getEvents()->shouldExecute($entity, 'before', $action);
95 6
        if (!$result) {
96 3
            return false;
97
        }
98 6
        $filesystem = $this->filesystem;
99
        $this->performOnFiles($entity, $entityName, function($entity, $entityName, $field) use ($filesystem, $request) {
100 6
            $file = $request->files->get($field);
101 6
            if ($file !== null && $file->isValid()) {
102 6
                $path     = $this->getPath($entityName, $entity, $field);
103 6
                $filename = $path.'/'.$file->getClientOriginalName();
104 6
                if ($filesystem->has($filename)) {
105 4
                    $filesystem->delete($filename);
106
                }
107 6
                $stream = fopen($file->getRealPath(), 'r+');
108 6
                $filesystem->writeStream($filename, $stream);
109 6
                fclose($stream);
110
            }
111 6
        });
112 6
        $data->getEvents()->shouldExecute($entity, 'after', $action);
113 6
        return true;
114
    }
115
116
    /**
117
     * FileHandler constructor.
118
     * @param FilesystemInterface $filesystem
119
     * the filesystem to use
120
     * @param EntityDefinition $entityDefinition
121
     * the entity definition to use
122
     */
123 10
    public function __construct(FilesystemInterface $filesystem, EntityDefinition $entityDefinition)
124
    {
125 10
        $this->filesystem       = $filesystem;
126 10
        $this->entityDefinition = $entityDefinition;
127 10
    }
128
129
130
    /**
131
     * Renders (outputs) a file of an entity. This includes setting headers
132
     * like the file size, mimetype and name, too.
133
     *
134
     * @param Entity $entity
135
     * the entity to render the file from
136
     * @param string $entityName
137
     * the name of the entity as this class here is not aware of it
138
     * @param string $field
139
     * the field of the entity containing the file to be rendered
140
     *
141
     * @return StreamedResponse
142
     * the HTTP streamed response
143
     */
144 2
    public function renderFile(Entity $entity, $entityName, $field)
145
    {
146 2
        $targetPath = $this->getPath($entityName, $entity, $field);
147 2
        $fileName   = $entity->get($field);
148 2
        $file       = $targetPath.'/'.$fileName;
149 2
        $mimeType   = $this->filesystem->getMimetype($file);
150 2
        $size       = $this->filesystem->getSize($file);
151 2
        $stream     = $this->filesystem->readStream($file);
152
        $response   = new StreamedResponse(function() use ($stream) {
153 1
            while ($data = fread($stream, 1024)) {
154 1
                echo $data;
155 1
                flush();
156
            }
157 1
            fclose($stream);
158 2
        }, 200, [
159 2
            'Cache-Control' => 'public, max-age=86400',
160 2
            'Content-length' => $size,
161 2
            'Content-Type' => $mimeType,
162 2
            'Content-Disposition' => 'attachment; filename="'.$fileName.'"'
163
        ]);
164 2
        return $response;
165
    }
166
167
168
    /**
169
     * Deletes all files of an existing entity.
170
     *
171
     * @param AbstractData $data
172
     * the AbstractData instance who should receive the events
173
     * @param Entity $entity
174
     * the entity to delete the files from
175
     * @param string $entityName
176
     * the name of the entity as this class here is not aware of it
177
     *
178
     * @return boolean
179
     * true on successful deletion
180
     */
181 2 View Code Duplication
    public function deleteFiles(AbstractData $data, Entity $entity, $entityName)
0 ignored issues
show
function deleteFiles() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
182
    {
183 2
        $result = $data->getEvents()->shouldExecute($entity, 'before', 'deleteFiles');
184 2
        if (!$result) {
185 2
            return false;
186
        }
187
        $this->performOnFiles($entity, $entityName, function($entity, $entityName, $field) {
188
            // For now, we are defensive and don't delete ever. As soon as soft deletion is optional, files will get deleted.
189 2
        });
190 2
        $data->getEvents()->shouldExecute($entity, 'after', 'deleteFiles');
191 2
        return true;
192
    }
193
194
    /**
195
     * Deletes a specific file from an existing entity.
196
     *
197
     * @param AbstractData $data
198
     * the AbstractData instance who should receive the events
199
     * @param Entity $entity
200
     * the entity to delete the file from
201
     * @param string $entityName
202
     * the name of the entity as this class here is not aware of it
203
     * @param string $field
204
     * the field of the entity containing the file to be deleted
205
     * @return bool true on successful deletion
206
     * true on successful deletion
207
     */
208 2 View Code Duplication
    public function deleteFile(AbstractData $data, Entity $entity, $entityName, $field)
0 ignored issues
show
function deleteFile() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
209
    {
210 2
        $result = $data->getEvents()->shouldExecute($entity, 'before', 'deleteFile');
211 2
        if (!$result) {
212 2
            return false;
213
        }
214
        // For now, we are defensive and don't delete ever. As soon as soft deletion is optional, files will get deleted.
215 2
        $data->getEvents()->shouldExecute($entity, 'after', 'deleteFile');
216 2
        return true;
217
    }
218
219
    /**
220
     * Creates the uploaded files of a newly created entity.
221
     *
222
     * @param AbstractData $data
223
     * the AbstractData instance who should receive the events
224
     * @param Request $request
225
     * the HTTP request containing the file data
226
     * @param Entity $entity
227
     * the just created entity
228
     * @param string $entityName
229
     * the name of the entity as this class here is not aware of it
230
     *
231
     * @return boolean
232
     * true if all before events passed
233
     */
234 4
    public function createFiles(AbstractData $data, Request $request, Entity $entity, $entityName)
0 ignored issues
show
function createFiles() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
235
    {
236 4
        return $this->shouldWriteFile($data, $request, $entity, $entityName, 'createFiles');
237
    }
238
239
    /**
240
     * Updates the uploaded files of an updated entity.
241
     *
242
     * @param AbstractData $data
243
     * the AbstractData instance who should receive the events
244
     * @param Request $request
245
     * the HTTP request containing the file data
246
     * @param Entity $entity
247
     * the updated entity
248
     * @param string $entityName
249
     * the name of the entity as this class here is not aware of it
250
     *
251
     * @return boolean
252
     * true on successful update
253
     */
254 2
    public function updateFiles(AbstractData $data, Request $request, Entity $entity, $entityName)
0 ignored issues
show
function updateFiles() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
255
    {
256
        // With optional soft deletion, the file should be deleted first.
257 2
        return $this->shouldWriteFile($data, $request, $entity, $entityName, 'updateFiles');
258
    }
259
260
}
261