Problem/Motivation

GA adds JS code to the webpage, but some chars get escaped for unknown reasons. e.g. < >

$page['#attached']['html_head'][] = [
      [
        '#tag' => 'script',
        '#value' => $script,
      ],
      'google_analytics_tracking_script',
    ];

I tried to add a condition before the ga-code but failed because my operator has been sanitized:

Input:
if (1 > 0) console.log("1");

output:
if (1 &gt; 0) console.log("1");

Proposed resolution

Allow valid JS code not getting escaped.

Remaining tasks

User interface changes

None

API changes

Comments

anonym-developer created an issue. See original summary.

hass’s picture

Version: 8.x-2.0 » 8.x-2.x-dev

This problem only exists in D8.

hass’s picture

Title: sanitizing code snippets (before/after) creates js error » Attached html_head get's escaped / destroys JS code
Project: Google Analytics » Drupal core
Version: 8.x-2.x-dev » 8.2.x-dev
Component: Code » asset library system
Priority: Normal » Major
Issue summary: View changes

Moving to core as this is a core bug.

alexpott’s picture

This is not a core bug. This is auto-escape working as expected. Adding random javascript to the page is tricky because if $script is variable we need to account for caching. The best way of dealing with this wrapping the html_response.attachments_processor service. For an example of this see the big_pipe module or http://cgit.drupalcode.org/dfp/tree/src/DfpHtmlResponseAttachmentsProces...

hass’s picture

This is a design flaw of core, see #2391025: Add support for inline JS/CSS with #attached. How can I disable crappy autoescape for my js code? Safe String is not working, too. I do not understand your examples.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.0-beta1 was released on August 3, 2016, which means new developments and disruptive changes should now be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

wim leers’s picture

Status: Active » Closed (works as designed)
hass’s picture

Status: Closed (works as designed) » Active

This is a bug.

cilefen’s picture

Title: Attached html_head get's escaped / destroys JS code » How to add JavaScript to html_head without it being escaped?
Category: Bug report » Support request
Priority: Major » Normal

I am changing this to a support request in order to reflect that a core committer and another maintainer feel this is not a bug and because there is a hint in #4 as to the way this should be done. The examples are not clear to me either, but that does not make this a bug.

cilefen’s picture

I see on #2391025: Add support for inline JS/CSS with #attached there have been many arguments about this over time. I'm just acknowledging that. I'm personally not interested in arguing.

hass’s picture

Inline JS must NOT escaped. This is always wrong. If a module adds JS code to inline areas this is JS code and not anything else. Applying security filtering that destroys the JS language is a bug per se.

We worked around this bug in GA module in near past by adding a custom render class http://cgit.drupalcode.org/google_analytics/tree/src/Component/Render/Go... and completly disabled the escaping. Code will be added with:

$page['#attached']['html_head'][] = [
      [
        '#tag' => 'script',
        '#value' => new GoogleAnalyticsJavaScriptSnippet($script),
      ],
      'google_analytics_tracking_script',
    ];

But this need to be done in core, not in the module. If something need to be escaped, it need to be done by the developer and the broken render function must NOT escape valid JS code. Therefore it is a bug.

joelpittet’s picture

I've done this in olark simply like this:

http://cgit.drupalcode.org/olark/tree/olark.module?h=8.x-1.x

/**
 * Implements hook_page_bottom().
 */
function olark_page_bottom(array &$page_bottom) {
  $settings = \Drupal::config('olark.settings');
  // ...
  $page_bottom['olark'] = [
    '#markup' => Markup::create($settings->get('olark_code')),
    '#attached' => [
      'library' => ['olark/integration'],
      'drupalSettings' => $js_settings,
    ],
    '#cache' => [
      'contexts' => ['user'],
      'tags' => ['user:' . $user->id()],
    ],
  ];

  // Add cachability metadata.
  /** @var Drupal\Core\Render\Renderer $renderer */
  $renderer = \Drupal::service('renderer');
  $renderer->addCacheableDependency($page_bottom['olark'], $settings);
}

It won't escape my JS and deals with caching at the bottom and for this module also for the user because I pass user data to the script.

hass’s picture

joelpittet’s picture

@hass, we could create a new contrib module to help:
I propose we make a render element called '#type' => 'script' and preprocess the value to wrap it as JS.

Using '#tag' => 'script', can't and probably shouldn't assume JS is in it (poor VB script people😜) in core. But we could simplify the process by creating our own MarkupInterface like you did already but make it generic enough that maybe that could be added to core eventually?

hass’s picture

You are joking aren‘t you? Drupal core only supports javascript and no vbscript? I would wonder if js compressor knows what to do here if you mix scripting languages today.

I have no idea why there is a filter that destroys valid javascript, but this just need to be fixed and we are done. We could also add #type="text/javascript" if this makes someone happy.

This is pure core... not a contrib thing.

joelpittet’s picture

... I wasn't joking... I am trying to help, I guess I shouldn't try with a reaction like that.

joelpittet’s picture

'#type' => 'html_tag', is super generic and just because you put a '#tag' => 'script' doesn't mean we should change how it treats HTML values, and if we did it would open up a box of complexity we'd rather not do.

You can wrap your value to tell the twig compiler not to escape the values as HTML... like you have. Or we can find a way to simplify this task without changing the way the system works. AKA my suggestion to create a new script render element, and I suggested Contrib because it would be faster and easier to iterate.

Autoescaping is on in D8 templating, it's to help security and prevent developers from shooting themselves in the foot (more themers but still). It has drawbacks but the benefits have been deemed greater than the drawbacks.

hass’s picture

AKA my suggestion to create a new script render element

Sounds good, contrib does not.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

mxh’s picture

Might this work?

$page['#attached']['html_head'][] = [
      [
        '#tag' => 'script',
        '#value' => \Drupal\Core\Render\Markup::create($script),
      ],
      'google_analytics_tracking_script',
    ];
hass’s picture

mxh’s picture

It tells that this part here
'#value' => \Drupal\Core\Render\Markup::create($script),
is not compliant to Drupal's coding standards. Instead, there should be a use statement.

<?php
// Beginning of module file ...
use Drupal\Core\Render\Markup;
// ...
'#value' => Markup::create($script),
// ...
?>
hass’s picture

Now the tests passed.

It this safe to use for future or can it break? :-)

mxh’s picture

I guess this should be safe even for D9, haven't seen anywhere that it's planned to be changed / deprecated.

alexpott’s picture

@mxh that class is marked as @internal. Ie.

 * @internal
 *   This object is marked as internal because it should only be used whilst
 *   rendering.

There are no API promises about it what-so-ever.

What is API is \Drupal\Component\Render\MarkupInterface - you can create your own object that implements that and the result of its __toString() will not be escaped.

As pointed out before this approach is really just a stop gap and in order to play nicely with bigpipe, turbo links or whatever advanced caching method comes next you need to look at the big pipe module or something like http://cgit.drupalcode.org/dfp/tree/src/DfpHtmlResponseAttachmentsProces...

mxh’s picture

So anytime there's a need for passing through markup to be not escaped, you'd then need to write your own class, which basically does the same as \Drupal\Core\Render\Markup. Does this make more sense?

There are no API promises about it what-so-ever.

Why? If it's being actually used whilst the rendering process, then you should also be able to rely on it, no? The renderer can be replaced by any other service, i.e. something which is not part of core.

alexpott’s picture

@mxh yes it makes total sense. When designing an object that implements MarkupInterface you are 100% responsible for the security of the input. You need to take this responsibility and just blindly wrapping in Markup does not really cut it. And the objects denote where they are being used which makes security reviews easier. And easier to see where something is coming from. Which can be very useful when tracking what's going on a system as recursive as the render. See all the things that implement MarkupInterface in core.

If you want Markup functionality there is \Drupal\Component\Render\MarkupTrait for you.

mxh’s picture

Which can be very useful when tracking what's going on a system as recursive as the render. If you want Markup functionality there is \Drupal\Component\Render\MarkupTrait for you.

Fair enough. Will update my implementations regards this.

Like the deprecation warning on class documentation at api.drupal.org, a warning message regards @internal would be nice too. This way it's more likely that developers take it more seriously and consider not using it.

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

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

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

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

anybody’s picture

Sorry to jump into your discussion. We have a similar issue with "style", I'd like you to let you know about:

We added some dynamic background images via https://www.drupal.org/project/responsive_background_image module.

That works nearly fine as described here:

If using drupalSettings plus a JavaScript file is not an option, then you still have one option left: use hook_page_attachments(), where you add a new value to $page['#attached']['html_head'], which contains either a script tag or a style tag, as the “Inline JavaScript that affects the entire page” section above already showed.

(Source: https://www.drupal.org/docs/8/creating-custom-modules/adding-stylesheets...)

But instead of the expected and working code (dump before adding it to html_head):

.fancy-background-image__img-wrapper--499 {
      background-image: url(/sites/default/files/styles/viewport_width_lg/public/media/image/pixabay_volkswagon-698531__web_ready.jpg?h=ca5d94f6&itok=QWBCKQ9u);
    }
    @media all and (max-width: 640px) {
      .fancy-background-image__img-wrapper--499 { 
        background-image: url(/sites/default/files/styles/viewport_width_sm/public/media/image/pixabay_volkswagon-698531__web_ready.jpg?h=ca5d94f6&itok=osMqExuD); 
      }
    }
    [...]

the "&" in the output is now also (auto-)escaped to "&amp;" and breaks the functionality. The image URLs are broken.

    .fancy-background-image__img-wrapper--499 {
      background-image: url(/sites/default/files/styles/viewport_width_lg/public/media/image/pixabay_volkswagon-698531__web_ready.jpg?h=ca5d94f6&amp;itok=QWBCKQ9u);
    }
    @media all and (max-width: 640px) {
      .fancy-background-image__img-wrapper--499 { 
        background-image: url(/sites/default/files/styles/viewport_width_sm/public/media/image/pixabay_volkswagon-698531__web_ready.jpg?h=ca5d94f6&amp;itok=osMqExuD); 
      }
    }
    [...]

Is that also expected and correct or a more special case for "style"? At least I think this belongs into this discussion as further example.
Perhaps we need some exceptions for certain kinds of tags?

So as a workaround we should create our custom markup class, I guess?

Thank you very much for your helpful discussion.

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

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

vidorado’s picture

#22 + #24 worked!!

firewaller’s picture

#24 worked for me as well.

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

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now 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.

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.

cilefen’s picture

Status: Active » Closed (outdated)

I am closing this support request because there have been no recent comments.

mxh’s picture

Guess closing issues as outdated don't give ppl who gave solution paths (like mentioned in #22, #24 and following) any sort of credit for fixing an issue, right?

cilefen’s picture

Status: Closed (outdated) » Fixed
cilefen’s picture

mxh’s picture

@alexpott any chance to get credit for this one?

nod_’s picture

added credit to several people in this issue.

mxh’s picture

Perfect, thank you 👍🏼

Status: Fixed » Closed (fixed)

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