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

Remove script and image element from Svg #1231

Merged
Merged
Show file tree
Hide file tree
Changes from 3 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
103 changes: 87 additions & 16 deletions src/modules/previewpane/SvgPreviewHandler/SvgPreviewControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.ComTypes;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Linq;
using Common;
using Common.Utilities;
using SvgPreviewHandler.Utilities;

namespace SvgPreviewHandler
{
Expand All @@ -16,7 +22,20 @@ namespace SvgPreviewHandler
/// </summary>
public class SvgPreviewControl : FormHandlerControl
{
private Stream dataSourceStream;
/// <summary>
/// Browser Control to display Svg.
/// </summary>
private WebBrowser browser;

/// <summary>
/// Text box to display the information about blocked elements from Svg.
/// </summary>
private RichTextBox textBox;

/// <summary>
/// Represent if any blocked element is found in the Svg.
/// </summary>
private bool foundFilteredElements = false;

/// <summary>
/// Start the preview on the Control.
Expand All @@ -26,30 +45,82 @@ public override void DoPreview<T>(T dataSource)
{
this.InvokeOnControlThread(() =>
{
WebBrowser browser = new WebBrowser();
this.dataSourceStream = new StreamWrapper(dataSource as IStream);

browser.DocumentStream = this.dataSourceStream;
browser.Dock = DockStyle.Fill;
browser.IsWebBrowserContextMenuEnabled = false;
browser.ScriptErrorsSuppressed = true;
browser.ScrollBarsEnabled = true;
this.Controls.Add(browser);
this.foundFilteredElements = false;
string svgData = null;
using (var stream = new StreamWrapper(dataSource as IStream))
{
using (var reader = new StreamReader(stream))
{
svgData = reader.ReadToEnd();
}
}

svgData = string.IsNullOrWhiteSpace(svgData) ? svgData : SvgPreviewHandlerHelper.RemoveElements(svgData, out this.foundFilteredElements);
if (this.foundFilteredElements)
{
this.AddTextBoxControl();
}

this.AddBrowserControl(svgData);
this.Resize += this.FormResized;
base.DoPreview(dataSource);
});
}

/// <summary>
/// Free resources on the unload of Preview.
/// Occurs when RichtextBox is resized.
/// </summary>
public override void Unload()
/// <param name="sender">Reference to resized control.</param>
/// <param name="e">Provides data for the ContentsResized event.</param>
private void RTBContentsResized(object sender, ContentsResizedEventArgs e)
{
base.Unload();
if (this.dataSourceStream != null)
var richTextBox = sender as RichTextBox;
richTextBox.Height = e.NewRectangle.Height + 5;
}

/// <summary>
/// Occurs when form is resized.
/// </summary>
/// <param name="sender">Reference to resized control.</param>
/// <param name="e">Provides data for the resize event.</param>
private void FormResized(object sender, EventArgs e)
{
if (this.foundFilteredElements)
{
this.dataSourceStream.Dispose();
this.dataSourceStream = null;
this.textBox.Width = this.Width;
}
}

/// <summary>
/// Adds a Web Browser Control to Control Collection.
/// </summary>
/// <param name="svgData">Svg to display on Browser Control.</param>
private void AddBrowserControl(string svgData)
{
this.browser = new WebBrowser();
this.browser.DocumentText = svgData;
this.browser.Dock = DockStyle.Fill;
this.browser.IsWebBrowserContextMenuEnabled = false;
this.browser.ScriptErrorsSuppressed = true;
this.browser.ScrollBarsEnabled = true;
this.Controls.Add(this.browser);
}

/// <summary>
/// Adds a Text Box in Controls for showing information about blocked elements.
/// </summary>
private void AddTextBoxControl()
{
this.textBox = new RichTextBox();
this.textBox.Text = "Some elements have been blocked to help prevent the sender from identifying your computer. Open this item to view all elements.";
this.textBox.BackColor = Color.LightYellow;
this.textBox.Multiline = true;
this.textBox.Dock = DockStyle.Top;
this.textBox.ReadOnly = true;
this.textBox.ContentsResized += this.RTBContentsResized;
this.textBox.ScrollBars = RichTextBoxScrollBars.None;
this.textBox.BorderStyle = BorderStyle.None;
this.Controls.Add(this.textBox);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
Expand All @@ -61,6 +62,7 @@
</Compile>
<Compile Include="SvgPreviewHandler.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Utilities\SvgPreviewHandlerHelper.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\common\PreviewHandlerCommon.csproj">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;

namespace SvgPreviewHandler.Utilities
{
/// <summary>
/// Helper utilities for Svg Preview Handler.
/// </summary>
public class SvgPreviewHandlerHelper
{
/// <summary>
/// Dictionary of elements that are blocked from Svg for preview pane.
/// </summary>
private static Dictionary<string, bool> blockedElementsName = new Dictionary<string, bool>
{
{ "script", true },
Copy link
Contributor

Choose a reason for hiding this comment

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

Script tags should always be blocked. But images are conditionally blocked. So should we display message for that?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Currently i'm blocking all the image tags. Not sure if we should allow relative path images as well.

{ "image", true },
};

/// <summary>
/// Remove blocked elements from the Input Svg.
/// </summary>
/// <param name="svgData">Input Svg to remove the blocked elements from.</param>
/// <param name="foundBlockedElement">Set true if any blocked element is present in the Svg otherwise false.</param>
/// <returns>Svg with removed blocked elements if present.</returns>
public static string RemoveElements(string svgData, out bool foundBlockedElement)
{
foundBlockedElement = false;
var doc = XDocument.Parse(svgData);
var elements = doc.Descendants().ToList();
foreach (XElement element in elements)
{
if (blockedElementsName.ContainsKey(element.Name.LocalName.ToLower()))
{
element.Remove();
foundBlockedElement = true;
}
}

return doc.ToString();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
Expand All @@ -18,10 +19,9 @@ public void SvgPreviewControl_ShouldAddBrowserControl_WhenDoPreviewCalled()
{
// Arrange
var svgPreviewControl = new SvgPreviewControl();
var mockStream = new Mock<IStream>();

// Act
svgPreviewControl.DoPreview(mockStream.Object);
svgPreviewControl.DoPreview(GetMockStream("<svg></svg>"));

// Assert
Assert.AreEqual(svgPreviewControl.Controls.Count, 1);
Expand All @@ -33,10 +33,9 @@ public void SvgPreviewControl_ShouldSetDocumentStream_WhenDoPreviewCalled()
{
// Arrange
var svgPreviewControl = new SvgPreviewControl();
var mockStream = new Mock<IStream>();

// Act
svgPreviewControl.DoPreview(mockStream.Object);
svgPreviewControl.DoPreview(GetMockStream("<svg></svg>"));

// Assert
Assert.IsNotNull(((WebBrowser)svgPreviewControl.Controls[0]).DocumentStream);
Expand All @@ -47,10 +46,9 @@ public void SvgPreviewControl_ShouldDisableWebBrowserContextMenu_WhenDoPreviewCa
{
// Arrange
var svgPreviewControl = new SvgPreviewControl();
var mockStream = new Mock<IStream>();

// Act
svgPreviewControl.DoPreview(mockStream.Object);
svgPreviewControl.DoPreview(GetMockStream("<svg></svg>"));

// Assert
Assert.AreEqual(((WebBrowser)svgPreviewControl.Controls[0]).IsWebBrowserContextMenuEnabled, false);
Expand All @@ -61,10 +59,9 @@ public void SvgPreviewControl_ShouldFillDockForWebBrowser_WhenDoPreviewCalled()
{
// Arrange
var svgPreviewControl = new SvgPreviewControl();
var mockStream = new Mock<IStream>();

// Act
svgPreviewControl.DoPreview(mockStream.Object);
svgPreviewControl.DoPreview(GetMockStream("<svg></svg>"));

// Assert
Assert.AreEqual(((WebBrowser)svgPreviewControl.Controls[0]).Dock, DockStyle.Fill);
Expand All @@ -75,10 +72,9 @@ public void SvgPreviewControl_ShouldSetScriptErrorsSuppressedProperty_WhenDoPrev
{
// Arrange
var svgPreviewControl = new SvgPreviewControl();
var mockStream = new Mock<IStream>();

// Act
svgPreviewControl.DoPreview(mockStream.Object);
svgPreviewControl.DoPreview(GetMockStream("<svg></svg>"));

// Assert
Assert.AreEqual(((WebBrowser)svgPreviewControl.Controls[0]).ScriptErrorsSuppressed, true);
Expand All @@ -89,13 +85,107 @@ public void SvgPreviewControl_ShouldSetScrollBarsEnabledProperty_WhenDoPreviewCa
{
// Arrange
var svgPreviewControl = new SvgPreviewControl();
var mockStream = new Mock<IStream>();

// Act
svgPreviewControl.DoPreview(mockStream.Object);
svgPreviewControl.DoPreview(GetMockStream("<svg></svg>"));

// Assert
Assert.AreEqual(((WebBrowser)svgPreviewControl.Controls[0]).ScrollBarsEnabled, true);
}

[TestMethod]
public void SvgPreviewControl_ShouldAddTextBox_IfBlockedElementsArePresent()
{
// Arrange
var svgPreviewControl = new SvgPreviewControl();

// Act
svgPreviewControl.DoPreview(GetMockStream("<svg>\r\n <script> valid </script>\r\n </svg>"));

// Assert
Assert.IsInstanceOfType(svgPreviewControl.Controls[0], typeof(RichTextBox));
Assert.IsInstanceOfType(svgPreviewControl.Controls[1], typeof(WebBrowser));
Assert.AreEqual(svgPreviewControl.Controls.Count, 2);
}

[TestMethod]
public void SvgPreviewControl_ShouldNotAddTextBox_IfNoBlockedElementsArePresent()
{
// Arrange
var svgPreviewControl = new SvgPreviewControl();

// Act
svgPreviewControl.DoPreview(GetMockStream("<svg>valid</svg>"));

// Assert
Assert.IsNotInstanceOfType(svgPreviewControl.Controls[0], typeof(RichTextBox));
Assert.AreEqual(svgPreviewControl.Controls.Count, 1);
}

[TestMethod]
public void SvgPreviewControl_ShouldAddValidTextBox_IfBlockedElementsArePresent()
{
// Arrange
var svgPreviewControl = new SvgPreviewControl();

// Act
svgPreviewControl.DoPreview(GetMockStream("<svg>\r\n <script> valid </script>\r\n </svg>"));
var textBox = svgPreviewControl.Controls[0] as RichTextBox;

// Assert
Assert.IsFalse(string.IsNullOrWhiteSpace(textBox.Text));
Assert.AreEqual(textBox.Dock, DockStyle.Top);
Assert.AreEqual(textBox.BackColor, Color.LightYellow);
Assert.IsTrue(textBox.Multiline);
Assert.IsTrue(textBox.ReadOnly);
Assert.AreEqual(textBox.ScrollBars, RichTextBoxScrollBars.None);
Assert.AreEqual(textBox.BorderStyle, BorderStyle.None);
}

[TestMethod]
public void SvgPreviewControl_RichTextBoxWidthShouldAdjust_IfParentControlWidthChanges()
{
// Arrange
var svgPreviewControl = new SvgPreviewControl();
svgPreviewControl.DoPreview(GetMockStream("<svg>\r\n <script> valid </script>\r\n </svg>"));
var textBox = svgPreviewControl.Controls[0] as RichTextBox;
var incrementParentControlWidth = 5;
var intialParentWidth = svgPreviewControl.Width;
var intitialTextBoxWidth = textBox.Width;
var finalParentWidth = intialParentWidth + incrementParentControlWidth;

// Act
svgPreviewControl.Width += incrementParentControlWidth;

// Assert
Assert.AreEqual(intialParentWidth, intitialTextBoxWidth);
Assert.AreEqual(finalParentWidth, textBox.Width);
}

private IStream GetMockStream(string streamData)
{
var mockStream = new Mock<IStream>();
var streamBytes = Encoding.UTF8.GetBytes(streamData);

var streamMock = new Mock<IStream>();
var firstCall = true;
streamMock
.Setup(x => x.Read(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<IntPtr>()))
.Callback<byte[], int, IntPtr>((buffer, countToRead, bytesReadPtr) =>
{
if (firstCall)
{
Array.Copy(streamBytes, 0, buffer, 0, streamBytes.Length);
Marshal.WriteInt32(bytesReadPtr, streamBytes.Length);
firstCall = false;
}
else
{
Marshal.WriteInt32(bytesReadPtr, 0);
}
});

return streamMock.Object;
}
}
}
Loading