LanguageManager does not check against altered language_switch_links

Created on 25 May 2023, almost 2 years ago

Problem/Motivation

NULL results are being passed to array_filter function.
Issue 3348686 ๐Ÿ› [regression] Inaccessible language switcher links are removed before alternatives can be provided Fixed allows for modules to provide translations for specific links using an implementation of moduleHandler->alter here. The problem is that this occurs after checking for empty results. Certain implementations of the moduleHandler->alter may empty this results array (see issue 3358576 ๐Ÿ› Fatal error "TypeError: array_filter(): Argument #1 ($array) must be of type array, null given." Needs review for language_switcher_extended module). This can result in an empty result array being passed to the array_filter function causing error.

          if (!empty($result)) {
            // Allow modules to provide translations for specific links.
            $this->moduleHandler->alter('language_switch_links', $result, $type, $url);

            $result = array_filter($result, function (array $link): bool {}

Although this issue is caused by the calling of moduleHandler->alter and works assuming all defaults, it should safely handle cases where a NULL value is passed as result by other core or contrib alterations.

Steps to reproduce

  1. Steps to reproduce
  2. Install all of the core Multilingual modules
  3. Add another language
  4. Enable a content type for content translation (including its published field)
  5. Use URL -> Path for the language detection
  6. Place the Language switcher content block
  7. configure a 3rd party module (for example, language switcher extended) to hide block when no translation is expected.
  8. Select Alter the language switcher for untranslated content entities
  9. Select Hide the language switcher link for the untranslated handler
  10. Navigate to a page with no translations

Proposed resolution

Add an additional !empty() check after all modules have made alterations to the link list.

๐Ÿ› Bug report
Status

Active

Version

9.5

Component
Language systemย  โ†’

Last updated about 10 hours ago

  • Maintained by
  • ๐Ÿ‡ฉ๐Ÿ‡ชGermany @sun
Created by

๐Ÿ‡บ๐Ÿ‡ธUnited States thhafner Chicago, IL

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @thhafner
  • ๐Ÿ‡บ๐Ÿ‡ธUnited States thhafner Chicago, IL
  • Status changed to Needs review almost 2 years ago
  • last update almost 2 years ago
    30,334 pass
  • ๐Ÿ‡บ๐Ÿ‡ธUnited States thhafner Chicago, IL
  • Status changed to Needs work almost 2 years ago
  • ๐Ÿ‡บ๐Ÿ‡ธUnited States smustgrave

    As a bug it will need a test case to show the issue.

    Triggering 11.x also.

  • last update almost 2 years ago
    29,397 pass
  • ๐Ÿ‡ต๐Ÿ‡ฑPoland lordzik

    When is this patch going to be commited? This bug is crashing websites updated from Drupal 9.5.7 to 9.5.10!
    Priority should be higher than Normal...

  • ๐Ÿ‡ต๐Ÿ‡ฑPoland lordzik

    I'm changing priority to critical as this bug makes WSOD!

    I'm not changing status to revieved and tested as i'm not a developer although this patch fixed WSOD.

  • ๐Ÿ‡ต๐Ÿ‡ฑPoland lordzik
  • ๐Ÿ‡บ๐Ÿ‡ธUnited States yospyn

    Confirming #2 worked for me on Core 10.2.3. The switcher was working fine on translation-enabled content that has a translation, but not on translation-enabled content that does NOT have a translation.

    The problem also only appeared if you were logged out, not if you were logged in (at least as an admin).

  • ๐Ÿ‡บ๐Ÿ‡ธUnited States devkinetic

    I ran into this bug as well, confirmed that the patch worked for me. I am also using "language switcher extended" module.

  • ๐Ÿ‡จ๐Ÿ‡ญSwitzerland juagarc4

    Patch #2 is working for me on Core 10.3.1.
    I am also using "language switcher extended" module.

  • ๐Ÿ‡ฉ๐Ÿ‡ชGermany szeidler Berlin

    Is this a Core responsibility or just a bug in "Language Switcher Extended" module? I'm happily merging a fix for it into the module, if it's handled wrong there.

  • ๐Ÿ‡ฉ๐Ÿ‡ชGermany jan kellermann

    jan kellermann โ†’ made their first commit to this issueโ€™s fork.

  • Pipeline finished with Failed
    9 months ago
    Total: 160s
    #247268
  • Pipeline finished with Failed
    9 months ago
    Total: 188s
    #247269
  • Merge request !9124Resolve #3362713 "10.3" โ†’ (Open) created by jan kellermann
  • ๐Ÿ‡ฉ๐Ÿ‡ชGermany jan kellermann

    @szeidler Here you returns NULL and this triggers the WPOD. Maybe you could try to return [] and filter out the block if link-array is empty later until this issue is solved.

  • Pipeline finished with Failed
    9 months ago
    Total: 467s
    #247276
  • Status changed to Needs review 9 months ago
  • ๐Ÿ‡ฉ๐Ÿ‡ชGermany jan kellermann

    I just created an issue fork and commitet tests and bugfixes for 10.3 and 11.x and created MRs.

    1st commit in each branch is the PHP-Unit test which fails (status code 500).

    2nd commit in each branch is the fix for this bug (test for type array).

    A 2nd test is added in 1st commit to validate that the switch-block is not shown if $result is NULL after alter()-function.

  • Pipeline finished with Canceled
    9 months ago
    Total: 432s
    #247295
  • Pipeline finished with Failed
    9 months ago
    Total: 166s
    #247303
  • Pipeline finished with Failed
    9 months ago
    Total: 425s
    #247305
  • ๐Ÿ‡ฉ๐Ÿ‡ชGermany jan kellermann

    General consideration: The function determines the links for the language switcher from the negotiators. If no links are returned here, the entire block is omitted.
    The aim must be to enable this behavior with the alter() function as well. So far, an empty array after the alter() function leads to a language switcher block without links. To hide the block, NULL must be returned. (This must also be permitted in the alter() function; simple typing via array() is then no longer possible - see patches for API file).

    The previous patches are based on allowing NULL. Another variant would be that the block is not rendered if the array is empty after the alter() function. This would eliminate the zero setting and also the display of an empty block.

    Would that be a more appropriate solution?

  • Status changed to Needs work 9 months ago
  • ๐Ÿ‡บ๐Ÿ‡ธUnited States smustgrave

    Both MRs appear to have failures. Would recommend just sticking with 11.x MR for now.

  • Pipeline finished with Failed
    9 months ago
    Total: 517s
    #248387
  • Merge request !9133#3362713: Changes for D10.3.x. โ†’ (Open) created by jan kellermann
  • ๐Ÿ‡ฉ๐Ÿ‡ชGermany jan kellermann

    jan kellermann โ†’ changed the visibility of the branch 3362713-10.3 to hidden.

  • ๐Ÿ‡ฉ๐Ÿ‡ชGermany jan kellermann

    jan kellermann โ†’ changed the visibility of the branch 11.x to hidden.

  • Pipeline finished with Failed
    9 months ago
    Total: 610s
    #248405
  • Status changed to Needs review 9 months ago
  • ๐Ÿ‡ฉ๐Ÿ‡ชGermany jan kellermann

    The MR for D11 is fixed.
    The MR for D10.3 fails because of #3466822, but the commit should be okay.

    But in general see comment 18 ๐Ÿ› LanguageManager does not check against altered language_switch_links Needs review

  • Pipeline finished with Success
    9 months ago
    Total: 458s
    #249223
  • Pipeline finished with Success
    9 months ago
    Total: 572s
    #249250
  • ๐Ÿ‡ฉ๐Ÿ‡ชGermany jan kellermann

    I added branch 3362713-11.x for D11 with proposals from comment 18. See interdiff.

    I think this is the cleanest way.

  • ๐Ÿ‡ฎ๐Ÿ‡ณIndia ankitv18

    Considering the implemented condition I'm assuming that $result is either empty or null or other non-array value and if it is non-array then no use to iterate through array_filter.
    I would suggest return false; if non-array value is there would help.

    Keeping this in review so that other reviewer have something else in the mind.

  • ๐Ÿ‡ฉ๐Ÿ‡ชGermany jan kellermann

    jan kellermann โ†’ changed the visibility of the branch 3362713-languagemanager-does-not to hidden.

  • Pipeline finished with Success
    9 months ago
    Total: 680s
    #252149
  • Merge request !9185Resolve #3362713 "10.3.x" โ†’ (Open) created by jan kellermann
  • ๐Ÿ‡ฉ๐Ÿ‡ชGermany jan kellermann

    jan kellermann โ†’ changed the visibility of the branch 10.3.x to hidden.

  • Pipeline finished with Failed
    9 months ago
    #252156
  • ๐Ÿ‡บ๐Ÿ‡ธUnited States smustgrave

    I wouldn't recommend opening MR for other branches as it will just make more work to keep up and could get kicked back by the review bot.

    If the 11.x doesn't apply to the other branches the committer will mark it for backport. But very seldom do the MRs themselves get merged.

  • Pipeline finished with Failed
    9 months ago
    Total: 438s
    #252161
  • ๐Ÿ‡บ๐Ÿ‡ธUnited States smustgrave

    smustgrave โ†’ changed the visibility of the branch 3362713-10.4.x to hidden.

  • ๐Ÿ‡บ๐Ÿ‡ธUnited States smustgrave

    smustgrave โ†’ changed the visibility of the branch 3362713-10.3.x to hidden.

  • Status changed to Needs work 9 months ago
  • ๐Ÿ‡บ๐Ÿ‡ธUnited States smustgrave

    Left some comments/question on the MR

  • Pipeline finished with Success
    9 months ago
    Total: 7188s
    #254415
  • ๐Ÿ‡ฉ๐Ÿ‡ชGermany jan kellermann

    Two questions are still open in MR that need to be answered by core developers. The other suggestions have been adopted.

  • Status changed to Needs review 5 months ago
  • The Needs Review Queue Bot โ†’ tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

    This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

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

  • Pipeline finished with Failed
    5 months ago
    Total: 172s
    #376076
  • Pipeline finished with Canceled
    5 months ago
    Total: 80s
    #376080
  • Pipeline finished with Failed
    5 months ago
    Total: 586s
    #376081
  • ๐Ÿ‡ฉ๐Ÿ‡ชGermany jan kellermann

    I added the missing declare(strict_types=1); and updated the MR.

    The failed tests are not related to this issue.

    Perhaps someone can decide the two open questions. I don't see any need for action on either point.

  • ๐Ÿ‡ฎ๐Ÿ‡ณIndia sagarmohite0031

    Hello,
    Getting error while applying patch.
    Attaching error screenshot.

  • ๐Ÿ‡ฉ๐Ÿ‡ชGermany jan kellermann

    @sagarmohite0031 I updated the fork. Please test again.

  • Pipeline finished with Success
    5 months ago
    Total: 520s
    #380139
  • Pipeline finished with Success
    4 months ago
    Total: 1026s
    #385948
  • ๐Ÿ‡บ๐Ÿ‡ธUnited States thhafner Chicago, IL

    Tested on 11.x with the language switcher use case in the initial issue description and functioning as expected.

  • ๐Ÿ‡บ๐Ÿ‡ธUnited States thhafner Chicago, IL
  • First commit to issue fork.
  • ๐Ÿ‡ณ๐Ÿ‡ฟNew Zealand quietone

    I started to review this but then decided to make the changes myself. I changed comments to remove the reference to the issue number, as that is not our practice and git can tell use the issue number. I moved the test code block to the end of the test method as was able to remove some clean up line, and the added an very simple example to the hook_language_switch_links_alter.

    Setting to review for someone to check those changes.

    And Un-assigning per Assigning ownership of a Drupal core issue โ†’ .

  • ๐Ÿ‡ณ๐Ÿ‡ฟNew Zealand quietone
  • Issue was unassigned.
  • Status changed to Needs work 2 months ago
  • ๐Ÿ‡บ๐Ÿ‡ธUnited States smustgrave

    Left small comments on MR

    If you are another contributor eager to jump in, please allow the previous poster at least 48 hours to respond to feedback first, so they have the opportunity to finish what they started!

  • Pipeline finished with Failed
    2 months ago
    Total: 767s
    #441458
  • Pipeline finished with Failed
    2 months ago
    Total: 181s
    #444292
  • Pipeline finished with Success
    2 months ago
    Total: 1042s
    #444296
  • ๐Ÿ‡ฉ๐Ÿ‡ชGermany jan kellermann

    Added new Hooks Implementation. Please review and feedback.

  • ๐Ÿ‡บ๐Ÿ‡ธUnited States smustgrave

    Unresolved 1 thread for the committer to see.

    New API will need a CR also and noticed that section was removed from the issue summary. Wouldn't recommend removing headers from the summary, even if they aren't used

  • ๐Ÿ‡ฉ๐Ÿ‡ชGermany jan kellermann

    I changed summary and created CR.

  • ๐Ÿ‡บ๐Ÿ‡ธUnited States smustgrave

    Re-unresolving the same thread, want the committers to see

  • ๐Ÿ‡บ๐Ÿ‡ธUnited States smustgrave

    Think this needs a core-committer to decide on open thread, so leaving it open and moving to RTBC as rest seems fine.

  • Pipeline finished with Success
    17 days ago
    Total: 3434s
    #484146
  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom alexpott ๐Ÿ‡ช๐Ÿ‡บ๐ŸŒ

    I think we should be logging an error when $results is altered to a non array type. Even though that used to work before the security issue fix it is non interoperable with other hook implementation.

    Also I think we should change the code to obey the interface and return NULL if the array filter results in $results being empty.

    Furthermore I think we need a try {} finally {} here to ensure that $this->negotiatedLanguages is reset regardless of what we do.

  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom alexpott ๐Ÿ‡ช๐Ÿ‡บ๐ŸŒ

    Here's how I think the code should look:

      public function getLanguageSwitchLinks($type, Url $url) {
        if ($this->negotiator) {
          foreach ($this->negotiator->getNegotiationMethods($type) as $method_id => $method) {
            if (is_subclass_of($method['class'], LanguageSwitcherInterface::class)) {
              $original_languages = $this->negotiatedLanguages;
              try {
                $result = $this->negotiator->getNegotiationMethodInstance($method_id)
                  ->getLanguageSwitchLinks($this->requestStack->getCurrentRequest(), $type, $url);
    
                if (!empty($result)) {
                  // Allow modules to provide translations for specific links.
                  $this->moduleHandler->alter('language_switch_links', $result, $type, $url);
    
                  if (!is_array($result)) {
                    trigger_error('A language_switch_links hook implementation has changed $result to a non-array.', E_USER_WARNING);
                    $result = [];
                  }
    
                  $result = array_filter($result, function (array $link): bool {
                    $url = $link['url'] ?? NULL;
                    $language = $link['language'] ?? NULL;
                    if ($language instanceof LanguageInterface) {
                      $this->negotiatedLanguages[LanguageInterface::TYPE_CONTENT] = $language;
                      $this->negotiatedLanguages[LanguageInterface::TYPE_INTERFACE] = $language;
                    }
                    try {
                      return $url instanceof Url && $url->access();
                    } catch (\Exception) {
                      return FALSE;
                    }
                  });
    
                  if (empty($result)) {
                    return NULL;
                  }
                  $links = (object) ['links' => $result, 'method_id' => $method_id];
                  break;
                }
              }
              finally {
                $this->negotiatedLanguages = $original_languages;
              }
            }
          }
        }
    
        return $links ?? NULL;
      }
    
  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom alexpott ๐Ÿ‡ช๐Ÿ‡บ๐ŸŒ

    I guess we could change trigger_error('A language_switch_links hook implementation has changed $result to a non-array.', E_USER_WARNING); to a proper deprecation and say that in Drupal 12 this will cause an exception when we remove the check...

Production build 0.71.5 2024