Issues (865)

Security Analysis    4 potential vulnerabilities

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

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

vendor/maxmind-db/reader/src/MaxMind/Db/Reader.php (1 issue)

1
<?php
2
3
namespace MaxMind\Db;
4
5
use BadMethodCallException;
6
use Exception;
7
use InvalidArgumentException;
8
use MaxMind\Db\Reader\Decoder;
9
use MaxMind\Db\Reader\InvalidDatabaseException;
10
use MaxMind\Db\Reader\Metadata;
11
use MaxMind\Db\Reader\Util;
12
use UnexpectedValueException;
13
14
/**
15
 * Instances of this class provide a reader for the MaxMind DB format. IP
16
 * addresses can be looked up using the get method.
17
 */
18
class Reader
19
{
20
    private static $DATA_SECTION_SEPARATOR_SIZE = 16;
21
    private static $METADATA_START_MARKER = "\xAB\xCD\xEFMaxMind.com";
22
    private static $METADATA_START_MARKER_LENGTH = 14;
23
    private static $METADATA_MAX_SIZE = 131072; // 128 * 1024 = 128KB
24
25
    private $decoder;
26
    private $fileHandle;
27
    private $fileSize;
28
    private $ipV4Start;
29
    private $metadata;
30
31
    /**
32
     * Constructs a Reader for the MaxMind DB format. The file passed to it must
33
     * be a valid MaxMind DB file such as a GeoIp2 database file.
34
     *
35
     * @param string $database
36
     *                         the MaxMind DB file to use
37
     *
38
     * @throws InvalidArgumentException                    for invalid database path or unknown arguments
39
     * @throws \MaxMind\Db\Reader\InvalidDatabaseException
40
     *                                                     if the database is invalid or there is an error reading
41
     *                                                     from it
42
     */
43
    public function __construct($database)
44
    {
45
        if (\func_num_args() !== 1) {
46
            throw new InvalidArgumentException(
47
                'The constructor takes exactly one argument.'
48
            );
49
        }
50
51
        if (!is_readable($database)) {
52
            throw new InvalidArgumentException(
53
                "The file \"$database\" does not exist or is not readable."
54
            );
55
        }
56
        $this->fileHandle = @fopen($database, 'rb');
57
        if ($this->fileHandle === false) {
58
            throw new InvalidArgumentException(
59
                "Error opening \"$database\"."
60
            );
61
        }
62
        $this->fileSize = @filesize($database);
63
        if ($this->fileSize === false) {
64
            throw new UnexpectedValueException(
65
                "Error determining the size of \"$database\"."
66
            );
67
        }
68
69
        $start = $this->findMetadataStart($database);
70
        $metadataDecoder = new Decoder($this->fileHandle, $start);
71
        list($metadataArray) = $metadataDecoder->decode($start);
72
        $this->metadata = new Metadata($metadataArray);
73
        $this->decoder = new Decoder(
74
            $this->fileHandle,
75
            $this->metadata->searchTreeSize + self::$DATA_SECTION_SEPARATOR_SIZE
0 ignored issues
show
Bug Best Practice introduced by
The property $searchTreeSize is declared private in MaxMind\Db\Reader\Metadata. Since you implement __get, consider adding a @property or @property-read.
Loading history...
76
        );
77
        $this->ipV4Start = $this->ipV4StartNode();
78
    }
79
80
    /**
81
     * Retrieves the record for the IP address.
82
     *
83
     * @param string $ipAddress
84
     *                          the IP address to look up
85
     *
86
     * @throws BadMethodCallException   if this method is called on a closed database
87
     * @throws InvalidArgumentException if something other than a single IP address is passed to the method
88
     * @throws InvalidDatabaseException
89
     *                                  if the database is invalid or there is an error reading
90
     *                                  from it
91
     *
92
     * @return mixed the record for the IP address
93
     */
94
    public function get($ipAddress)
95
    {
96
        if (\func_num_args() !== 1) {
97
            throw new InvalidArgumentException(
98
                'Method takes exactly one argument.'
99
            );
100
        }
101
        list($record) = $this->getWithPrefixLen($ipAddress);
102
103
        return $record;
104
    }
105
106
    /**
107
     * Retrieves the record for the IP address and its associated network prefix length.
108
     *
109
     * @param string $ipAddress
110
     *                          the IP address to look up
111
     *
112
     * @throws BadMethodCallException   if this method is called on a closed database
113
     * @throws InvalidArgumentException if something other than a single IP address is passed to the method
114
     * @throws InvalidDatabaseException
115
     *                                  if the database is invalid or there is an error reading
116
     *                                  from it
117
     *
118
     * @return array an array where the first element is the record and the
119
     *               second the network prefix length for the record
120
     */
121
    public function getWithPrefixLen($ipAddress)
122
    {
123
        if (\func_num_args() !== 1) {
124
            throw new InvalidArgumentException(
125
                'Method takes exactly one argument.'
126
            );
127
        }
128
129
        if (!\is_resource($this->fileHandle)) {
130
            throw new BadMethodCallException(
131
                'Attempt to read from a closed MaxMind DB.'
132
            );
133
        }
134
135
        if (!filter_var($ipAddress, FILTER_VALIDATE_IP)) {
136
            throw new InvalidArgumentException(
137
                "The value \"$ipAddress\" is not a valid IP address."
138
            );
139
        }
140
141
        list($pointer, $prefixLen) = $this->findAddressInTree($ipAddress);
142
        if ($pointer === 0) {
143
            return [null, $prefixLen];
144
        }
145
146
        return [$this->resolveDataPointer($pointer), $prefixLen];
147
    }
148
149
    private function findAddressInTree($ipAddress)
150
    {
151
        $rawAddress = unpack('C*', inet_pton($ipAddress));
152
153
        $bitCount = \count($rawAddress) * 8;
154
155
        // The first node of the tree is always node 0, at the beginning of the
156
        // value
157
        $node = 0;
158
159
        $metadata = $this->metadata;
160
161
        // Check if we are looking up an IPv4 address in an IPv6 tree. If this
162
        // is the case, we can skip over the first 96 nodes.
163
        if ($metadata->ipVersion === 6) {
164
            if ($bitCount === 32) {
165
                $node = $this->ipV4Start;
166
            }
167
        } elseif ($metadata->ipVersion === 4 && $bitCount === 128) {
168
            throw new InvalidArgumentException(
169
                "Error looking up $ipAddress. You attempted to look up an"
170
                . ' IPv6 address in an IPv4-only database.'
171
            );
172
        }
173
174
        $nodeCount = $metadata->nodeCount;
175
176
        for ($i = 0; $i < $bitCount && $node < $nodeCount; ++$i) {
177
            $tempBit = 0xFF & $rawAddress[($i >> 3) + 1];
178
            $bit = 1 & ($tempBit >> 7 - ($i % 8));
179
180
            $node = $this->readNode($node, $bit);
181
        }
182
        if ($node === $nodeCount) {
183
            // Record is empty
184
            return [0, $i];
185
        } elseif ($node > $nodeCount) {
186
            // Record is a data pointer
187
            return [$node, $i];
188
        }
189
        throw new InvalidDatabaseException('Something bad happened');
190
    }
191
192
    private function ipV4StartNode()
193
    {
194
        // If we have an IPv4 database, the start node is the first node
195
        if ($this->metadata->ipVersion === 4) {
196
            return 0;
197
        }
198
199
        $node = 0;
200
201
        for ($i = 0; $i < 96 && $node < $this->metadata->nodeCount; ++$i) {
202
            $node = $this->readNode($node, 0);
203
        }
204
205
        return $node;
206
    }
207
208
    private function readNode($nodeNumber, $index)
209
    {
210
        $baseOffset = $nodeNumber * $this->metadata->nodeByteSize;
211
212
        switch ($this->metadata->recordSize) {
213
            case 24:
214
                $bytes = Util::read($this->fileHandle, $baseOffset + $index * 3, 3);
215
                list(, $node) = unpack('N', "\x00" . $bytes);
216
217
                return $node;
218
            case 28:
219
                $bytes = Util::read($this->fileHandle, $baseOffset + 3 * $index, 4);
220
                if ($index === 0) {
221
                    $middle = (0xF0 & \ord($bytes[3])) >> 4;
222
                } else {
223
                    $middle = 0x0F & \ord($bytes[0]);
224
                }
225
                list(, $node) = unpack('N', \chr($middle) . substr($bytes, $index, 3));
226
227
                return $node;
228
            case 32:
229
                $bytes = Util::read($this->fileHandle, $baseOffset + $index * 4, 4);
230
                list(, $node) = unpack('N', $bytes);
231
232
                return $node;
233
            default:
234
                throw new InvalidDatabaseException(
235
                    'Unknown record size: '
236
                    . $this->metadata->recordSize
237
                );
238
        }
239
    }
240
241
    private function resolveDataPointer($pointer)
242
    {
243
        $resolved = $pointer - $this->metadata->nodeCount
244
            + $this->metadata->searchTreeSize;
245
        if ($resolved >= $this->fileSize) {
246
            throw new InvalidDatabaseException(
247
                "The MaxMind DB file's search tree is corrupt"
248
            );
249
        }
250
251
        list($data) = $this->decoder->decode($resolved);
252
253
        return $data;
254
    }
255
256
    /*
257
     * This is an extremely naive but reasonably readable implementation. There
258
     * are much faster algorithms (e.g., Boyer-Moore) for this if speed is ever
259
     * an issue, but I suspect it won't be.
260
     */
261
    private function findMetadataStart($filename)
262
    {
263
        $handle = $this->fileHandle;
264
        $fstat = fstat($handle);
265
        $fileSize = $fstat['size'];
266
        $marker = self::$METADATA_START_MARKER;
267
        $markerLength = self::$METADATA_START_MARKER_LENGTH;
268
269
        $minStart = $fileSize - min(self::$METADATA_MAX_SIZE, $fileSize);
270
271
        for ($offset = $fileSize - $markerLength; $offset >= $minStart; --$offset) {
272
            if (fseek($handle, $offset) !== 0) {
273
                break;
274
            }
275
276
            $value = fread($handle, $markerLength);
277
            if ($value === $marker) {
278
                return $offset + $markerLength;
279
            }
280
        }
281
        throw new InvalidDatabaseException(
282
            "Error opening database file ($filename). " .
283
            'Is this a valid MaxMind DB file?'
284
        );
285
    }
286
287
    /**
288
     * @throws InvalidArgumentException if arguments are passed to the method
289
     * @throws BadMethodCallException   if the database has been closed
290
     *
291
     * @return Metadata object for the database
292
     */
293
    public function metadata()
294
    {
295
        if (\func_num_args()) {
296
            throw new InvalidArgumentException(
297
                'Method takes no arguments.'
298
            );
299
        }
300
301
        // Not technically required, but this makes it consistent with
302
        // C extension and it allows us to change our implementation later.
303
        if (!\is_resource($this->fileHandle)) {
304
            throw new BadMethodCallException(
305
                'Attempt to read from a closed MaxMind DB.'
306
            );
307
        }
308
309
        return $this->metadata;
310
    }
311
312
    /**
313
     * Closes the MaxMind DB and returns resources to the system.
314
     *
315
     * @throws Exception
316
     *                   if an I/O error occurs
317
     */
318
    public function close()
319
    {
320
        if (!\is_resource($this->fileHandle)) {
321
            throw new BadMethodCallException(
322
                'Attempt to close a closed MaxMind DB.'
323
            );
324
        }
325
        fclose($this->fileHandle);
326
    }
327
}
328