Problem/Motivation

I have installed the module simple access log to log any request made to drupal. This module intercepts the event kernel.request via event subscriber to extract request information to save it on the database.

However when a batch process is launched, the request is broken because this event subscriber tries to get the title of the batch page calling \Drupal::service('title_resolver')->getTitle($request, $route), which on turn calls Drupal\system\Controller\BatchController::batchPageTitle(), and this method try to call the function _batch_current_set, but the file core/includes/batch.inc that contains this function is not loaded yet, causing the next error:

The website encountered an unexpected error. Please try again later.
Error: Call to undefined function Drupal\system\Controller\_batch_current_set() in Drupal\system\Controller\BatchController->batchPageTitle() (line 92 of core/modules/system/src/Controller/BatchController.php).
Drupal\system\Controller\BatchController->batchPageTitle()
call_user_func_array(Array, Array) (Line: 58)
Drupal\Core\Controller\TitleResolver->getTitle(Object, Object) (Line: 82)
Drupal\simple_access_log\Controller\SimpleAccessLog->logValues() (Line: 25)
Drupal\simple_access_log\EventSubscriber\SimpleAccessLogSubscriber->executeLogFunction(Object, ‘kernel.request’, Object)
call_user_func(Array, Object, ‘kernel.request’, Object) (Line: 111)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(‘kernel.request’, Object) (Line: 127)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 68)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 67)
Drupal\simple_oauth\HttpMiddleware\BasicAuthSwap->handle(Object, 1, 1) (Line: 57)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 106)
Drupal\page_cache\StackMiddleware\PageCache->pass(Object, 1, 1) (Line: 85)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 49)
Asm89\Stack\Cors->handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 52)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 693)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)

Everything seems to indicate that it is assumed that this file is already loaded when before executing this method.

Steps to reproduce

To reproduce this bug with Drupal core alone, enable the block module, place the breadcrumb block for your theme, and visit the /batch/anything path, where anything is any string.

Proposed resolution

Include the file required before calling _batch_current_set() as the method batchPage does, or check if the function exists before calling it.

Comments

dogamboar created an issue. See original summary.

dogamboar’s picture

StatusFileSize
new595 bytes

I add the patch with the proposed solution to include the file "/core/includes/batch.inc" in the batchPageTitle method as it is done in the batchPage method.

dogamboar’s picture

Sorry, after more testing, the last patch I sent does not work in the batch workflow. Apparently the order in which certain methods are called within the batch.in file alter their result after when the batch page is processed. Needs work.

dogamboar’s picture

StatusFileSize
new732 bytes

I include a new patch with another approach to handle the event error in which the batch file has not yet been loaded.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.9 was released on November 6 and is the final full bugfix release for the Drupal 8.7.x series. Drupal 8.7.x will not receive any further development aside from security fixes. Sites should prepare to update to 8.8.0 on December 4, 2019. (Drupal 8.8.0-beta1 is available for testing.)

Bug reports should be targeted against the 8.8.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.9.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.7 was released on June 3, 2020 and is the final full bugfix release for the Drupal 8.8.x series. Drupal 8.8.x will not receive any further development aside from security fixes. Sites should prepare to update to Drupal 8.9.0 or Drupal 9.0.0 for ongoing support.

Bug reports should be targeted against the 8.9.x-dev branch from now on, and new development or disruptive changes should be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

demonde’s picture

Same error, patch in #4 helps.

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

Drupal 8 is end-of-life as of November 17, 2021. There will not be further changes made to Drupal 8. Bugfixes are now made to the 9.3.x and higher branches only. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.2.x-dev » 9.3.x-dev
mfb’s picture

Issue summary: View changes
Status: Active » Needs review

Adding steps to reproduce the bug on Drupal core without any other modules installed.

mfb’s picture

Issue summary: View changes
StatusFileSize
new1.07 KB

Here's a test-only patch (should fail). To reproduce the bug, I had to add block module and enable the breadcrumb block. Could be done in a separate test class if preferred.

Status: Needs review » Needs work
mfb’s picture

Status: Needs work » Needs review
StatusFileSize
new947 bytes
new1.78 KB

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

Drupal 9.3.15 was released on June 1st, 2022 and is the final full bugfix release for the Drupal 9.3.x series. Drupal 9.3.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.4.x-dev branch from now on, and new development or disruptive changes should 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.4.x-dev » 9.5.x-dev

Drupal 9.4.9 was released on December 7, 2022 and is the final full bugfix release for the Drupal 9.4.x series. Drupal 9.4.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.5.x-dev branch from now on, and new development or disruptive changes should 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.

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: +Needs Review Queue Initiative

This issue is being reviewed by the kind folks in Slack, #needs-review-queue-initiative. We are working to keep the size of Needs Review queue [2700+ issues] to around 400 (1 month or less), following Review a patch or merge request as a guide.

Re-triggering for 10.1 and 9.5.x tests but don't expect failures

Followed the steps in the IS and can confirm the issue
Patch does fix the issue

alexpott’s picture

Status: Reviewed & tested by the community » Needs work

The test being added here is kinda of incorrect. Batches are never requested at /batch/something - they are requested at /batch?id=12

Also the fix for the breadcrumbs is different than for the bug reported in the issue summary. We're never really going to have a breadcrumb for /batch so we should be exlcuding it like we do /user. I.e in \Drupal\system\PathBasedBreadcrumbBuilder::build() we could do something like:

    // /user is just a redirect, so skip it.
    // @todo Find a better way to deal with /user.
    $exclude['/user'] = TRUE;
    $exclude['/batch'] = TRUE;

But I think this issue points to a need to add a route option to disable the breadcrumb - that way contrib could make a similar fix. This probably should be split out into a separate fix.

So thinking about the original issue. The problem is you're not going to log the correct title with the current fix because in order to do that you need to load the batch. I think we need to do something like:

  • Load core/includes/batch.inc in the constructor
  • Change the batchPageTitle() to get batch and load it from storage if necessary.

I.e

  public function batchPageTitle(Request $request) {
    $batch = &batch_get();

    if (!($request_id = $request->query->get('id'))) {
      return '';
    }

    // Retrieve the current state of the batch.
    if (!$batch) {
      $batch = \Drupal::service('batch.storage')->load($request_id);
    }

    if (!$batch) {
      return '';
    }

    $current_set = _batch_current_set();
    return !empty($current_set['title']) ? $current_set['title'] : '';
  }
mfb’s picture

Version: 9.5.x-dev » 10.1.x-dev
Status: Needs work » Needs review
StatusFileSize
new1.44 KB
new2.52 KB

Ok let's see if that passes tests.

alexpott’s picture

Status: Needs review » Needs work
+++ b/core/modules/system/tests/src/Functional/Batch/PageTest.php
@@ -67,6 +67,11 @@ public function testBatchProgressPageTitle() {
+    // Test that the batch system emits a 404 page when appropriate.
+    $this->drupalPlaceBlock('system_breadcrumb_block');
+    $this->drupalGet('batch/foobar');
+    $this->assertSession()->pageTextContains('Page not found');

We should change this test. We should be getting the title for route like /batch?id=1234124123 ... we'll have to create a request and route match object to pass to the title resolver - see how \Drupal\system\PathBasedBreadcrumbBuilder::build() does it.

mfb’s picture

Status: Needs work » Needs review
StatusFileSize
new1.58 KB
new2.39 KB

is it enough to just call the broken method in a test, like this?

If not, then yeah the test could call \Drupal::service('title_resolver')->getTitle($request, $route) as per the original issue summary.

For the record, I actually hit this bug as per the test I originally wrote - via an automated pentesting bot hitting random bogus paths including batch/[some string] - which tried and failed to build the breadcrumb.

alexpott’s picture

@mfb so we need to fix the path breadcrumb builder issue in a separate related issue because even with this fix the breadcrumbs on batch/some_nonsense is incorrect. I think the test in #20 is okay - I think what'd be really nice is a unit test that tests getting the batch from storage (using a mock). Also we need to inject the batch storage service into the class.

alexpott’s picture

Status: Needs review » Needs work

THis part of #21

I think what'd be really nice is a unit test that tests getting the batch from storage (using a mock). Also we need to inject the batch storage service into the class.

Is something we should do on this issue. We also need to open another issue about excluding paths from the breadcrumb builder.

mfb’s picture

Status: Needs work » Needs review
StatusFileSize
new3.15 KB
new3.37 KB

Did a bit more work on this - now injecting the service, and changed test from kernel test to unit test. But for the latter to work, I had to require form.inc, otherwise function batch_get() does not exist.

alexpott’s picture

Status: Needs review » Needs work
+++ b/core/modules/system/src/Controller/BatchController.php
--- /dev/null
+++ b/core/modules/system/tests/src/Unit/Batch/BatchControllerTest.php

+++ b/core/modules/system/tests/src/Unit/Batch/BatchControllerTest.php
@@ -0,0 +1,30 @@
+  /**
+   * Tests title callback.
+   *
+   * @covers ::batchPageTitle
+   */
+  public function testBatchPageTitle() {
+    $controller = new BatchController($this->root, $this->createMock(BatchStorageInterface::class));
+    require_once $this->root . '/core/includes/form.inc';
+    $this->assertSame('', $controller->batchPageTitle(new Request()));
+  }

Nice. So we should also test that a title is returned when batch_get() returns something. TO do this we can mock the load method on the batch storage.

Also as we're loading code and changing the state of the system let's add the @runTestsInSeparateProcesses annotation to the test class.

alexpott’s picture

mfb’s picture

Status: Needs work » Needs review
StatusFileSize
new1.2 KB
new3.65 KB

This seems to work as a minimal mock batch array

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community

Seems #25 have been addressed in
+ $this->assertSame('foobar', $controller->batchPageTitle(new Request(['id' => 1234])));

This looks good. Adding 3344200 to my list also.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/modules/system/tests/src/Unit/Batch/BatchControllerTest.php
@@ -0,0 +1,34 @@
+  /**
+   * Tests title callback.
+   *
+   * @covers ::batchPageTitle
+   */
+  public function testBatchPageTitle() {
+    $batch_storage = $this->createMock(BatchStorageInterface::class);
+    $controller = new BatchController($this->root, $batch_storage);
+    require_once $this->root . '/core/includes/form.inc';
+    $this->assertSame('', $controller->batchPageTitle(new Request()));
+    $batch = ['sets' => [['title' => 'foobar']], 'current_set' => 0];
+    $batch_storage->method('load')->willReturn($batch);
+    $this->assertSame('foobar', $controller->batchPageTitle(new Request(['id' => 1234])));
+  }

This test should cover both situations - where the batch exists and where it does not.

mfb’s picture

Status: Needs work » Needs review
StatusFileSize
new1.1 KB
new3.98 KB

Added another test case in there too - batch could be returned by &batch_get()

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community

Going to try it again. If it's wrong I do apologize!

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Given the constructor changes I think we can only commit this fix to 10.1.x - I considered asking for the deprecation dance and allow a NULL for batch storage but there's nothing in contrib that extends this class so I think we fine.

Committed cdd8b0522b and pushed to 10.1.x. Thanks!

diff --git a/core/modules/system/tests/src/Unit/Batch/BatchControllerTest.php b/core/modules/system/tests/src/Unit/Batch/BatchControllerTest.php
index 172e2feae9..e68649fd37 100644
--- a/core/modules/system/tests/src/Unit/Batch/BatchControllerTest.php
+++ b/core/modules/system/tests/src/Unit/Batch/BatchControllerTest.php
@@ -32,7 +32,9 @@ public function testBatchPageTitle() {
     $this->assertSame('', $controller->batchPageTitle(new Request(['id' => 1234])));
     $this->assertSame('foobar', $controller->batchPageTitle(new Request(['id' => 1234])));
     // Test batch returned by &batch_get() call.
-    $this->assertSame('foobar', $controller->batchPageTitle(new Request(['id' => 1234])));
+    $batch = &batch_get();
+    $batch['sets']['0']['title'] = 'Updated title';
+    $this->assertSame('Updated title', $controller->batchPageTitle(new Request(['id' => 1234])));
   }
 
 }

Slightly improved the test on commit.

  • alexpott committed cdd8b052 on 10.1.x
    Issue #3083106 by mfb, dogamboar, alexpott, smustgrave: Method...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.