Issues (43)

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/Data/MongoDb/MongoDbIndexer.php (2 issues)

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
 * File was created 29.02.2016 17:08
4
 */
5
6
namespace PeekAndPoke\Component\Slumber\Data\MongoDb;
7
8
use MongoDB\Collection;
9
use MongoDB\Driver\Exception;
10
use MongoDB\Model\IndexInfo;
11
use PeekAndPoke\Component\Psi\Psi;
12
use PeekAndPoke\Component\Slumber\Annotation\CompoundIndexDefinition;
13
use PeekAndPoke\Component\Slumber\Annotation\IndexDefinition;
14
use PeekAndPoke\Component\Slumber\Annotation\PropertyStorageIndexMarker;
15
use PeekAndPoke\Component\Slumber\Annotation\Slumber\AsObject;
16
use PeekAndPoke\Component\Slumber\Annotation\Slumber\Store\AsDbReference;
17
use PeekAndPoke\Component\Slumber\Core\LookUp\PropertyMarkedForSlumber;
18
use PeekAndPoke\Component\Slumber\Data\LookUp\PropertyMarkedForIndexing;
19
use Psr\Log\LoggerInterface;
20
21
/**
22
 * @author Karsten J. Gerber <[email protected]>
23
 */
24
class MongoDbIndexer
25
{
26
    /** @var MongoDbEntityConfigReader */
27
    private $lookUp;
28
    /** @var LoggerInterface */
29
    private $logger;
30
31
    /**
32
     * MongoDbIndexer constructor.
33
     *
34
     * @param MongoDbEntityConfigReader $lookUp
35
     * @param LoggerInterface           $logger
36
     */
37
    public function __construct(MongoDbEntityConfigReader $lookUp, LoggerInterface $logger)
38
    {
39
        $this->lookUp = $lookUp;
40
        $this->logger = $logger;
41
    }
42
43
    /**
44
     * @param Collection       $collection
45
     * @param \ReflectionClass $class
46
     *
47
     * @return array
48
     */
49
    public function ensureIndexes(Collection $collection, \ReflectionClass $class)
50
    {
51
        $createdIndexes = $this->ensureIndexesRecursive([], $class, $collection);
52
53
        // delete all indexes that are no longer needed
54
        $indexInfos     = $collection->listIndexes();
55
        $deletedIndexes = [];
56
57
        /** @var IndexInfo $indexInfo */
58
        foreach ($indexInfos as $indexInfo) {
59
60
            $indexInfoName = $indexInfo->getName();
61
62
            if ($indexInfoName !== '_id_' && ! \in_array($indexInfoName, $createdIndexes, true)) {
63
                $collection->dropIndex($indexInfoName);
64
                $deletedIndexes[] = $indexInfoName;
65
            }
66
        }
67
68
        $this->logger->debug(
69
            'Creating indexes for repository ' . $collection->getCollectionName() . ' - ' .
70
            'Ensured indexes: ' . implode(', ', $createdIndexes) . ' - ' .
71
            'Deleted indexes: ' . implode(', ', $deletedIndexes)
72
        );
73
74
        return Psi::it($collection->listIndexes())->toArray();
75
    }
76
77
    /**
78
     * @param string[]         $prefixes
79
     * @param \ReflectionClass $entity
80
     * @param Collection       $collection
81
     *
82
     * @return array
83
     */
84
    private function ensureIndexesRecursive($prefixes, \ReflectionClass $entity, Collection $collection)
85
    {
86
        $entityConfig = $this->lookUp->getEntityConfig($entity);
87
88
        $createdIndexes = array_merge(
89
            $this->createCompoundIndexes($prefixes, $collection, $entityConfig->getCompoundIndexes()),
90
            $this->createPropertyIndexes($prefixes, $collection, $entityConfig->getIndexedProperties())
91
        );
92
93
        // also index child objects
94
        Psi::it($entityConfig->getMarkedProperties())
95
            ->filter(new Psi\IsInstanceOf(PropertyMarkedForSlumber::class))
96
            // we also look into child objects
97
            ->filter(function (PropertyMarkedForSlumber $p) {
98
                return $p->getFirstMarkerOf(AsObject::class) !== null;
99
            })
100
            // but NOT if they are db-references
101
            ->filter(function (PropertyMarkedForSlumber $p) {
102
                return $p->getFirstMarkerOf(AsDbReference::class) === null;
103
            })
104
            ->each(function (PropertyMarkedForSlumber $p) use ($prefixes, $collection, &$createdIndexes) {
105
                /** @var AsObject $asObject */
106
                $asObject = $p->getFirstMarkerOf(AsObject::class);
107
108
                /** @noinspection NullPointerExceptionInspection */
109
                $subCreated = $this->ensureIndexesRecursive(
110
                    array_merge($prefixes, [$p->alias]),
111
                    new \ReflectionClass($asObject->value),
112
                    $collection
113
                );
114
115
                $createdIndexes = array_merge($createdIndexes, $subCreated);
116
            })->toArray();
117
118
        return $createdIndexes;
119
    }
120
121
    /**
122
     * @param string[]                  $prefixes
123
     * @param Collection                $collection
124
     * @param CompoundIndexDefinition[] $compoundIndexes
125
     *
126
     * @return string[] The names of all indexes that where created
127
     */
128
    private function createCompoundIndexes($prefixes, Collection $collection, $compoundIndexes)
129
    {
130
        $createdIndexes = [];
131
132
        foreach ($compoundIndexes as $compoundIndex) {
133
134
            $definition = [];
135
            // append the prefixes to all fields
136
            foreach ($compoundIndex->getDefinition() as $k => $v) {
137
                $definition[$this->buildFieldName($prefixes, $k)] = $v;
138
            }
139
140
            $name    = $this->buildCompoundIndexName($prefixes, $definition);
141
            $options = $this->assembleOptions($name, $compoundIndex);
142
143
            $createdIndexes[] = $this->createIndex($collection, $definition, $options);
144
        }
145
146
        return $createdIndexes;
147
    }
148
149
    /**
150
     * @param string[]                    $prefixes
151
     * @param Collection                  $collection
152
     * @param PropertyMarkedForIndexing[] $indexedProperties
153
     *
154
     * @return string[] The names of all indexes that where created
155
     */
156
    private function createPropertyIndexes($prefixes, Collection $collection, $indexedProperties)
157
    {
158
        $createdIndexes = [];
159
160
        foreach ($indexedProperties as $indexedProperty) {
161
162
            foreach ($indexedProperty->markers as $marker) {
163
164
                $definition = [
165
                    // append the prefixes to the field
166
                    $this->buildFieldName($prefixes, $indexedProperty->propertyName) => $this->mapDirection($marker),
167
                ];
168
                $name       = $this->buildPropertyIndexName($prefixes, $indexedProperty->propertyName, $marker);
169
                $options    = $this->assembleOptions($name, $marker);
170
171
                $createdIndexes[] = $this->createIndex($collection, $definition, $options);
172
            }
173
        }
174
175
        return $createdIndexes;
176
    }
177
178
    /**
179
     * @param Collection $collection
180
     * @param array      $fields
181
     * @param array      $options
182
     *
183
     * @return string The resulting index names
184
     */
185
    private function createIndex(Collection $collection, array $fields, array $options)
186
    {
187
        $debugInfo = 'collection "' . $collection->getCollectionName() . '" for fields: ' . json_encode($fields) . ' - Options: ' . json_encode($options);
188
189
        try {
190
            $collection->createIndex($fields, $options);
191
        } catch (Exception\RuntimeException $e) {
0 ignored issues
show
The class MongoDB\Driver\Exception\RuntimeException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
192
193
            // Do we have a duplicate key problem?
194
            if ($e->getCode() === 11000 || strpos($e->getMessage(), 'E11000') !== false) {
195
                throw new \RuntimeException('Duplicate key problems on ' . $debugInfo, 0, $e);
196
            }
197
198
            // Have the options changes? So we try to drop the index and then create it again
199
            if ($e->getCode() === 85 || strpos($e->getMessage(), 'already exists with different options') === false) {
200
                // drop index by name
201
                $collection->dropIndex($options['name']);
202
203
                // and try the creation again
204
                try {
205
                    $collection->createIndex($fields, $options);
206
                } catch (Exception\RuntimeException $e) {
0 ignored issues
show
The class MongoDB\Driver\Exception\RuntimeException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
207
                    throw new \RuntimeException('Cannot create index - even after dropping it - on ' . $debugInfo, 0, $e);
208
                }
209
            }
210
211
            // something else has happened
212
            throw new \RuntimeException('Unknown problem on ' . $debugInfo, 0, $e);
213
        }
214
215
        return $options['name'];
216
    }
217
218
    /**
219
     * @param string          $indexName
220
     * @param IndexDefinition $marker
221
     *
222
     * @return array
223
     */
224
    private function assembleOptions($indexName, IndexDefinition $marker)
225
    {
226
        $options = [
227
            'name'       => (string) $indexName,
228
            'background' => (bool) $marker->isBackground(),
229
            'unique'     => (bool) $marker->isUnique(),
230
            'dropDups'   => (bool) $marker->isDropDups(),
231
            'sparse'     => (bool) $marker->isSparse(),
232
        ];
233
234
        if ($marker->getExpireAfterSeconds() >= 0) {
235
            $options['expireAfterSeconds'] = $marker->getExpireAfterSeconds();
236
        }
237
238
        return $options;
239
    }
240
241
    /**
242
     * @param string[] $prefixes
243
     * @param string   $fieldName
244
     *
245
     * @return string
246
     */
247
    private function buildFieldName($prefixes, $fieldName)
248
    {
249
        if (\count($prefixes) === 0) {
250
            return $fieldName;
251
        }
252
253
        return implode('.', $prefixes) . '.' . $fieldName;
254
    }
255
256
    /**
257
     * @param string[]        $prefixes
258
     * @param string          $propertyName
259
     * @param IndexDefinition $marker
260
     *
261
     * @return string
262
     */
263
    private function buildPropertyIndexName($prefixes, $propertyName, IndexDefinition $marker)
264
    {
265
        // is the name overridden by the user ?
266
        if (! empty($marker->getName())) {
267
            $rest = $marker->getName();
268
        } else {
269
            $rest = $propertyName . '_' . $this->mapDirection($marker);
270
        }
271
272
        if (\count($prefixes) > 0) {
273
            return implode('.', $prefixes) . '.' . $rest;
274
        }
275
276
        return $rest;
277
    }
278
279
    /**
280
     * @param string[] $prefixes
281
     * @param array    $definition
282
     *
283
     * @return string
284
     */
285
    private function buildCompoundIndexName($prefixes, $definition)
286
    {
287
        $parts = [];
288
289
        foreach ($definition as $k => $v) {
290
            $parts[] = ((string) $k) . '_' . ((string) $v);
291
        }
292
293
        $rest = implode('_', $parts);
294
295
        if (\count($prefixes) > 0) {
296
            return implode('.', $prefixes) . '.' . $rest;
297
        }
298
299
        return $rest;
300
    }
301
302
    /**
303
     * @param IndexDefinition $marker
304
     *
305
     * @return int
306
     */
307
    private function mapDirection(IndexDefinition $marker)
308
    {
309
        return $marker->getDirection() === PropertyStorageIndexMarker::ASCENDING ? 1 : -1;
310
    }
311
}
312