JsOptimizer incorrectly changes external JS to local filesystem causing live-only JS failure

Created on 20 August 2019, almost 5 years ago
Updated 21 February 2023, over 1 year ago

Problem/Motivation

The JsOptimizer class doesn't properly match the domain when preserving external JS, which can create issues that only occur on a live site. This requires explanation, listed below.

This applies to 8.x-4.x and 8.x-3.x, and potentially previous branches as well.

The scenario:

Imagine you have an external JavaScript named like so:

https://cdn.example.com/js/www.mysite.com/myfile.js

This is added as an external JavaScript to the website "www.mysite.com".

On the live site, you observe the following error when AdvAgg aggregates JS:

Warning: file_get_contents(513640/mmapi.js): failed to open stream: No such file or directory in Drupal\Core\Asset\JsOptimizer->optimize() (line 25 of /mnt/www/html/mysite01live/docroot/core/lib/Drupal/Core/Asset/JsOptimizer.php)

You also observe that there is a JavaScript whose entire contents is a single semi-colon:

;

The cause:

The cause for this behavior appears to be around line 46 in src/Asset/JsOptimizer.php. Here you will see this:

    if ($asset['type'] === 'external') {
      // If type is external but path doesn't start with http, https, or //
      // change it to file.
      if (stripos($path, 'http') !== 0 && stripos($path, '//') !== 0) {
        $asset['type'] = 'file';
      }
      // If type is external and starts with http, https, or // but points to
      // this host change to file, but move it to the top of the aggregation
      // stack as long as js.preserve_external is not set.
      elseif (stripos($path, $this->basePath) !== FALSE && !$this->config->get('js.preserve_external')) {
        $asset['type'] = 'file';
        $asset['group'] = JS_LIBRARY;
        $asset['every_page'] = TRUE;
        $asset['weight'] = -40000;
        $asset['data'] = substr($asset['data'], stripos($asset['data'], $this->basePath) + $this->basePathLen);
      }
    }

Notice this specific part:

stripos($path, $this->basePath) !== FALSE

This condition will match, causing the JS being loaded to change from loading over HTTP to loading via local filesystem.

Proposed resolution

Change the "stripos" line to check for the domain match instead of simply whether the current domain exists in the external JS URL.

Remaining tasks

Create a patch / MR.

๐Ÿ› Bug report
Status

Active

Version

4.0

Component

Compression/Minification

Created by

๐Ÿ‡บ๐Ÿ‡ธUnited States josephdpurcell@gmail.com

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

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

Production build 0.69.0 2024