This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * @author Sergii Bondarenko, <[email protected]> |
||
4 | */ |
||
5 | namespace Drupal\TqExtension\Context; |
||
6 | |||
7 | // Scope definitions. |
||
8 | use Behat\Behat\Hook\Scope; |
||
9 | // Utils. |
||
10 | use Drupal\TqExtension\Utils\Database\Database; |
||
11 | use Drupal\TqExtension\Utils\LogicalAssertion; |
||
12 | |||
13 | class TqContext extends RawTqContext |
||
14 | { |
||
15 | use LogicalAssertion; |
||
16 | |||
17 | /** |
||
18 | * The name and working element of main window. |
||
19 | * |
||
20 | * @var array |
||
21 | */ |
||
22 | private $mainWindow = []; |
||
23 | /** |
||
24 | * @var Database |
||
25 | */ |
||
26 | private static $database; |
||
27 | |||
28 | /** |
||
29 | * Supports switching between the two windows only. |
||
30 | * |
||
31 | * @Given /^(?:|I )switch to opened window$/ |
||
32 | * @Then /^(?:|I )switch back to main window$/ |
||
33 | */ |
||
34 | public function iSwitchToWindow() |
||
35 | { |
||
36 | $windows = $this->getWindowNames(); |
||
37 | |||
38 | // If the window was not switched yet, then store it name and working element for future switching back. |
||
39 | if (empty($this->mainWindow)) { |
||
40 | $this->mainWindow['name'] = array_shift($windows); |
||
41 | $this->mainWindow['element'] = $this->getWorkingElement(); |
||
42 | |||
43 | $window = reset($windows); |
||
44 | } else { |
||
45 | $window = $this->mainWindow['name']; |
||
46 | $element = $this->mainWindow['element']; |
||
47 | |||
48 | $this->mainWindow = []; |
||
49 | } |
||
50 | |||
51 | $this->getSession()->switchToWindow($window); |
||
52 | $this->setWorkingElement(isset($element) ? $element : $this->getBodyElement()); |
||
53 | } |
||
54 | |||
55 | /** |
||
56 | * @Given /^(?:|I )switch to CKFinder window$/ |
||
57 | * |
||
58 | * @javascript |
||
59 | */ |
||
60 | public function switchToCKFinderWindow() |
||
61 | { |
||
62 | $this->iSwitchToWindow(); |
||
63 | $this->executeJsOnElement( |
||
64 | $this->element('css', 'iframe'), |
||
65 | "{{ELEMENT}}.setAttribute('id', 'behat_ckfinder');" |
||
66 | ); |
||
67 | $this->iSwitchToAnIframe('behat_ckfinder'); |
||
68 | } |
||
69 | |||
70 | /** |
||
71 | * @param string $name |
||
72 | * An iframe name (null for switching back). |
||
73 | * |
||
74 | * @Given /^(?:|I )switch to an iframe "([^"]*)"$/ |
||
75 | * @Then /^(?:|I )switch back from an iframe$/ |
||
76 | */ |
||
77 | public function iSwitchToAnIframe($name = null) |
||
78 | { |
||
79 | $this->getSession()->switchToIFrame($name); |
||
80 | } |
||
81 | |||
82 | /** |
||
83 | * Open the page with specified resolution. |
||
84 | * |
||
85 | * @param string $width_height |
||
86 | * String that satisfy the condition "<WIDTH>x<HEIGHT>". |
||
87 | * |
||
88 | * @example |
||
89 | * Given I should use the "1280x800" resolution |
||
90 | * |
||
91 | * @Given /^(?:|I should )use the "([^"]*)" screen resolution$/ |
||
92 | */ |
||
93 | public function useScreenResolution($width_height) |
||
94 | { |
||
95 | list($width, $height) = explode('x', $width_height); |
||
96 | |||
97 | $this->getSessionDriver()->resizeWindow((int) $width, (int) $height); |
||
98 | } |
||
99 | |||
100 | /** |
||
101 | * @param string $action |
||
102 | * The next actions can be: "press", "click", "double click" and "right click". |
||
103 | * @param string $selector |
||
104 | * CSS, inaccurate text or selector name from behat.yml can be used. |
||
105 | * |
||
106 | * @throws \WebDriver\Exception\NoSuchElement |
||
107 | * When element was not found. |
||
108 | * |
||
109 | * @Given /^(?:|I )((?:|(?:double|right) )click|press) on "([^"]*)"$/ |
||
110 | */ |
||
111 | public function pressElement($action, $selector) |
||
112 | { |
||
113 | // 1. Get the action, divide string by spaces and put it parts into an array. |
||
114 | // 2. Apply the "ucfirst" function for each array element. |
||
115 | // 3. Make string from an array. |
||
116 | // 4. Set the first letter of a string to lower case. |
||
117 | $this->element('*', $selector)->{lcfirst(implode(array_map('ucfirst', explode(' ', $action))))}(); |
||
118 | } |
||
119 | |||
120 | /** |
||
121 | * @Given /^(?:|I )wait until AJAX is finished$/ |
||
122 | * |
||
123 | * @javascript |
||
124 | */ |
||
125 | public function waitUntilAjaxIsFinished() |
||
126 | { |
||
127 | $this->waitAjaxAndAnimations(); |
||
128 | } |
||
129 | |||
130 | /** |
||
131 | * @param string $selector |
||
132 | * CSS selector or region name. |
||
133 | * |
||
134 | * @Then /^(?:|I )work with elements in "([^"]*)"(?:| region)$/ |
||
135 | */ |
||
136 | public function workWithElementsInRegion($selector) |
||
137 | { |
||
138 | if (in_array($selector, ['html', 'head'])) { |
||
139 | $element = $this->getSession()->getPage()->find('css', $selector); |
||
140 | } else { |
||
141 | $element = $this->element('css', $selector); |
||
142 | } |
||
143 | |||
144 | $this->setWorkingElement($element); |
||
0 ignored issues
–
show
|
|||
145 | } |
||
146 | |||
147 | /** |
||
148 | * @Then /^(?:|I )checkout to whole page$/ |
||
149 | */ |
||
150 | public function unsetWorkingElementScope() |
||
151 | { |
||
152 | $this->unsetWorkingElement(); |
||
153 | } |
||
154 | |||
155 | /** |
||
156 | * @param int $seconds |
||
157 | * Amount of seconds when nothing to happens. |
||
158 | * |
||
159 | * @Given /^(?:|I )wait (\d+) seconds$/ |
||
160 | */ |
||
161 | public function waitSeconds($seconds) |
||
162 | { |
||
163 | sleep($seconds); |
||
164 | } |
||
165 | |||
166 | /** |
||
167 | * @param string $selector |
||
168 | * Text or CSS. |
||
169 | * |
||
170 | * @throws \Exception |
||
171 | * |
||
172 | * @Given /^(?:|I )scroll to "([^"]*)" element$/ |
||
173 | * |
||
174 | * @javascript |
||
175 | */ |
||
176 | public function scrollToElement($selector) |
||
177 | { |
||
178 | if (!self::hasTag('javascript')) { |
||
179 | throw new \Exception('Scrolling to an element is impossible without a JavaScript.'); |
||
180 | } |
||
181 | |||
182 | $this->executeJsOnElement($this->findElement($selector), '{{ELEMENT}}.scrollIntoView(true);'); |
||
0 ignored issues
–
show
It seems like
$this->findElement($selector) can be null ; however, executeJsOnElement() does not accept null , maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
![]() |
|||
183 | } |
||
184 | |||
185 | /** |
||
186 | * @param string $message |
||
187 | * JS error. |
||
188 | * @param bool $negate |
||
189 | * Whether page should or should not contain the error. |
||
190 | * @param string $file |
||
191 | * File where error appears. |
||
192 | * |
||
193 | * @throws \RuntimeException |
||
194 | * @throws \Exception |
||
195 | * |
||
196 | * @example |
||
197 | * Then check that "TypeError: cell[0] is undefined" JS error appears in "misc/tabledrag.js" file |
||
198 | * |
||
199 | * @Then /^check that "([^"]*)" JS error(| not) appears in "([^"]*)" file$/ |
||
200 | * |
||
201 | * @javascript |
||
202 | */ |
||
203 | public function checkJavaScriptError($message, $negate, $file) |
||
204 | { |
||
205 | $errors = $this->getSession()->evaluateScript('return JSON.stringify(window.errors);'); |
||
206 | $negate = (bool) $negate; |
||
207 | |||
208 | if (empty($errors)) { |
||
209 | if (!$negate) { |
||
210 | throw new \RuntimeException('Page does not contain JavaScript errors.'); |
||
211 | } |
||
212 | } else { |
||
213 | $base_url = $this->locatePath(); |
||
214 | |||
215 | foreach (json_decode($errors) as $error) { |
||
216 | $error->location = str_replace($base_url, '', $error->location); |
||
217 | |||
218 | switch (static::assertion( |
||
219 | strpos($error->message, $message) === 0 && strpos($error->location, $file) === 0, |
||
220 | $negate |
||
221 | )) { |
||
222 | case 1: |
||
223 | throw new \Exception(sprintf( |
||
224 | 'The "%s" error found in "%s" file, but should not be.', |
||
225 | $message, |
||
226 | $file |
||
227 | )); |
||
228 | |||
229 | case 2: |
||
230 | throw new \Exception(sprintf( |
||
231 | 'The "%s" error not found in "%s" file, but should be.', |
||
232 | $message, |
||
233 | $file |
||
234 | )); |
||
235 | } |
||
236 | } |
||
237 | } |
||
238 | } |
||
239 | |||
240 | /** |
||
241 | * @param string $selector |
||
242 | * @param string $attribute |
||
243 | * @param string $expectedValue |
||
244 | * |
||
245 | * @throws \Exception |
||
246 | * |
||
247 | * @example |
||
248 | * Then I should see the "#table_cell" element with "colspan" attribute having "3" value |
||
249 | * |
||
250 | * @Then /^(?:|I )should see the "([^"]*)" element with "([^"]*)" attribute having "([^"]*)" value$/ |
||
251 | */ |
||
252 | public function assertElementAttribute($selector, $attribute, $expectedValue) |
||
253 | { |
||
254 | $actualValue = $this->element('*', $selector)->getAttribute($attribute); |
||
255 | |||
256 | if (null === $actualValue) { |
||
257 | throw new \InvalidArgumentException(sprintf( |
||
258 | 'Element does not contain the "%s" attribute.', |
||
259 | $attribute |
||
260 | )); |
||
261 | } elseif ($actualValue !== $expectedValue) { |
||
262 | throw new \Exception(sprintf( |
||
263 | 'Attribute "%s" have the "%s" value which is not equal to "%s".', |
||
264 | $attribute, |
||
265 | $actualValue, |
||
266 | $expectedValue |
||
267 | )); |
||
268 | } |
||
269 | } |
||
270 | |||
271 | /** |
||
272 | * @param Scope\BeforeFeatureScope $scope |
||
273 | * Scope of the processing feature. |
||
274 | * |
||
275 | * @BeforeFeature |
||
276 | */ |
||
277 | public static function beforeFeature(Scope\BeforeFeatureScope $scope) |
||
278 | { |
||
279 | self::collectTags($scope->getFeature()->getTags()); |
||
280 | |||
281 | // Database will be cloned for every feature with @cloneDB tag. |
||
282 | if (self::hasTag('clonedb')) { |
||
283 | self::$database = clone new Database(self::getTag('clonedb', 'default')); |
||
284 | } |
||
285 | |||
286 | static::setDrupalVariables([ |
||
287 | // Set to "false", because the administration menu will not be rendered. |
||
288 | // @see https://www.drupal.org/node/2023625#comment-8607207 |
||
289 | 'admin_menu_cache_client' => false, |
||
290 | ]); |
||
291 | |||
292 | static::injectCustomJavascript('CatchErrors'); |
||
293 | } |
||
294 | |||
295 | /** |
||
296 | * @AfterFeature |
||
297 | */ |
||
298 | public static function afterFeature() |
||
299 | { |
||
300 | // Restore initial database when feature is done (call __destruct). |
||
301 | self::$database = null; |
||
302 | |||
303 | // Remove injected script. |
||
304 | static::injectCustomJavascript('CatchErrors', true); |
||
305 | } |
||
306 | |||
307 | /** |
||
308 | * @param Scope\BeforeScenarioScope $scope |
||
309 | * Scope of the processing scenario. |
||
310 | * |
||
311 | * @BeforeScenario |
||
312 | */ |
||
313 | public function beforeScenario(Scope\BeforeScenarioScope $scope) |
||
314 | { |
||
315 | self::collectTags($scope->getScenario()->getTags()); |
||
316 | |||
317 | // No need to keep working element between scenarios. |
||
318 | $this->unsetWorkingElement(); |
||
319 | // Any page should be visited due to using jQuery and checking the cookies. |
||
320 | $this->visitPath('/'); |
||
321 | // By "Goutte" session we need to visit any page to be able to set a cookie |
||
322 | // for this session and use it for checking request status codes. |
||
323 | $this->visitPath('/', 'goutte'); |
||
324 | } |
||
325 | |||
326 | /** |
||
327 | * Set the jQuery handlers for "start" and "finish" events of AJAX queries. |
||
328 | * In each method can be used the "waitAjaxAndAnimations" method for check |
||
329 | * that AJAX was finished. |
||
330 | * |
||
331 | * @see RawTqContext::waitAjaxAndAnimations() |
||
332 | * |
||
333 | * @BeforeScenario @javascript |
||
334 | */ |
||
335 | public function beforeScenarioJS() |
||
336 | { |
||
337 | $javascript = ''; |
||
338 | |||
339 | foreach (['Start' => 'true', 'Complete' => 'false'] as $event => $state) { |
||
340 | $javascript .= "$(document).bind('ajax$event', function() {window.__behatAjax = $state;});"; |
||
341 | } |
||
342 | |||
343 | $this->executeJs($javascript); |
||
344 | } |
||
345 | |||
346 | /** |
||
347 | * IMPORTANT! The "BeforeStep" hook should not be tagged, because steps has no tags! |
||
348 | * |
||
349 | * @param Scope\StepScope|Scope\BeforeStepScope $scope |
||
350 | * Scope of the processing step. |
||
351 | * |
||
352 | * @BeforeStep |
||
353 | */ |
||
354 | public function beforeStep(Scope\StepScope $scope) |
||
0 ignored issues
–
show
beforeStep uses the super-global variable $_GET 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);
}
}
![]() |
|||
355 | { |
||
356 | self::$pageUrl = $this->getCurrentUrl(); |
||
357 | // To allow Drupal use its internal, web-based functionality, such as "arg()" or "current_path()" etc. |
||
358 | $_GET['q'] = ltrim(parse_url(static::$pageUrl)['path'], '/'); |
||
359 | drupal_path_initialize(); |
||
360 | } |
||
361 | |||
362 | /** |
||
363 | * IMPORTANT! The "AfterStep" hook should not be tagged, because steps has no tags! |
||
364 | * |
||
365 | * @param Scope\StepScope|Scope\AfterStepScope $scope |
||
366 | * Scope of the processing step. |
||
367 | * |
||
368 | * @AfterStep |
||
369 | */ |
||
370 | public function afterStep(Scope\StepScope $scope) |
||
371 | { |
||
372 | // If "mainWindow" variable is not empty that means that additional window has been opened. |
||
373 | // Then, if number of opened windows equals to one, we need to switch back to original window, |
||
374 | // otherwise an error will occur: "Window not found. The browser window may have been closed". |
||
375 | // This happens due to auto closing window by JavaScript (CKFinder does this after choosing a file). |
||
376 | if (!empty($this->mainWindow) && count($this->getWindowNames()) == 1) { |
||
377 | $this->iSwitchToWindow(); |
||
378 | } |
||
379 | |||
380 | if (self::hasTag('javascript') && self::isStepImpliesJsEvent($scope)) { |
||
381 | $this->waitAjaxAndAnimations(); |
||
382 | } |
||
383 | } |
||
384 | } |
||
385 |
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: