Skip to content

Fix scripts in SSR updates#50453

Merged
mkArtakMSFT merged 9 commits intorelease/8.0from
stevesa/fix-scripts-in-dynamic-content
Sep 1, 2023
Merged

Fix scripts in SSR updates#50453
mkArtakMSFT merged 9 commits intorelease/8.0from
stevesa/fix-scripts-in-dynamic-content

Conversation

@SteveSandersonMS
Copy link
Member

Fixes #50424. See that issue for details.

The solution here involves several elements:

  • Changing the JS-side code so it uses a different part of the custom elements API that is not subject to the same problems as slotchange. This is nice anyway as the updated implementation is considerably simpler and less reliant on hard-to-reason-about behaviors and timings.
  • Supporting rendering <script> tags properly from StaticHtmlRenderer. Previously we were using HTML encoding wrongly, so JS code would have been broken anyway.
  • Cleaning up some repetition in the E2E tests regarding "suppress enhanced navigation"

@SteveSandersonMS SteveSandersonMS requested a review from a team as a code owner August 31, 2023 14:19
@ghost ghost added the area-blazor Includes: Blazor, Razor Components label Aug 31, 2023
@SteveSandersonMS SteveSandersonMS changed the title Fix scripts in dynamic content Fix scripts in SSR updates Aug 31, 2023
var originalEncoder = _htmlEncoder;
try
{
_htmlEncoder = _nullHtmlEncoder;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +183 to +191
// Script tags are special. Unlike in XHTML, the contents of script elements in HTML
// are parsed as plain text, so HTML entities are not recognized and are treated as
// literal text. Code like console.log(&quot;Hello&quot;) would be a syntax error.
// HTML does not make use of <![CDATA[ ... ]]> either. So inside <script>...</script>,
// we must disable HTML encoding. This is hardly a XSS issue since if an application
// is rendering user content inside <script>, they already have an XSS problem unless
// they have carefully escaped the user content.
// https://stackoverflow.com/a/14781466
// https://html.spec.whatwg.org/multipage/scripting.html#restrictions-for-contents-of-script-elements
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think security wise this is enough guarantee, as per the comment above, we should use the JS encoder for this. See

protected JavaScriptEncoder JavaScriptEncoder { get; set; } = JavaScriptEncoder.Default;

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JavaScriptEncoder produces broken output in this scenario, e.g,:

<script>alert(\u0027The apostrophes must not be encoded...

The \u0027 is not valid there and is a syntax error.

Copy link
Member Author

@SteveSandersonMS SteveSandersonMS Aug 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So after more investigation, if our goal is to be consistent with .cshtml, then it's best to understand this as a Razor compiler limitation: dotnet/razor#9204. We would need a compiler change to be able to know when it's safe to emit some unencoded content into <script> and when it is not.

If that is the case, then I think that means we have to not support it in the short term. So my proposed plan is:

  • Keep the parts of this PR that make it possible to add <script> tags at all
  • Remove the parts that deal with HTML encoding, so it will only work for <script src='someUrl'></script> and <script>@(new MarkupString("myscript"))</script>
  • In the future, maybe Unable to render script elements with content from .razor files razor#9204 will get fixed and then it would automatically just start working for <script>code</script> as well (as long as code is fixed at compile time, and does not include C# expressions).

Copy link
Member Author

@SteveSandersonMS SteveSandersonMS Sep 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, with a bit of extra insight from dotnet/razor#9204, I think we can do a bit better than the above, and can actually make everything work with the correct encodings [*]

@javiercn's original suggestion to use JavaScriptEncoder was correct. It doesn't break cases where the developer's compile-time constant code gets compiled as AddMarkupContent, since that's emitted verbatim. All it does is force any dynamic expressions to be JavaScript-string encoded, which is correct and equivalent to the HTML-encoding that we enforce for dynamic expressions outside <script> contexts.

So altogether I think this PR is ready now.


[*] Until dotnet/razor#9204 is fixed, single-line <script> blocks will have too much encoding (even compile-time constant code will get JavaScript encoded), which is safe but prevents the code from executing. Developers can work around this by adding at least one linebreak. The issue will go away when that Razor compiler issue is fixed.

@SteveSandersonMS SteveSandersonMS force-pushed the stevesa/fix-scripts-in-dynamic-content branch from 957031d to e973b81 Compare September 1, 2023 08:08
@mkArtakMSFT mkArtakMSFT merged commit e221f8f into release/8.0 Sep 1, 2023
@mkArtakMSFT mkArtakMSFT deleted the stevesa/fix-scripts-in-dynamic-content branch September 1, 2023 17:47
@ghost ghost added this to the 8.0-rc2 milestone Sep 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-blazor Includes: Blazor, Razor Components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants