Skip to content
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

A malformed SVG can tank the entire page #72

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 13 additions & 10 deletions Our.Umbraco.TagHelpers.Tests/InlineSvgTagHelperTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Moq;
using NUnit.Framework;
Expand Down Expand Up @@ -56,7 +57,7 @@ public void Setup()
public void NoOutputIfNoMediaOrFileSet()
{

var tagHelper = new InlineSvgTagHelper(null, null, null, _settings, null);
var tagHelper = new InlineSvgTagHelper(null, null, null, _settings, null, Mock.Of<ILogger<InlineSvgTagHelper>>());

tagHelper.Process(_context, _output);

Expand All @@ -67,7 +68,7 @@ public void NoOutputIfNoMediaOrFileSet()
public void NoOutputIfBothMediaAndFileSet()
{
var umbContent = Mock.Of<IPublishedContent>(c => c.ContentType.ItemType == PublishedItemType.Media);
var tagHelper = new InlineSvgTagHelper(null, null, null, _settings, null)
var tagHelper = new InlineSvgTagHelper(null, null, null, _settings, null, Mock.Of<ILogger<InlineSvgTagHelper>>())
{
FileSource = "test.svg",
MediaItem = umbContent
Expand All @@ -81,7 +82,7 @@ public void NoOutputIfBothMediaAndFileSet()
[Test]
public void NoOutputIfFileNotSvg()
{
var tagHelper = new InlineSvgTagHelper(null, null, null, _settings, null)
var tagHelper = new InlineSvgTagHelper(null, null, null, _settings, null, Mock.Of<ILogger<InlineSvgTagHelper>>())
{
FileSource = "test.notsvg"
};
Expand All @@ -97,7 +98,7 @@ public void NoOutputIfFileNotFound()
var fileProvider = new Mock<IFileProvider>();
fileProvider.Setup(p => p.GetFileInfo(It.IsAny<string>())).Returns(Mock.Of<IFileInfo>(f => !f.Exists));
var hostEnv = Mock.Of<IWebHostEnvironment>(e => e.WebRootFileProvider == fileProvider.Object);
var tagHelper = new InlineSvgTagHelper(null, hostEnv, null, _settings, null)
var tagHelper = new InlineSvgTagHelper(null, hostEnv, null, _settings, null, Mock.Of<ILogger<InlineSvgTagHelper>>())
{
FileSource = "test.svg"
};
Expand All @@ -113,7 +114,7 @@ public void ExpectedOutputIfValidFile()
var fileProvider = new Mock<IFileProvider>();
fileProvider.Setup(p => p.GetFileInfo(It.IsAny<string>())).Returns(Mock.Of<IFileInfo>(f => f.Exists && f.CreateReadStream() == new MemoryStream(Encoding.UTF8.GetBytes("test svg"))));
var hostEnv = Mock.Of<IWebHostEnvironment>(e => e.WebRootFileProvider == fileProvider.Object);
var tagHelper = new InlineSvgTagHelper(null, hostEnv, null, _settings, null)
var tagHelper = new InlineSvgTagHelper(null, hostEnv, null, _settings, null, Mock.Of<ILogger<InlineSvgTagHelper>>())
{
FileSource = "test.svg"
};
Expand All @@ -131,7 +132,7 @@ public void NoOutputIfMediaUrlNull()
{
var urlProvider = new Mock<IPublishedUrlProvider>();
urlProvider.Setup(p => p.GetMediaUrl(It.IsAny<IPublishedContent>(), It.IsAny<UrlMode>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<Uri>())).Returns((string)null!);
var tagHelper = new InlineSvgTagHelper(null, null, urlProvider.Object, _settings, null)
var tagHelper = new InlineSvgTagHelper(null, null, urlProvider.Object, _settings, null, Mock.Of<ILogger<InlineSvgTagHelper>>())
{
MediaItem = Mock.Of<IPublishedContent>(c => c.ContentType.ItemType == PublishedItemType.Media)
};
Expand All @@ -147,7 +148,7 @@ public void NoOutputIfMediaNotSvg()
var umbContent = Mock.Of<IPublishedContent>(c => c.ContentType.ItemType == PublishedItemType.Media);
var urlProvider = new Mock<IPublishedUrlProvider>();
urlProvider.Setup(p => p.GetMediaUrl(umbContent, It.IsAny<UrlMode>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<Uri>())).Returns("test.notsvg");
var tagHelper = new InlineSvgTagHelper(null, null, urlProvider.Object, _settings, null)
var tagHelper = new InlineSvgTagHelper(null, null, urlProvider.Object, _settings, null, Mock.Of<ILogger<InlineSvgTagHelper>>())
{
MediaItem = umbContent
};
Expand All @@ -169,7 +170,8 @@ public void NoOutputIfMediaNotFound()
null,
urlProvider.Object,
_settings,
null)
null,
Mock.Of<ILogger<InlineSvgTagHelper>>())
{
MediaItem = umbContent
};
Expand All @@ -191,7 +193,8 @@ public void ExpectedOutputIfValidMedia()
null,
urlProvider.Object,
_settings,
null)
null,
Mock.Of<ILogger<InlineSvgTagHelper>>())
{
MediaItem = umbContent
};
Expand All @@ -212,7 +215,7 @@ public void SanitizesJavascript()
.Setup(p => p.GetFileInfo(It.IsAny<string>()))
.Returns(Mock.Of<IFileInfo>(f => f.Exists && f.CreateReadStream() == new MemoryStream(Encoding.UTF8.GetBytes("<a xlink:href=\"javascript:alert('test');\">Click here</a><script attr=\"test\">test</script>end"))));
var hostEnv = Mock.Of<IWebHostEnvironment>(e => e.WebRootFileProvider == fileProvider.Object);
var tagHelper = new InlineSvgTagHelper(null, hostEnv, null, _settings, null)
var tagHelper = new InlineSvgTagHelper(null, hostEnv, null, _settings, null, Mock.Of<ILogger<InlineSvgTagHelper>>())
{
FileSource = "test.svg"
};
Expand Down
42 changes: 27 additions & 15 deletions Our.Umbraco.TagHelpers/InlineSvgTagHelper.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using HtmlAgilityPack;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Our.Umbraco.TagHelpers.Configuration;
using Our.Umbraco.TagHelpers.Utils;
Expand All @@ -27,14 +28,16 @@ public class InlineSvgTagHelper : TagHelper
private IPublishedUrlProvider _urlProvider;
private OurUmbracoTagHelpersConfiguration _globalSettings;
private AppCaches _appCaches;
private readonly ILogger<InlineSvgTagHelper> _logger;

public InlineSvgTagHelper(MediaFileManager mediaFileManager, IWebHostEnvironment webHostEnvironment, IPublishedUrlProvider urlProvider, IOptions<OurUmbracoTagHelpersConfiguration> globalSettings, AppCaches appCaches)
public InlineSvgTagHelper(MediaFileManager mediaFileManager, IWebHostEnvironment webHostEnvironment, IPublishedUrlProvider urlProvider, IOptions<OurUmbracoTagHelpersConfiguration> globalSettings, AppCaches appCaches, ILogger<InlineSvgTagHelper> logger)
{
_mediaFileManager = mediaFileManager;
_webHostEnvironment = webHostEnvironment;
_urlProvider = urlProvider;
_globalSettings = globalSettings.Value;
_appCaches = appCaches;
_logger = logger;
}

/// <summary>
Expand Down Expand Up @@ -204,25 +207,34 @@ public override void Process(TagHelperContext context, TagHelperOutput output)
if ((EnsureViewBox || (_globalSettings.OurSVG.EnsureViewBox && !IgnoreAppSettings)) || !string.IsNullOrEmpty(CssClass))
{
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(cleanedFileContents);
var svgs = doc.DocumentNode.SelectNodes("//svg");
foreach (var svgNode in svgs)
{
if (!string.IsNullOrEmpty(CssClass))
try {
doc.LoadHtml(cleanedFileContents);
var svgs = doc.DocumentNode.SelectNodes("//svg");
foreach (var svgNode in svgs)
{
svgNode.AddClass(CssClass);
if (!string.IsNullOrEmpty(CssClass))
{
svgNode.AddClass(CssClass);
}
if ((EnsureViewBox || (_globalSettings.OurSVG.EnsureViewBox && !IgnoreAppSettings)) && svgNode.Attributes.Contains("width") && svgNode.Attributes.Contains("height") && !svgNode.Attributes.Contains("viewbox"))
{
var width = StringUtils.GetDecimal(svgNode.GetAttributeValue("width", "0"));
var height = StringUtils.GetDecimal(svgNode.GetAttributeValue("height", "0"));
svgNode.SetAttributeValue("viewbox", $"0 0 {width} {height}");

svgNode.Attributes.Remove("width");
svgNode.Attributes.Remove("height");
}
}
if ((EnsureViewBox || (_globalSettings.OurSVG.EnsureViewBox && !IgnoreAppSettings)) && svgNode.Attributes.Contains("width") && svgNode.Attributes.Contains("height") && !svgNode.Attributes.Contains("viewbox"))
cleanedFileContents = doc.DocumentNode.OuterHtml;
}
catch(Exception exc) {
if(_logger.IsEnabled(LogLevel.Warning))
{
var width = StringUtils.GetDecimal(svgNode.GetAttributeValue("width", "0"));
var height = StringUtils.GetDecimal(svgNode.GetAttributeValue("height", "0"));
svgNode.SetAttributeValue("viewbox", $"0 0 {width} {height}");

svgNode.Attributes.Remove("width");
svgNode.Attributes.Remove("height");
_logger.LogWarning(exc, "Invalid svg markup");
}
return string.Empty;
}
cleanedFileContents = doc.DocumentNode.OuterHtml;
}

return cleanedFileContents;
Expand Down
Loading