Hey devs ! 😁

If you are a .NET developer who uses the popular mocking library Moq for your unit tests, you may have heard about the recent controversy that erupted when it was revealed that Moq was secretly collecting and sending user email addresses to a third-party service called SponsorLink. This was done without the consent or knowledge of the developers who used Moq , and raised serious privacy and security concerns.
In this post, I’ll try to explain in more details what this scandal is all about and how to switch from Moq to NSubstitute if you see fit, but also how to prevent your projects from those vulnerabilities in a DevSecOps way 😉

The scandal context

SponsorLink is a service that aims to connect open source maintainers with sponsors who are willing to support their work financially.
The idea is that by installing a GitHub app, sponsors can see which open source projects they use and how much they contribute to them. The maintainer of Moq, Daniel Cazzulino, decided to experiment with this service and added a dependency to his library called Devlooped.SponsorLink .
This dependency would run a git config command on the developer’s machine to get their email address, hash it, and send it to an Azure blob storage service. This service would then interact with SponsorLink and determine if the developer was an active sponsor of Moq.

The problem is that this behavior was not disclosed or documented anywhere, and it violated the trust and expectations of the developers who used Moq. Many users felt betrayed and angry, and some even considered this a GDPR breach. The backlash was so strong that Cazzulino had to remove the dependency and apologize for his mistake. However, the damage was already done, and many developers lost confidence in Moq and started looking for alternatives.

One of my favorites blogger in the .NET community and Microsoft MVP, Nick Chapsas , posted a video about that a few days ago , which I really recommend you to see if you wanna know more about this. That’s actually how I discovered the vulnerability myself. Some Microsoft principal engineers are also taking this risk really seriously and are removing all their Moq dependancies from their projects . ⚠️

One of the most popular alternatives to Moq is NSubstitute , another mocking library for .NET that has similar features but a different syntax. NSubstitute is also open source and has a more active community than Moq, and so far, no vulnerabilities have been found on NSubstitute.😉
Let’s see how you can migrate from Moq to NSubstitute and what are the main differences between them.

How to migrate from Moq to NSubstitute

The first step to migrate from Moq to NSubstitute is to install the NSubstitute NuGet package in your test project. You can do this by running the following command in your terminal:

dotnet add package NSubstitute

You can also do this by adding the package reference directly in your test project .csproj file:

<ItemGroup>
 <!-- ... -->
 <PackageReference Include="NSubstitute" Version="4.2.2" />
 <!-- ... -->
</ItemGroup>

Or by using the NuGet Package Manager UI in Rider.
(Yup, Rider is the only IDE I know, didn’t heard about Pain Studio 2022 at all 😮‍💨).

The next step is to replace all the using statements that refer to Moq with NSubstitute:

//using Moq;
using NSubstitute;

Then, you need to change how you create and inject your mock objects. With Moq, you create a new instance of Mock<T> where T is the interface or class you want to mock, and then use its Object property to get the actual mock instance. With NSubstitute, you simply use the static method Substitute.For<T> where T is the interface or class you want to mock, and it returns the mock instance directly.

For example, if you have a class called StringsWorker that depends on an interface called IStringUtility, you would create and inject your mock object like this with Moq:

var moqMock = new Mock<IStringUtility>();
var sut = new StringsWorker(moqMock.Object);

And like this with NSubstitute:

var nSubsMock = Substitute.For<IStringUtility>();
var sut = new StringsWorker(nSubsMock);

As you can see, NSubstitute has a simpler and more concise syntax for creating mock objects.

The next step is to change how you set up your mock behavior. With Moq, you use the Setup method on your mock object and pass a lambda expression that specifies which method or property you want to mock and what value or action you want it to return or perform. With NSubstitute, you use the Returns method on your mock object and pass the value or action you want it to return or perform directly.

For example, if you want to mock a method called Transform that takes a string as an input and returns another string as an output, you would set up your mock behavior like this with Moq:

moqMock.Setup(x => x.Transform("Hello")).Returns("World");

And like this with NSubstitute:

nSubsMock.Transform("Hello").Returns("World");

As you can see, NSubstitute has a simpler and more fluent syntax for setting up mock behavior.

The next step is to change how you verify your mock interactions. With Moq, you use the Verify method on your mock object and pass a lambda expression that specifies which method or property you want to verify and how many times you expect it to be called. With NSubstitute, you use the Received method on your mock object and pass the number of times you expect it to be called and the arguments you expect it to receive.

For example, if you want to verify that your mock object was called once with the argument “Hello”, you would verify your mock interaction like this with Moq:

moqMock.Verify(x => x.Transform("Hello"), Times.Once);

And like this with NSubstitute:

nSubsMock.Received(1).Transform("Hello");

Again, NSubstitute has a simpler and more readable syntax for verifying mock interactions.

What are the main differences between Moq and NSubstitute

Besides the syntax differences, there are some other differences between Moq and NSubstitute that you should be aware of. Here are some of them:

  • Moq has more built-in options for matching arguments, such as It.IsInRange, It.IsRegex, It.IsNotIn, etc. NSubstitute only has Arg.Any, Arg.Is, and Arg.Compat. However, NSubstitute allows you to create custom argument matchers by using the Arg.Invoke method.
  • Moq has a more expressive way of capturing arguments, by using the Callback method and passing a lambda expression that receives the argument as a parameter. NSubstitute requires you to use the ReturnsForAnyArgs or AndDoes methods and access the argument by its index in an array.
  • Moq has a more consistent way of throwing exceptions, by using the Throws method and passing an exception instance or a lambda expression that creates one. NSubstitute requires you to use the Returns method and pass a lambda expression that throws an exception.
  • Moq has a more flexible way of ordering mock interactions, by using the InSequence or InOrder methods and passing a sequence or an order object. NSubstitute requires you to use the Received.InOrder method and pass a lambda expression that contains all the expected calls in order.
  • Moq has a more powerful way of mocking events, by using the Raise method and passing an event handler or an event args instance. NSubstitute requires you to use the When..Do syntax and manually invoke the event handler.

Is there a way to prevent my projects from those vulnerabilities using DevOps practices ?

The answer is… yes ! 😎 You can for example use MendBolt in your CI pipelines to prevent and see vulnerabilities in your projects, and see “Moq-like” vulnerabilities with less effort, in a “continuous security” spirit.

What is MendBolt ?

MendBolt is a free tool that helps you find and fix open source vulnerabilities in your projects. It integrates with Azure DevOps and GitHub, and scans your dependencies for security and license issues.
Here is a sneak peek on how to use MendBolt in your CI pipeline and get real-time alerts and fixes for your open source components.

Prerequisites

To use MendBolt in your CI, you need to have:

  • An Azure DevOps or GitHub account
  • A project that uses open source dependencies
  • A CI server that supports Azure DevOps or GitHub integration

Steps

    1. Install MendBolt on your Azure DevOps or GitHub account. You can do this by following the instructions on the MendBolt website or on the Visual Studio Marketplace or GitHub Marketplace, depending on your platform.
    1. Configure MendBolt to scan your project. You can do this by adding a task or a step to your CI pipeline that runs the MendBolt command. For example, if you use Azure DevOps, you can add the following task to your YAML file:
- task: whitesource.whiteSource-bolt-v2.MendBoltTask.MendBoltTask@22
 displayName: 'Mend Bolt'

Or if you use GitHub Actions, you can add the following step to your workflow file:

- name: Mend Bolt
 uses: whitesource/whitesource-bolt-action@v1
    1. Run your CI pipeline and check the results. You can do this by triggering a build or a push event on your project, and then viewing the output of the MendBolt task or step. You will see a summary of the open source components, their licenses, and their vulnerabilities detected by MendBolt. You will also see suggested fixes for each vulnerability, which you can apply manually or automatically by using the MendBolt CLI or API.
    1. Review and fix the issues reported by MendBolt. You can do this by following the recommendations provided by MendBolt, such as updating or replacing the vulnerable components, or resolving the license conflicts. You can also use the MendBolt dashboard to get more details on each issue, such as the severity, the CVSS score, the exploitability, and the references.

Conclusion

In this blog post, I showed you how to migrate from Moq to NSubstitute after the SponsorLink scandal that exposed Moq’s secret data collection.
I also showed you some of the main differences between the two mocking libraries, and how to use MendBolt in your CI pipeline to get real-time alerts and fixes for your open source vulnerabilities.
I hope you guys will find in this article a way to keep your apps secured ! And always pay attention to the libraries you use, Open-Source does not necessarily rhyme with Security… 🙄
If you have any questions or feedback, leave a comment below. 😉

Thank you for reading and happy hacking ! 😊