Problem/Motivation

The warning occurs during every use of the pager.parameters service and when the GET query is not page=1 but something array-like, e.g. page[]=1.

This sort of query can easily happen due to bugs in javascript or triggered by automated scanners.

Steps to reproduce

Visit a page with a pager, like the recipe page in umami, and add the query argument page[]=1

This will trigger the following exception in the watchdog:

Symfony\Component\HttpKernel\Exception\BadRequestHttpException: Input value "page" contains a non-scalar value. in Symfony\Component\HttpKernel\HttpKernel->handle() (line 83 of /app/vendor/symfony/http-kernel/HttpKernel.php).

This is triggered by a previous exception that has a stack trace/signature similar to this from the original report:

Warning: explode() expects parameter 2 to be string, array given in Drupal\Core\Pager\PagerParameters->getPagerQuery() (line 58 of /home/catuser/app/public/docroot/core/lib/Drupal/Core/Pager/PagerParameters.php)
#0 /home/catuser/app/public/docroot/core/includes/bootstrap.inc(600): _drupal_error_handler_real(2, 'explode() expec...', '/home/catuser/a...', 58, Array)
#1 [internal function]: _drupal_error_handler(2, 'explode() expec...', '/home/catuser/a...', 58, Array)
#2 /home/catuser/app/public/docroot/core/lib/Drupal/Core/Pager/PagerParameters.php(58): explode(',', Array)
#3 /home/catuser/app/public/docroot/core/lib/Drupal/Core/Pager/PagerParameters.php(49): Drupal\Core\Pager\PagerParameters->getPagerQuery()
#4 /home/catuser/app/public/docroot/core/lib/Drupal/Core/Pager/PagerManager.php(49): Drupal\Core\Pager\PagerParameters->findPage(0)
#5 /home/catuser/app/public/docroot/core/modules/views/src/Plugin/views/pager/SqlBase.php(341): Drupal\Core\Pager\PagerManager->createPager('1470', 25, 0)
#6 /home/catuser/app/public/docroot/core/modules/views/src/ViewExecutable.php(1422): Drupal\views\Plugin\views\pager\SqlBase->updatePageInfo()
#7 /home/catuser/app/public/docroot/core/modules/views/src/ViewExecutable.php(1454): Drupal\views\ViewExecutable->execute(NULL)
#8 /home/catuser/app/public/docroot/core/modules/rest/src/Plugin/views/display/RestExport.php(441): Drupal\views\ViewExecutable->render()
#9 /home/catuser/app/public/docroot/core/modules/views/src/ViewExecutable.php(1630): Drupal\rest\Plugin\views\display\RestExport->execute()
#10 /home/catuser/app/public/docroot/core/modules/views/src/Element/View.php(77): Drupal\views\ViewExecutable->executeDisplay('json_seo_dashbo...', Array)
#11 [internal function]: Drupal\views\Element\View::preRenderViewElement(Array)
#12 /home/catuser/app/public/docroot/core/lib/Drupal/Core/Security/DoTrustedCallbackTrait.php(100): call_user_func_array(Array, Array)
#13 /home/catuser/app/public/docroot/core/lib/Drupal/Core/Render/Renderer.php(781): Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_ren...', 'silenced_deprec...', 'Drupal\\Core\\Ren...')
#14 /home/catuser/app/public/docroot/core/lib/Drupal/Core/Render/Renderer.php(372): Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array)
#15 /home/catuser/app/public/docroot/core/lib/Drupal/Core/Render/Renderer.php(200): Drupal\Core\Render\Renderer->doRender(Array, true)
#16 /home/catuser/app/public/docroot/core/lib/Drupal/Core/Render/Renderer.php(144): Drupal\Core\Render\Renderer->render(Array, true)
#17 /home/catuser/app/public/docroot/core/lib/Drupal/Core/Render/Renderer.php(573): Drupal\Core\Render\Renderer->Drupal\Core\Render\{closure}()
#18 /home/catuser/app/public/docroot/core/lib/Drupal/Core/Render/Renderer.php(145): Drupal\Core\Render\Renderer->executeInRenderContext(Object(Drupal\Core\Render\RenderContext), Object(Closure))
#19 /home/catuser/app/public/docroot/core/modules/rest/src/Plugin/views/display/RestExport.php(424): Drupal\Core\Render\Renderer->renderRoot(Array)
#20 /home/catuser/app/public/docroot/core/modules/views/src/Routing/ViewPageController.php(54): Drupal\rest\Plugin\views\display\RestExport::buildResponse('url_list', 'json_seo_dashbo...', Array)
#21 [internal function]: Drupal\views\Routing\ViewPageController->handle('url_list', 'json_seo_dashbo...', Object(Drupal\Core\Routing\RouteMatch))
#22 /home/catuser/app/public/docroot/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(123): call_user_func_array(Array, Array)
#23 /home/catuser/app/public/docroot/core/lib/Drupal/Core/Render/Renderer.php(573): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}()
#24 /home/catuser/app/public/docroot/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(124): Drupal\Core\Render\Renderer->executeInRenderContext(Object(Drupal\Core\Render\RenderContext), Object(Closure))
#25 /home/catuser/app/public/docroot/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(97): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext(Array, Array)
#26 /home/catuser/app/public/vendor/symfony/http-kernel/HttpKernel.php(151): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}()
#27 /home/catuser/app/public/vendor/symfony/http-kernel/HttpKernel.php(68): Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object(Symfony\Component\HttpFoundation\Request), 1)
#28 /home/catuser/app/public/docroot/core/lib/Drupal/Core/StackMiddleware/Session.php(57): Symfony\Component\HttpKernel\HttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#29 /home/catuser/app/public/docroot/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(47): Drupal\Core\StackMiddleware\Session->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#30 /home/catuser/app/public/docroot/core/modules/page_cache/src/StackMiddleware/PageCache.php(106): Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#31 /home/catuser/app/public/docroot/core/modules/page_cache/src/StackMiddleware/PageCache.php(85): Drupal\page_cache\StackMiddleware\PageCache->pass(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#32 /home/catuser/app/public/vendor/asm89/stack-cors/src/Asm89/Stack/Cors.php(49): Drupal\page_cache\StackMiddleware\PageCache->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#33 /home/catuser/app/public/docroot/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(47): Asm89\Stack\Cors->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#34 /home/catuser/app/public/docroot/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(52): Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#35 /home/catuser/app/public/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#36 /home/catuser/app/public/docroot/core/lib/Drupal/Core/DrupalKernel.php(708): Stack\StackedHttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#37 /home/catuser/app/public/docroot/index.php(19): Drupal\Core\DrupalKernel->handle(Object(Symfony\Component\HttpFoundation\Request))
#38 {main}

Proposed resolution

Make the code in the pager service more defensive to query values outside the expected structure.

Remaining tasks

n/a

User interface changes

n/a

API changes

n/a

Data model changes

n/a

Release notes snippet

n/a

Issue fork drupal-3143617

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

BR0kEN created an issue. See original summary.

br0ken’s picture

Status: Active » Needs review
StatusFileSize
new612 bytes
br0ken’s picture

Issue tags: -Needs tests
StatusFileSize
new1.13 KB

Proof.

br0ken’s picture

StatusFileSize
new1.73 KB
daffie’s picture

Status: Needs review » Needs work

@BROkEN: Thank you for this bug report, fix and the added tests.

  1. +++ b/core/lib/Drupal/Core/Pager/PagerParameters.php
    @@ -63,8 +63,8 @@ public function getPagerQuery() {
    +    if ($request && $page = $request->query->get('page')) {
    

    I do not think that we should put the part $page = $request->query->get('page') in the if-statement. Put it on its own line just before the return-statement.

  2. +++ b/core/modules/system/tests/src/Functional/Pager/PagerTest.php
    @@ -52,6 +52,20 @@ protected function setUp(): void {
    +    $this->drupalGet('admin/reports/dblog', ['query' => ['page[offset]' => [1]]]);
    

    Can we change the line to: $this->drupalGet('admin/reports/dblog', ['query' => ['page' => ['offset' => 1]]]);

pavnish’s picture

Assigned: br0ken » pavnish
pavnish’s picture

@BROkEN i will be very happy if i can do something in this issue so i assign this to my self.
Thanks
Pavnish

pavnish’s picture

Assigned: pavnish » Unassigned
Status: Needs work » Needs review
StatusFileSize
new1.69 KB
new1.2 KB

@daffie I have changed the code as suggest by you in #5.
It's my honour if you can review this patch.
Thanks
Pavnish

br0ken’s picture

@daffie Yeah, that also working variant but in terms of efficiency it's a bit worse since is_string will be called on null all the times the page is not in the GET query.

daffie’s picture

StatusFileSize
new1.12 KB

Uploading patch from comment #8 with only the added test. This patch should fail.

Status: Needs review » Needs work

The last submitted patch, 10: 3143617-8-test-only-should-fail.patch, failed testing. View results

pavnish’s picture

Status: Needs work » Needs review
pavnish’s picture

StatusFileSize
new1.12 KB
new1.69 KB
new1.2 KB

@daffie
Upload all the patch #8 with fix and test only patch

daffie’s picture

Status: Needs review » Reviewed & tested by the community

The patch contains a bugfix and testing for the bugfix.
All code changes look good to me.
For me it is RTBC.

@BROkEN Likes to see another bugfix for this patch:

--- a/core/lib/Drupal/Core/Pager/PagerParameters.php
+++ b/core/lib/Drupal/Core/Pager/PagerParameters.php
@@ -63,8 +63,8 @@ public function getPagerQuery() {
    */
   public function getPagerParameter() {
     $request = $this->requestStack->getCurrentRequest();
-    if ($request) {
-      return $request->query->get('page', '');
+    if ($request && $page = $request->query->get('page')) {
+      return is_string($page) ? $page : '';
     }
     return '';
   }

I leave it to the committer to choose which bugfix to commit.

The last submitted patch, 13: 3143617-8-test-only-should-fail.patch, failed testing. View results

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 13: 3143617-8.patch, failed testing. View results

durgeshs’s picture

Assigned: Unassigned » durgeshs
shaktik’s picture

Status: Needs work » Needs review
StatusFileSize
new1.73 KB
new503 bytes

Hi @daffie,

made changes please review.

br0ken’s picture

Assigned: durgeshs » Unassigned

I cannot understand why this easy issue produced such a lot of reinvented solution of what I initially posted.

#5.1 The reason to not do this is because it's not a common syntax in Drupal core, isn't it?
#5.2 Of course we can. We can even reformat it to multiline.

I am super frustrated that the issue like this becomes blocked due to the far-fetched problems.

@pavnish, @durgeshs, @shaktik please stop assigning this issue on yourselves. I'm able to complete it even if each new reviewer will say that we should put brackets as they want.

br0ken’s picture

StatusFileSize
new2.09 KB

No interdiff since the patch is small and easy to read.

br0ken’s picture

#5.1 was actually correct not in terms of code formatting, but in terms of functionality where '0' might have been skipped.

shaktik’s picture

Hi @BR0kEN,

Please assign the issue to yourself or add a comment that you are working on this and please try to provide a solution within 1 or 2 days. If it will take more time to complete this please add a comment or draft patch otherwise community members will pick this which is very normal. Please check the guidelines

chi’s picture

+ return is_string($page) ? $page : '';

The $request->query object offers a few helper methods to deal with this. Like getDigits() and getInt(). We could use them.

jungle’s picture

Status: Needs review » Reviewed & tested by the community
StatusFileSize
new2.09 KB
new653 bytes
  1. Re #23, $request->query->getDigits(), $request->query->getInt() and other variations are inapplicable here, as page is a comma-delimited string of pager element values. For example: ?page=5,2, ?page=2.1,6.999,,,5.

  2. +++ b/core/modules/system/tests/src/Functional/Pager/PagerTest.php
    @@ -52,6 +52,26 @@ protected function setUp(): void {
    +   * Test that overlapping `page` GET query does not produce errors.
    

    Fixing one nitpick: Test that -> Tests that

  3. See #10 for the TEST-ONLY patch
  4. Setting to RTBC

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 24: 3143617-24.patch, failed testing. View results

jungle’s picture

Status: Needs work » Reviewed & tested by the community

Re-queued

chi’s picture

@jungle good point.
For the record, retrieving non-string values with $request->query->get() method was deprecated in Symfony 5 and will throw an exception in Symfony 6.
https://github.com/symfony/symfony/pull/34363

neclimdul’s picture

Status: Reviewed & tested by the community » Needs review
StatusFileSize
new2.57 KB
new1.06 KB

Yeah, please don't assign core issues. Those guidelines specifically says to avoid it and for something like this it generally just adds noise to the discussion. If you're going to propose a different approach just provide a patch without 3 issue updates.

That Symfony changes is pretty frustrating. I guess there's not a great solution to the ambiguity and dealing with "user" input but throwing an exception... seems like the method is becoming unusable. Something to consider for 10,

I was going to +1 the RTBC because in a narrow scope this obviously addresses the issue. But reading the patch one last time one questions lead to another, then a full on review, then digging in more I ended up rewriting the patch trying to sort out the answers. I don't think this fully addresses the problems with these methods.

Here's the review:
1) Why is this a functional test. It should just be a unit test, The extra complexity, boilerplating and slowness provides no benefit.
2) Why the link to the issue in the test comment? Isn't the git log good enough?
3) Why are we only testing one value? This doesn't fully flesh out the possible values affected by this change.
4) Why do we care if its a string? Could it be numeric? Parameter bags definitely allow that. We only care that its not an array right so is_scalar and a string cast seems more resilient.
5) Scope creapish, why is getPagerQuery using empty? The current patch documents how this will fail but only fixes it in getPagerParameter. findPage fixes handles the edge case and since its main entry point in core so functional tests aren't catching this method being broken.
6) More scope creapish, explode() returns an array of _strings_ but the interface requires returning int[]. Fixing it to match the interface _could_ break contrib or sites hacking strings or floats through a pager value. However those cases would be broken in findPage which does the cast already so it was probably already effectively broken. They can still access the request so I think we should fix it. Also not fixing it makes testing inconsistent.

So here's a patch
1) Converted to unit test.
2) Removed in functional test removal.
3) Added edge case assertions around NULL, 0, '0' and []. Tested these for getQuery, getQueryParameter, and findPage to assert they work consistently.
4) fixed
5) fixed
6) fixed

chi’s picture

1) Converted to unit test.
2) Removed in functional test removal.

@neclimdul Neither patch #24 nor #28 adds any tests. Have I missed something?

Status: Needs review » Needs work

The last submitted patch, 28: 3143617-28.patch, failed testing. View results

neclimdul’s picture

StatusFileSize
new5.36 KB
new3.85 KB

Insomnia review/patches... am i right? I diffed off the wrong branch, sorry.

The failure looks to have been random. Here's the patch I _meant_ to upload in 28.

neclimdul’s picture

Status: Needs work » Needs review

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new2.12 KB

The Needs Review Queue Bot tested this issue. It either no longer applies to Drupal core, or fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

Apart from a re-roll or rebase, this issue may need more work to address feedback in the issue or MR comments. To progress an issue, incorporate this feedback as part of the process of updating the issue. This helps other contributors to know what is outstanding.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

douggreen’s picture

Title: Warning: explode() expects parameter 2 to be string, array given in Drupal\Core\Pager\PagerParameters->getPagerQuery() » Protect pager against [] for the page
StatusFileSize
new3.96 KB

Attached patch fixes issue raised by the bot. It also cleans up the comment a tad.

* The both complained about the isset() which was introduced in #28, I've replace it with a simple condition because IMO neither isset() nor !empty is necessary here.

douggreen’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 40: 3143617-40.patch, failed testing. View results

neclimdul’s picture

Its been 3 years so its hard to remember but pretty sure the isset was the to preserve the behavior of the empty check as demonstrated by the failing tests. What was the problem with it?

robertom’s picture

StatusFileSize
new3.78 KB
new613 bytes

Attached patch fixes issues

Symfony\Component\HttpFoundation\Exception\BadRequestException: Input value "page" contains a non-scalar value.

raised by the test bot

robertom’s picture

Status: Needs work » Needs review
needs-review-queue-bot’s picture

Status: Needs review » Needs work

The Needs Review Queue Bot tested this issue.

While you are making the above changes, we recommend that you convert this patch to a merge request. Merge requests are preferred over patches. Be sure to hide the old patch files as well. (Converting an issue to a merge request without other contributions to the issue will not receive credit.)

robertom’s picture

Status: Needs work » Needs review
StatusFileSize
new3.79 KB
new852 bytes

Attached a patch without the use of ->all()['page'] for avoid warning like

Undefined array key "page"

smustgrave’s picture

Status: Needs review » Needs work
Issue tags: +Needs issue summary update

Issue summary should be updated to the standard issue template, even if sections don't apply leaving them with NA is easier for reviews/committers.

neclimdul’s picture

Issue summary: View changes
Status: Needs work » Needs review

Updated.

Turns out its been long enough a few things have changed. Specifically, current versions of PHP throw an exception instead of a warning which makes it a bit harder to see. Specifically, real exception gets gobbled up by a more generic exception and Drupal's exception logging in watchdog doesn't show previous exceptions.

Since the actual exception is hidden and its hard to validate the root cause without hacking the exception handler or using a debugger, I've left the original trace in place. The fix is the same and the trace captures the essence of the problem.

smustgrave’s picture

Status: Needs review » Needs work
Issue tags: -Needs issue summary update +Needs Review Queue Initiative

Ran patch #47 and it has a test failure that seems legit.

Recommended to convert to MRs

neclimdul’s picture

"pretty sure the isset was the to preserve the behavior of the empty check as demonstrated by the failing tests." 🤷

Added it back because it was there to defensively handle a null value from the getPagerParameter method. Since isset is a language level feature there's no real cost to being defensive like this.

I'm already tired of the 500 remotes and all the commit juggling. I'm going to really miss the simple patch workflow. But ok, its in a MR now.

neclimdul’s picture

Turns around it was a little more complicated. I went ahead and took the opportunity to update the tests and make things a little more clear.

neclimdul’s picture

Status: Needs work » Needs review
smustgrave’s picture

Status: Needs review » Reviewed & tested by the community

Thanks looks good!

quietone’s picture

Status: Reviewed & tested by the community » Needs work

I'm triaging RTBC issues. I read the IS, the comments and the MR.

Thanks for updating the Issue Summary, that is always helpful! The review in #28 by @neclimdul was also useful as it explained what they were thinking and checking. That provided me with insight into understanding the issue and when else I may need to check.

The changes to getPagerQuery and getPagerParameter are changing the behavior, making it conform to the interface. So, it there any ramification for contrib/custom?

I left suggestions in the MR for coding standards and one asking for documentation.

I hide all the patches.

smustgrave’s picture

Status: Needs work » Needs review

This issue got reported as a security issue for better_exposed_filter, but was later made public. #3466314: Inclusion of parameter exhausts site memory with exposed filter block

Addressed the feedback

feyp’s picture

Status: Needs review » Needs work
Issue tags: +Security improvements

Thanks for working on this.

Regarding @quietone's outstanding question in #56:

The changes to getPagerQuery and getPagerParameter are changing the behavior, making it conform to the interface. So, it there any ramification for contrib/custom?

I think this question has already been answered by @neclimdul in #28 6):

Fixing it to match the interface _could_ break contrib or sites hacking strings or floats through a pager value. However those cases would be broken in findPage which does the cast already so it was probably already effectively broken.

The code changes look good. I added one suggestion and a few comments inline, mostly concerning comments in the test.

The MR introduces new test coverage for the pager parameter. The pipeline is green. The tests fail locally as expected without the fix. I'll run the test only job just before I'm otherwise ready to RTBC, so not quite yet. With the MR applied, I can't reproduce the original issue. I also did some manual testing both with the original and modified (invalid) pager parameters: The administrative content view (using AJAX), a regular view (without AJAX), multiple views with different pager ids on the same page, the administrative URL Alias overview page (no view, probably entity query pager?). I noticed an issue with 3 views on the same page, but re-testing this without the MR applied, the issue was already present before, so no regression.

Tagging security improvement (for contrib) based on #57.

So overall this looks good and almost ready. Ready to RTBC once my very minor MR comments have been addressed.

smustgrave’s picture

Status: Needs work » Needs review

Did not mean to take so long but gave it a shot. May need to expand on the docs some though.

feyp’s picture

Status: Needs review » Reviewed & tested by the community

Thanks Stephen, the changes look good to me.

Looks like the CI runners are a little busy right now, the repeated failures due to GitHub timeouts and no test nodes available gave me sufficient time to also retest this locally. Almost like Drupal CI, oh the good old times, where I could work on another issue while I waited for the tests to complete... Let's call it the DrupalCon review bonus :)

Test only job fails as expected:

Configuration: /builds/issue/drupal-3143617/core/phpunit.xml.dist
..EE.......EE.FF....FEE..F...                                     29 / 29 (100%)
Time: 00:00.034, Memory: 8.00 MB
There were 6 errors:
1) Drupal\Tests\Core\Pager\PagerParametersTest::testFindPage with data set "invalid empty page array" ([], '', [])
Symfony\Component\HttpFoundation\Exception\BadRequestException: Input value "page" contains a non-scalar value.
/builds/issue/drupal-3143617/vendor/symfony/http-foundation/InputBag.php:38
/builds/issue/drupal-3143617/core/lib/Drupal/Core/Pager/PagerParameters.php:67
/builds/issue/drupal-3143617/core/lib/Drupal/Core/Pager/PagerParameters.php:57
/builds/issue/drupal-3143617/core/lib/Drupal/Core/Pager/PagerParameters.php:49
/builds/issue/drupal-3143617/core/tests/Drupal/Tests/Core/Pager/PagerParametersTest.php:47
2) Drupal\Tests\Core\Pager\PagerParametersTest::testFindPage with data set "invalid populated array" ([1, 2, 3], '', [])
Symfony\Component\HttpFoundation\Exception\BadRequestException: Input value "page" contains a non-scalar value.
/builds/issue/drupal-3143617/vendor/symfony/http-foundation/InputBag.php:38
/builds/issue/drupal-3143617/core/lib/Drupal/Core/Pager/PagerParameters.php:67
/builds/issue/drupal-3143617/core/lib/Drupal/Core/Pager/PagerParameters.php:57
/builds/issue/drupal-3143617/core/lib/Drupal/Core/Pager/PagerParameters.php:49
/builds/issue/drupal-3143617/core/tests/Drupal/Tests/Core/Pager/PagerParametersTest.php:47
3) Drupal\Tests\Core\Pager\PagerParametersTest::testGetPagerQuery with data set "invalid empty page array" ([], '', [])
Symfony\Component\HttpFoundation\Exception\BadRequestException: Input value "page" contains a non-scalar value.
/builds/issue/drupal-3143617/vendor/symfony/http-foundation/InputBag.php:38
/builds/issue/drupal-3143617/core/lib/Drupal/Core/Pager/PagerParameters.php:67
/builds/issue/drupal-3143617/core/lib/Drupal/Core/Pager/PagerParameters.php:57
/builds/issue/drupal-3143617/core/tests/Drupal/Tests/Core/Pager/PagerParametersTest.php:60
4) Drupal\Tests\Core\Pager\PagerParametersTest::testGetPagerQuery with data set "invalid populated array" ([1, 2, 3], '', [])
Symfony\Component\HttpFoundation\Exception\BadRequestException: Input value "page" contains a non-scalar value.
/builds/issue/drupal-3143617/vendor/symfony/http-foundation/InputBag.php:38
/builds/issue/drupal-3143617/core/lib/Drupal/Core/Pager/PagerParameters.php:67
/builds/issue/drupal-3143617/core/lib/Drupal/Core/Pager/PagerParameters.php:57
/builds/issue/drupal-3143617/core/tests/Drupal/Tests/Core/Pager/PagerParametersTest.php:60
5) Drupal\Tests\Core\Pager\PagerParametersTest::testGetPagerParameter with data set "invalid empty page array" ([], '', [])
Symfony\Component\HttpFoundation\Exception\BadRequestException: Input value "page" contains a non-scalar value.
/builds/issue/drupal-3143617/vendor/symfony/http-foundation/InputBag.php:38
/builds/issue/drupal-3143617/core/lib/Drupal/Core/Pager/PagerParameters.php:67
/builds/issue/drupal-3143617/core/tests/Drupal/Tests/Core/Pager/PagerParametersTest.php:83
6) Drupal\Tests\Core\Pager\PagerParametersTest::testGetPagerParameter with data set "invalid populated array" ([1, 2, 3], '', [])
Symfony\Component\HttpFoundation\Exception\BadRequestException: Input value "page" contains a non-scalar value.
/builds/issue/drupal-3143617/vendor/symfony/http-foundation/InputBag.php:38
/builds/issue/drupal-3143617/core/lib/Drupal/Core/Pager/PagerParameters.php:67
/builds/issue/drupal-3143617/core/tests/Drupal/Tests/Core/Pager/PagerParametersTest.php:83
--
There were 4 failures:
1) Drupal\Tests\Core\Pager\PagerParametersTest::testGetPagerQuery with data set "page 0 as a string" ('0', '0', [0])
Failed asserting that two arrays are equal.
--- Expected
+++ Actual
@@ @@
 Array (
-    0 => 0
 )
/builds/issue/drupal-3143617/core/tests/Drupal/Tests/Core/Pager/PagerParametersTest.php:60
2) Drupal\Tests\Core\Pager\PagerParametersTest::testGetPagerQuery with data set "page 0 as a integer" (0, '0', [0])
Failed asserting that two arrays are equal.
--- Expected
+++ Actual
@@ @@
 Array (
-    0 => 0
 )
/builds/issue/drupal-3143617/core/tests/Drupal/Tests/Core/Pager/PagerParametersTest.php:60
3) Drupal\Tests\Core\Pager\PagerParametersTest::testGetPagerParameter with data set "defensive null page value" (null, '', [])
Failed asserting that null is identical to ''.
/builds/issue/drupal-3143617/core/tests/Drupal/Tests/Core/Pager/PagerParametersTest.php:83
4) Drupal\Tests\Core\Pager\PagerParametersTest::testGetPagerParameter with data set "page 0 as a integer" (0, '0', [0])
Failed asserting that 0 is identical to '0'.
/builds/issue/drupal-3143617/core/tests/Drupal/Tests/Core/Pager/PagerParametersTest.php:83
ERRORS!
Tests: 29, Assertions: 33, Errors: 6, Failures: 4.
Exiting with EXIT_CODE=2

The documentation for the data provider return value could always be a bit more verbose of course, but I think documenting that the keys returned by the data provider correspond to the variable names that are also used in the test methods should be enough for developers looking at this test code in the future to get the idea behind this setup. It should also address @quietone's concern:

For whatever reason, I started reading this from the provider and kept wondering why there were three return values when testGetPagerParameter() is only using one.

So let's give the RTBC queue triage team another shot at this... I think this issue is RTBC 🎉!

quietone’s picture

So let's give the RTBC queue triage team

Who are members of this team? :-)

Thanks for updating the MR and making the improvements to the test documentation. Hopefully, it will help the next person to work on this.
There are no unanswered questions. Leaving at RTBC.

larowlan’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Bug Smash Initiative

Updating issue credits

Left some comments on the MR. I think we should be using the new Symfony API, not inventing our own protection

piotr pakulski’s picture

trying to update the code as suggested in the MR

prudloff made their first commit to this issue’s fork.

prudloff’s picture

Status: Needs work » Needs review

Rebased and applied suggestions.

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community

Resolved the threads and believe all feedback has been addressed.

catch’s picture

Status: Reviewed & tested by the community » Needs review

Looks like there is still an unresolved thread on the MR.

feyp’s picture

Status: Needs review » Reviewed & tested by the community

Looks like there is still an unresolved thread on the MR.

Thanks @catch for looking at this! I think the open thread you're referring to is from an earlier review I did? This thread had been addressed by the time I wrote #60, also confirmed by another review by @quietone in #61. I would have resolved it at the time, but even though I have push access and I am the original author of the thread, I didn't have permission to resolve the thread and I still don't have permission to do so, unfortunately. I know this is kind of confusing, but imo the thread can be considered resolved.

Setting back to RTBC per #66.

longwave made their first commit to this issue’s fork.

longwave’s picture

Status: Reviewed & tested by the community » Needs review

Committed a simplification, but added some comments about additional test cases.

smustgrave’s picture

Status: Needs review » Needs work

NW for the open threads.

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.

piotr pakulski’s picture

Status: Needs work » Closed (outdated)

The fix from this MR is already in Drupal 11 core. It was probably added during the Symfony 7 upgrade.

In the current D11 core, PagerParameters.php already has both changes:

getPagerQuery() now uses array_map('intval', ...) to safely handle the page values.
getPagerParameter() now catches BadRequestException when someone sends page[]=1 instead of page=1.
The only small difference is that the MR uses get('page', '') while core uses get('page') ?? '', but the result is the same.

This MR is 1357 commits behind main and the pipeline is failing. Since the problem is already fixed in D11, I think we can close this MR. If a backport to 10.x is still needed, it would require a new MR against the 10.x branch.

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.