Skip to content

[AssetMapper] es-module-shim script attributes conflicts with Turbo #59097

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
fracsi opened this issue Dec 5, 2024 · 9 comments
Open

[AssetMapper] es-module-shim script attributes conflicts with Turbo #59097

fracsi opened this issue Dec 5, 2024 · 9 comments

Comments

@fracsi
Copy link
Contributor

fracsi commented Dec 5, 2024

Symfony version(s) affected

7.2.0

Description

Using CSP nonce on importmap generates different script body for the es-module-shim injection code on each request.
This conflicts with @hotwired/turbo element signature check - if the new and the old signature differs, the page is reloaded and not replaced/morphed.
#58121 introduced on-demand loading of the shim - all attributes that are added to the importmap script tag are also added as setAttribute calls in the injected script body.

How to reproduce

  1. Use AssetMapper with es-module-shim
  2. Use @hotwired/turbo (e.g. with symfony/ux-turbo)
  3. Add nonce to the importmap call in twig:
    {{ importmap('app', { nonce: random() }) }}
  4. Load the page
  5. Navigate to any link - you can see that Turbo fetches the new page - the HeadSnapshot.trackedElementSignature is different for the new page script body for es-module-shim is different, so the browser is navigated to the new page (normaly navigation)

Possible Solution

No response

Additional Context

No response

@smnandre
Copy link
Member

So we "just" need to "not pass" the attributes to the script, that's right ?

@fracsi
Copy link
Contributor Author

fracsi commented Dec 13, 2024

So we "just" need to "not pass" the attributes to the script, that's right ?

In theory yes.

@smnandre
Copy link
Member

@nicolas-grekas do you remember why you added all the attributes ? to handle CSP ?

@fracsi
Copy link
Contributor Author

fracsi commented Dec 14, 2024

@nicolas-grekas do you remember why you added all the attributes ? to handle CSP ?

If CSP was the reason, adding strict-dynamic is enough to allow the injected script. No need to add nonce to the shim via setAttribute call.

@nicolas-grekas
Copy link
Member

I'm not fluent in CSP, I didn't think much of it. PR welcome.

@fracsi
Copy link
Contributor Author

fracsi commented Dec 16, 2024

@nicolas-grekas
I'm not sure how to fix this properly - there can be some edge-cases where a dev wants to add some attributes to the shim script tag.

@smnandre
Copy link
Member

strict-dynamic cannot be set on a single script, afaik :|

I'd suggest we remove any CSP attribute per default on the polyfill loading script.

Users can then chose to add custom attributes on this script (via config), or to hide it do things manually, etc etc

@cristi-contiu
Copy link

cristi-contiu commented Jan 5, 2025

I got it working by duplicating the inline script block which dynamically creates the shim script tag and removing both the nonce attribute (so that the script block's code is identical across page visits) and the data-turbo-track attribute from the resulting script tag (as the shim is now dynamically added, it will be missing in the response body of Turbo page fetches which causes Turbo to think the assets have changed and performs a full page load).

I think removing these attributes on the resulting script tag would be the way to go:

  • we can safely remove the data-turbo-track - if the shim version changes between Turbo visits, the inline script block will change as well (different paths / asset versions) and since it has data-turbo-track it will trigger a full page load
  • for the nonce, the resulting script tag passes CSP under the self rule - I don't think importmaps even work without whitelisting the hosts in scripts-src CSP (self or hostname), even if the script tag with type=importmap has a nonce

On the other hand, the AssetMapper docs mention adding strict-dynamic to CSP script-src for CSS loading, which would partially fix this issue as well. But from my understanding, if you add strict-dynamic on Turbo-powered apps, you basically empower Turbo to execute all inline scripts it founds on future page visits, even if they normally would not pass CSP nonce or hash validations on full-page loads. While this would allow the execution of the script block loading the ES Module shim with the random nonce, it could also potentially allow malitious injected scripts, defeating the purpose of CSP. And the data-turbo-track=reload attribute would still this trigger a full-page reload on every Trubo visit.

@Jontsa
Copy link
Contributor

Jontsa commented May 8, 2025

We encountered the same issue after upgrading from AssetMapper 7.1 to 7.2 in a project that uses symfony/ux-turbo. Form submissions reloaded page instead of replace. Adding data-turbo-track: dynamic to asset_mapper.yaml fixed this issue:

    asset_mapper:
        importmap_script_attributes:
            data-turbo-track: dynamic

Side note: After upgrading AssetMapper, we also had to allow script-src: strict-dynamic in nelmio_security config. Without it, we saw following errors in browser console:
Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' ... data: 'nonce-j9qZU0a7Sjrx5O+kla4cVg==' 'unsafe-inline' 'nonce-b8c0b3da51b4d3c9917134dca9a1d5f7'". Note that 'unsafe-inline' is ignored if either a hash or nonce value is present in the source list.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants