Test Failed
Branch develop (db5506)
by Felipe
03:46
created

ConstraintsController::doDrop()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 30
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 25
nc 3
nop 1
dl 0
loc 30
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
namespace PHPPgAdmin\Controller;
4
5
use \PHPPgAdmin\Decorators\Decorator;
6
7
/**
8
 * Base controller class
9
 */
10
class ConstraintsController extends BaseController
11
{
12
    public $_name = 'ConstraintsController';
13
14
    public function render()
0 ignored issues
show
Coding Style introduced by
render uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
render uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
15
    {
16
        $conf = $this->conf;
0 ignored issues
show
Unused Code introduced by
The assignment to $conf is dead and can be removed.
Loading history...
17
        $misc = $this->misc;
0 ignored issues
show
Unused Code introduced by
The assignment to $misc is dead and can be removed.
Loading history...
18
        $lang = $this->lang;
19
20
        $action = $this->action;
21
        if ($action == 'tree') {
22
            return $this->doTree();
0 ignored issues
show
Bug introduced by
The method doTree() does not exist on PHPPgAdmin\Controller\ConstraintsController. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

22
            return $this->/** @scrutinizer ignore-call */ doTree();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
23
        }
24
25
        $this->printHeader($lang['strtables'] . ' - ' . $_REQUEST['table'] . ' - ' . $lang['strconstraints'],
26
            '<script src="' . SUBFOLDER . '/js/indexes.js" type="text/javascript"></script>', true, 'header_select2.twig');
27
28
        if ($action == 'add_unique_key' || $action == 'save_add_unique_key'
29
            || $action == 'add_primary_key' || $action == 'save_add_primary_key'
30
            || $action == 'add_foreign_key' || $action == 'save_add_foreign_key') {
31
            echo '<body onload="init();">';
32
        } else {
33
            $this->printBody();
34
        }
35
36
        switch ($action) {
37
            case 'add_foreign_key':
38
                $this->addForeignKey(1);
39
                break;
40
            case 'save_add_foreign_key':
41
                if (isset($_POST['cancel'])) {
42
                    $this->doDefault();
43
                } else {
44
                    $this->addForeignKey($_REQUEST['stage']);
45
                }
46
47
                break;
48
            case 'add_unique_key':
49
                $this->addPrimaryOrUniqueKey('unique', true);
50
                break;
51
            case 'save_add_unique_key':
52
                if (isset($_POST['cancel'])) {
53
                    $this->doDefault();
54
                } else {
55
                    $this->addPrimaryOrUniqueKey('unique', false);
56
                }
57
58
                break;
59
            case 'add_primary_key':
60
                $this->addPrimaryOrUniqueKey('primary', true);
61
                break;
62
            case 'save_add_primary_key':
63
                if (isset($_POST['cancel'])) {
64
                    $this->doDefault();
65
                } else {
66
                    $this->addPrimaryOrUniqueKey('primary', false);
67
                }
68
69
                break;
70
            case 'add_check':
71
                $this->addCheck(true);
72
                break;
73
            case 'save_add_check':
74
                if (isset($_POST['cancel'])) {
75
                    $this->doDefault();
76
                } else {
77
                    $this->addCheck(false);
78
                }
79
80
                break;
81
            case 'save_create':
82
                $this->doSaveCreate();
0 ignored issues
show
Bug introduced by
The method doSaveCreate() does not exist on PHPPgAdmin\Controller\ConstraintsController. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

82
                $this->/** @scrutinizer ignore-call */ doSaveCreate();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
83
                break;
84
            case 'create':
85
                $this->doCreate();
0 ignored issues
show
Bug introduced by
The method doCreate() does not exist on PHPPgAdmin\Controller\ConstraintsController. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

85
                $this->/** @scrutinizer ignore-call */ doCreate();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
86
                break;
87
            case 'drop':
88
                if (isset($_POST['drop'])) {
89
                    $this->doDrop(false);
90
                } else {
91
                    $this->doDefault();
92
                }
93
94
                break;
95
            case 'confirm_drop':
96
                $this->doDrop(true);
97
                break;
98
            default:
99
                $this->doDefault();
100
                break;
101
        }
102
103
        $this->printFooter();
104
    }
105
106
    /**
107
     * List all the constraints on the table
108
     */
109
    public function doDefault($msg = '')
0 ignored issues
show
Coding Style introduced by
doDefault uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
110
    {
111
        $conf = $this->conf;
112
        $misc = $this->misc;
113
        $lang = $this->lang;
114
        $data = $misc->getDatabaseAccessor();
115
116
        $cnPre = function (&$rowdata) use ($data) {
117
            if (is_null($rowdata->fields['consrc'])) {
118
                $atts                           = $data->getAttributeNames($_REQUEST['table'], explode(' ', $rowdata->fields['indkey']));
119
                $rowdata->fields['+definition'] = ($rowdata->fields['contype'] == 'u' ? 'UNIQUE (' : 'PRIMARY KEY (') . join(',', $atts) . ')';
120
            } else {
121
                $rowdata->fields['+definition'] = $rowdata->fields['consrc'];
122
            }
123
        };
124
125
        $this->printTrail('table');
126
        $this->printTabs('table', 'constraints');
127
        $this->printMsg($msg);
128
129
        $constraints = $data->getConstraints($_REQUEST['table']);
130
131
        $columns = [
132
            'constraint' => [
133
                'title' => $lang['strname'],
134
                'field' => Decorator::field('conname'),
135
            ],
136
            'definition' => [
137
                'title' => $lang['strdefinition'],
138
                'field' => Decorator::field('+definition'),
139
                'type'  => 'pre',
140
            ],
141
            'actions'    => [
142
                'title' => $lang['stractions'],
143
            ],
144
            'comment'    => [
145
                'title' => $lang['strcomment'],
146
                'field' => Decorator::field('constcomment'),
147
            ],
148
        ];
149
150
        $actions = [
151
            'drop' => [
152
                'content' => $lang['strdrop'],
153
                'attr'    => [
154
                    'href' => [
155
                        'url'     => 'constraints.php',
156
                        'urlvars' => [
157
                            'action'     => 'confirm_drop',
158
                            'table'      => $_REQUEST['table'],
159
                            'constraint' => Decorator::field('conname'),
160
                            'type'       => Decorator::field('contype'),
161
                        ],
162
                    ],
163
                ],
164
            ],
165
        ];
166
167
        echo $this->printTable($constraints, $columns, $actions, 'constraints-constraints', $lang['strnoconstraints'], $cnPre);
168
169
        $navlinks = [
170
            'addcheck' => [
171
                'attr'    => [
172
                    'href' => [
173
                        'url'     => 'constraints.php',
174
                        'urlvars' => [
175
                            'action'   => 'add_check',
176
                            'server'   => $_REQUEST['server'],
177
                            'database' => $_REQUEST['database'],
178
                            'schema'   => $_REQUEST['schema'],
179
                            'table'    => $_REQUEST['table'],
180
                        ],
181
                    ],
182
                ],
183
                'content' => $lang['straddcheck'],
184
            ],
185
            'adduniq'  => [
186
                'attr'    => [
187
                    'href' => [
188
                        'url'     => 'constraints.php',
189
                        'urlvars' => [
190
                            'action'   => 'add_unique_key',
191
                            'server'   => $_REQUEST['server'],
192
                            'database' => $_REQUEST['database'],
193
                            'schema'   => $_REQUEST['schema'],
194
                            'table'    => $_REQUEST['table'],
195
                        ],
196
                    ],
197
                ],
198
                'content' => $lang['stradduniq'],
199
            ],
200
            'addpk'    => [
201
                'attr'    => [
202
                    'href' => [
203
                        'url'     => 'constraints.php',
204
                        'urlvars' => [
205
                            'action'   => 'add_primary_key',
206
                            'server'   => $_REQUEST['server'],
207
                            'database' => $_REQUEST['database'],
208
                            'schema'   => $_REQUEST['schema'],
209
                            'table'    => $_REQUEST['table'],
210
                        ],
211
                    ],
212
                ],
213
                'content' => $lang['straddpk'],
214
            ],
215
            'addfk'    => [
216
                'attr'    => [
217
                    'href' => [
218
                        'url'     => 'constraints.php',
219
                        'urlvars' => [
220
                            'action'   => 'add_foreign_key',
221
                            'server'   => $_REQUEST['server'],
222
                            'database' => $_REQUEST['database'],
223
                            'schema'   => $_REQUEST['schema'],
224
                            'table'    => $_REQUEST['table'],
225
                        ],
226
                    ],
227
                ],
228
                'content' => $lang['straddfk'],
229
            ],
230
        ];
231
        $this->printNavLinks($navlinks, 'constraints-constraints', get_defined_vars());
232
    }
233
234
    /**
235
     * Confirm and then actually add a FOREIGN KEY constraint
236
     */
237
    public function addForeignKey($stage, $msg = '')
0 ignored issues
show
Coding Style introduced by
addForeignKey uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
addForeignKey uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
238
    {
239
        $conf = $this->conf;
0 ignored issues
show
Unused Code introduced by
The assignment to $conf is dead and can be removed.
Loading history...
240
        $misc = $this->misc;
241
        $lang = $this->lang;
242
        $data = $misc->getDatabaseAccessor();
243
244
        if (!isset($_POST['name'])) {
245
            $_POST['name'] = '';
246
        }
247
248
        if (!isset($_POST['target'])) {
249
            $_POST['target'] = '';
250
        }
251
252
        switch ($stage) {
253
            case 2:
254
                // Check that they've given at least one source column
255
                if (!isset($_REQUEST['SourceColumnList']) && (!isset($_POST['IndexColumnList']) || !is_array($_POST['IndexColumnList']) || sizeof($_POST['IndexColumnList']) == 0)) {
256
                    $this->addForeignKey(1, $lang['strfkneedscols']);
257
                } else {
258
                    // Copy the IndexColumnList variable from stage 1
259
                    if (isset($_REQUEST['IndexColumnList']) && !isset($_REQUEST['SourceColumnList'])) {
260
                        $_REQUEST['SourceColumnList'] = serialize($_REQUEST['IndexColumnList']);
261
                    }
262
263
                    // Initialise variables
264
                    if (!isset($_POST['upd_action'])) {
265
                        $_POST['upd_action'] = null;
266
                    }
267
268
                    if (!isset($_POST['del_action'])) {
269
                        $_POST['del_action'] = null;
270
                    }
271
272
                    if (!isset($_POST['match'])) {
273
                        $_POST['match'] = null;
274
                    }
275
276
                    if (!isset($_POST['deferrable'])) {
277
                        $_POST['deferrable'] = null;
278
                    }
279
280
                    if (!isset($_POST['initially'])) {
281
                        $_POST['initially'] = null;
282
                    }
283
284
                    $_REQUEST['target'] = unserialize($_REQUEST['target']);
0 ignored issues
show
Security introduced by
$_REQUEST['target'] can contain request data and is used in unserialized context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_REQUEST
    in src/controllers/ConstraintsController.php on line 284

Preventing Object Injection Attacks

If you pass raw user-data to unserialize() for example, this can be used to create an object of any class that is available in your local filesystem. For an attacker, classes that have magic methods like __destruct or __wakeup are particularly interesting in such a case, as they can be exploited very easily.

We recommend to not pass user data to such a function. In case of unserialize, better use JSON to transfer data.

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...
285
286
                    $this->printTrail('table');
287
                    $this->printTitle($lang['straddfk'], 'pg.constraint.foreign_key');
288
                    $this->printMsg($msg);
289
290
                    // Unserialize target and fetch appropriate table. This is a bit messy
291
                    // because the table could be in another schema.
292
                    $data->setSchema($_REQUEST['target']['schemaname']);
293
                    $attrs = $data->getTableAttributes($_REQUEST['target']['tablename']);
294
                    $data->setSchema($_REQUEST['schema']);
295
296
                    $selColumns = new \PHPPgAdmin\XHtml\XHtmlSelect('TableColumnList', true, 10);
297
                    $selColumns->set_style('width: 15em;');
298
299 View Code Duplication
                    if ($attrs->recordCount() > 0) {
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...
300
                        while (!$attrs->EOF) {
301
                            $selColumns->add(new \PHPPgAdmin\XHtml\XHtmlOption($attrs->fields['attname']));
302
                            $attrs->moveNext();
303
                        }
304
                    }
305
306
                    $selIndex = new \PHPPgAdmin\XHtml\XHtmlSelect('IndexColumnList[]', true, 10);
307
                    $selIndex->set_style('width: 15em;');
308
                    $selIndex->set_attribute('id', 'IndexColumnList');
309
                    $buttonAdd = new \PHPPgAdmin\XHtml\XHtmlButton('add', '>>');
310
                    $buttonAdd->set_attribute('onclick', 'buttonPressed(this);');
311
                    $buttonAdd->set_attribute('type', 'button');
312
313
                    $buttonRemove = new \PHPPgAdmin\XHtml\XHtmlButton('remove', '<<');
314
                    $buttonRemove->set_attribute('onclick', 'buttonPressed(this);');
315
                    $buttonRemove->set_attribute('type', 'button');
316
317
                    echo "<form onsubmit=\"doSelectAll();\" name=\"formIndex\" action=\"constraints.php\" method=\"post\">\n";
318
319
                    echo "<table>\n";
320
                    echo "<tr><th class=\"data\" colspan=\"3\">{$lang['strfktarget']}</th></tr>";
321
                    echo "<tr><th class=\"data\">{$lang['strtablecolumnlist']}</th><th class=\"data\">&nbsp;</th><th class=data>{$lang['strfkcolumnlist']}</th></tr>\n";
322
                    echo '<tr><td class="data1">' . $selColumns->fetch() . "</td>\n";
323
                    echo '<td class="data1" style="text-align: center">' . $buttonRemove->fetch() . $buttonAdd->fetch() . '</td>';
324
                    echo '<td class="data1">' . $selIndex->fetch() . "</td></tr>\n";
325
                    echo "<tr><th class=\"data\" colspan=\"3\">{$lang['stractions']}</th></tr>";
326
                    echo '<tr>';
327
                    echo "<td class=\"data1\" colspan=\"3\">\n";
328
                    // ON SELECT actions
329
                    echo "{$lang['stronupdate']} <select name=\"upd_action\">";
330
                    foreach ($data->fkactions as $v) {
331
                        echo "<option value=\"{$v}\"", ($_POST['upd_action'] == $v) ? ' selected="selected"' : '', ">{$v}</option>\n";
332
                    }
333
334
                    echo "</select><br />\n";
335
336
                    // ON DELETE actions
337
                    echo "{$lang['strondelete']} <select name=\"del_action\">";
338
                    foreach ($data->fkactions as $v) {
339
                        echo "<option value=\"{$v}\"", ($_POST['del_action'] == $v) ? ' selected="selected"' : '', ">{$v}</option>\n";
340
                    }
341
342
                    echo "</select><br />\n";
343
344
                    // MATCH options
345
                    echo '<select name="match">';
346
                    foreach ($data->fkmatches as $v) {
347
                        echo "<option value=\"{$v}\"", ($_POST['match'] == $v) ? ' selected="selected"' : '', ">{$v}</option>\n";
348
                    }
349
350
                    echo "</select><br />\n";
351
352
                    // DEFERRABLE options
353
                    echo '<select name="deferrable">';
354
                    foreach ($data->fkdeferrable as $v) {
355
                        echo "<option value=\"{$v}\"", ($_POST['deferrable'] == $v) ? ' selected="selected"' : '', ">{$v}</option>\n";
356
                    }
357
358
                    echo "</select><br />\n";
359
360
                    // INITIALLY options
361
                    echo '<select name="initially">';
362
                    foreach ($data->fkinitial as $v) {
363
                        echo "<option value=\"{$v}\"", ($_POST['initially'] == $v) ? ' selected="selected"' : '', ">{$v}</option>\n";
364
                    }
365
366
                    echo "</select>\n";
367
                    echo "</td></tr>\n";
368
                    echo "</table>\n";
369
370
                    echo "<p><input type=\"hidden\" name=\"action\" value=\"save_add_foreign_key\" />\n";
371
                    echo $misc->form;
372
                    echo '<input type="hidden" name="table" value="', htmlspecialchars($_REQUEST['table']), "\" />\n";
373
                    echo '<input type="hidden" name="name" value="', htmlspecialchars($_REQUEST['name']), "\" />\n";
374
                    echo '<input type="hidden" name="target" value="', htmlspecialchars(serialize($_REQUEST['target'])), "\" />\n";
375
                    echo '<input type="hidden" name="SourceColumnList" value="', htmlspecialchars($_REQUEST['SourceColumnList']), "\" />\n";
376
                    echo "<input type=\"hidden\" name=\"stage\" value=\"3\" />\n";
377
                    echo "<input type=\"submit\" value=\"{$lang['stradd']}\" />\n";
378
                    echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" /></p>\n";
379
                    echo "</form>\n";
380
                }
381
                break;
382
            case 3:
383
                // Unserialize target
384
                $_POST['target'] = unserialize($_POST['target']);
385
386
                // Check that they've given at least one column
387
                if (isset($_POST['SourceColumnList'])) {
388
                    $temp = unserialize($_POST['SourceColumnList']);
0 ignored issues
show
Security introduced by
$_POST['SourceColumnList'] can contain request data and is used in unserialized context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_POST
    in src/controllers/ConstraintsController.php on line 388

Preventing Object Injection Attacks

If you pass raw user-data to unserialize() for example, this can be used to create an object of any class that is available in your local filesystem. For an attacker, classes that have magic methods like __destruct or __wakeup are particularly interesting in such a case, as they can be exploited very easily.

We recommend to not pass user data to such a function. In case of unserialize, better use JSON to transfer data.

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...
389
                }
390
391
                if (!isset($_POST['IndexColumnList']) || !is_array($_POST['IndexColumnList'])
392
                    || sizeof($_POST['IndexColumnList']) == 0 || !isset($temp)
393
                    || !is_array($temp) || sizeof($temp) == 0) {
394
                    $this->addForeignKey(2, $lang['strfkneedscols']);
395
                } else {
396
                    $status = $data->addForeignKey($_POST['table'], $_POST['target']['schemaname'], $_POST['target']['tablename'],
397
                        unserialize($_POST['SourceColumnList']), $_POST['IndexColumnList'], $_POST['upd_action'], $_POST['del_action'],
0 ignored issues
show
Security introduced by
$_POST['SourceColumnList'] can contain request data and is used in unserialized context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_POST
    in src/controllers/ConstraintsController.php on line 397

Preventing Object Injection Attacks

If you pass raw user-data to unserialize() for example, this can be used to create an object of any class that is available in your local filesystem. For an attacker, classes that have magic methods like __destruct or __wakeup are particularly interesting in such a case, as they can be exploited very easily.

We recommend to not pass user data to such a function. In case of unserialize, better use JSON to transfer data.

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...
398
                        $_POST['match'], $_POST['deferrable'], $_POST['initially'], $_POST['name']);
399
                    if ($status == 0) {
400
                        $this->doDefault($lang['strfkadded']);
401
                    } else {
402
                        $this->addForeignKey(2, $lang['strfkaddedbad']);
403
                    }
404
                }
405
                break;
406
            default:
407
                $this->printTrail('table');
408
                $this->printTitle($lang['straddfk'], 'pg.constraint.foreign_key');
409
                $this->printMsg($msg);
410
411
                $attrs  = $data->getTableAttributes($_REQUEST['table']);
412
                $tables = $data->getTables(true);
413
414
                $selColumns = new \PHPPgAdmin\XHtml\XHtmlSelect('TableColumnList', true, 10);
415
                $selColumns->set_style('width: 15em;');
416
417 View Code Duplication
                if ($attrs->recordCount() > 0) {
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...
418
                    while (!$attrs->EOF) {
419
                        $selColumns->add(new \PHPPgAdmin\XHtml\XHtmlOption($attrs->fields['attname']));
420
                        $attrs->moveNext();
421
                    }
422
                }
423
424
                $selIndex = new \PHPPgAdmin\XHtml\XHtmlSelect('IndexColumnList[]', true, 10);
425
                $selIndex->set_style('width: 15em;');
426
                $selIndex->set_attribute('id', 'IndexColumnList');
427
                $buttonAdd = new \PHPPgAdmin\XHtml\XHtmlButton('add', '>>');
428
                $buttonAdd->set_attribute('onclick', 'buttonPressed(this);');
429
                $buttonAdd->set_attribute('type', 'button');
430
431
                $buttonRemove = new \PHPPgAdmin\XHtml\XHtmlButton('remove', '<<');
432
                $buttonRemove->set_attribute('onclick', 'buttonPressed(this);');
433
                $buttonRemove->set_attribute('type', 'button');
434
435
                echo "<form onsubmit=\"doSelectAll();\" name=\"formIndex\" action=\"constraints.php\" method=\"post\">\n";
436
437
                echo "<table>\n";
438
                echo "<tr><th class=\"data\" colspan=\"3\">{$lang['strname']}</th></tr>\n";
439
                echo "<tr><td class=\"data1\" colspan=\"3\"><input type=\"text\" name=\"name\" size=\"32\" maxlength=\"{$data->_maxNameLen}\" /></td></tr>\n";
440
                echo "<tr><th class=\"data\">{$lang['strtablecolumnlist']}</th><th class=\"data\">&nbsp;</th><th class=\"data required\">{$lang['strfkcolumnlist']}</th></tr>\n";
441
                echo '<tr><td class="data1">' . $selColumns->fetch() . "</td>\n";
442
                echo '<td class="data1" style="text-align: center">' . $buttonRemove->fetch() . $buttonAdd->fetch() . "</td>\n";
443
                echo '<td class=data1>' . $selIndex->fetch() . "</td></tr>\n";
444
                echo "<tr><th class=\"data\" colspan=\"3\">{$lang['strfktarget']}</th></tr>";
445
                echo '<tr>';
446
                echo '<td class="data1" colspan="3"><select class="select2" name="target">';
447
                while (!$tables->EOF) {
448
                    $key = ['schemaname' => $tables->fields['nspname'], 'tablename' => $tables->fields['relname']];
449
                    $key = serialize($key);
450
                    echo '<option value="', htmlspecialchars($key), '">';
451
                    if ($tables->fields['nspname'] != $_REQUEST['schema']) {
452
                        echo htmlspecialchars($tables->fields['nspname']), '.';
453
                    }
454
                    echo htmlspecialchars($tables->fields['relname']), "</option>\n";
455
                    $tables->moveNext();
456
                }
457
                echo "</select>\n";
458
                echo '</td></tr>';
459
                echo "</table>\n";
460
461
                echo "<p><input type=\"hidden\" name=\"action\" value=\"save_add_foreign_key\" />\n";
462
                echo $misc->form;
463
                echo '<input type="hidden" name="table" value="', htmlspecialchars($_REQUEST['table']), "\" />\n";
464
                echo "<input type=\"hidden\" name=\"stage\" value=\"2\" />\n";
465
                echo "<input type=\"submit\" value=\"{$lang['stradd']}\" />\n";
466
                echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" /></p>\n";
467
                echo "</form>\n";
468
                //echo "<script>jQuery('select[name=\"target\"]').select2()</script>";
469
                break;
470
        }
471
    }
472
473
    /**
474
     * Confirm and then actually add a PRIMARY KEY or UNIQUE constraint
475
     */
476
    public function addPrimaryOrUniqueKey($type, $confirm, $msg = '')
0 ignored issues
show
Coding Style introduced by
addPrimaryOrUniqueKey uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
addPrimaryOrUniqueKey uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
477
    {
478
        $conf = $this->conf;
0 ignored issues
show
Unused Code introduced by
The assignment to $conf is dead and can be removed.
Loading history...
479
        $misc = $this->misc;
480
        $lang = $this->lang;
481
        $data = $misc->getDatabaseAccessor();
482
483
        if (!isset($_POST['name'])) {
484
            $_POST['name'] = '';
485
        }
486
487
        if ($confirm) {
488
            if (!isset($_POST['name'])) {
489
                $_POST['name'] = '';
490
            }
491
492
            if (!isset($_POST['tablespace'])) {
493
                $_POST['tablespace'] = '';
494
            }
495
496
            $this->printTrail('table');
497
498
            switch ($type) {
499
                case 'primary':
500
                    $this->printTitle($lang['straddpk'], 'pg.constraint.primary_key');
501
                    break;
502
                case 'unique':
503
                    $this->printTitle($lang['stradduniq'], 'pg.constraint.unique_key');
504
                    break;
505
                default:
506
                    $this->doDefault($lang['strinvalidparam']);
507
                    return;
508
            }
509
510
            $this->printMsg($msg);
511
512
            $attrs = $data->getTableAttributes($_REQUEST['table']);
513
            // Fetch all tablespaces from the database
514
            if ($data->hasTablespaces()) {
515
                $tablespaces = $data->getTablespaces();
516
            }
517
518
            $selColumns = new \PHPPgAdmin\XHtml\XHtmlSelect('TableColumnList', true, 10);
519
            $selColumns->set_style('width: 15em;');
520
521 View Code Duplication
            if ($attrs->recordCount() > 0) {
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...
522
                while (!$attrs->EOF) {
523
                    $new_option = new \PHPPgAdmin\XHtml\XHtmlOption($attrs->fields['attname']);
524
                    $selColumns->add($new_option);
525
                    $attrs->moveNext();
526
                }
527
            }
528
529
            $selIndex = new \PHPPgAdmin\XHtml\XHtmlSelect('IndexColumnList[]', true, 10);
530
            $selIndex->set_style('width: 15em;');
531
            $selIndex->set_attribute('id', 'IndexColumnList');
532
            $buttonAdd = new \PHPPgAdmin\XHtml\XHtmlButton('add', '>>');
533
            $buttonAdd->set_attribute('onclick', 'buttonPressed(this);');
534
            $buttonAdd->set_attribute('type', 'button');
535
536
            $buttonRemove = new \PHPPgAdmin\XHtml\XHtmlButton('remove', '<<');
537
            $buttonRemove->set_attribute('onclick', 'buttonPressed(this);');
538
            $buttonRemove->set_attribute('type', 'button');
539
540
            echo "<form onsubmit=\"doSelectAll();\" name=\"formIndex\" action=\"constraints.php\" method=\"post\">\n";
541
542
            echo "<table>\n";
543
            echo "<tr><th class=\"data\" colspan=\"3\">{$lang['strname']}</th></tr>";
544
            echo '<tr>';
545
            echo '<td class="data1" colspan="3"><input type="text" name="name" value="', htmlspecialchars($_POST['name']),
546
                "\" size=\"32\" maxlength=\"{$data->_maxNameLen}\" /></td></tr>";
547
            echo "<tr><th class=\"data\">{$lang['strtablecolumnlist']}</th><th class=\"data\">&nbsp;</th><th class=\"data required\">{$lang['strindexcolumnlist']}</th></tr>\n";
548
            echo '<tr><td class="data1">' . $selColumns->fetch() . "</td>\n";
549
            echo '<td class="data1" style="text-align: center">' . $buttonRemove->fetch() . $buttonAdd->fetch() . '</td>';
550
            echo '<td class=data1>' . $selIndex->fetch() . "</td></tr>\n";
551
552
            // Tablespace (if there are any)
553 View Code Duplication
            if ($data->hasTablespaces() && $tablespaces->recordCount() > 0) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $tablespaces does not seem to be defined for all execution paths leading up to this point.
Loading history...
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...
554
                echo "<tr><th class=\"data\" colspan=\"3\">{$lang['strtablespace']}</th></tr>";
555
                echo "<tr><td class=\"data1\" colspan=\"3\"><select name=\"tablespace\">\n";
556
                // Always offer the default (empty) option
557
                echo "\t\t\t\t<option value=\"\"",
558
                ($_POST['tablespace'] == '') ? ' selected="selected"' : '', "></option>\n";
559
                // Display all other tablespaces
560
                while (!$tablespaces->EOF) {
561
                    $spcname = htmlspecialchars($tablespaces->fields['spcname']);
562
                    echo "\t\t\t\t<option value=\"{$spcname}\"",
563
                    ($spcname == $_POST['tablespace']) ? ' selected="selected"' : '', ">{$spcname}</option>\n";
564
                    $tablespaces->moveNext();
565
                }
566
                echo "</select></td></tr>\n";
567
            }
568
569
            echo "</table>\n";
570
571
            echo "<p><input type=\"hidden\" name=\"action\" value=\"save_add_primary_key\" />\n";
572
            echo $misc->form;
573
            echo '<input type="hidden" name="table" value="', htmlspecialchars($_REQUEST['table']), "\" />\n";
574
            echo '<input type="hidden" name="type" value="', htmlspecialchars($type), "\" />\n";
575
            echo "<input type=\"submit\" value=\"{$lang['stradd']}\" />\n";
576
            echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" /></p>\n";
577
            echo "</form>\n";
578
        } else {
579
            // Default tablespace to empty if it isn't set
580
            if (!isset($_POST['tablespace'])) {
581
                $_POST['tablespace'] = '';
582
            }
583
584
            if ($_POST['type'] == 'primary') {
585
                // Check that they've given at least one column
586 View Code Duplication
                if (!isset($_POST['IndexColumnList']) || !is_array($_POST['IndexColumnList'])
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...
587
                    || sizeof($_POST['IndexColumnList']) == 0) {
588
                    $this->addPrimaryOrUniqueKey($_POST['type'], true, $lang['strpkneedscols']);
589
                } else {
590
                    $status = $data->addPrimaryKey($_POST['table'], $_POST['IndexColumnList'], $_POST['name'], $_POST['tablespace']);
591
                    if ($status == 0) {
592
                        $this->doDefault($lang['strpkadded']);
593
                    } else {
594
                        $this->addPrimaryOrUniqueKey($_POST['type'], true, $lang['strpkaddedbad']);
595
                    }
596
                }
597 View Code Duplication
            } elseif ($_POST['type'] == 'unique') {
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...
598
                // Check that they've given at least one column
599
                if (!isset($_POST['IndexColumnList']) || !is_array($_POST['IndexColumnList'])
600
                    || sizeof($_POST['IndexColumnList']) == 0) {
601
                    $this->addPrimaryOrUniqueKey($_POST['type'], true, $lang['struniqneedscols']);
602
                } else {
603
                    $status = $data->addUniqueKey($_POST['table'], $_POST['IndexColumnList'], $_POST['name'], $_POST['tablespace']);
604
                    if ($status == 0) {
605
                        $this->doDefault($lang['struniqadded']);
606
                    } else {
607
                        $this->addPrimaryOrUniqueKey($_POST['type'], true, $lang['struniqaddedbad']);
608
                    }
609
                }
610
            } else {
611
                $this->doDefault($lang['strinvalidparam']);
612
            }
613
        }
614
    }
615
616
    /**
617
     * Confirm and then actually add a CHECK constraint
618
     */
619 View Code Duplication
    public function addCheck($confirm, $msg = '')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
Coding Style introduced by
addCheck uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
addCheck uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
620
    {
621
        $conf = $this->conf;
0 ignored issues
show
Unused Code introduced by
The assignment to $conf is dead and can be removed.
Loading history...
622
        $misc = $this->misc;
623
        $lang = $this->lang;
624
        $data = $misc->getDatabaseAccessor();
625
626
        if (!isset($_POST['name'])) {
627
            $_POST['name'] = '';
628
        }
629
630
        if (!isset($_POST['definition'])) {
631
            $_POST['definition'] = '';
632
        }
633
634
        if ($confirm) {
635
            $this->printTrail('table');
636
            $this->printTitle($lang['straddcheck'], 'pg.constraint.check');
637
            $this->printMsg($msg);
638
639
            echo '<form action="' . SUBFOLDER . "/src/views/constraints.php\" method=\"post\">\n";
640
            echo "<table>\n";
641
            echo "<tr><th class=\"data\">{$lang['strname']}</th>\n";
642
            echo "<th class=\"data required\">{$lang['strdefinition']}</th></tr>\n";
643
644
            echo "<tr><td class=\"data1\"><input name=\"name\" size=\"24\" maxlength=\"{$data->_maxNameLen}\" value=\"",
645
            htmlspecialchars($_POST['name']), "\" /></td>\n";
646
647
            echo '<td class="data1">(<input name="definition" size="64" value="',
648
            htmlspecialchars($_POST['definition']), "\" />)</td></tr>\n";
649
            echo "</table>\n";
650
651
            echo "<input type=\"hidden\" name=\"action\" value=\"save_add_check\" />\n";
652
            echo '<input type="hidden" name="table" value="', htmlspecialchars($_REQUEST['table']), "\" />\n";
653
            echo $misc->form;
654
            echo "<p><input type=\"submit\" name=\"ok\" value=\"{$lang['stradd']}\" />\n";
655
            echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" /></p>\n";
656
            echo "</form>\n";
657
        } else {
658
            if (trim($_POST['definition']) == '') {
659
                $this->addCheck(true, $lang['strcheckneedsdefinition']);
660
            } else {
661
                $status = $data->addCheckConstraint($_POST['table'],
662
                    $_POST['definition'], $_POST['name']);
663
                if ($status == 0) {
664
                    $this->doDefault($lang['strcheckadded']);
665
                } else {
666
                    $this->addCheck(true, $lang['strcheckaddedbad']);
667
                }
668
            }
669
        }
670
    }
671
672
    /**
673
     * Show confirmation of drop and perform actual drop
674
     */
675
    public function doDrop($confirm)
0 ignored issues
show
Coding Style introduced by
doDrop uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
doDrop uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
676
    {
677
        $conf = $this->conf;
0 ignored issues
show
Unused Code introduced by
The assignment to $conf is dead and can be removed.
Loading history...
678
        $misc = $this->misc;
679
        $lang = $this->lang;
680
        $data = $misc->getDatabaseAccessor();
681
682
        if ($confirm) {
683
            $this->printTrail('constraint');
684
            $this->printTitle($lang['strdrop'], 'pg.constraint.drop');
685
686
            echo '<p>', sprintf($lang['strconfdropconstraint'], $misc->printVal($_REQUEST['constraint']),
687
                $misc->printVal($_REQUEST['table'])), "</p>\n";
688
689
            echo '<form action="' . SUBFOLDER . "/src/views/constraints.php\" method=\"post\">\n";
690
            echo "<input type=\"hidden\" name=\"action\" value=\"drop\" />\n";
691
            echo '<input type="hidden" name="table" value="', htmlspecialchars($_REQUEST['table']), "\" />\n";
692
            echo '<input type="hidden" name="constraint" value="', htmlspecialchars($_REQUEST['constraint']), "\" />\n";
693
            echo '<input type="hidden" name="type" value="', htmlspecialchars($_REQUEST['type']), "\" />\n";
694
            echo $misc->form;
695
            echo "<p><input type=\"checkbox\" id=\"cascade\" name=\"cascade\" /> <label for=\"cascade\">{$lang['strcascade']}</label></p>\n";
696
            echo "<input type=\"submit\" name=\"drop\" value=\"{$lang['strdrop']}\" />\n";
697
            echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" />\n";
698
            echo "</form>\n";
699
        } else {
700
            $status = $data->dropConstraint($_POST['constraint'], $_POST['table'], $_POST['type'], isset($_POST['cascade']));
701
            if ($status == 0) {
702
                $this->doDefault($lang['strconstraintdropped']);
703
            } else {
704
                $this->doDefault($lang['strconstraintdroppedbad']);
705
            }
706
        }
707
    }
708
}
709