I have a custom model binder which takes user input in HTML format, sanitize the HTML input by removing any script or XSS threat and returns the sanitized HTML string:
public class AllowAndSanitizeHtmlBinder : IModelBinder
{
private HtmlSanitizer _htmlSanitizer = HtmlSanitizerFactory.CreateInstance();
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var request = controllerContext.HttpContext.Request;
var name = bindingContext.ModelName;
// get the un-validated user input in HTML format
var unsanitizedMessageHtmlString = request.Unvalidated[name];
// removed script or any XSS threat from user input
return _htmlSanitizer.Sanitize(unsanitizedMessageHtmlString);
}
}
I am using HtmlSanitizer Nuget package to do this. I need to configure this object and tell it to add "nofollow" to all the links.
So I have created HtmlSanitizerFactory to do this configuration:
public static class HtmlSanitizerFactory
{
public static HtmlSanitizer CreateInstance()
{
var sanitizer = new HtmlSanitizer();
sanitizer.PostProcessNode += (s, e) => (e.Node as IHtmlAnchorElement)?.SetAttribute("rel", "nofollow noreferrer");
return sanitizer;
}
}
I am not entirely happy with this code. The reason is, in the future, a developer may forget that HtmlSanitizerFactory should be used for instantiating HtmlSanitizer... so he would create his new ModelBinder and initialize the object like:
HtmlSanitizer _htmlSanitizer = new HtmlSanitizer();
This would introduce a bug because links won't be converted to "nofollow"
I think DI would have worked much better here, but I don't know how to inject a class into a custom ModelBinder