Completed
Push — master ( 966378...4a4c6c )
by Stefan
04:14
created

option_parse.inc.php ➔ postProcessValidAttribtues()   C

Complexity

Conditions 13
Paths 12

Size

Total Lines 54
Code Lines 41

Duplication

Lines 27
Ratio 50 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 13
eloc 41
c 1
b 0
f 0
nc 12
nop 3
dl 27
loc 54
rs 6.7593

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/* * *********************************************************************************
4
 * (c) 2011-15 GÉANT on behalf of the GN3, GN3plus and GN4 consortia
5
 * License: see the LICENSE file in the root directory
6
 * ********************************************************************************* */
7
?>
8
<?php
9
10
require_once(dirname(dirname(dirname(dirname(__FILE__)))) . "/config/_config.php");
11
12
require_once("Options.php");
13
14
require_once("input_validation.inc.php");
15
16
function postProcessValidAttribtues($options, &$good, &$bad) {
17
    foreach ($options as $index => $iterate_option) {
18
        foreach ($iterate_option as $name => $value) {
19
            switch ($name) {
20 View Code Duplication
                case "eap:ca_url": // eap:ca_url becomes eap:ca_file by downloading the file
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...
21
                    if (empty($value)) {
22
                        break;
23
                    }
24
                    $content = downloadFile($value);
25
                    unset($options[$index]);
26
                    if (check_upload_sanity("eap:ca_file", $content)) {
27
                        $content = base64_encode($content);
28
                        $options[] = ["eap:ca_file" => $content];
29
                        $good[] = $name;
30
                    } else {
31
                        $bad[] = $name;
32
                    }
33
                    break;
34
                case "eap:ca_file": // CA files get split (PEM files can contain more than one CA cert)
35
                    // the data being processed here is always "good": 
36
                    // if it was eap:ca_file initially then its sanity was checked in step 1;
37
                    // if it was eap:ca_url then it was checked after we downloaded it
38
                    if (empty($value) || preg_match('/^ROWID-/', $value)) {
39
                        break;
40
                    }
41
                    $content = base64_decode($value);
42
                    unset($options[$index]);
43
                    $ca_files = X509::splitCertificate($content);
0 ignored issues
show
Documentation introduced by
$content is of type string, but the function expects a object<blob>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
44
                    foreach ($ca_files as $ca_file) {
45
                        $options[] = ["eap:ca_file" => base64_encode(X509::pem2der($ca_file))];
46
                    }
47
                    $good[] = $name;
48
                    break;
49 View Code Duplication
                case "general:logo_url": // logo URLs become logo files by downloading the file
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...
50
                    if (empty($value)) {
51
                        break;
52
                    }
53
                    $bindata = downloadFile($value);
54
                    unset($options[$index]);
55
                    if (check_upload_sanity("general:logo_file", $bindata)) {
56
                        $good[] = $name;
57
                        $options[] = ["general:logo_file" => base64_encode($bindata)];
58
                    } else {
59
                        $bad[] = $name;
60
                    }
61
                    break;
62
                default:
63
                    $good[] = $name; // all other options were checked and are sane in step 1 already
64
                    break;
65
            }
66
        }
67
    }
68
    return $options;
69
}
70
71
function postProcessCoordinates($options, &$good) {
72
    if (!empty($_POST['geo_long']) && !empty($_POST['geo_lat'])) {
73
74
        $lat = valid_coordinate($_POST['geo_lat']);
75
        $lon = valid_coordinate($_POST['geo_long']);
76
77
        $options[] = ["general:geo_coordinates" => serialize(["lon" => $lon, "lat" => $lat])];
78
        $good[] = ("general:geo_coordinates");
79
    }
80
    return $options;
81
}
82
83
function displaySummaryInUI($good, $bad, $multilangAttribsWithC) {
84
    $retval = "";
85
    // don't do your own table - only the <tr>s here
86
    // list all attributes that were set correctly
87
    $listGood = array_count_values($good);
88
    foreach ($listGood as $name => $count) {
89
    /// number of times attribute is present, and its name
90
    /// Example: "5x Support E-Mail"
91
        $retval .= UI_okay(sprintf(_("%dx %s"), $count, display_name($name)));
92
    }
93
    // list all atributes that had errors
94
    $listBad = array_count_values($bad);
95
    foreach ($listBad as $name => $count) {
96
        $retval .= UI_error(sprintf(_("%dx %s"), $count, display_name($name)));
97
    }
98
    // list multilang without default
99
    foreach ($multilangAttribsWithC as $attrib_name => $isitsetornot) {
100
        if ($isitsetornot == FALSE) {
101
            $retval .= UI_warning(sprintf(_("You did not set a 'default language' value for %s. This means we can only display this string for installers which are <strong>exactly</strong> in the language you configured. For the sake of all other languages, you may want to edit the profile again and populate the 'default/other' language field."), display_name($attrib_name)));
102
        }
103
    }
104
    return $retval;
105
}
106
107
function processSubmittedFields($object, $pendingattributes, $eaptype = 0, $device = 0, $silent = 0) {
108
109
// construct new array with all non-empty options for later feeding into DB
110
111
    $optionsStep1 = [];
112
    $good = [];
113
    $bad = [];
114
115
    $killlist = $pendingattributes;
116
117
    $iterator = [];
118
119
    $optioninfoObject = Options::instance();
120
// Step 1a: parse the arrays for text-based input
121
122
    if (isset($_POST)) {
123
        if (isset($_POST['option'])) {
124
            foreach ($_POST['option'] as $optId => $optname) {
125
                $iterator[$optId] = $optname;
126
            }
127
        }
128
        if (isset($_POST['value'])) {
129
            foreach ($_POST['value'] as $optId => $optvalue) {
130
                $iterator[$optId] = $optvalue;
131
            }
132
        }
133
    }
134
    if (isset($_FILES) && isset($_FILES['value']) && isset($_FILES['value']['tmp_name'])) {
135
        foreach ($_FILES['value']['tmp_name'] as $opt_id => $optfileref) {
136
            $iterator[$opt_id] = $optfileref;
137
        }
138
    }
139
140
    // following is a helper array to keep track of multilang options that were set in a specific language
141
    // but are not accompanied by a "default" language setting
142
    // if the array isn't empty by the end of processing, we need to warn the admin that this attribute
143
    // is "invisible" in certain languages
144
    // attrib_name -> boolean
145
146
    $multilangAttribsWithC = [];
147
148
    foreach ($iterator as $objId => $objValueRaw) {
149
// pick those without dash - they indicate a new value        
150
        if (preg_match('/^S[0123456789]*$/', $objId)) {
151
            $objValue = preg_replace('/#.*$/', '', $objValueRaw);
152
            $optioninfo = $optioninfoObject->optionType($objValue);
153
            $lang = "";
154
            if ($optioninfo["flag"] == "ML") {
155
                if (isset($iterator["$objId-lang"])) {
156
                    $lang = $iterator["$objId-lang"];
157
                    if ($lang == "") { // user forgot to select a language
158
                        $lang = "C";
159
                    }
160
                } else {
161
                    $bad[] = $objValue;
162
                    continue;
163
                }
164
                // did we get a C language? set corresponding value to TRUE
165
                if ($lang == "C") {
166
                    $multilangAttribsWithC[$objValue] = TRUE;
167
                } else { // did we get a C earlier - fine, don't touch the array. Otherwise, set FALSE
168
                    if (!isset($multilangAttribsWithC[$objValue]) || $multilangAttribsWithC[$objValue] != TRUE) {
169
                        $multilangAttribsWithC[$objValue] = FALSE;
170
                    }
171
                }
172
            }
173
            $content = "";
0 ignored issues
show
Unused Code introduced by
$content 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...
174
            switch ($optioninfo["type"]) {
175
                case "string":
176
                    if (!empty($iterator["$objId-0"])) {
177
                        if ($objValue == "media:consortium_OI") {
178
                            $content = valid_consortium_oi($iterator["$objId-0"]);
179
                            if ($content === FALSE) {
180
                                $bad[] = $objValue;
181
                                continue 2;
182
                            }
183
                        } elseif ($objValue == "media:remove_SSID") {
184
                            $content = valid_string_db($iterator["$objId-0"]);
185
                            if ($content == "eduroam") {
186
                                $bad[] = $objValue;
187
                                continue 2;
188
                            }
189
                        } else {
190
                            $content = valid_string_db($iterator["$objId-0"]);
191
                        }
192
                        break;
193
                    }
194
                    continue 2;
195
                case "text":
196 View Code Duplication
                    if (!empty($iterator["$objId-1"])) {
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...
197
                        $content = valid_string_db($iterator["$objId-1"], 1);
198
                        break;
199
                    }
200
                    continue 2;
201
                case "coordinates":
202 View Code Duplication
                    if (!empty($iterator["$objId-1"])) {
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
                        $content = valid_coord_serialized($iterator["$objId-1"]);
204
                        break;
205
                    }
206
                    continue 2;
207
                case "file":
208
// echo "In file processing ...<br/>";
209
                    if (!empty($iterator["$objId-1"])) { // was already in, by ROWID reference, extract
210
                        // ROWID means it's a multi-line string (simple strings are inline in the form; so allow whitespace)
211
                        $content = valid_string_db(urldecode($iterator["$objId-1"]), 1);
212
                        break;
213
                    } else if (isset($iterator["$objId-2"]) && ($iterator["$objId-2"] != "")) { // let's do the download
214
// echo "Trying to download file:///".$a["$obj_id-2"]."<br/>";
215
                        $content = downloadFile("file:///" . $iterator["$objId-2"]);
216
                        if (!check_upload_sanity($objValue, $content)) {
217
                            $bad[] = $objValue;
218
                            continue 2;
219
                        }
220
                        $content = base64_encode($content);
221
                        break;
222
                    }
223
                    continue 2;
224
225
                case "boolean":
226 View Code Duplication
                    if (!empty($iterator["$objId-3"])) {
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...
227
                        $content = valid_boolean($iterator["$objId-3"]);
228
                        break;
229
                    }
230
                    continue 2;
231
                case "integer":
232 View Code Duplication
                    if (!empty($iterator["$objId-4"])) {
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...
233
                        $content = valid_integer($iterator["$objId-4"]);
234
                        break;
235
                    }
236
                    continue 2;
237
                default:
238
                    throw new Exception("Internal Error: Unknown option type " . $objValue . "!");
239
            }
240
            if ($lang != "" && preg_match("/^ROWID-.*-([0-9]+)/", $content) == 0) { // new value, encode as language array
241
                // add the new option with lang 
242
                $optionsStep1[] = ["$objValue" => serialize(["lang" => $lang, "content" => $content])];
243
            } else { // just store it (could be a literal value or a ROWID reference)
244
                $optionsStep1[] = ["$objValue" => $content];
245
            }
246
        }
247
    }
248
249
// Step 2: now we have clean input data. Some attributes need special care:
250
// URL-based attributes need to be downloaded to get their actual content
251
// CA files may need to be split (PEM can contain multiple CAs 
252
253
    $optionsStep2 = postProcessValidAttributes($optionsStep1, $good, $bad);
254
255
256
// Step 3: coordinates do not follow the usual POST array as they are two values forming one attribute
257
258
    $options = postProcessCoordinates($optionsStep2, $good);
259
260
// finally, some attributes are in the DB and were only called by reference
261
// keep those which are still referenced, throw the rest away
262
263
    foreach ($options as $iterate_option) {
264
        foreach ($iterate_option as $name => $value) {
265
            $optiontype = $optioninfoObject->optionType($name);
266
            if ($optiontype["type"] == "file" && preg_match("/^ROWID-.*-([0-9]+)/", $value, $retval)) {
267
                unset($killlist[$retval[1]]);
268
                continue;
269
            }
270
            if ($object instanceof IdP || $object instanceof User || $object instanceof Federation) {
271
                $object->addAttribute($name, $value);
272
            } elseif ($object instanceof Profile) {
0 ignored issues
show
Bug introduced by
The class Profile does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
273
                if ($device !== 0) {
274
                    $object->addAttributeDeviceSpecific($name, $value, $device);
275
                } elseif ($eaptype != 0) {
276
                    $object->addAttributeEAPSpecific($name, $value, $eaptype);
277
                } else {
278
                    $object->addAttribute($name, $value);
279
                }
280
            }
281
        }
282
    }
283
284
    if ($silent == 0) {
285
        echo displaySummaryInUI($good, $bad, $multilangAttribsWithC);
0 ignored issues
show
Security Cross-Site Scripting introduced by
displaySummaryInUI($good...$multilangAttribsWithC) can contain request data and is used in output context(s) leading to a potential security vulnerability.

2 paths for user data to reach this point

  1. Path: Read from $_POST, and $optname is assigned in web/admin/inc/option_parse.inc.php on line 124
  1. Read from $_POST, and $optname is assigned
    in web/admin/inc/option_parse.inc.php on line 124
  2. $iterator is assigned
    in web/admin/inc/option_parse.inc.php on line 125
  3. $objValueRaw is assigned
    in web/admin/inc/option_parse.inc.php on line 148
  4. $objValueRaw is passed through preg_replace(), and $objValue is assigned
    in web/admin/inc/option_parse.inc.php on line 151
  5. $multilangAttribsWithC is assigned
    in web/admin/inc/option_parse.inc.php on line 166
  6. $attrib_name is assigned
    in vendor/web/admin/inc/option_parse.inc.php on line 99
  7. Data is passed through sprintf()
    in vendor/web/admin/inc/option_parse.inc.php on line 101
  2. Path: Read from $_POST, and $optvalue is assigned in web/admin/inc/option_parse.inc.php on line 129
  1. Read from $_POST, and $optvalue is assigned
    in web/admin/inc/option_parse.inc.php on line 129
  2. $iterator is assigned
    in web/admin/inc/option_parse.inc.php on line 130
  3. $objValueRaw is assigned
    in web/admin/inc/option_parse.inc.php on line 148
  4. $objValueRaw is passed through preg_replace(), and $objValue is assigned
    in web/admin/inc/option_parse.inc.php on line 151
  5. $multilangAttribsWithC is assigned
    in web/admin/inc/option_parse.inc.php on line 166
  6. $attrib_name is assigned
    in vendor/web/admin/inc/option_parse.inc.php on line 99
  7. Data is passed through sprintf()
    in vendor/web/admin/inc/option_parse.inc.php on line 101

Preventing Cross-Site-Scripting Attacks

Cross-Site-Scripting allows an attacker to inject malicious code into your website - in particular Javascript code, and have that code executed with the privileges of a visiting user. This can be used to obtain data, or perform actions on behalf of that visiting user.

In order to prevent this, make sure to escape all user-provided data:

// for HTML
$sanitized = htmlentities($tainted, ENT_QUOTES);

// for URLs
$sanitized = urlencode($tainted);

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
286
    }
287
    return $killlist;
288
}
289