GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Issues (1881)

Security Analysis    not enabled

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.

freemius/includes/class-fs-options.php (21 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
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 18 and the first side effect is on line 10.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
    /**
3
     * @package     Freemius
4
     * @copyright   Copyright (c) 2015, Freemius, Inc.
5
     * @license     https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
     * @since       1.2.3
7
     */
8
9
    if ( ! defined( 'ABSPATH' ) ) {
10
        exit;
11
    }
12
13
    /**
14
     * Class FS_Options
15
     *
16
     * A wrapper class for handling network level and single site level options.
17
     */
18
    class FS_Options {
0 ignored issues
show
Since you have declared the constructor as private, maybe you should also declare the class as final.
Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
19
        /**
20
         * @var string
21
         */
22
        private $_id;
23
24
        /**
25
         * @var array[string]FS_Options {
26
         * @key   string
27
         * @value FS_Options
28
         * }
29
         */
30
        private static $_instances;
31
32
        /**
33
         * @var FS_Option_Manager Site level options.
34
         */
35
        private $_options;
36
37
        /**
38
         * @var FS_Option_Manager Network level options.
39
         */
40
        private $_network_options;
41
42
        /**
43
         * @var int The ID of the blog that is associated with the current site level options.
44
         */
45
        private $_blog_id = 0;
46
47
        /**
48
         * @var bool
49
         */
50
        private $_is_multisite;
51
52
        /**
53
         * @var string[] Lazy collection of params on the site level.
54
         */
55
        private static $_SITE_OPTIONS_MAP;
56
57
        /**
58
         * @author Leo Fajardo (@leorw)
59
         * @since  2.0.0
60
         *
61
         * @param string $id
62
         * @param bool   $load
63
         *
64
         * @return FS_Options
65
         */
66
        static function instance( $id, $load = false ) {
0 ignored issues
show
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
67
            if ( ! isset( self::$_instances[ $id ] ) ) {
68
                self::$_instances[ $id ] = new FS_Options( $id, $load );
69
            }
70
71
            return self::$_instances[ $id ];
72
        }
73
74
        /**
75
         * @author Leo Fajardo (@leorw)
76
         * @since  2.0.0
77
         *
78
         * @param string $id
79
         * @param bool   $load
80
         */
81
        private function __construct( $id, $load = false ) {
82
            $this->_id           = $id;
83
            $this->_is_multisite = is_multisite();
84
85
            if ( $this->_is_multisite ) {
86
                $this->_blog_id         = get_current_blog_id();
87
                $this->_network_options = FS_Option_Manager::get_manager( $id, $load, true );
88
            }
89
90
            $this->_options = FS_Option_Manager::get_manager( $id, $load, $this->_blog_id );
91
        }
92
93
        /**
94
         * Switch the context of the site level options manager.
95
         *
96
         * @author Vova Feldman (@svovaf)
97
         * @since  2.0.0
98
         *
99
         * @param $blog_id
100
         */
101
        function set_site_blog_context( $blog_id ) {
0 ignored issues
show
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
102
            $this->_blog_id = $blog_id;
103
104
            $this->_options = FS_Option_Manager::get_manager( $this->_id, false, $this->_blog_id );
105
        }
106
107
        /**
108
         * @author Leo Fajardo (@leorw)
109
         *
110
         * @param string        $option
111
         * @param mixed         $default
112
         * @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_SITE_LEVEL_PARAMS).
113
         *
114
         * @return mixed
115
         */
116
        function get_option( $option, $default = null, $network_level_or_blog_id = null ) {
0 ignored issues
show
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
117
            if ( $this->should_use_network_storage( $option, $network_level_or_blog_id ) ) {
118
                return $this->_network_options->get_option( $option, $default );
119
            }
120
121
            $site_options = $this->get_site_options( $network_level_or_blog_id );
0 ignored issues
show
It seems like $network_level_or_blog_id defined by parameter $network_level_or_blog_id on line 116 can also be of type boolean or null; however, FS_Options::get_site_options() does only seem to accept integer, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
122
123
            return $site_options->get_option( $option, $default );
124
        }
125
126
        /**
127
         * @author Leo Fajardo (@leorw)
128
         * @since  2.0.0
129
         *
130
         * @param string        $option
131
         * @param mixed         $value
132
         * @param bool          $flush
133
         * @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_SITE_LEVEL_PARAMS).
134
         */
135
        function set_option( $option, $value, $flush = false, $network_level_or_blog_id = null ) {
0 ignored issues
show
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
136
            if ( $this->should_use_network_storage( $option, $network_level_or_blog_id ) ) {
137
                $this->_network_options->set_option( $option, $value, $flush );
138
            } else {
139
                $site_options = $this->get_site_options( $network_level_or_blog_id );
0 ignored issues
show
It seems like $network_level_or_blog_id defined by parameter $network_level_or_blog_id on line 135 can also be of type boolean or null; however, FS_Options::get_site_options() does only seem to accept integer, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
140
                $site_options->set_option( $option, $value, $flush );
141
            }
142
        }
143
144
        /**
145
         * @author Vova Feldman (@svovaf)
146
         * @since  2.0.0
147
         *
148
         * @param string        $option
149
         * @param bool          $flush
150
         * @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_SITE_LEVEL_PARAMS).
151
         */
152
        function unset_option( $option, $flush = false, $network_level_or_blog_id = null ) {
0 ignored issues
show
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
153
            if ( $this->should_use_network_storage( $option, $network_level_or_blog_id ) ) {
154
                $this->_network_options->unset_option( $option, $flush );
155
            } else {
156
                $site_options = $this->get_site_options( $network_level_or_blog_id );
0 ignored issues
show
It seems like $network_level_or_blog_id defined by parameter $network_level_or_blog_id on line 152 can also be of type boolean or null; however, FS_Options::get_site_options() does only seem to accept integer, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
157
                $site_options->unset_option( $option, $flush );
158
            }
159
        }
160
161
        /**
162
         * @author Leo Fajardo (@leorw)
163
         * @since  2.0.0
164
         *
165
         * @param bool $flush
166
         * @param bool $network_level
167
         */
168
        function load( $flush = false, $network_level = true ) {
0 ignored issues
show
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
169
            if ( $this->_is_multisite && $network_level ) {
170
                $this->_network_options->load( $flush );
171
            } else {
172
                $this->_options->load( $flush );
173
            }
174
        }
175
176
        /**
177
         * @author Leo Fajardo (@leorw)
178
         * @since  2.0.0
179
         *
180
         * @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, store both network storage and the current context blog storage.
181
         */
182
        function store( $network_level_or_blog_id = null ) {
0 ignored issues
show
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
183
            if ( ! $this->_is_multisite ||
184
                 false === $network_level_or_blog_id ||
185
                 0 == $network_level_or_blog_id ||
186
                 is_null( $network_level_or_blog_id )
187
            ) {
188
                $site_options = $this->get_site_options( $network_level_or_blog_id );
0 ignored issues
show
It seems like $network_level_or_blog_id defined by parameter $network_level_or_blog_id on line 182 can also be of type boolean or null; however, FS_Options::get_site_options() does only seem to accept integer, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
189
                $site_options->store();
190
            }
191
192
            if ( $this->_is_multisite &&
193
                 ( is_null( $network_level_or_blog_id ) || true === $network_level_or_blog_id )
194
            ) {
195
                $this->_network_options->store();
196
            }
197
        }
198
199
        /**
200
         * @author Vova Feldman (@svovaf)
201
         * @since  2.0.0
202
         *
203
         * @param int|null|bool $network_level_or_blog_id
204
         * @param bool          $flush
205
         */
206
        function clear( $network_level_or_blog_id = null, $flush = false ) {
0 ignored issues
show
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
207
            if ( ! $this->_is_multisite ||
208
                 false === $network_level_or_blog_id ||
209
                 is_null( $network_level_or_blog_id ) ||
210
                 is_numeric( $network_level_or_blog_id )
211
            ) {
212
                $site_options = $this->get_site_options( $network_level_or_blog_id );
0 ignored issues
show
It seems like $network_level_or_blog_id defined by parameter $network_level_or_blog_id on line 206 can also be of type boolean or null; however, FS_Options::get_site_options() does only seem to accept integer, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
213
                $site_options->clear( $flush );
214
            }
215
216
            if ( $this->_is_multisite &&
217
                 ( true === $network_level_or_blog_id || is_null( $network_level_or_blog_id ) )
218
            ) {
219
                $this->_network_options->clear( $flush );
220
            }
221
        }
222
223
        /**
224
         * Migration script to the new storage data structure that is network compatible.
225
         *
226
         * IMPORTANT:
227
         *      This method should be executed only after it is determined if this is a network
228
         *      level compatible product activation.
229
         *
230
         * @author Vova Feldman (@svovaf)
231
         * @since  2.0.0
232
         *
233
         * @param int $blog_id
234
         */
235
        function migrate_to_network( $blog_id = 0 ) {
0 ignored issues
show
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
236
            if ( ! $this->_is_multisite ) {
237
                return;
238
            }
239
240
            $updated = false;
241
242
            $site_options = $this->get_site_options( $blog_id );
243
244
            $keys = $site_options->get_options_keys();
245
246
            foreach ( $keys as $option ) {
247
                if ( $this->is_site_option( $option ) ||
248
                     // Don't move admin notices to the network storage.
249
                    in_array($option, array(
250
                        // Don't move admin notices to the network storage.
251
                        'admin_notices',
252
                        // Don't migrate the module specific data, it will be migrated by the FS_Storage.
253
                        'plugin_data',
254
                        'theme_data',
255
                    ))
256
                ) {
257
                    continue;
258
                }
259
260
                $option_updated = false;
261
262
                // Migrate option to the network storage.
263
                $site_option = $site_options->get_option( $option );
264
265
                if ( ! $this->_network_options->has_option( $option ) ) {
266
                    // Option not set on the network level, so just set it.
267
                    $this->_network_options->set_option( $option, $site_option, false );
268
269
                    $option_updated = true;
270
                } else {
271
                    // Option already set on the network level, so we need to merge it inelegantly.
272
                    $network_option = $this->_network_options->get_option( $option );
273
274
                    if ( is_array( $network_option ) && is_array( $site_option ) ) {
275
                        // Option is an array.
276
                        foreach ( $site_option as $key => $value ) {
277
                            if ( ! isset( $network_option[ $key ] ) ) {
278
                                $network_option[ $key ] = $value;
279
280
                                $option_updated = true;
281
                            } else if ( is_array( $network_option[ $key ] ) && is_array( $value ) ) {
282
                                if ( empty( $network_option[ $key ] ) ) {
283
                                    $network_option[ $key ] = $value;
284
285
                                    $option_updated = true;
286
                                } else if ( empty( $value ) ) {
0 ignored issues
show
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
287
                                    // Do nothing.
288
                                } else {
289
                                    reset($value);
290
                                    $first_key = key($value);
291
                                    if ( $value[$first_key] instanceof FS_Entity ) {
292
                                        // Merge entities by IDs.
293
                                        $network_entities_ids = array();
294
                                        foreach ( $network_option[ $key ] as $entity ) {
295
                                            $network_entities_ids[ $entity->id ] = true;
296
                                        }
297
298
                                        foreach ( $value as $entity ) {
299
                                            if ( ! isset( $network_entities_ids[ $entity->id ] ) ) {
300
                                                $network_option[ $key ][] = $entity;
301
302
                                                $option_updated = true;
303
                                            }
304
                                        }
305
                                    }
306
                                }
307
                            }
308
                        }
309
                    }
310
311
                    if ( $option_updated ) {
312
                        $this->_network_options->set_option( $option, $network_option, false );
313
                    }
314
                }
315
316
                /**
317
                 * Remove the option from site level storage.
318
                 *
319
                 * IMPORTANT:
320
                 *      The line below is intentionally commented since we want to preserve the option
321
                 *      on the site storage level for "downgrade compatibility". Basically, if the user
322
                 *      will downgrade to an older version of the plugin with the prev storage structure,
323
                 *      it will continue working.
324
                 *
325
                 * @todo After a few releases we can remove this.
326
                 */
327
//                    $site_options->unset_option($option, false);
0 ignored issues
show
Unused Code Comprehensibility introduced by
73% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
328
329
                if ( $option_updated ) {
330
                    $updated = true;
331
                }
332
            }
333
334
            if ( ! $updated ) {
335
                return;
336
            }
337
338
            // Update network level storage.
339
            $this->_network_options->store();
340
//            $site_options->store();
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
341
        }
342
343
344
        #--------------------------------------------------------------------------------
345
        #region Helper Methods
346
        #--------------------------------------------------------------------------------
347
348
        /**
349
         * We don't want to load the map right away since it's not even needed in a non-MS environment.
350
         *
351
         * @author Vova Feldman (@svovaf)
352
         * @since  2.0.0
353
         */
354
        private static function load_site_options_map() {
355
            self::$_SITE_OPTIONS_MAP = array(
0 ignored issues
show
Documentation Bug introduced by
It seems like array('sites' => true, '...ctive_plugins' => true) of type array<string,boolean,{"s...ve_plugins":"boolean"}> is incompatible with the declared type array<integer,string> of property $_SITE_OPTIONS_MAP.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
356
                'sites'          => true,
357
                'theme_sites'    => true,
358
                'unique_id'      => true,
359
                'active_plugins' => true,
360
            );
361
        }
362
363
        /**
364
         * @author Vova Feldman (@svovaf)
365
         * @since  2.0.0
366
         *
367
         * @param string $option
368
         *
369
         * @return bool
370
         */
371
        private function is_site_option( $option ) {
372
            if ( WP_FS__ACCOUNTS_OPTION_NAME != $this->_id ) {
373
                return false;
374
            }
375
376
            if ( ! isset( self::$_SITE_OPTIONS_MAP ) ) {
377
                self::load_site_options_map();
378
            }
379
380
            return isset( self::$_SITE_OPTIONS_MAP[ $option ] );
381
        }
382
383
        /**
384
         * @author Vova Feldman (@svovaf)
385
         * @since  2.0.0
386
         *
387
         * @param int $blog_id
388
         *
389
         * @return FS_Option_Manager
390
         */
391
        private function get_site_options( $blog_id = 0 ) {
392
            if ( 0 == $blog_id || $blog_id == $this->_blog_id ) {
393
                return $this->_options;
394
            }
395
396
            return FS_Option_Manager::get_manager( $this->_id, true, $blog_id );
397
        }
398
399
        /**
400
         * Check if an option should be stored on the MS network storage.
401
         *
402
         * @author Vova Feldman (@svovaf)
403
         * @since  2.0.0
404
         *
405
         * @param string        $option
406
         * @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_SITE_LEVEL_PARAMS).
407
         *
408
         * @return bool
409
         */
410
        private function should_use_network_storage( $option, $network_level_or_blog_id = null ) {
411
            if ( ! $this->_is_multisite ) {
412
                // Not a multisite environment.
413
                return false;
414
            }
415
416
            if ( is_numeric( $network_level_or_blog_id ) ) {
417
                // Explicitly asked to use a specified blog storage.
418
                return false;
419
            }
420
421
            if ( is_bool( $network_level_or_blog_id ) ) {
422
                // Explicitly specified whether should use the network or blog level storage.
423
                return $network_level_or_blog_id;
424
            }
425
426
            // Determine which storage to use based on the option.
427
            return ! $this->is_site_option( $option );
428
        }
429
430
        #endregion
431
    }