diff --git a/docs/DE0001.md b/docs/DE0001.md index 762813a..238594a 100644 --- a/docs/DE0001.md +++ b/docs/DE0001.md @@ -6,26 +6,28 @@ T:System.Security.SecureString ## Motivation -* The purpose of `SecureString` is to avoid having secrets stored in the process +* The purpose of [`SecureString`][SecureString] is to avoid having secrets stored in the process memory as plain text. -* However, even on Windows `SecureString` doesn't exist as an OS concept - - It just makes the window getting the plain text shorter; it doesn't fully +* However, even on Windows, [`SecureString`][SecureString] doesn't exist as an OS concept. + * It just makes the window getting the plain text shorter; it doesn't fully prevent it as .NET still has to convert the string to a plain text - representation - - The benefit is that the plain text representation doesn't hang around - as an instance of `System.String` -- the lifetime of the native buffer is + representation. + * The benefit is that the plain text representation doesn't hang around + as an instance of [`System.String`][String] -- the lifetime of the native buffer is shorter. * The contents of the array is unencrypted except on .NET Framework. - - In .NET Framework, the contents of the internal char array is encrypted. - We've trouble with supporting the encryption in all environments, either + * In .NET Framework, the contents of the internal char array is encrypted. + .NET doesn't support encryption in all environments, either due to missing APIs or key management issues. ## Recommendation -Don't use `SecureString` for new code. When porting code to .NET Core, consider -that the contents of the array will not be encrypted in memory. +Don't use [`SecureString`][SecureString] for new code. When porting code to .NET Core, consider +that the contents of the array are not encrypted in memory. The general approach of dealing with credentials is to avoid them and instead rely on other means to authenticate, such as certificates or Windows authentication. +[SecureString]: https://docs.microsoft.com/dotnet/api/system.security.securestring +[String]: https://docs.microsoft.com/dotnet/api/system.string diff --git a/docs/DE0002.md b/docs/DE0002.md index 7b81f4d..b65926e 100644 --- a/docs/DE0002.md +++ b/docs/DE0002.md @@ -6,7 +6,7 @@ N:System.Security.Permissions ## Motivation -When .NET Framework was originally it tried to provide a managed sandbox that +When .NET Framework was originally created, it tried to provide a managed sandbox that allows restricting what parts of the process can do. Unfortunately, providing an additional security boundary on top of the operating system has been proven to be too difficult. As a result, there have been quite a few exploits that allows diff --git a/docs/DE0003.md b/docs/DE0003.md index 037a0a2..f238446 100644 --- a/docs/DE0003.md +++ b/docs/DE0003.md @@ -9,9 +9,9 @@ T:System.Net.HttpWebRequest ## Motivation - `WebRequest`-based APIs are on life-support only (i.e. only critical fixes, no +`WebRequest`-based APIs are on life-support only (that is, only critical fixes, no new improvements, enhancements). ## Recommendation -Use `HttpClient` instead. +Use [`HttpClient`](https://docs.microsoft.com/dotnet/api/system.net.http.httpclient) instead. diff --git a/docs/DE0004.md b/docs/DE0004.md index f4d4369..7306480 100644 --- a/docs/DE0004.md +++ b/docs/DE0004.md @@ -6,9 +6,9 @@ T:System.Net.WebClient ## Motivation - `WebClient` is on life-support only (i.e. only critical fixes, no new + `WebClient` is on life-support only (that is, only critical fixes, no new improvements, enhancements). ## Recommendation -Use `HttpClient` instead. +Use [`HttpClient`](https://docs.microsoft.com/dotnet/api/system.net.http.httpclient) instead. diff --git a/docs/DE0005.md b/docs/DE0005.md index 265e1b4..7435f1f 100644 --- a/docs/DE0005.md +++ b/docs/DE0005.md @@ -6,10 +6,9 @@ T:System.Net.Mail.SmtpClient ## Motivation -`SmtpClient` does not support many modern protocols. It is compat-only. It's -great for one off emails from tools, but does not scale to modern requirements -of the protocol. +[`SmtpClient`](https://docs.microsoft.com/dotnet/api/system.net.mail.smtpclient) doesn't support many modern protocols. It is compat-only. It's +great for one off emails from tools, but doesn't scale to modern requirements of the protocol. ## Recommendation -Use `MailKit` or other libraries. +Use [`MailKit`](https://github.com/jstedfast/MailKit) or other libraries. \ No newline at end of file diff --git a/docs/DE0006.md b/docs/DE0006.md index 27de968..d37787c 100644 --- a/docs/DE0006.md +++ b/docs/DE0006.md @@ -18,38 +18,67 @@ T:System.Collections.CaseInsensitiveHashCodeProvider ## Motivation When .NET was created, generic data types didn't exist, which is why the -collection types in `System.Collections` are untyped. However, since then -generic data types and thus a new set of collections in -`System.Collections.Generic` were made available. +collection types in the [`System.Collections`][collections] namespace are untyped. However, since then, +generic data types were introduced and thus a new set of collections +were made available in the [`System.Collections.Generic`][generic] and +[`System.Collections.ObjectModel`][objectmodel] namespaces. ## Recommendation For new code, you shouldn't use non-generic collections: -* **Error prone**. Since non-generic collections are untyped, it requires frequent +* **Error prone**: since non-generic collections are untyped, it requires frequent casting between `object` and the actual type you're expecting. Since the compiler - can't check that your types are consistent it's easer to put the wrong type in + can't check that your types are consistent, it's easier to put the wrong type in the wrong collection. -* **Less performant**. Generic collection have the advantage that value types +* **Less performant**: generic collections have the advantage that value types don't have to be boxed as object. For instance, a `List` stores its data in an `int[]`. That's far better than storing the data in `object[]` as that requires boxing. -Consult the following table which how the non-generic collection types can be -replaced by their generic counterparts in `System.Collections.Generic`: +The following table shows how the non-generic collection types can be +replaced by their generic counterparts from the [`System.Collections.Generic`][generic] or +[`System.Collections.ObjectModel`][objectmodel] namespaces: -| Type | Replacement | -|-----------------------------------|------------------------------------------------------------| -| `ArrayList` | `List` | -| `Hashtable` | `Dictionary` | -| `Queue` | `Queue` | -| `Stack` | `Stack` | -| `SortedList` | `SortedList` | -| `DictionaryEntry` | `KeyValuePair` | -| `DictionaryBase` | `Dictionary or KeyedCollection` | -| `CollectionBase` | `Collection` | -| `ReadOnlyCollectionBase` | `ReadOnlyCollection` | -| `Comparer` | `Comparer` | -| `CaseInsensitiveComparer` | `StringComparer.OrdinalIgnoreCase` | -| `CaseInsensitiveHashCodeProvider` | `StringComparer.OrdinalIgnoreCase` | +| Type | Replacement | +|----------------------------------------------------------------------|-----------------------------------------------------------------------------------------------| +| [`ArrayList`][arraylist] | [`List`][list] | +| [`CaseInsensitiveComparer`][caseinsensitivecomparer] | [`StringComparer.OrdinalIgnoreCase`][ordinalignorecase] | +| [`CaseInsensitiveHashCodeProvider`][caseinsensitivehashcodeprovider] | [`StringComparer.OrdinalIgnoreCase`][ordinalignorecase] | +| [`CollectionBase`][collectionbase] | [`Collection`][collection-1] | +| [`Comparer`][comparer] | [`Comparer`][comparer-1] | +| [`DictionaryBase`][dictionarybase] | [`Dictionary`][dictionary] or [`KeyedCollection`][keyedcollection] | +| [`DictionaryEntry`][dictionaryentry] | [`KeyValuePair`][keyvaluepair] | +| [`Hashtable`][hashtable] | [`Dictionary`][dictionary] | +| [`Queue`][queue] | [`Queue`][queue-1] | +| [`ReadOnlyCollectionBase`][readonlycollectionbase] | [`ReadOnlyCollection`][readonlycollection] | +| [`SortedList`][sortedlist] | [`SortedList`][sortedlist-2] | +| [`Stack`][stack] | [`Stack`][stack-1] | + +[arraylist]: https://docs.microsoft.com/dotnet/api/system.collections.arraylist +[caseinsensitivecomparer]: https://docs.microsoft.com/dotnet/api/system.collections.caseinsensitivecomparer +[caseinsensitivehashcodeprovider]: https://docs.microsoft.com/dotnet/api/system.collections.caseinsensitivehashcodeprovider +[collection-1]: https://docs.microsoft.com/dotnet/api/system.collections.objectmodel.collection-1 +[collectionbase]: https://docs.microsoft.com/dotnet/api/system.collections.collectionbase +[collections]: https://docs.microsoft.com/dotnet/api/system.collections +[comparer]: https://docs.microsoft.com/dotnet/api/system.collections.comparer +[comparer-1]: https://docs.microsoft.com/dotnet/api/system.collections.generic.comparer-1 +[dictionary]: https://docs.microsoft.com/dotnet/api/system.collections.generic.dictionary-2 +[dictionarybase]: https://docs.microsoft.com/dotnet/api/system.collections.dictionarybase +[dictionaryentry]: https://docs.microsoft.com/dotnet/api/system.collections.dictionaryentry +[generic]: https://docs.microsoft.com/dotnet/api/system.collections.generic +[hashtable]: https://docs.microsoft.com/dotnet/api/system.collections.hashtable +[keyedcollection]: https://docs.microsoft.com/dotnet/api/system.collections.objectmodel.keyedcollection-2 +[keyvaluepair]: https://docs.microsoft.com/dotnet/api/system.collections.generic.keyvaluepair-2 +[list]: https://docs.microsoft.com/dotnet/api/system.collections.generic.list-1 +[objectmodel]: https://docs.microsoft.com/dotnet/api/system.collections.objectmodel +[ordinalignorecase]: https://docs.microsoft.com/dotnet/api/system.stringcomparer.ordinalignorecase +[queue]: https://docs.microsoft.com/dotnet/api/system.collections.queue +[queue-1]: https://docs.microsoft.com/dotnet/api/system.collections.generic.queue-1 +[readonlycollection]: https://docs.microsoft.com/dotnet/api/system.collections.objectmodel.readonlycollection-1 +[readonlycollectionbase]: https://docs.microsoft.com/dotnet/api/system.collections.readonlycollectionbase +[stack]: https://docs.microsoft.com/dotnet/api/system.collections.stack +[stack-1]: https://docs.microsoft.com/dotnet/api/system.collections.generic.stack-1 +[sortedlist]: https://docs.microsoft.com/dotnet/api/system.collections.sortedlist +[sortedlist-2]: https://docs.microsoft.com/dotnet/api/system.collections.generic.sortedlist-2 \ No newline at end of file diff --git a/docs/DE0007.md b/docs/DE0007.md index 719ad1f..ea459a2 100644 --- a/docs/DE0007.md +++ b/docs/DE0007.md @@ -10,8 +10,8 @@ F:System.PlatformID.MacOSX ## Motivation -Certain enum values on `System.PlatformID` are no longer in use and will never -be returned from `Environment.OSVersion.Platform`. +Certain enum values on [`System.PlatformID`][PlatformID] are no longer in use and will never +be returned from [`Environment.OSVersion.Platform`][Platform]. ## Recommendation @@ -24,8 +24,11 @@ the correct ones. | Win32Windows | Unused | WinCE | Unused | Xbox | Unused -| MacOSX | Replaced. This value was only returned by Silverlight. On .NET Core, it will return `Unix` +| MacOSX | Replaced. This value was only returned by Silverlight. On .NET Core, it returns `Unix`. -Code that compares `Environment.OSVersion.Platform` to `PlatformID.MacOSX` -should use the newer `RuntimeInformation.IsOSPlatform(OSPlatform.OSX)` API -instead. +Code that compares [`Environment.OSVersion.Platform`][Platform] to [`PlatformID.MacOSX`][PlatformID] +should use the newer [`RuntimeInformation.IsOSPlatform(OSPlatform.OSX)`][IsOSPlatform] method instead. + +[IsOSPlatform]: https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.runtimeinformation.isosplatform +[Platform]: https://docs.microsoft.com/dotnet/api/system.operatingsystem.platform +[PlatformID]: https://docs.microsoft.com/dotnet/api/system.platformid \ No newline at end of file diff --git a/docs/DE0008.md b/docs/DE0008.md index 1878b92..fe83842 100644 --- a/docs/DE0008.md +++ b/docs/DE0008.md @@ -9,13 +9,17 @@ M:System.Threading.Thread.set_CurrentUICulture(System.Globalization.CultureInfo) ## Motivation -These properties don't work consistently across operating systems and runtimes when used against +Thread culture properties don't work consistently across operating systems and runtimes when used against any thread other than the current thread. -* In .NET Core, an `InvalidOperationException` is thrown if a thread tries -to read or write these properties on a different thread. -* In .NET Framework, setting these properties isn't reliable for a different thread. +* In .NET Core, if a thread tries to read or write these properties on a different thread, an [`InvalidOperationException`][InvalidOperationException] is thrown. + +* In .NET Framework, setting these properties isn't reliable for a different thread. ## Recommendation -Use `CultureInfo.CurrentCulture` and `CultureInfo.CurrentUICulture` instead. +Use [`CultureInfo.CurrentCulture`][CurrentCulture] and [`CultureInfo.CurrentUICulture`][CurrentUICulture] instead. + +[CurrentCulture]: https://docs.microsoft.com/dotnet/api/system.globalization.cultureinfo.currentculture +[CurrentUICulture]: https://docs.microsoft.com/dotnet/api/system.globalization.cultureinfo.currentuiculture +[InvalidOperationException]: https://docs.microsoft.com/dotnet/api/system.invalidoperationexception \ No newline at end of file diff --git a/docs/PC001.md b/docs/PC001.md index 16f9a9f..00e4492 100644 --- a/docs/PC001.md +++ b/docs/PC001.md @@ -2,30 +2,30 @@ ## Cause -You're calling an API in .NET Core or .NET Standard that will throw -`PlatformNotSupportedException` in all or some circumstances. +You're calling an API in .NET Core or .NET Standard that throws +[`PlatformNotSupportedException`][PlatformNotSupportedException] in all or some circumstances. -## Rule Description +## Rule description The goal of this rule is assisting you in writing robust code that will work across all platforms that your code might run on. -## How to Fix Violations +## How to fix violations The analyzer has no way to detect whether your code is correctly guarded against -`PlatformNotSupportedException`. The fix is to review the API documentation for -which this rule was triggered for and to do one of the following: +[`PlatformNotSupportedException`][PlatformNotSupportedException]. The fix is to review the API documentation for +which this rule was triggered and to do one of the following: -1. Not calling the API -2. Guard this call and suppress the specific occurrence of this rule -3. Not running your code on the affected platforms and suppressing this rule for - said platforms +1. Not call the API. +2. Guard this call and suppress the specific occurrence of this rule. +3. Not run your code on the affected platforms and suppress this rule for + said platforms. -## When to Suppress Warnings +## When to suppress warnings -Unless you stop calling the API entirely, you'll have to suppress this warning. +Unless you stop calling the API entirely, you have to suppress this warning. -If you want to suppress a specific occurrence, use `#pragma warning disable`: +If you want to suppress a specific occurrence, use `#pragma warning disable` as shown in the following example: ```C# #pragma warning disable PC001 // API not supported on all platforms @@ -34,11 +34,19 @@ If you want to suppress a specific occurrence, use `#pragma warning disable`: ``` If you want to ignore certain platforms, you need to edit your project file and -add a property `PlatformCompatIgnore` that lists all platforms you don't plan to -run your code on: +add a `PlatformCompatIgnore` property that lists all platforms you don't plan to +run your code on like in the following example: ```XML Linux;MacOSX ``` + +The `PlatformCompatIgnore` property accepts the following values: + +* Linux +* macOS +* Windows + +[PlatformNotSupportedException]: https://docs.microsoft.com/dotnet/api/system.platformnotsupportedexception diff --git a/docs/PC002.md b/docs/PC002.md index cf1a0fb..fa4eb57 100644 --- a/docs/PC002.md +++ b/docs/PC002.md @@ -2,26 +2,26 @@ ## Cause -You're calling an API in .NET Standard that isn't available in .NET Framework +You're calling an API in .NET Standard that isn't available in the .NET Framework 4.6.1. -## Rule Description +## Rule description -While .NET Framework 4.6.1 is treated by NuGet as implementing .NET Standard -2.0, it doesn't fully implement it. This was trade-off that is explained in the -documentation of [.NET Standard][netfx-netstandard]. +While the .NET Framework 4.6.1 is treated by NuGet as implementing .NET Standard +2.0, it doesn't fully implement it. This trade-off is explained in the +[.NET Standard][netfx-netstandard] specification. -## How to Fix Violations +## How to fix violations You cannot reasonably guard your code against missing APIs. You only have two options: -1. Not calling the affected API -2. Not running your code on .NET Framework 4.6.1 +1. Not call the affected API. +2. Not run your code on .NET Framework 4.6.1. -## When to Suppress Warnings +## When to suppress warnings -Unless you stop calling the API entirely, you'll have to suppress this warning: +Unless you stop calling the API entirely, you have to suppress this warning as shown in the following example: ```C# using System.Diagnostics.CodeAnalysis; diff --git a/docs/PC003.md b/docs/PC003.md index 00d6d70..f240614 100644 --- a/docs/PC003.md +++ b/docs/PC003.md @@ -5,18 +5,18 @@ You're P/Invoking into a native API from a .NET Standard project or a UWP project but that API isn't available in UWP. -## Rule Description +## Rule description Not all Win32 APIs are available in UWP. While calling the API might work when debugging the app, the app store will reject any apps that contain P/Invoke declarations with unsupported APIs. -## How to Fix Violations +## How to fix violations Remove the P/Invoke declaration. When targeting .NET Standard, consider cross- targeting for UWP. -## When to Suppress Warnings +## When to suppress warnings The tool might not be able to detect usages of APIs that part of your application. If it's a native module that you deploy with your app, you should