Don't hard-code TLS/SSL protocol versions

Rule ND3122 Security

Matches methods setting ServicePointManager.SecurityProtocol, HttpClientHandler.SslProtocols, SslClientAuthenticationOptions.EnabledSslProtocols, Kestrel HttpsConnectionAdapterOptions.SslProtocols, or calling an SslStream.Authenticate... overload with an explicit SslProtocols. Microsoft's guidance since .NET Framework 4.7 / .NET Core is to leave the protocol at SystemDefault / None so that the operating system picks the strongest version available and drops SSL 3.0, TLS 1.0 and TLS 1.1 as they are retired: https://learn.microsoft.com/dotnet/framework/network-programming/tls

This is CWE-326 Inadequate Encryption Strength when the pinned list includes an obsolete version.

Flagged Code
public HttpClient CreateClient() {
   // flagged: freezes the application on TLS 1.2 and blocks TLS 1.3
   ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
   var handler = new HttpClientHandler { SslProtocols = SslProtocols.Tls12 };  // flagged
   return new HttpClient(handler);
}
Fixed Code
public HttpClient CreateClient() {
   // Target .NET Framework 4.7+ or .NET Core and leave the protocol to the OS
   // (SystemDefault): the strongest version is negotiated, retired ones drop out.
   return new HttpClient(new HttpClientHandler());
}

How to fix

Remove the assignment (or set SecurityProtocolType.SystemDefault / SslProtocols.None) and target .NET Framework 4.7+ or .NET Core, so the OS controls protocol selection. If a peer requires a specific version, isolate that configuration and suppress the issue with a justification.

CQLinq source

This rule is written in CQLinq, C# LINQ over the NDepend code model. It ships with every new NDepend project: you can read it, adapt it, or use it as a starting point for your own custom rules.

// <Name>Don't hard-code TLS/SSL protocol versions</Name>
// <Id>ND3122:DontHardCodeTlsSslProtocolVersions</Id>
warnif count > 0

// Third-party members are part of the code model only when the application uses them.
// The enum value assigned is not visible in IL, but pinning any version is discouraged:
// the OS negotiates the best protocol when the setting is left to SystemDefault.
let protocolPinningMembers = ThirdParty.Methods.Where(m =>
     (m.SimpleName == "set_SecurityProtocol"    && m.ParentType.FullName == "System.Net.ServicePointManager") ||
     (m.SimpleName == "set_SslProtocols"        && m.ParentType.FullNameEqualsAny("System.Net.Http.HttpClientHandler", "System.Net.Http.WebRequestHandler", "Microsoft.AspNetCore.Server.Kestrel.Https.HttpsConnectionAdapterOptions")) ||
     (m.SimpleName == "set_EnabledSslProtocols" && m.ParentType.FullNameEqualsAny("System.Net.Security.SslClientAuthenticationOptions", "System.Net.Security.SslServerAuthenticationOptions")) ||
     // SslStream.AuthenticateAsClient(host, certs, SslProtocols, checkRevocation) and its Server / Async siblings
     (m.ParentType.FullName == "System.Net.Security.SslStream" && m.SimpleName.StartsWith("Authenticate") && m.Name.Contains("SslProtocols")))
   .ToHashSetEx()

where protocolPinningMembers.Any()

from m in Application.Methods.UsingAny(protocolPinningMembers)
let protocolPinningMembersUsed = m.MethodsCalled.Intersect(protocolPinningMembers).ToArray()
select new {
  m,
  protocolPinningMembersUsed,
  Debt = 15.ToMinutes().ToDebt(),
  Severity = Severity.Medium
}

//<Expl>
// The *{0}* pins the TLS/SSL protocol version(s) through *{1}*.
// A hard-coded version list freezes the application on today's protocols:
// it keeps deprecated ones enabled after they are broken and blocks newer ones.
//</Expl>

//<Desc>
// Matches methods setting *ServicePointManager.SecurityProtocol*,
// *HttpClientHandler.SslProtocols*, *SslClientAuthenticationOptions.EnabledSslProtocols*,
// Kestrel *HttpsConnectionAdapterOptions.SslProtocols*, or calling an
// *SslStream.Authenticate...* overload with an explicit *SslProtocols*.
// Microsoft's guidance since .NET Framework 4.7 / .NET Core is to leave
// the protocol at *SystemDefault* / *None* so that the operating system
// picks the strongest version available and drops SSL 3.0, TLS 1.0 and
// TLS 1.1 as they are retired:
// https://learn.microsoft.com/dotnet/framework/network-programming/tls
//
// This is CWE-326 *Inadequate Encryption Strength* when the pinned list
// includes an obsolete version.
//
// **Flagged Code**
// ```csharp
// public HttpClient CreateClient() {
//    // flagged: freezes the application on TLS 1.2 and blocks TLS 1.3
//    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
//    var handler = new HttpClientHandler { SslProtocols = SslProtocols.Tls12 };  // flagged
//    return new HttpClient(handler);
// }
// ```
//
// **Fixed Code**
// ```csharp
// public HttpClient CreateClient() {
//    // Target .NET Framework 4.7+ or .NET Core and leave the protocol to the OS
//    // (SystemDefault): the strongest version is negotiated, retired ones drop out.
//    return new HttpClient(new HttpClientHandler());
// }
// ```
//</Desc>

//<HowToFix>
// Remove the assignment (or set *SecurityProtocolType.SystemDefault* /
// *SslProtocols.None*) and target .NET Framework 4.7+ or .NET Core, so
// the OS controls protocol selection. If a peer requires a specific
// version, isolate that configuration and suppress the issue with a
// justification.
//</HowToFix>

Security: all 39 items