U4-9134 XSS security issue in the grid
exposing xss clean method on templateutilities. making the clean xss string extensions public instead of internal. ensuring the included grid renderers clean for xss. ensuring the included grid editors using html.raw with value directly, cleans for xss.
This commit is contained in:
@@ -184,7 +184,7 @@ namespace Umbraco.Core
|
||||
/// <param name="input"></param>
|
||||
/// <param name="ignoreFromClean"></param>
|
||||
/// <returns></returns>
|
||||
internal static string CleanForXss(this string input, params char[] ignoreFromClean)
|
||||
public static string CleanForXss(this string input, params char[] ignoreFromClean)
|
||||
{
|
||||
//remove any html
|
||||
input = input.StripHtml();
|
||||
|
||||
@@ -1973,6 +1973,8 @@
|
||||
<Content Include="Views\Partials\Grid\Editors\Textstring.cshtml" />
|
||||
<Content Include="Views\Partials\Grid\Bootstrap2.cshtml" />
|
||||
<Content Include="Views\Partials\Grid\Editors\Base.cshtml" />
|
||||
<Content Include="Views\Partials\Grid\Bootstrap3-Fluid.cshtml" />
|
||||
<Content Include="Views\Partials\Grid\Bootstrap2-Fluid.cshtml" />
|
||||
<None Include="Web.Debug.config.transformed" />
|
||||
<None Include="web.Template.Debug.config">
|
||||
<DependentUpon>Web.Template.config</DependentUpon>
|
||||
|
||||
@@ -64,21 +64,32 @@
|
||||
JObject cfg = contentItem.config;
|
||||
|
||||
if(cfg != null)
|
||||
foreach (JProperty property in cfg.Properties()) {
|
||||
attrs.Add(property.Name + "='" + property.Value.ToString() + "'");
|
||||
foreach (JProperty property in cfg.Properties())
|
||||
{
|
||||
var propertyValue = TemplateUtilities.CleanForXss(property.Value.ToString());
|
||||
if (string.IsNullOrWhiteSpace(propertyValue) == false)
|
||||
{
|
||||
attrs.Add(property.Name + "='" + propertyValue + "'");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
JObject style = contentItem.styles;
|
||||
|
||||
if (style != null) {
|
||||
var cssVals = new List<string>();
|
||||
foreach (JProperty property in style.Properties())
|
||||
cssVals.Add(property.Name + ":" + property.Value.ToString() + ";");
|
||||
if (style != null) {
|
||||
var cssVals = new List<string>();
|
||||
foreach (JProperty property in style.Properties())
|
||||
{
|
||||
var propertyValue = TemplateUtilities.CleanForXss(property.Value.ToString());
|
||||
if (string.IsNullOrWhiteSpace(propertyValue) == false)
|
||||
{
|
||||
cssVals.Add(property.Name + ":" + propertyValue + ";");
|
||||
}
|
||||
}
|
||||
|
||||
if (cssVals.Any())
|
||||
attrs.Add("style='" + string.Join(" ", cssVals) + "'");
|
||||
if (cssVals.Any())
|
||||
attrs.Add("style='" + string.Join(" ", cssVals) + "'");
|
||||
}
|
||||
|
||||
|
||||
return new MvcHtmlString(string.Join(" ", attrs));
|
||||
}
|
||||
}
|
||||
@@ -64,21 +64,32 @@
|
||||
JObject cfg = contentItem.config;
|
||||
|
||||
if(cfg != null)
|
||||
foreach (JProperty property in cfg.Properties()) {
|
||||
attrs.Add(property.Name + "=\"" + property.Value.ToString() + "\"");
|
||||
foreach (JProperty property in cfg.Properties())
|
||||
{
|
||||
var propertyValue = TemplateUtilities.CleanForXss(property.Value.ToString());
|
||||
if (string.IsNullOrWhiteSpace(propertyValue) == false)
|
||||
{
|
||||
attrs.Add(property.Name + "=\"" + propertyValue + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
JObject style = contentItem.styles;
|
||||
|
||||
if (style != null) {
|
||||
var cssVals = new List<string>();
|
||||
foreach (JProperty property in style.Properties())
|
||||
cssVals.Add(property.Name + ":" + property.Value.ToString() + ";");
|
||||
if (style != null) {
|
||||
var cssVals = new List<string>();
|
||||
foreach (JProperty property in style.Properties())
|
||||
{
|
||||
var propertyValue = TemplateUtilities.CleanForXss(property.Value.ToString());
|
||||
if (string.IsNullOrWhiteSpace(propertyValue) == false)
|
||||
{
|
||||
cssVals.Add(property.Name + ":" + propertyValue + ";");
|
||||
}
|
||||
}
|
||||
|
||||
if (cssVals.Any())
|
||||
attrs.Add("style=\"" + string.Join(" ", cssVals) + "\"");
|
||||
if (cssVals.Any())
|
||||
attrs.Add("style=\"" + string.Join(" ", cssVals) + "\"");
|
||||
}
|
||||
|
||||
|
||||
return new MvcHtmlString(string.Join(" ", attrs));
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
@*
|
||||
Razor helpers located at the bottom of this file
|
||||
*@
|
||||
|
||||
@if (Model != null && Model.sections != null)
|
||||
{
|
||||
var oneColumn = ((System.Collections.ICollection)Model.sections).Count == 1;
|
||||
@@ -59,21 +60,32 @@
|
||||
JObject cfg = contentItem.config;
|
||||
|
||||
if(cfg != null)
|
||||
foreach (JProperty property in cfg.Properties()) {
|
||||
attrs.Add(property.Name + "='" + property.Value.ToString() + "'");
|
||||
foreach (JProperty property in cfg.Properties())
|
||||
{
|
||||
var propertyValue = TemplateUtilities.CleanForXss(property.Value.ToString());
|
||||
if (string.IsNullOrWhiteSpace(propertyValue) == false)
|
||||
{
|
||||
attrs.Add(property.Name + "='" + propertyValue + "'");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
JObject style = contentItem.styles;
|
||||
|
||||
if (style != null) {
|
||||
var cssVals = new List<string>();
|
||||
foreach (JProperty property in style.Properties())
|
||||
cssVals.Add(property.Name + ":" + property.Value.ToString() + ";");
|
||||
if (style != null) {
|
||||
var cssVals = new List<string>();
|
||||
foreach (JProperty property in style.Properties())
|
||||
{
|
||||
var propertyValue = TemplateUtilities.CleanForXss(property.Value.ToString());
|
||||
if (string.IsNullOrWhiteSpace(propertyValue) == false)
|
||||
{
|
||||
cssVals.Add(property.Name + ":" + propertyValue + ";");
|
||||
}
|
||||
}
|
||||
|
||||
if (cssVals.Any())
|
||||
attrs.Add("style='" + string.Join(" ", cssVals) + "'");
|
||||
if (cssVals.Any())
|
||||
attrs.Add("style='" + string.Join(" ", cssVals) + "'");
|
||||
}
|
||||
|
||||
|
||||
return new MvcHtmlString(string.Join(" ", attrs));
|
||||
}
|
||||
}
|
||||
@@ -64,21 +64,32 @@
|
||||
JObject cfg = contentItem.config;
|
||||
|
||||
if(cfg != null)
|
||||
foreach (JProperty property in cfg.Properties()) {
|
||||
attrs.Add(property.Name + "=\"" + property.Value.ToString() + "\"");
|
||||
foreach (JProperty property in cfg.Properties())
|
||||
{
|
||||
var propertyValue = TemplateUtilities.CleanForXss(property.Value.ToString());
|
||||
if (string.IsNullOrWhiteSpace(propertyValue) == false)
|
||||
{
|
||||
attrs.Add(property.Name + "=\"" + propertyValue +"\"");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
JObject style = contentItem.styles;
|
||||
|
||||
if (style != null) {
|
||||
var cssVals = new List<string>();
|
||||
foreach (JProperty property in style.Properties())
|
||||
cssVals.Add(property.Name + ":" + property.Value.ToString() + ";");
|
||||
if (style != null) {
|
||||
var cssVals = new List<string>();
|
||||
foreach (JProperty property in style.Properties())
|
||||
{
|
||||
var propertyValue = TemplateUtilities.CleanForXss(property.Value.ToString());
|
||||
if (string.IsNullOrWhiteSpace(propertyValue) == false)
|
||||
{
|
||||
cssVals.Add(property.Name + ":" + propertyValue + ";");
|
||||
}
|
||||
}
|
||||
|
||||
if (cssVals.Any())
|
||||
attrs.Add("style=\"" + string.Join(" ", cssVals) + "\"");
|
||||
if (cssVals.Any())
|
||||
attrs.Add("style=\"" + string.Join(" ", cssVals) + "\"");
|
||||
}
|
||||
|
||||
|
||||
return new MvcHtmlString(string.Join(" ", attrs));
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
@model dynamic
|
||||
@using Umbraco.Web.Templates
|
||||
|
||||
@functions {
|
||||
public static string EditorView(dynamic contentItem)
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
@model dynamic
|
||||
@using Umbraco.Web.Templates
|
||||
@Html.Raw(Model.value)
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
@inherits UmbracoViewPage<dynamic>
|
||||
@using Umbraco.Web.Templates
|
||||
|
||||
|
||||
@if (Model.value != null)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
@model dynamic
|
||||
@using Umbraco.Web.Templates
|
||||
|
||||
@if (Model.value != null)
|
||||
{
|
||||
|
||||
@@ -4,10 +4,9 @@
|
||||
@if (Model.editor.config.markup != null)
|
||||
{
|
||||
string markup = Model.editor.config.markup.ToString();
|
||||
|
||||
var UmbracoHelper = new UmbracoHelper(UmbracoContext.Current);
|
||||
|
||||
markup = markup.Replace("#value#", UmbracoHelper.ReplaceLineBreaksForHtml(Model.value.ToString()));
|
||||
markup = markup.Replace("#value#", UmbracoHelper.ReplaceLineBreaksForHtml(TemplateUtilities.CleanForXss(Model.value.ToString())));
|
||||
markup = markup.Replace("#style#", Model.editor.config.style.ToString());
|
||||
|
||||
<text>
|
||||
@@ -17,6 +16,6 @@
|
||||
else
|
||||
{
|
||||
<text>
|
||||
<div style="@Model.editor.config.style">@Model.value</div>
|
||||
<div style="@Model.editor.config.style">@TemplateUtilities.CleanForXss(Model.value.ToString())</div>
|
||||
</text>
|
||||
}
|
||||
|
||||
@@ -7,17 +7,17 @@ using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Web.Templates
|
||||
{
|
||||
//NOTE: I realize there is only one class in this namespace but I'm pretty positive that there will be more classes in
|
||||
//this namespace once we start migrating and cleaning up more code.
|
||||
//NOTE: I realize there is only one class in this namespace but I'm pretty positive that there will be more classes in
|
||||
//this namespace once we start migrating and cleaning up more code.
|
||||
|
||||
/// <summary>
|
||||
/// Utility class used for templates
|
||||
/// </summary>
|
||||
public static class TemplateUtilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility class used for templates
|
||||
/// </summary>
|
||||
public static class TemplateUtilities
|
||||
{
|
||||
//TODO: Pass in an Umbraco context!!!!!!!! Don't rely on the singleton so things are more testable
|
||||
internal static string ParseInternalLinks(string text, bool preview)
|
||||
{
|
||||
{
|
||||
// save and set for url provider
|
||||
var inPreviewMode = UmbracoContext.Current.InPreviewMode;
|
||||
UmbracoContext.Current.InPreviewMode = preview;
|
||||
@@ -33,79 +33,84 @@ namespace Umbraco.Web.Templates
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the string looking for the {localLink} syntax and updates them to their correct links.
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <returns></returns>
|
||||
public static string ParseInternalLinks(string text)
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses the string looking for the {localLink} syntax and updates them to their correct links.
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <returns></returns>
|
||||
public static string ParseInternalLinks(string text)
|
||||
{
|
||||
//TODO: Pass in an Umbraco context!!!!!!!! Don't rely on the singleton so things are more testable, better yet, pass in urlprovider, routing context, separately
|
||||
|
||||
//don't attempt to proceed without a context as we cannot lookup urls without one
|
||||
if (UmbracoContext.Current == null || UmbracoContext.Current.RoutingContext == null)
|
||||
{
|
||||
return text;
|
||||
}
|
||||
//don't attempt to proceed without a context as we cannot lookup urls without one
|
||||
if (UmbracoContext.Current == null || UmbracoContext.Current.RoutingContext == null)
|
||||
{
|
||||
return text;
|
||||
}
|
||||
|
||||
var urlProvider = UmbracoContext.Current.UrlProvider;
|
||||
var urlProvider = UmbracoContext.Current.UrlProvider;
|
||||
|
||||
// Parse internal links
|
||||
var tags = Regex.Matches(text, @"href=""[/]?(?:\{|\%7B)localLink:([0-9]+)(?:\}|\%7D)", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
|
||||
foreach (Match tag in tags)
|
||||
if (tag.Groups.Count > 0)
|
||||
{
|
||||
var id = tag.Groups[1].Value; //.Remove(tag.Groups[1].Value.Length - 1, 1);
|
||||
var newLink = urlProvider.GetUrl(int.Parse(id));
|
||||
text = text.Replace(tag.Value, "href=\"" + newLink);
|
||||
}
|
||||
// Parse internal links
|
||||
var tags = Regex.Matches(text, @"href=""[/]?(?:\{|\%7B)localLink:([0-9]+)(?:\}|\%7D)", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
|
||||
foreach (Match tag in tags)
|
||||
if (tag.Groups.Count > 0)
|
||||
{
|
||||
var id = tag.Groups[1].Value; //.Remove(tag.Groups[1].Value.Length - 1, 1);
|
||||
var newLink = urlProvider.GetUrl(int.Parse(id));
|
||||
text = text.Replace(tag.Value, "href=\"" + newLink);
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
// static compiled regex for faster performance
|
||||
private readonly static Regex ResolveUrlPattern = new Regex("(=[\"\']?)(\\W?\\~(?:.(?![\"\']?\\s+(?:\\S+)=|[>\"\']))+.)[\"\']?", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
|
||||
// static compiled regex for faster performance
|
||||
private readonly static Regex ResolveUrlPattern = new Regex("(=[\"\']?)(\\W?\\~(?:.(?![\"\']?\\s+(?:\\S+)=|[>\"\']))+.)[\"\']?",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
|
||||
|
||||
/// <summary>
|
||||
/// The RegEx matches any HTML attribute values that start with a tilde (~), those that match are passed to ResolveUrl to replace the tilde with the application path.
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// When used with a Virtual-Directory set-up, this would resolve all URLs correctly.
|
||||
/// The recommendation is that the "ResolveUrlsFromTextString" option (in umbracoSettings.config) is set to false for non-Virtual-Directory installs.
|
||||
/// </remarks>
|
||||
public static string ResolveUrlsFromTextString(string text)
|
||||
{
|
||||
/// <summary>
|
||||
/// The RegEx matches any HTML attribute values that start with a tilde (~), those that match are passed to ResolveUrl to replace the tilde with the application path.
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// When used with a Virtual-Directory set-up, this would resolve all URLs correctly.
|
||||
/// The recommendation is that the "ResolveUrlsFromTextString" option (in umbracoSettings.config) is set to false for non-Virtual-Directory installs.
|
||||
/// </remarks>
|
||||
public static string ResolveUrlsFromTextString(string text)
|
||||
{
|
||||
if (UmbracoConfig.For.UmbracoSettings().Content.ResolveUrlsFromTextString == false) return text;
|
||||
|
||||
using (var timer = DisposableTimer.DebugDuration(typeof(IOHelper), "ResolveUrlsFromTextString starting", "ResolveUrlsFromTextString complete"))
|
||||
{
|
||||
// find all relative urls (ie. urls that contain ~)
|
||||
var tags = ResolveUrlPattern.Matches(text);
|
||||
LogHelper.Debug(typeof(IOHelper), "After regex: " + timer.Stopwatch.ElapsedMilliseconds + " matched: " + tags.Count);
|
||||
foreach (Match tag in tags)
|
||||
{
|
||||
var url = "";
|
||||
if (tag.Groups[1].Success)
|
||||
url = tag.Groups[1].Value;
|
||||
using (var timer = DisposableTimer.DebugDuration(typeof(IOHelper), "ResolveUrlsFromTextString starting", "ResolveUrlsFromTextString complete"))
|
||||
{
|
||||
// find all relative urls (ie. urls that contain ~)
|
||||
var tags = ResolveUrlPattern.Matches(text);
|
||||
LogHelper.Debug(typeof(IOHelper), "After regex: " + timer.Stopwatch.ElapsedMilliseconds + " matched: " + tags.Count);
|
||||
foreach (Match tag in tags)
|
||||
{
|
||||
var url = "";
|
||||
if (tag.Groups[1].Success)
|
||||
url = tag.Groups[1].Value;
|
||||
|
||||
// The richtext editor inserts a slash in front of the url. That's why we need this little fix
|
||||
// if (url.StartsWith("/"))
|
||||
// text = text.Replace(url, ResolveUrl(url.Substring(1)));
|
||||
// else
|
||||
if (String.IsNullOrEmpty(url) == false)
|
||||
{
|
||||
var resolvedUrl = (url.Substring(0, 1) == "/") ? IOHelper.ResolveUrl(url.Substring(1)) : IOHelper.ResolveUrl(url);
|
||||
text = text.Replace(url, resolvedUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
// The richtext editor inserts a slash in front of the url. That's why we need this little fix
|
||||
// if (url.StartsWith("/"))
|
||||
// text = text.Replace(url, ResolveUrl(url.Substring(1)));
|
||||
// else
|
||||
if (String.IsNullOrEmpty(url) == false)
|
||||
{
|
||||
var resolvedUrl = (url.Substring(0, 1) == "/") ? IOHelper.ResolveUrl(url.Substring(1)) : IOHelper.ResolveUrl(url);
|
||||
text = text.Replace(url, resolvedUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
}
|
||||
public static string CleanForXss(string text, params char[] ignoreFromClean)
|
||||
{
|
||||
return text.CleanForXss(ignoreFromClean);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user