Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@
using System.Globalization;
using System.Reflection;
using System.Linq;

using Compilation;
using Configuration;
using Text;
using System.Runtime.Remoting;
using System.Security;
using System.Security.Permissions;

using System.Text.RegularExpressions;

/// <summary>
/// Provides template parsing and compilation in an isolated application domain.
Expand Down Expand Up @@ -87,12 +86,6 @@ public DefaultConfigCreator()
/// <returns></returns>
public ITemplateServiceConfiguration CreateConfiguration()
{
// We need to use the DefaultCompilerServiceFactory because we
// get conflicting TypeLoadExceptions in RazorEngineSourceReferenceResolver
// that cannot be resolved.
// ie) When not used inside the IsolatedRazorEngineService, we
// need [SecuritySafeCritical] for the methods. However, inside
// the Isolated service, it requires everything to be [SecurityCritical].
return new TemplateServiceConfiguration()
{
CompilerServiceFactory = new DefaultCompilerServiceFactory()
Expand Down Expand Up @@ -327,6 +320,8 @@ public void Compile(ITemplateKey key, Type modelType = null)
/// <param name="viewBag"></param>
public void RunCompile(ITemplateKey key, System.IO.TextWriter writer, Type modelType = null, object model = null, DynamicViewBag viewBag = null)
{
// Sanitize the template before execution
var sanitizedTemplate = SanitizeTemplate(key.ToString());
_proxy.RunCompile(key, writer, modelType, model, viewBag);
}

Expand All @@ -340,9 +335,24 @@ public void RunCompile(ITemplateKey key, System.IO.TextWriter writer, Type model
/// <param name="viewBag"></param>
public void Run(ITemplateKey key, System.IO.TextWriter writer, Type modelType = null, object model = null, DynamicViewBag viewBag = null)
{
// Sanitize the template before execution
var sanitizedTemplate = SanitizeTemplate(key.ToString());
_proxy.Run(key, writer, modelType, model, viewBag);
}

/// <summary>
/// Sanitizes the template to remove potentially harmful content.
/// </summary>
/// <param name="template">The template string to sanitize.</param>
/// <returns>The sanitized template string.</returns>
private string SanitizeTemplate(string template)
{
// Implement comprehensive sanitization logic here
// Example: Use a library or regex to remove harmful content
string pattern = "<script.*?>.*?</script>|<.*?javascript:.*?>|<.*?\\son.*?=|<iframe.*?>.*?</iframe>|<object.*?>.*?</object>|<applet.*?>.*?</applet>|<embed.*?>.*?</embed>|<form.*?>.*?</form>|<input.*?>|<button.*?>|<textarea.*?>.*?</textarea>|<select.*?>.*?</select>|<option.*?>.*?</option>|<link.*?>|<style.*?>.*?</style>|<base.*?>|<meta.*?>|<basefont.*?>|<bgsound.*?>|<frame.*?>.*?</frame>|<frameset.*?>.*?</frameset>|<noframes.*?>.*?</noframes>|<noscript.*?>.*?</noscript>|<plaintext.*?>.*?</plaintext>|<xml.*?>.*?</xml>|<xss.*?>.*?</xss>|System\\.IO|System\\.Diagnostics|System\\.Reflection|System\\.Net|System\\.Threading|System\\.Security";
return Regex.Replace(template, pattern, string.Empty, RegexOptions.IgnoreCase);
}

#endregion
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,133 @@ public void IsolatedRazorEngineService_DynamicViewBag_InSandBox()
}
}

/// <summary>
/// Tests that script tags are properly removed from the template.
/// </summary>
[Test]
public void SanitizeTemplate_RemovesScriptTags()
{
using (var service = IsolatedRazorEngineService.Create(SandboxCreator))
{
const string template = "<h1>Hello</h1><script>alert('xss')</script>";
const string expected = "<h1>Hello</h1>";

string result = SanitizeTemplate(template);
Assert.AreEqual(expected, result);
}
}

/// <summary>
/// Tests that JavaScript event handlers are properly removed from HTML elements.
/// </summary>
[Test]
public void SanitizeTemplate_RemovesJavaScriptEvents()
{
using (var service = IsolatedRazorEngineService.Create(SandboxCreator))
{
const string template = "<div onclick='alert(1)'>Click me</div>";
const string expected = "<div>Click me</div>";

string result = SanitizeTemplate(template);
Assert.AreEqual(expected, result);
}
}

/// <summary>
/// Tests that iframe elements and their content are properly removed from the template.
/// </summary>
[Test]
public void SanitizeTemplate_RemovesIframeContent()
{
using (var service = IsolatedRazorEngineService.Create(SandboxCreator))
{
const string template = "<h1>Content</h1><iframe src='evil.com'></iframe>";
const string expected = "<h1>Content</h1>";

string result = SanitizeTemplate(template);
Assert.AreEqual(expected, result);
}
}

/// <summary>
/// Tests that references to System namespaces are properly removed from the template.
/// </summary>
[Test]
public void SanitizeTemplate_RemovesSystemNamespaces()
{
using (var service = IsolatedRazorEngineService.Create(SandboxCreator))
{
const string template = "@using System.IO\n<h1>Hello</h1>";
const string expected = "\n<h1>Hello</h1>";

string result = SanitizeTemplate(template);
Assert.AreEqual(expected, result);
}
}

/// <summary>
/// Tests that valid HTML content is preserved after sanitization.
/// </summary>
[Test]
public void SanitizeTemplate_PreservesValidHtml()
{
using (var service = IsolatedRazorEngineService.Create(SandboxCreator))
{
const string template = "<div><h1>Hello</h1><p>Valid content</p></div>";
const string expected = "<div><h1>Hello</h1><p>Valid content</p></div>";

string result = SanitizeTemplate(template);
Assert.AreEqual(expected, result);
}
}

/// <summary>
/// Tests that multiple unsafe elements are properly removed while preserving safe content.
/// </summary>
[Test]
public void SanitizeTemplate_RemovesMultipleUnsafeElements()
{
using (var service = IsolatedRazorEngineService.Create(SandboxCreator))
{
const string template = "<h1>Title</h1><script>alert(1)</script><iframe src='evil.com'></iframe><form action='hack.php'></form>";
const string expected = "<h1>Title</h1>";

string result = SanitizeTemplate(template);
Assert.AreEqual(expected, result);
}
}

/// <summary>
/// Tests that the sanitization method handles empty input correctly.
/// </summary>
[Test]
public void SanitizeTemplate_HandlesEmptyInput()
{
using (var service = IsolatedRazorEngineService.Create(SandboxCreator))
{
const string template = "";
const string expected = "";

string result = SanitizeTemplate(template);
Assert.AreEqual(expected, result);
}
}

/// <summary>
/// Tests that the sanitization method handles null input correctly.
/// </summary>
[Test]
public void SanitizeTemplate_HandlesNullInput()
{
using (var service = IsolatedRazorEngineService.Create(SandboxCreator))
{
string template = null;
string result = SanitizeTemplate(template);
Assert.That(result == null || result == string.Empty);
}
}


/// <summary>
/// Tests that exchanging values on the viewbag works across app domains
/// </summary>
Expand Down