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/Serializer.php (2 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
    namespace NokitaKaze\Serializer;
4
5
    class Serializer {
6
        const TYPE_INT = 0;
7
        const TYPE_DOUBLE = 1;
8
        const TYPE_STRING = 2;
9
        const TYPE_ARRAY_INDEX = 3;
10
        const TYPE_ARRAY_KEY_VALUE = 4;
11
        const TYPE_OBJECT_KEY_VALUE = 5;
12
        const TYPE_SERIALIZABLE = ISerializable::TYPE;
13
        const TYPE_RESOURCE = 7;
14
        const TYPE_BOOLEAN = 7;
15
        const TYPE_NULL = 8;
16
17
        const DEFAULT_DEPTH = 255;
18
19
        /**
20
         * @param mixed   $data
21
         * @param integer $depth
22
         *
23
         * @return string
24
         */
25 23
        public static function serialize($data, $depth = self::DEFAULT_DEPTH) {
26 23
            return json_encode(static::serialize_to_variant($data, $depth));
27
        }
28
29
        /**
30
         * @param mixed   $text
31
         * @param boolean $is_valid
32
         * @param boolean $safe
33
         *
34
         * @return mixed
35
         */
36 28
        public static function unserialize($text, &$is_valid, $safe = true) {
37 28
            $is_valid = true;
38 28
            if ($text === 'null') {
39 1
                return null;
40
            }
41 27
            $data = json_decode($text);
42 27
            if (is_null($data)) {
43 1
                $is_valid = false;
44
45 1
                return null;
46
            }
47
48
            try {
49 26
                $object = static::unserialize_variant_to_value($data, $safe);
50 4
            } catch (UnserializeException $e) {
51 4
                $is_valid = false;
52
53 4
                return null;
54
            }
55
56 22
            return $object;
57
        }
58
59
        /**
60
         * @param mixed   $data
61
         * @param integer $depth
62
         *
63
         * @return Variant|object|boolean|null
64
         */
65 23
        public static function serialize_to_variant($data, $depth = self::DEFAULT_DEPTH) {
66 23
            if ($depth < 0) {
67 1
                return null;
68
            }
69 23
            if (is_double($data)) {
70 8
                return static::serialize_double($data);
71 17
            } elseif (is_int($data)) {
72 4
                return static::serialize_int($data);
73 16
            } elseif (is_string($data)) {
74 5
                return static::serialize_string($data);
75 14
            } elseif (is_array($data) and static::is_indexed_array($data)) {
76 4
                return static::serialize_indexed_array($data, $depth - 1);
77 12
            } elseif (is_array($data)) {
78 2
                return static::serialize_array_key_value($data, $depth - 1);
79 11
            } elseif (is_object($data) and ($data instanceof ISerializable)) {
80
                /**
81
                 * @var ISerializable $data
82
                 */
83 3
                return $data->ns_serialize();
84 8
            } elseif (is_object($data)) {
85 4
                return static::serialize_object_key_value($data);
86 4
            } elseif (is_resource($data)) {
87
                // @todo Придумать что делать с ресурсами
88
                return null;
89 4
            } elseif (is_bool($data)) {
90 3
                return $data;
91 2
            } elseif (is_null($data)) {
92 2
                return null;
93
            } else {
94
                return null;
95
            }
96
        }
97
98
        protected static $_binary_codes = [
99
            0, 1, 2, 3, 4, 5, 6, 7, 11, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 128, 129, 130,
100
            131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153,
101
            154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176,
102
            177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199,
103
            200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222,
104
            223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245,
105
            246, 247, 248, 249, 250, 251, 252, 253, 254,
106
        ];
107
108
        protected static $_binary_codes_flip = null;
109
110
        /**
111
         * @param string $value
112
         *
113
         * @return boolean
114
         */
115 19
        public static function is_binary_string($value) {
116 19
            if (is_null(self::$_binary_codes_flip)) {
117 1
                self::$_binary_codes_flip = array_flip(self::$_binary_codes);
118
            }
119
120
            // @todo Оптимизировать через > & <
121 19
            for ($i = 0; $i < strlen($value); $i++) {
122 19
                $ord = ord(substr($value, $i, 1));
123 19
                if (isset(self::$_binary_codes_flip[$ord])) {
124 1
                    return true;
125
                }
126
            }
127
128 19
            return false;
129
        }
130
131
        /**
132
         * @param double $value
133
         *
134
         * @return Variant|object
135
         */
136 8
        public static function serialize_double($value) {
137
            return (object) [
138 8
                'type' => self::TYPE_DOUBLE,
139 8
                'value' => serialize($value),
140
            ];
141
        }
142
143
        /**
144
         * @param integer $value
145
         *
146
         * @return Variant|object
147
         */
148 4
        public static function serialize_int($value) {
149
            return (object) [
150 4
                'type' => self::TYPE_INT,
151 4
                'value' => $value,
152
            ];
153
        }
154
155
        /**
156
         * @param array   $value
157
         * @param integer $depth
158
         *
159
         * @return Variant|object
160
         */
161 4
        public static function serialize_indexed_array(array $value, $depth) {
162 4
            $output = [];
163 4
            foreach ($value as $sub_value) {
164 3
                $output[] = self::serialize_to_variant($sub_value, $depth - 1);
165
            }
166
167
            return (object) [
168 4
                'type' => self::TYPE_ARRAY_INDEX,
169 4
                'value' => $output,
170
            ];
171
        }
172
173
        /**
174
         * @param array   $value
175
         * @param integer $depth
176
         *
177
         * @return Variant|object
178
         */
179 2
        public static function serialize_array_key_value(array $value, $depth) {
180 2
            $output = [];
181 2
            $keys = [];
182 2
            foreach ($value as $key => $sub_value) {
183 2
                $keys[] = self::serialize_to_variant($key, $depth - 1);
184 2
                $output[] = self::serialize_to_variant($sub_value, $depth - 1);
185
            }
186
187
            return (object) [
188 2
                'type' => self::TYPE_ARRAY_KEY_VALUE,
189 2
                'value' => $output,
190 2
                'keys' => $keys,
191
            ];
192
        }
193
194
        /**
195
         * @param \stdClass|object $value
196
         *
197
         * @return Variant|object
198
         */
199 4
        public static function serialize_object_key_value($value) {
200 4
            $output = [];
201 4
            $keys = [];
202
203 4
            foreach (get_object_vars($value) as $key => $value) {
204 1
                $keys[] = self::serialize_to_variant($key);
205 1
                $output[] = self::serialize_to_variant($value);
206
            }
207
208
            return (object) [
209 4
                'type' => self::TYPE_OBJECT_KEY_VALUE,
210 4
                'value' => $output,
211 4
                'keys' => $keys,
212
            ];
213
        }
214
215
        /**
216
         * @param string $value
217
         *
218
         * @return Variant|object
219
         */
220 5
        public static function serialize_string($value) {
221
            return (object) [
222 5
                'type' => self::TYPE_STRING,
223 5
                'value' => base64_encode(gzcompress($value)),
224
            ];
225
        }
226
227
        /**
228
         * @param array $input
229
         *
230
         * @return boolean
231
         */
232 6
        public static function is_indexed_array(array $input) {
233 6
            if (empty($input)) {
234 1
                return true;
235
            }
236
237 5
            $keys = array_keys($input);
238 5
            for ($i = 0; $i < count($keys); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
239 5
                if ($keys[$i] !== $i) {
240 2
                    return false;
241
                }
242
            }
243
244 3
            return true;
245
        }
246
247
        /**
248
         * @param \stdClass|Variant $variant
249
         *
250
         * @return string
251
         */
252 5
        public static function unserialize_string($variant) {
253 5
            return gzuncompress(base64_decode($variant->value));
254
        }
255
256
        /**
257
         * @param \stdClass|Variant $variant
258
         * @param boolean           $safe
259
         *
260
         * @return array
261
         * @throws UnserializeException
262
         */
263 4
        public static function unserialize_array_index($variant, $safe) {
264 4
            $output = [];
265 4
            foreach ($variant->value as $sub_value) {
266 3
                $output[] = self::unserialize_variant_to_value($sub_value, $safe);
267
            }
268
269 4
            return $output;
270
        }
271
272
        /**
273
         * @param \stdClass|KeyValueVariant $variant
274
         * @param boolean                   $safe
275
         *
276
         * @return array
277
         * @throws UnserializeException
278
         */
279 6
        public static function unserialize_array_key_value($variant, $safe) {
280 6
            $count = count($variant->keys);
281 6
            if ($count != count($variant->value)) {
282
                throw new UnserializeException(sprintf('Array keys count (%d) does not equal value count (%d)',
283
                    $count,
284
                    count($variant->value)
285
                ), 2);
286
            }
287
288 6
            $output = [];
289 6
            for ($i = 0; $i < count($variant->keys); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
290 3
                $output[self::unserialize_variant_to_value($variant->keys[$i], $safe)]
291 3
                    = self::unserialize_variant_to_value($variant->value[$i], $safe);
292
            }
293
294 6
            return $output;
295
        }
296
297
        /**
298
         * @param SerializableVariant|boolean|null|object $variant
299
         * @param boolean                                 $safe
300
         *
301
         * @return mixed
302
         * @throws UnserializeException
303
         */
304 6
        public static function unserialize_object($variant, $safe) {
305 6
            if (!isset($variant->class_FQDN)) {
306 1
                throw new UnserializeException('Variant does not contain field with FQDN', 3);
307
            }
308 5
            if (!class_exists($variant->class_FQDN)) {
309 1
                throw new UnserializeException(sprintf('Class "%s" does not exist', $variant->class_FQDN), 4);
310
            }
311 4
            if (!is_subclass_of($variant->class_FQDN, 'NokitaKaze\\Serializer\\ISerializable')) {
312 1
                throw new UnserializeException(sprintf('Class "%s" does not implement ISerializable', $variant->class_FQDN), 5);
313
            }
314 3
            if ($safe and !is_subclass_of($variant->class_FQDN, 'NokitaKaze\\Serializer\\ISafeSerializable')) {
315
                // Не поддерживает safe-десериализацию
316 1
                return null;
317
            }
318
319
            /**
320
             * @var ISerializable $class
321
             */
322 2
            $class = $variant->class_FQDN;
323
324 2
            return $class::ns_unserialize($variant);
325
        }
326
327
        /**
328
         * @param Variant|boolean|null|object $variant
329
         * @param boolean                     $safe
330
         *
331
         * @return mixed
332
         * @throws UnserializeException
333
         */
334 26
        public static function unserialize_variant_to_value($variant, $safe) {
335 26
            if (is_null($variant)) {
336 2
                return null;
337 26
            } elseif (is_bool($variant)) {
338 3
                return $variant;
339
            } else {
340 26
                switch ($variant->type) {
341 26
                    case self::TYPE_INT:
342 4
                        return $variant->value;
343 25
                    case self::TYPE_DOUBLE:
344 8
                        return \unserialize($variant->value);
345 19
                    case self::TYPE_STRING:
346 5
                        return self::unserialize_string($variant);
347 17
                    case self::TYPE_ARRAY_INDEX:
348 4
                        return self::unserialize_array_index($variant, $safe);
349 13
                    case self::TYPE_ARRAY_KEY_VALUE:
350 2
                        return self::unserialize_array_key_value($variant, $safe);
351 11
                    case self::TYPE_OBJECT_KEY_VALUE:
352 4
                        return (object) self::unserialize_array_key_value($variant, $safe);
353 7
                    case self::TYPE_SERIALIZABLE:
354 6
                        return self::unserialize_object($variant, $safe);
355
                    default:
356 1
                        throw new UnserializeException('Malformed type '.$variant->type, 1);
357
                }
358
            }
359
        }
360
361
    }
362
363
?>