Creating a ModelBinder to Sanitize user input in HTML format
Developer gone 'rogue'


  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
  ..


For this to happen, a developer has to dive in your code and deliberately change


private HtmlSanitizer _htmlSanitizer = HtmlSanitizerFactory.CreateInstance();



to

 private HtmlSanitizer _htmlSanitizer = new HtmlSanitizer();


He would probably have a good reason to. And if not, other developers in the team should review and challenge that change. After all, it's best practice to perform code reviews, typically with pull requests.



Test-Driven Development

You have introduced a dependency on a third-party class in your code. Generally, when providing such API code, you would like to reduce the hard dependencies to a minimum. What you could do, is use its interface IHtmlSanitizer instead. This allows for white-bow testing such as

string unsanitizedMessageHtmlString = ".."; // some input
IHtmlSanitizer sanitizer = Mock&lt;IHtmlSanitizer&gt;(); // using some mock method ..
sanitizer.AssertWasCalled(m =&gt; m.Sanitize(unsanitizedMessageHtmlString));


You could go a step further and refactor out the third-party dependency entirely by providing your own interface. An adapter class mapping the third-party interface to yours can than be provided in a different library if you wish interop with that API. I am not suggesting this is a must. You should decide which dependencies you want to include in your own API.

Regression


  But it is likely that a year from now, we need to developer a new model binder to sanitize a different HTML input. It is at that point
  that the developer could forget to initialize the sanitizer using the
  factory.


In the comments you stated you are worried for new developers to use that class in a wrong way, possibly introducing bugs. These bugs should become apparent when providing unit tests. With white-box tests you can check whether PostProcessNode was registered to. And with black-box tests you could easily verify the content of the sanitized string.

It also wouldn't hurt to provide a team Wiki with guidelines, do's and dont's for new developers to get acquainted with the conventions and avoidable regressions.



Dependency Injection

Instead of calling the factory to get an instance of HtmlSanitizer, you should use Dependency Injection. One way to do so is to have the dependency injected through the constructor.

public AllowAndSanitizeHtmlBinder(IHtmlSanitizer htmlSanitizer)
{
    _htmlSanitizer = htmlSanitizer;
}


You would typically have an Ioc container where you register the dependencies. I don't know enough about the IModelBinder's lifetime scope, so you may also want to register these to the container.

container.RegisterSingleInstance&lt;IHtmlSanitizer&gt;(HtmlSanitizerFactory.CreateInstance());




Resource Management

I don't like the third-party class HtmlSanitizer. The events are never cleared and the class does not implement IDisposable. It's a memory leak waiting to occur. You need to find a way to dispose these instances yourself and make sure to unsubscribe from:


 sanitizer.PostProcessNode += (s, e) =&gt;  (e.Node 
     as IHtmlAnchorElement)?.SetAttribute("red", "nofollow noreferrer");