choices/choices 9.0.1 is affected by polyfill.io

Created on 26 June 2024, 6 days ago
Updated 2 July 2024, about 4 hours ago

Problem/Motivation

Choices-js/choices includes a script hosted by polyfill.io

3rd Party Libraries and Supply Chains - PSA-2024-06-26

Steps to reproduce

The current require in composer.libraries.json is this:

        "choices": {
            "type": "package",
            "package": {
                "name": "choices/choices",
                "version": "9.0.1",
                "type": "drupal-library",
                "extra": {
                    "installer-name": "choices"
                },
                "dist": {
                    "url": "https://github.com/Choices-js/Choices/archive/refs/tags/v9.0.1.zip",
                    "type": "zip"
                },
                "license": "MIT"
            }
        },

If you open the Zip file and look at public/index.html, it includes a script hosted by polyfill.io.

    <!-- Optional includes -->
    <script src="https://polyfill.io/v3/polyfill.min.js?features=es5%2Ces6%2CArray.prototype.includes%2Cfetch%2CCustomEvent%2CElement.prototype.closest"></script>
    <!-- End optional includes -->

I will note that the index.html file might not actually be used by or affect the actual operation of the library, and users might not actually be at risk, unless they deliberately visit that HTML file. Still, I think it's good to point out potentially malicious sites that are referenced, so that people are aware, and so that we can get the library updated as soon as possible.

Proposed resolution

There is an open pull request on the library. Assuming it gets merged and a new release is created, composer.libraries.json will need to be updated to include a newer version of the library that is no longer using polyfill.io.

Remaining tasks

User interface changes

API changes

Data model changes

📌 Task
Status

Active

Version

6.2

Component

Code

Created by

Live updates comments and jobs are added and updated live.
  • Security

    It is used for security vulnerabilities which do not need a security advisory. For example, security issues in projects which do not have security advisory coverage, or forward-porting a change already disclosed in a security advisory. See Drupal’s security advisory policy for details. Be careful publicly disclosing security vulnerabilities! Use the “Report a security vulnerability” link in the project page’s sidebar. See how to report a security issue for details.

Sign in to follow issues

Comments & Activities

  • Issue created by @solideogloria
  • 🇺🇸United States ransomweaver

    There is also a polyfill.io url in webform.libraries.yml, under js for the external library webform.choices

    This external library is a dependency of the webform.element.choices, which is conditionally attached in prepare() for the Select element plugin class Plugin/WebformElement/Select.php.

    // Enhance select element using select2, chosen, or choices.
        if (isset($element['#select2']) && $select2_exists) {
          $element['#attached']['library'][] = 'webform/webform.element.select2';
          $element['#attributes']['class'][] = 'js-webform-select2';
          $element['#attributes']['class'][] = 'webform-select2';
        }
        elseif (isset($element['#choices']) && $choices_exists) {
          $element['#attached']['library'][] = 'webform/webform.element.choices';
          $element['#attributes']['class'][] = 'js-webform-choices';
          $element['#attributes']['class'][] = 'webform-choices';
        }
        elseif (isset($element['#chosen']) && $chosen_exists) {
          $element['#attached']['library'][] = 'webform/webform.element.chosen';
          $element['#attributes']['class'][] = 'js-webform-chosen';
          $element['#attributes']['class'][] = 'webform-chosen';
        }

    BUT, It looks like the system will only offer one of the above, and the preferred option is Select2, so Choices is a fallback from that.

    // If select2, choices, or chosen is not available,
        // see if we can use the alternative.
        $select2_exists = $this->librariesManager->isIncluded('jquery.select2');
        $choices_exists = $this->librariesManager->isIncluded('choices');
        $chosen_exists = $this->librariesManager->isIncluded('jquery.chosen');
        $default_select = ($select2_exists ? '#select2' :
          ($choices_exists ? '#choices' :
            ($chosen_exists ? '#chosen' : NULL)
          )
        );

    So i think the default setup with Select2 available means this polyfill.io script isn't loaded, but it should be fixed in webform.libraries.yml, or just removed altogether since it's just one of two fallbacks from Select2.

  • I wouldn't remove it altogether. It's not a "fallback", it's a valid configuration option. I'm sure there's lots of sites using it.

  • 🇺🇸United States chartmann

    Thank you for the patch. I've emailed the maintainer of the Choices library to request action and offer to find a new maintainer. Hopefully the related PR, which has already been reviewed and approved, will soon be merged.

  • 🇺🇸United States Luke.Leber Pennsylvania

    Just chiming in here from a different perspective: our group has disallowed the use of Choices.js for years due to inherent accessibility issues.

    Given that...

    1. The software hasn't had a commit in ~3 years
    2. The software ships with a vulnerability
    3. The Select2 implementation ranked higher in [our] a11y testing

    Might it be an alternative to deprecate / disable its use in Webform by default? Shipping what is effectively abandonware doesn't seem sustainable.

  • 🇺🇸United States Luke.Leber Pennsylvania

    Savvy users can also utilize composer to further harden their applications:

    Example: Nuke the vulnerable file from orbit on `composer install`

        "scripts": {
            "drupal-scaffold": "DrupalComposer\\DrupalScaffold\\Plugin::scaffold",
            "post-install-cmd": [
              "rm docroot/libraries/choices/public/index.html"
            ]
        }
    

    🛡️

  • 🇺🇸United States wesleymusgrove

    Another thing you can do in composer.json is tell Composer not to even try downloading and installing the `choices/choices` library even if it is required by another package, like webform's `composer.libraries.json`. This is informing Composer that I already have a package that replaces the functionality of all versions of `choices/choices` (even though I don't have a replacement). So, Composer will not attempt to download and install `choices/choices`..

    "replace": {
        "choices/choices": "*"
    }
    
  • 🇦🇹Austria tgoeg

    Be aware that in case of choices 9.0.1 (contrary to the current master), which gets included by webform, the URL is *not* cdn.polyfill.io but <script src="https://polyfill.io/v3/polyfill.min.js?features=es5%2Ces6%2CArray.prototype.includes%2Cfetch%2CCustomEvent%2CElement.prototype.closest"></script> as mentioned by OP.
    If you use some kind of search & replace mechanism, it should rather be something along these lines:

    "scripts": {
        "drupal-scaffold": "DrupalComposer\\DrupalScaffold\\Plugin::scaffold",
        "post-install-cmd": [
          "sed -i 's#https://polyfill\.io/#https://polyfill-fastly\.io/#g' web/libraries/choices/public/index.html"
        ]
    },

    As already mentioned in [ 🐛 polyfill.io Library is no longer considered safe to use Fixed ], I'd very much favor that composer.libraries.json included this or some other form of patching the file without user interaction for the time being. Users should get a secure setup by just running composer update for their drupal installation, at least that's my way of thinking.
    We can update to a fixed version later on (or switch to a better maintained lib as mentioned above) anyway.

Production build 0.69.0 2024