Issues (115)

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.

BehatExtension/Context/SilverStripeContext.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace SilverStripe\BehatExtension\Context;
4
5
use Behat\Behat\Context\Step;
6
use Behat\Behat\Event\FeatureEvent;
7
use Behat\Behat\Event\ScenarioEvent;
8
use Behat\Behat\Event\SuiteEvent;
9
use Behat\Gherkin\Node\PyStringNode;
10
use Behat\MinkExtension\Context\MinkContext;
11
use Behat\Mink\Driver\GoutteDriver;
12
use Behat\Mink\Driver\Selenium2Driver;
13
use Behat\Mink\Exception\UnsupportedDriverActionException;
14
use Behat\Mink\Exception\ElementNotFoundException;
15
16
use SilverStripe\BehatExtension\Context\SilverStripeAwareContextInterface;
17
18
use Symfony\Component\Yaml\Yaml;
19
20
// Mink etc.
21
require_once 'vendor/autoload.php';
22
23
require_once BASE_PATH . '/vendor/phpunit/phpunit/src/Framework/Assert/Functions.php';
24
25
/**
26
 * SilverStripeContext
27
 *
28
 * Generic context wrapper used as a base for Behat FeatureContext.
29
 */
30
class SilverStripeContext extends MinkContext implements SilverStripeAwareContextInterface
31
{
32
    protected $databaseName;
33
34
    /**
35
     * @var Array Partial string match for step names
36
     * that are considered to trigger Ajax request in the CMS,
37
     * and hence need special timeout handling.
38
     * @see \SilverStripe\BehatExtension\Context\BasicContext->handleAjaxBeforeStep().
39
     */
40
    protected $ajaxSteps;
41
42
    /**
43
     * @var Int Timeout in milliseconds, after which the interface assumes
44
     * that an Ajax request has timed out, and continues with assertions.
45
     */
46
    protected $ajaxTimeout;
47
48
    /**
49
     * @var String Relative URL to the SilverStripe admin interface.
50
     */
51
    protected $adminUrl;
52
53
    /**
54
     * @var String Relative URL to the SilverStripe login form.
55
     */
56
    protected $loginUrl;
57
58
    /**
59
     * @var String Relative path to a writeable folder where screenshots can be stored.
60
     * If set to NULL, no screenshots will be stored.
61
     */
62
    protected $screenshotPath;
63
64
    protected $context;
65
66
    protected $testSessionEnvironment;
67
68
69
    /**
70
     * Initializes context.
71
     * Every scenario gets it's own context object.
72
     *
73
     * @param   array   $parameters     context parameters (set them up through behat.yml)
74
     */
75
    public function __construct(array $parameters)
76
    {
77
        // Initialize your context here
78
        $this->context = $parameters;
79
        $this->testSessionEnvironment = new \TestSessionEnvironment();
80
    }
81
82
    public function setDatabase($databaseName)
83
    {
84
        $this->databaseName = $databaseName;
85
    }
86
87
    public function setAjaxSteps($ajaxSteps)
88
    {
89
        if ($ajaxSteps) {
90
            $this->ajaxSteps = $ajaxSteps;
91
        }
92
    }
93
94
    public function getAjaxSteps()
95
    {
96
        return $this->ajaxSteps;
97
    }
98
99
    public function setAjaxTimeout($ajaxTimeout)
100
    {
101
        $this->ajaxTimeout = $ajaxTimeout;
102
    }
103
104
    public function getAjaxTimeout()
105
    {
106
        return $this->ajaxTimeout;
107
    }
108
109
    public function setAdminUrl($adminUrl)
110
    {
111
        $this->adminUrl = $adminUrl;
112
    }
113
114
    public function getAdminUrl()
115
    {
116
        return $this->adminUrl;
117
    }
118
119
    public function setLoginUrl($loginUrl)
120
    {
121
        $this->loginUrl = $loginUrl;
122
    }
123
124
    public function getLoginUrl()
125
    {
126
        return $this->loginUrl;
127
    }
128
129
    public function setScreenshotPath($screenshotPath)
130
    {
131
        $this->screenshotPath = $screenshotPath;
132
    }
133
134
    public function getScreenshotPath()
135
    {
136
        return $this->screenshotPath;
137
    }
138
139
    public function getRegionMap()
140
    {
141
        return $this->regionMap;
142
    }
143
144
    public function setRegionMap($regionMap)
145
    {
146
        $this->regionMap = $regionMap;
147
    }
148
149
    /**
150
     * Returns MinkElement based off region defined in .yml file.
151
     * Also supports direct CSS selectors and regions identified by a "data-title" attribute.
152
     * When using the "data-title" attribute, ensure not to include double quotes.
153
     *
154
     * @param String $region Region name or CSS selector
155
     * @return MinkElement|null
156
     */
157
    public function getRegionObj($region)
158
    {
159
        // Try to find regions directly by CSS selector.
160
        try {
161
            $regionObj = $this->getSession()->getPage()->find(
162
                'css',
163
                // Escape CSS selector
164
                (false !== strpos($region, "'")) ? str_replace("'", "\'", $region) : $region
165
            );
166
            if ($regionObj) {
167
                return $regionObj;
168
            }
169
        } catch (\Symfony\Component\CssSelector\Exception\SyntaxErrorException $e) {
170
            // fall through to next case
171
        }
172
173
        // Fall back to region identified by data-title.
174
        // Only apply if no double quotes exist in search string,
175
        // which would break the CSS selector.
176
        if (false === strpos($region, '"')) {
177
            $regionObj = $this->getSession()->getPage()->find(
178
                'css',
179
                '[data-title="' . $region . '"]'
180
            );
181
            if ($regionObj) {
182
                return $regionObj;
183
            }
184
        }
185
186
        // Look for named region
187
        if (!$this->regionMap) {
188
            throw new \LogicException("Cannot find 'region_map' in the behat.yml");
189
        }
190
        if (!array_key_exists($region, $this->regionMap)) {
191
            throw new \LogicException("Cannot find the specified region in the behat.yml");
192
        }
193
        $regionObj = $this->getSession()->getPage()->find('css', $region);
194
        if (!$regionObj) {
195
            throw new ElementNotFoundException("Cannot find the specified region on the page");
0 ignored issues
show
'Cannot find the specified region on the page' is of type string, but the function expects a object<Behat\Mink\Session>.

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...
196
        }
197
198
        return $regionObj;
199
    }
200
201
    /**
202
     * @BeforeScenario
203
     */
204
    public function before(ScenarioEvent $event)
205
    {
206
        if (!isset($this->databaseName)) {
207
            throw new \LogicException(
208
                'Context\'s $databaseName has to be set when implementing SilverStripeAwareContextInterface.'
209
            );
210
        }
211
212
        $state = $this->getTestSessionState();
213
        $this->testSessionEnvironment->startTestSession($state);
214
215
        // Optionally import database
216
        if (!empty($state['importDatabasePath'])) {
217
            $this->testSessionEnvironment->importDatabase(
218
                $state['importDatabasePath'],
219
                !empty($state['requireDefaultRecords']) ? $state['requireDefaultRecords'] : false
220
            );
221
        } elseif (!empty($state['requireDefaultRecords']) && $state['requireDefaultRecords']) {
222
            $this->testSessionEnvironment->requireDefaultRecords();
223
        }
224
225
        // Fixtures
226
        $fixtureFile = (!empty($state['fixture'])) ? $state['fixture'] : null;
227
        if ($fixtureFile) {
228
            $this->testSessionEnvironment->loadFixtureIntoDb($fixtureFile);
229
        }
230
231
        if ($screenSize = getenv('BEHAT_SCREEN_SIZE')) {
232
            list($screenWidth, $screenHeight) = explode('x', $screenSize);
233
            $this->getSession()->resizeWindow((int)$screenWidth, (int)$screenHeight);
234
        } else {
235
            $this->getSession()->resizeWindow(1024, 768);
236
        }
237
    }
238
239
    /**
240
     * Returns a parameter map of state to set within the test session.
241
     * Takes TESTSESSION_PARAMS environment variable into account for run-specific configurations.
242
     *
243
     * @return array
244
     */
245
    public function getTestSessionState()
246
    {
247
        $extraParams = array();
248
        parse_str(getenv('TESTSESSION_PARAMS'), $extraParams);
249
        return array_merge(
250
            array(
251
                'database' => $this->databaseName,
252
                'mailer' => 'SilverStripe\BehatExtension\Utility\TestMailer',
253
            ),
254
            $extraParams
255
        );
256
    }
257
258
    /**
259
     * Parses given URL and returns its components
260
     *
261
     * @param $url
262
     * @return array|mixed Parsed URL
263
     */
264
    public function parseUrl($url)
265
    {
266
        $url = parse_url($url);
267
        $url['vars'] = array();
268
        if (!isset($url['fragment'])) {
269
            $url['fragment'] = null;
270
        }
271
        if (isset($url['query'])) {
272
            parse_str($url['query'], $url['vars']);
273
        }
274
275
        return $url;
276
    }
277
278
    /**
279
     * Checks whether current URL is close enough to the given URL.
280
     * Unless specified in $url, get vars will be ignored
281
     * Unless specified in $url, fragment identifiers will be ignored
282
     *
283
     * @param $url string URL to compare to current URL
284
     * @return boolean Returns true if the current URL is close enough to the given URL, false otherwise.
285
     */
286
    public function isCurrentUrlSimilarTo($url)
287
    {
288
        $current = $this->parseUrl($this->getSession()->getCurrentUrl());
289
        $test = $this->parseUrl($url);
290
291
        if ($current['path'] !== $test['path']) {
292
            return false;
293
        }
294
295
        if (isset($test['fragment']) && $current['fragment'] !== $test['fragment']) {
296
            return false;
297
        }
298
299
        foreach ($test['vars'] as $name => $value) {
300
            if (!isset($current['vars'][$name]) || $current['vars'][$name] !== $value) {
301
                return false;
302
            }
303
        }
304
305
        return true;
306
    }
307
308
    /**
309
     * Returns base URL parameter set in MinkExtension.
310
     * It simplifies configuration by allowing to specify this parameter
311
     * once but makes code dependent on MinkExtension.
312
     *
313
     * @return string
314
     */
315
    public function getBaseUrl()
316
    {
317
        return $this->getMinkParameter('base_url') ?: '';
318
    }
319
320
    /**
321
     * Joins URL parts into an URL using forward slash.
322
     * Forward slash usages are normalised to one between parts.
323
     * This method takes variable number of parameters.
324
     *
325
     * @param $...
326
     * @return string
327
     * @throws \InvalidArgumentException
328
     */
329
    public function joinUrlParts()
330
    {
331
        if (0 === func_num_args()) {
332
            throw new \InvalidArgumentException('Need at least one argument');
333
        }
334
335
        $parts = func_get_args();
336
        $trimSlashes = function (&$part) {
337
            $part = trim($part, '/');
338
        };
339
        array_walk($parts, $trimSlashes);
340
341
        return implode('/', $parts);
342
    }
343
344
    public function canIntercept()
345
    {
346
        $driver = $this->getSession()->getDriver();
347
        if ($driver instanceof GoutteDriver) {
348
            return true;
349
        } else {
350
            if ($driver instanceof Selenium2Driver) {
351
                return false;
352
            }
353
        }
354
355
        throw new UnsupportedDriverActionException('You need to tag the scenario with "@mink:goutte" or
356
			"@mink:symfony". Intercepting the redirections is not supported by %s', $driver);
357
    }
358
359
    /**
360
     * @Given /^(.*) without redirection$/
361
     */
362
    public function theRedirectionsAreIntercepted($step)
363
    {
364
        if ($this->canIntercept()) {
365
            $this->getSession()->getDriver()->getClient()->followRedirects(false);
366
        }
367
368
        return new Step\Given($step);
369
    }
370
371
    /**
372
     * Fills in form field with specified id|name|label|value.
373
     * Overwritten to select the first *visible* element, see https://github.com/Behat/Mink/issues/311
374
     */
375 View Code Duplication
    public function fillField($field, $value)
376
    {
377
        $value = $this->fixStepArgument($value);
378
        $fields = $this->getSession()->getPage()->findAll('named', array(
379
            'field', $this->getSession()->getSelectorsHandler()->xpathLiteral($field)
380
        ));
381
        if ($fields) {
382
            foreach ($fields as $f) {
383
                if ($f->isVisible()) {
384
                    $f->setValue($value);
385
                    return;
386
                }
387
            }
388
        }
389
390
        throw new ElementNotFoundException(
391
            $this->getSession(),
392
            'form field',
393
            'id|name|label|value',
394
            $field
395
        );
396
    }
397
398
    /**
399
     * Overwritten to click the first *visable* link the DOM.
400
     */
401 View Code Duplication
    public function clickLink($link)
402
    {
403
        $link = $this->fixStepArgument($link);
404
        $links = $this->getSession()->getPage()->findAll('named', array(
405
            'link', $this->getSession()->getSelectorsHandler()->xpathLiteral($link)
406
        ));
407
        if ($links) {
408
            foreach ($links as $l) {
409
                if ($l->isVisible()) {
410
                    $l->click();
411
                    return;
412
                }
413
            }
414
        }
415
        throw new ElementNotFoundException(
416
            $this->getSession(),
417
            'link',
418
            'id|name|label|value',
419
            $link
420
        );
421
    }
422
423
     /**
424
     * Sets the current date. Relies on the underlying functionality using
425
     * {@link SS_Datetime::now()} rather than PHP's system time methods like date().
426
     * Supports ISO fomat: Y-m-d
427
     * Example: Given the current date is "2009-10-31"
428
     *
429
     * @Given /^the current date is "([^"]*)"$/
430
     */
431 View Code Duplication
    public function givenTheCurrentDateIs($date)
432
    {
433
        $newDatetime = \DateTime::createFromFormat('Y-m-d', $date);
434
        if (!$newDatetime) {
435
            throw new InvalidArgumentException(sprintf('Invalid date format: %s (requires "Y-m-d")', $date));
436
        }
437
438
        $state = $this->testSessionEnvironment->getState();
439
        $oldDatetime = \DateTime::createFromFormat('Y-m-d H:i:s', isset($state->datetime) ? $state->datetime : null);
440
        if ($oldDatetime) {
441
            $newDatetime->setTime($oldDatetime->format('H'), $oldDatetime->format('i'), $oldDatetime->format('s'));
442
        }
443
        $state->datetime = $newDatetime->format('Y-m-d H:i:s');
444
        $this->testSessionEnvironment->applyState($state);
445
    }
446
447
    /**
448
     * Sets the current time. Relies on the underlying functionality using
449
     * {@link \SS_Datetime::now()} rather than PHP's system time methods like date().
450
     * Supports ISO fomat: H:i:s
451
     * Example: Given the current time is "20:31:50"
452
     *
453
     * @Given /^the current time is "([^"]*)"$/
454
     */
455 View Code Duplication
    public function givenTheCurrentTimeIs($time)
456
    {
457
        $newDatetime = \DateTime::createFromFormat('H:i:s', $date);
458
        if (!$newDatetime) {
459
            throw new InvalidArgumentException(sprintf('Invalid date format: %s (requires "H:i:s")', $date));
460
        }
461
462
        $state = $this->testSessionEnvironment->getState();
463
        $oldDatetime = \DateTime::createFromFormat('Y-m-d H:i:s', isset($state->datetime) ? $state->datetime : null);
464
        if ($oldDatetime) {
465
            $newDatetime->setDate($oldDatetime->format('Y'), $oldDatetime->format('m'), $oldDatetime->format('d'));
466
        }
467
        $state->datetime = $newDatetime->format('Y-m-d H:i:s');
468
        $this->testSessionEnvironment->applyState($state);
469
    }
470
471
    /**
472
     * Selects option in select field with specified id|name|label|value.
473
     *
474
     * @override /^(?:|I )select "(?P<option>(?:[^"]|\\")*)" from "(?P<select>(?:[^"]|\\")*)"$/
475
     */
476
    public function selectOption($select, $option)
477
    {
478
        // Find field
479
        $field = $this
480
            ->getSession()
481
            ->getPage()
482
            ->findField($this->fixStepArgument($select));
483
484
        // If field is visible then select it as per normal
485
        if ($field && $field->isVisible()) {
486
            parent::selectOption($select, $option);
487
        } else {
488
            $this->selectOptionWithJavascript($select, $option);
489
        }
490
    }
491
492
    /**
493
     * Selects option in select field with specified id|name|label|value using javascript
494
     * This method uses javascript to allow selection of options that may be
495
     * overridden by javascript libraries, and thus hide the element.
496
     *
497
     * @When /^(?:|I )select "(?P<option>(?:[^"]|\\")*)" from "(?P<select>(?:[^"]|\\")*)" with javascript$/
498
     */
499
    public function selectOptionWithJavascript($select, $option)
500
    {
501
        $select = $this->fixStepArgument($select);
502
        $option = $this->fixStepArgument($option);
503
        $page = $this->getSession()->getPage();
504
505
        // Find field
506
        $field = $page->findField($select);
507
        if (null === $field) {
508
            throw new ElementNotFoundException($this->getSession(), 'form field', 'id|name|label|value', $select);
509
        }
510
511
        // Find option
512
        $opt = $field->find('named', array(
513
            'option', $this->getSession()->getSelectorsHandler()->xpathLiteral($option)
514
        ));
515
        if (null === $opt) {
516
            throw new ElementNotFoundException($this->getSession(), 'select option', 'value|text', $option);
517
        }
518
519
        // Merge new option in with old handling both multiselect and single select
520
        $value = $field->getValue();
521
        $newValue = $opt->getAttribute('value');
522
        if (is_array($value)) {
523
            if (!in_array($newValue, $value)) {
524
                $value[] = $newValue;
525
            }
526
        } else {
527
            $value = $newValue;
528
        }
529
        $valueEncoded = json_encode($value);
530
531
        // Inject this value via javascript
532
        $fieldID = $field->getAttribute('ID');
533
        $script = <<<EOS
534
			(function($) {
535
				$("#$fieldID")
536
					.val($valueEncoded)
537
					.change()
538
					.trigger('liszt:updated')
539
					.trigger('chosen:updated');
540
			})(jQuery);
541
EOS;
542
        $this->getSession()->getDriver()->executeScript($script);
543
    }
544
}
545