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.
Completed
Push — master ( ceb3b5...20e764 )
by Omar El
02:50
created

SecurityComponent::startup()   D

Complexity

Conditions 9
Paths 10

Size

Total Lines 26
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 26
rs 4.909
cc 9
eloc 13
nc 10
nop 0
1
<?php
2
3
/**
4
 * Security component class.
5
 *
6
 * Provides security methods for various tasks and validations.
7
 *
8
 * @license    http://opensource.org/licenses/MIT The MIT License (MIT)
9
 * @author     Omar El Gabry <[email protected]>
10
 */
11
12
class SecurityComponent extends Component{
0 ignored issues
show
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...
13
14
    /**
15
     * Default configurations
16
     *
17
     * @var array
18
     */
19
    protected $config = [
20
        'form' => [],
21
        'requireSecure' => [],
22
        'requirePost' => [],
23
        'requireAjax' => [],
24
        'requireGet' => [],
25
        'validateForm' => true,
26
        'validateCsrfToken' => false
27
    ];
28
29
    /**
30
     * Auth startup
31
     * All security checking are done in this method
32
     *
33
     */
34
    public function startup(){
35
36
        if(!$this->requestRequired()){
37
            return $this->invalidRequest();
38
        }
39
40
        if(!$this->secureRequired()){
41
            return $this->invalidRequest('forceSSL');
42
        }
43
44
        if(!$this->validateDomain()){
45
            return $this->invalidRequest();
46
        }
47
48
        if($this->request->isPost() && $this->config["validateForm"]){
49
            if(!$this->form($this->config["form"])){
50
                return $this->invalidRequest();
51
            }
52
        }
53
54
        if($this->config["validateCsrfToken"]){
55
            if(!$this->CsrfToken()){
56
                return $this->invalidRequest();
57
            }
58
        }
59
    }
60
61
    /**
62
     * Check & validate from the required HTTP methods, like: Post, Ajax, Get
63
     *
64
     * @return bool
65
    */
66
    private function requestRequired(){
67
        foreach (['Post', 'Ajax', 'Get'] as $method) {
68
            $key = 'require' . $method;
69 View Code Duplication
            if (!empty($this->config[$key])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
70
                if (in_array($this->request->param('action'), $this->config[$key], true) || $this->config[$key] === ['*']) {
71
                    if (!$this->request->{"is" . $method}()) {
72
                        return false;
73
                    }
74
                }
75
            }
76
        }
77
        return true;
78
    }
79
80
    /**
81
     * Check & validate if secured connection is required.
82
     *
83
     * @return bool
84
     */
85
    private function secureRequired(){
86
        $key = "requireSecure";
87 View Code Duplication
        if(!empty($this->config[$key])){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
88
            if (in_array($this->request->param('action'), $this->config[$key], true) || $this->config[$key] === ['*']) {
89
                if (!$this->request->isSSL()) {
90
                    return false;
91
                }
92
            }
93
        }
94
        return true;
95
    }
96
97
    /**
98
     * Check & validate if request is coming from the same domain; if equals to $this->request->host()
99
     * HTTP referer tells the domain where the request came from.
100
     *
101
     * @return bool
102
     */
103
    private function validateDomain(){
104
105
        $isValid = true;
106
        $referer = $this->request->referer();
107
108
        if($this->request->isPost()){
109
            if(!isset($referer)) {
110
                $isValid = false;
111
            } else {
112
                $referer_host = parse_url($referer, PHP_URL_HOST);
113
                $server_host  = $this->request->host();
114
                $isValid = ($referer_host === $server_host)? true: false;
115
            }
116
        }
117
118
        if(!$isValid){
119
            Logger::log("Request Domain", "User: ". Session::getUserId() ." Request is not coming from the same domain with invalid HTTP referer", __FILE__, __LINE__);
120
            return false;
121
        }
122
        return true;
123
    }
124
125
    /**
126
     * Handles invalid request with a 400 Bad Request Error If no callback is specified.
127
     *
128
     * @param string|null $callback
129
     * @return mixed
130
     * @throws Exception
131
     */
132
    private function invalidRequest($callback = null){
133
        if(is_callable([$this->controller, $callback])){
134
            return $this->controller->{$callback}();
135
        }
136
        throw new Exception('The request has been deined', 400);
137
    }
138
139
    /**
140
     * Sets the actions that require secured connection(SSL)
141
     *
142
     * @param array $actions
143
     */
144
    public function requireSecure($actions = []){
145
        $this->config['requireSecure'] = (array)$actions;
146
    }
147
148
    /**
149
     * Sets the actions that require a POST request
150
     *
151
     * @param array $actions
152
     */
153
    public function requirePost($actions = []){
154
        $this->config['requirePost'] = (array)$actions;
155
    }
156
157
    /**
158
     * Sets the actions that require a Ajax request
159
     *
160
     * @param array $actions
161
     */
162
    public function requireAjax($actions = []){
163
        $this->config['requireAjax'] = (array)$actions;
164
    }
165
166
    /**
167
     * Sets the actions that require a GET request
168
     *
169
     * @param array $actions
170
     */
171
    public function requireGet($actions = []){
172
        $this->config['requireGet'] = (array)$actions;
173
    }
174
175
     /**
176
      * validate submitted form
177
      * - Unknown fields cannot be added to the form.
178
      * - Fields cannot be removed from the form.
179
      *
180
      * Use $exclude to exclude anything mightn't be sent with the form, like possible empty arrays, checkboxes, radio buttons, ...etc.
181
      * By default, the submit field will be excluded.
182
      *
183
      * @param array  $config  configuration data
184
      * @return boolean
185
      */
186
    public function form($config){
187
188
        if(empty($config['fields']) || $this->request->dataSizeOverflow()){
189
             return false;
190
        }
191
192
        if(!in_array('csrf_token', $config['fields'], true)){
193
            $config['fields'][] = 'csrf_token';
194
        }
195
196
        // exclude any checkboxes, radio buttons, possible empty arrays, ...etc.
197
        $exclude = empty($config["exclude"])? []: (array)$config["exclude"];
198
        if(!in_array('submit', $exclude, true)){
199
            $exclude[] = 'submit';
200
        }
201
202 View Code Duplication
        if($this->request->countData($exclude) !== count($config['fields'])){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
203
            Logger::log("Form Tampering", "User: ". Session::getUserId() ." is tampering the form with invalid number of fields", __FILE__, __LINE__);
204
            return false;
205
        }
206
207
        foreach($config['fields'] as $field){
208
209 View Code Duplication
            if(!array_key_exists($field, $this->request->data)){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
210
                Logger::log("Form Tampering", "User: ". Session::getUserId() ." is tampering the form with invalid fields", __FILE__, __LINE__);
211
                return false;
212
            }
213
        }
214
215
        // by default, validate csrf token as well.
216
        return $this->CsrfToken();
217
    }
218
219
     /**
220
      * validate CSRF token
221
      * CSRF token can be passed with submitted forms and links associated with sensitive server-side operations.
222
      *
223
      * In case of GET request, you need to set 'validateCsrfToken' in $config to true.
224
      *
225
      * @param array  $config  configuration data
226
      * @return boolean
227
      */
228
    public function CsrfToken($config = []){
0 ignored issues
show
Unused Code introduced by
The parameter $config is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
229
230
        $userToken = null;
0 ignored issues
show
Unused Code introduced by
$userToken is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
231
        if($this->request->isPost()){
232
            $userToken = $this->request->data('csrf_token');
233
        }else{
234
            $userToken = $this->request->query('csrf_token');
235
        }
236
237
        if(empty($userToken) || $userToken !== Session::getCsrfToken()){
238
            Logger::log("CSRF Attack", "User: ". Session::getUserId() ." provided invalid CSRF Token " . $userToken, __FILE__, __LINE__);
239
            return false;
240
        }
241
242
        return $userToken === Session::getCsrfToken();
243
    }
244
245
}
246