London Dev Meetup Rescheduled! Due to unavoidable reasons, the event has been moved to 21st May. Speakers remain the same—any changes will be communicated. Seats are limited—register here to secure your spot!

RenderEPiServerQuickNavigator uses unsafe inline, how to add nonce?

Vote:
 

Hello guys,

i have a question regarding CSP unsafe-inline of RenderEPiServerQuickNavigator.

It renders script like this:

<link rel="stylesheet" type="text/css" href="/Util/styles/quicknavigator.css" />
<script type="text/javascript" src="/Util/javascript/quicknavigator.js"></script>
<script type="text/javascript">

                //<![CDATA[ 
                (function () { new epi.QuickNavigator({"menuItems":{"dashboard":{"caption":"Dashboard","url":"/Smarthouse","javascript":null,"enabledScript":"true","imageUrl":null},"editMode":{"caption":"CMS Edit","url":"/Smarthouse/CMS/?language=en#context=epi.cms.contentdata:///9","javascript":null,"enabledScript":"true","imageUrl":null}},"menuTitle":"Episerver","defaultUrl":""}); }()); 
                //]]>
</script>

How can i add a nonce value to the inline script to avoid csp violation? The only way i see is using string replace.

Pseudo code:

public static IHtmlString RenderEPiServerQuickNavigatorWithCspNonce(this HtmlHelper htmlHelper, string partialViewName = "QuickNavigator")
{
	if (PageEditing.PageIsInEditMode || !PathAccessChecker.HasEditAccess(PrincipalInfo.CurrentPrincipal) || ServiceLocator.Current.GetInstance<IDatabaseMode>().DatabaseMode == DatabaseMode.ReadOnly)
	{
		return htmlHelper.Raw(string.Empty);
	}
	QuickNavigatorMenu quickNavigatorMenu = new QuickNavigatorMenu();
	List<KeyValuePair<string, QuickNavigatorMenuItem>> quickNavigatorMenuProviders = 
		ServiceLocator.Current.GetAllInstances<IQuickNavigatorItemProvider>()
		.OrderBy(p => p.SortOrder)
		.SelectMany(provider => provider.GetMenuItems(quickNavigatorMenu.CurrentContentLink))
		.ToList();
	quickNavigatorMenuProviders.ForEach(item => quickNavigatorMenu.Items.Add(item));
	
	quickNavigatorMenu.RegisterRequiredResources();
	return ReplaceQuickNavigatorScriptWithNonceScript(htmlHelper.RequiredClientResources(partialViewName));
}

Regards,

Tim

#221125
Edited, Apr 14, 2020 8:18
Vote:
 

I have solved it in the "dirty" way above.

In case someone wants to know how, here's also the missing ReplaceQuickNavigatorScriptWithNonceScript which uses HtmlAgilityPack:

private static IHtmlString ReplaceQuickNavigatorScriptWithNonceScript(IHtmlString originalEpiServerScript)
{
	ICspConfiguration cspConfig = ServiceLocator.Current.GetInstance<ICspConfiguration>();
	if (!Feature<SwitchContentSecurityOptimizations>.Is().Enabled || !cspConfig.IsCspScriptNonceEnabled)
	{
		return originalEpiServerScript;
	}

	INonceProvider nonceProvider = ServiceLocator.Current.GetInstance<INonceProvider>();
	HtmlDocument doc = new HtmlDocument();
	doc.LoadHtml(originalEpiServerScript.ToHtmlString());
	IEnumerable<HtmlNode> relevantScriptTags = doc.DocumentNode.Descendants("script")
		.Where(script => script.Attributes["nonce"] == null &&
			(script.Attributes["src"] != null || !string.IsNullOrWhiteSpace(script.InnerText)));

	foreach (HtmlNode script in relevantScriptTags)
	{
		script.Attributes.Add("nonce", nonceProvider.CspScriptNonce);
	}

	return new HtmlString(doc.DocumentNode.OuterHtml);
}
#221246
Edited, Apr 15, 2020 16:11
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.