.Assembly)
14 | resourceMan <- Some temp
15 | resourceMan.Value
16 |
17 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/RuleRunner.fs:
--------------------------------------------------------------------------------
1 | module SonarAnalyzer.FSharp.RuleRunner
2 |
3 | open FSharpAst
4 |
5 | let logger = Serilog.Log.Logger
6 | let loggerPrefix = "SonarAnalyzer.FSharp.RuleRunner"
7 |
8 | (* ===============================================
9 | Run one or more rules
10 | =============================================== *)
11 |
12 | /// run a specific list of rules on a file
13 | let analyzeFileWithRules (rules:Rule list) (filename:string) :Diagnostic list =
14 | logger.Information("[{prefix}] Analyzing {filename} ", loggerPrefix, filename)
15 |
16 | let config = TransformerConfig.Default
17 |
18 | let results = ResizeArray()
19 | let accept ctx =
20 | for rule in rules do
21 | rule ctx |> Option.iter (fun result -> results.Add result)
22 | true // keep going
23 | let visitor = TastVisitor(accept)
24 |
25 | let tastResult = FileApi.translateFile config filename
26 | match tastResult with
27 | | Error errs ->
28 | errs |> Array.map Diagnostic.CompilationError |> Array.toList
29 | | Ok tast ->
30 | visitor.Visit(tast)
31 | results |> Seq.toList
32 |
33 | |> fun results -> logger.Information("[{prefix}] ... {count} issues found", loggerPrefix, results.Length); results
34 |
35 | /// run all the rules on a file
36 | let analyzeFileWithAllRules (filename:string) :Diagnostic list =
37 |
38 | let availableRules = RuleManager.getAvailableRules()
39 | let rulesToUse =
40 | availableRules
41 | |> List.map (fun rule -> rule.Rule)
42 |
43 | filename |> analyzeFileWithRules rulesToUse
44 |
45 |
46 | /// analyze everything as specified in the AnalysisConfig
47 | let analyzeConfig (root:AnalysisConfig.Root) :Diagnostic list =
48 |
49 | let availableRules = RuleManager.getAvailableRules()
50 | let rulesToUse =
51 | match root.RuleSelection with
52 | | AnalysisConfig.AllRules ->
53 | availableRules
54 | | AnalysisConfig.SelectedRules selectedRules ->
55 | let isSelected (availableRule:AvailableRule) =
56 | selectedRules |> List.exists (fun selectedRule -> selectedRule.Key = availableRule.RuleId)
57 | availableRules |> List.filter isSelected
58 |
59 | |> List.map (fun availableRule -> availableRule.Rule)
60 |
61 | logger.Information("[{prefix}] Analyzing {ruleCount} rules", loggerPrefix, rulesToUse.Length)
62 |
63 | match root.FileSelection with
64 | | AnalysisConfig.SelectedFiles files ->
65 | files
66 | |> fun files -> logger.Information("[{prefix}] Analyzing {fileCount} files", loggerPrefix, files.Length); files
67 | |> List.collect (fun file -> analyzeFileWithRules rulesToUse file.Filename)
68 |
69 | | AnalysisConfig.Projects _ -> failwithf "Projects not yet implemented"
70 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules.Description/S1104.html:
--------------------------------------------------------------------------------
1 | Public fields in public classes do not respect the encapsulation principle and has three main disadvantages:
2 |
3 | - Additional behavior such as validation cannot be added.
4 | - The internal representation is exposed, and cannot be changed afterwards.
5 | - Member values are subject to change from anywhere in the code and may not meet the programmer's assumptions.
6 |
7 | By using private fields and public properties (set and get), unauthorized modifications are prevented. Properties also benefit from additional
8 | protection (security) features such as Link Demands.
9 | Note that due to optimizations on simple properties, public fields provide only very little performance gain.
10 | Noncompliant Code Example
11 |
12 | public class Foo
13 | {
14 | public int instanceData = 32; // Noncompliant
15 | }
16 |
17 | Compliant Solution
18 |
19 | public class Foo
20 | {
21 | private int instanceData = 32;
22 |
23 | public int InstanceData
24 | {
25 | get { return instanceData; }
26 | set { instanceData = value ; }
27 | }
28 | }
29 |
30 | Exceptions
31 | Fields marked as readonly
or const
are ignored by this rule.
32 | Fields inside classes or structs annotated with the StructLayoutAttribute
are ignored by this rule.
33 | See
34 |
35 | - MITRE, CWE-493 - Critical Public Variable Without Final Modifier
36 |
37 |
38 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules.Description/S1313.html:
--------------------------------------------------------------------------------
1 | Hardcoding IP addresses is security-sensitive. It has led in the past to the following vulnerabilities:
2 |
6 | Today's services have an ever-changing architecture due to their scaling and redundancy needs. It is a mistake to think that a service will always
7 | have the same IP address. When it does change, the hardcoded IP will have to be modified too. This will have an impact on the product development,
8 | delivery and deployment:
9 |
10 | - The developers will have to do a rapid fix every time this happens, instead of having an operation team change a configuration file.
11 | - It forces the same address to be used in every environment (dev, sys, qa, prod).
12 |
13 | Last but not least it has an effect on application security. Attackers might be able to decompile the code and thereby discover a potentially
14 | sensitive address. They can perform a Denial of Service attack on the service at this address or spoof the IP address. Such an attack is always
15 | possible, but in the case of a hardcoded IP address the fix will be much slower, which will increase an attack's impact.
16 | Recommended Secure Coding Practices
17 |
18 | - make the IP address configurable.
19 |
20 | Noncompliant Code Example
21 |
22 | let ip = "192.168.12.42"
23 | let address = IPAddress.Parse(ip)
24 |
25 | Compliant Solution
26 |
27 | let ip = ConfigurationManager.AppSettings.["myapplication.ip"]
28 | let address = IPAddress.Parse(ip)
29 |
30 | Exceptions
31 |
32 | - Although "::" is a valid IPv6 address, the rule doesn't report on it.
33 | - No issue is reported for 127.0.0.1 because loopback is not considered as sensitive
34 |
35 | See
36 |
41 |
42 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules.Description/S2068.html:
--------------------------------------------------------------------------------
1 | Because it is easy to extract strings from a compiled application, credentials should never be hard-coded. Do so, and they're almost guaranteed to
2 | end up in the hands of an attacker. This is particularly true for applications that are distributed.
3 | Credentials should be stored outside of the code in a strongly-protected encrypted configuration file or database.
4 | Noncompliant Code Example
5 |
6 | string username = "admin";
7 | string password = "Password123"; // Noncompliant
8 | string usernamePassword = "user=admin&password=Password123"; // Noncompliant
9 | string usernamePassword2 = "user=admin&" + "password=" + password; // Noncompliant
10 |
11 | Compliant Solution
12 |
13 | string username = "admin";
14 | string password = GetEncryptedPassword();
15 | string usernamePassword = string.Format("user={0}&password={1}", GetEncryptedUsername(), GetEncryptedPassword());
16 |
17 | See
18 |
26 |
27 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules.Description/S2070.html:
--------------------------------------------------------------------------------
1 | The MD5 algorithm and its successor, SHA-1, are no longer considered secure, because it is too easy to create hash collisions with them. That is,
2 | it takes too little computational effort to come up with a different input that produces the same MD5 or SHA-1 hash, and using the new, same-hash
3 | value gives an attacker the same access as if he had the originally-hashed value. This applies as well to the other Message-Digest algorithms: MD2,
4 | MD4, MD6.
5 | This rule tracks usage of the System.Security.Cryptography.CryptoConfig.CreateFromName()
, and
6 | System.Security.Cryptography.HashAlgorithm.Create()
methods to instantiate MD5, DSA, HMACMD5, HMACRIPEMD160, RIPEMD-160 or SHA-1
7 | algorithms, and of derived class instances of System.Security.Cryptography.SHA1
and System.Security.Cryptography.MD5
.
8 | Consider using safer alternatives, such as SHA-256, or SHA-3.
9 | Noncompliant Code Example
10 |
11 | var hashProvider1 = new MD5CryptoServiceProvider(); //Noncompliant
12 | var hashProvider2 = (HashAlgorithm)CryptoConfig.CreateFromName("MD5"); //Noncompliant
13 | var hashProvider3 = new SHA1Managed(); //Noncompliant
14 | var hashProvider4 = HashAlgorithm.Create("SHA1"); //Noncompliant
15 |
16 | Compliant Solution
17 |
18 | var hashProvider1 = new SHA256Managed();
19 | var hashProvider2 = (HashAlgorithm)CryptoConfig.CreateFromName("SHA256Managed");
20 | var hashProvider3 = HashAlgorithm.Create("SHA256Managed");
21 |
22 | See
23 |
31 | Deprecated
32 | This rule is deprecated; use {rule:csharpsquid:S4790} instead.
33 |
34 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules.Description/S2092.html:
--------------------------------------------------------------------------------
1 | The "secure" attribute prevents cookies from being sent over plaintext connections such as HTTP, where they would be easily eavesdropped upon.
2 | Instead, cookies with the secure attribute are only sent over encrypted HTTPS connections.
3 | Recommended Secure Coding Practices
4 |
5 | - set the field
Secure
to true
on the HttpCookie
object
6 |
7 | Noncompliant Code Example
8 |
9 | HttpCookie myCookie = new HttpCookie("UserSettings");
10 | myCookie.Secure = false; // Noncompliant; explicitly set to false
11 | ...
12 | Response.Cookies.Add(myCookie);
13 |
14 |
15 | HttpCookie myCookie = new HttpCookie("UserSettings"); // Noncompliant; the default value of 'Secure' is used (=false)
16 | ...
17 | Response.Cookies.Add(myCookie);
18 |
19 | Compliant Solution
20 |
21 | HttpCookie myCookie = new HttpCookie("UserSettings");
22 | myCookie.Secure = true; // Compliant
23 | ...
24 | Response.Cookies.Add(myCookie);
25 |
26 | See
27 |
36 |
37 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules.Description/S2228.html:
--------------------------------------------------------------------------------
1 | Debug statements are always useful during development. But include them in production code - particularly in code that runs client-side - and you
2 | run the risk of inadvertently exposing sensitive information.
3 | Noncompliant Code Example
4 |
5 | private void DoSomething()
6 | {
7 | // ...
8 | Console.WriteLine("so far, so good..."); // Noncompliant
9 | // ...
10 | }
11 |
12 | Exceptions
13 | The following are ignored by this rule:
14 |
15 | - Console Applications
16 | - Calls in methods decorated with
[Conditional ("DEBUG")]
17 | - Calls included in DEBUG preprocessor branches (
#if DEBUG
)
18 |
19 | See
20 |
24 |
25 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules.Description/S2245.html:
--------------------------------------------------------------------------------
1 | Using pseudorandom number generators (PRNGs) is security-sensitive. For example, it has led in the past to the following vulnerabilities:
2 | * CVE-2013-6386
3 | * CVE-2006-3419
4 | * CVE-2008-4102
5 | When software generates predictable values in a context requiring unpredictability, it may be possible for an attacker to guess the next value that
6 | will be generated, and use this guess to impersonate another user or access sensitive information.
7 | As the System.Random
class relies on a pseudorandom number generator, it should not be used for security-critical applications or for
8 | protecting sensitive data. In such context, the System.Cryptography.RandomNumberGenerator
class which relies on a cryptographically
9 | strong random number generator (RNG) should be used in place.
10 | Ask Yourself Whether
11 |
12 | - the code using the generated value requires it to be unpredictable. It is the case for all encryption mechanisms or when a secret value, such
13 | as a password, is hashed.
14 | - the function you use generates a value which can be predicted (pseudo-random).
15 | - the generated value is used multiple times.
16 | - an attacker can access the generated value.
17 |
18 | You are at risk if you answered yes to the first question and any of the following ones.
19 | Recommended Secure Coding Practices
20 |
21 | - Only use random number generators which are recommended by OWASP or any other
23 | trusted organization.
24 | - Use the generated random values only once.
25 | - You should not expose the generated random value. If you have to store it, make sure that the database or file is secure.
26 |
27 | Sensitive Code Example
28 |
29 | var random = new Random(); // Sensitive use of Random
30 | byte[] data = new byte[16];
31 | random.NextBytes(data);
32 | return BitConverter.ToString(data); // Check if this value is used for hashing or encryption
33 |
34 | Compliant Solution
35 |
36 | using System.Security.Cryptography;
37 | ...
38 | var randomGenerator = RandomNumberGenerator.Create(); // Compliant for security-sensitive use cases
39 | byte[] data = new byte[16];
40 | randomGenerator.GetBytes(data);
41 | return BitConverter.ToString(data);
42 |
43 | See
44 |
59 |
60 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules.Description/S2255.html:
--------------------------------------------------------------------------------
1 | Using cookies is security-sensitive. It has led in the past to the following vulnerabilities:
2 |
6 | Attackers can use widely-available tools to read cookies, sensitive information written by the server will be exposed.
7 | This rule flags code that writes cookies.
8 | Ask Yourself Whether
9 |
10 | - sensitive information is stored inside the cookie.
11 |
12 | You are at risk if you answered yes to this question.
13 | Recommended Secure Coding Practices
14 | Cookies should only be used to manage the user session. The best practice is to keep all user-related information server-side and link them to the
15 | user session, never sending them to the client. In a very few corner cases, cookies can be used for non-sensitive information that need to live longer
16 | than the user session.
17 | Do not try to encode sensitive information in a non human-readable format before writing them in a cookie. The encoding can be reverted and the
18 | original information will be exposed.
19 | Using cookies only for session IDs doesn't make them secure. Follow OWASP best practices when you configure your cookies.
21 | As a side note, every information read from a cookie should be Sanitized.
23 | Sensitive Code Example
24 |
25 | // === .Net Framework ===
26 |
27 | HttpCookie myCookie = new HttpCookie("UserSettings");
28 | myCookie["CreditCardNumber"] = "1234 1234 1234 1234"; // Sensitive; sensitive data stored
29 | myCookie.Values["password"] = "5678"; // Sensitive
30 | myCookie.Value = "mysecret"; // Sensitive
31 | ...
32 | Response.Cookies.Add(myCookie);
33 |
34 |
35 | // === .Net Core ===
36 |
37 | Response.Headers.Add("Set-Cookie", ...); // Sensitive
38 | Response.Cookies.Append("mykey", "myValue"); // Sensitive
39 |
40 | See
41 |
50 |
51 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules.Description/S2278.html:
--------------------------------------------------------------------------------
1 | According to the US National Institute of Standards and Technology (NIST), the Data Encryption Standard (DES) is no longer considered secure:
2 |
3 | Adopted in 1977 for federal agencies to use in protecting sensitive, unclassified information, the DES is being withdrawn because it no longer
4 | provides the security that is needed to protect federal government information.
5 | Federal agencies are encouraged to use the Advanced Encryption Standard, a faster and stronger algorithm approved as FIPS 197 in 2001.
6 |
7 | For similar reasons, RC2 should also be avoided.
8 | Noncompliant Code Example
9 |
10 | using (var tripleDES = new TripleDESCryptoServiceProvider()) //Noncompliant
11 | {
12 | //...
13 | }
14 |
15 | Compliant Solution
16 |
17 | using (var aes = new AesCryptoServiceProvider())
18 | {
19 | //...
20 | }
21 |
22 | See
23 |
32 |
33 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules.Description/S2386.html:
--------------------------------------------------------------------------------
1 | public static
mutable fields of classes which are accessed directly should be protected to the degree possible. This can be done by
2 | reducing the accessibility of the field or by changing the return type to an immutable type.
3 | This rule raises issues for public static
fields with a type inheriting/implementing System.Array
or
4 | System.Collections.Generic.ICollection<T>
.
5 | Noncompliant Code Example
6 |
7 | public class A
8 | {
9 | public static string[] strings1 = {"first","second"}; // Noncompliant
10 | public static List<String> strings3 = new List<String>(); // Noncompliant
11 | // ...
12 | }
13 |
14 | Compliant Solution
15 |
16 | public class A
17 | {
18 | protected static string[] strings1 = {"first","second"};
19 | protected static List<String> strings3 = new List<String>();
20 | // ...
21 | }
22 |
23 | Exceptions
24 | No issue is reported:
25 |
26 | - If the type of the field inherits/implements one (at least) of the following types:
27 |
28 | -
System.Collections.ObjectModel.ReadOnlyCollection<T>
29 | -
System.Collections.ObjectModel.ReadOnlyDictionary<TKey, TValue>
30 | -
System.Collections.Immutable.IImmutableArray<T>
31 | -
System.Collections.Immutable.IImmutableDictionary<TKey, TValue>
32 | -
System.Collections.Immutable.IImmutableList<T>
33 | -
System.Collections.Immutable.IImmutableSet<T>
34 | -
System.Collections.Immutable.IImmutableStack<T>
35 | -
System.Collections.Immutable.IImmutableQueue<T>
36 |
37 | - If the field is
readonly
and is initialized inline with an immutable type (i.e. inherits/implements one of the types in the
38 | previous list) or null.
39 |
40 | See
41 |
48 |
49 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules.Description/S2486.html:
--------------------------------------------------------------------------------
1 | When exceptions occur, it is usually a bad idea to simply ignore them. Instead, it is better to handle them properly, or at least to log them.
2 | This rule only reports on empty catch clauses that catch generic Exception
s.
3 | Noncompliant Code Example
4 |
5 | string text = "";
6 | try
7 | {
8 | text = File.ReadAllText(fileName);
9 | }
10 | catch (Exception exc) // Noncompliant
11 | {
12 | }
13 |
14 | Compliant Solution
15 |
16 | string text = "";
17 | try
18 | {
19 | text = File.ReadAllText(fileName);
20 | }
21 | catch (Exception exc)
22 | {
23 | logger.Log(exc);
24 | }
25 |
26 | Exceptions
27 | When a block contains a comment, it is not considered to be empty.
28 | See
29 |
34 |
35 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules.Description/S3011.html:
--------------------------------------------------------------------------------
1 | Changing or bypassing accessibility is security-sensitive. For example, it has led in the past to the following vulnerability:
2 |
5 | private
methods were made private
for a reason, and the same is true of every other visibility level. Altering or
6 | bypassing the accessibility of classes, methods, or fields violates the encapsulation principle and could introduce security holes.
7 | This rule raises an issue when reflection is used to change the visibility of a class, method or field, and when it is used to directly update a
8 | field value.
9 | Ask Yourself Whether
10 |
11 | - there is a good reason to override the existing accessibility level of the method/field. This is very rarely the case. Accessing hidden fields
12 | and methods will make your code unstable as they are not part of the public API and may change in future versions.
13 | - this method is called by untrusted code. *
14 | - it is possible to modify or bypass the accessibility of sensitive methods or fields using this code. *
15 |
16 | * You are at risk if you answered yes to those questions.
17 | Recommended Secure Coding Practices
18 | Don't change or bypass the accessibility of any method or field if possible.
19 | If untrusted code can execute this method, make sure that it cannot decide which method or field's accessibility can be modified or bypassed.
20 | Sensitive Code Example
21 |
22 | using System.Reflection;
23 |
24 | Type dynClass = Type.GetType("MyInternalClass");
25 | // Sensitive. Using BindingFlags.NonPublic will return non-public members
26 | BindingFlags bindingAttr = BindingFlags.NonPublic | BindingFlags.Static;
27 | MethodInfo dynMethod = dynClass.GetMethod("mymethod", bindingAttr);
28 | object result = dynMethod.Invoke(dynClass, null);
29 |
30 | See
31 |
37 |
38 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules.Description/S3330.html:
--------------------------------------------------------------------------------
1 | The HttpOnly
cookie attribute tells the browser to prevent client-side scripts from reading cookies with the attribute, and its use
2 | can go a long way to defending against Cross-Site Scripting (XSS) attacks. Thus, as a precaution, the attribute should be set by default on all
3 | cookies set server-side, such as session id cookies.
4 | When implementing Cross Site Request Forgery (XSRF) protection, a JavaScript-readable session cookie, generally named XSRF-TOKEN, should be created
5 | on the first HTTP GET request. For such a cookie, the HttpOnly
attribute should be set to "false".
6 | Setting the attribute can be done either programmatically, or globally via configuration files.
7 | Noncompliant Code Example
8 |
9 | HttpCookie myCookie = new HttpCookie("UserSettings");
10 | myCookie.HttpOnly = false; // Noncompliant; explicitly set to false
11 | ...
12 | Response.Cookies.Add(myCookie);
13 |
14 |
15 | HttpCookie myCookie = new HttpCookie("UserSettings"); // Noncompliant; the default value of 'HttpOnly' is used (=false)
16 | ...
17 | Response.Cookies.Add(myCookie);
18 |
19 | Compliant Solution
20 |
21 | HttpCookie myCookie = new HttpCookie("UserSettings");
22 | myCookie.HttpOnly = true; // Compliant
23 | ...
24 | Response.Cookies.Add(myCookie);
25 |
26 | See
27 |
37 |
38 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules.Description/S3884.html:
--------------------------------------------------------------------------------
1 | CoSetProxyBlanket
and CoInitializeSecurity
both work to set the permissions context in which the process invoked
2 | immediately after is executed. Calling them from within that process is useless because it's too late at that point; the permissions context has
3 | already been set.
4 | Specifically, these methods are meant to be called from non-managed code such as a C++ wrapper that then invokes the managed, i.e. C# or VB.NET,
5 | code.
6 | Noncompliant Code Example
7 |
8 | [DllImport("ole32.dll")]
9 | static extern int CoSetProxyBlanket([MarshalAs(UnmanagedType.IUnknown)]object pProxy, uint dwAuthnSvc, uint dwAuthzSvc,
10 | [MarshalAs(UnmanagedType.LPWStr)] string pServerPrincName, uint dwAuthnLevel, uint dwImpLevel, IntPtr pAuthInfo,
11 | uint dwCapabilities);
12 |
13 | public enum RpcAuthnLevel
14 | {
15 | Default = 0,
16 | None = 1,
17 | Connect = 2,
18 | Call = 3,
19 | Pkt = 4,
20 | PktIntegrity = 5,
21 | PktPrivacy = 6
22 | }
23 |
24 | public enum RpcImpLevel
25 | {
26 | Default = 0,
27 | Anonymous = 1,
28 | Identify = 2,
29 | Impersonate = 3,
30 | Delegate = 4
31 | }
32 |
33 | public enum EoAuthnCap
34 | {
35 | None = 0x00,
36 | MutualAuth = 0x01,
37 | StaticCloaking = 0x20,
38 | DynamicCloaking = 0x40,
39 | AnyAuthority = 0x80,
40 | MakeFullSIC = 0x100,
41 | Default = 0x800,
42 | SecureRefs = 0x02,
43 | AccessControl = 0x04,
44 | AppID = 0x08,
45 | Dynamic = 0x10,
46 | RequireFullSIC = 0x200,
47 | AutoImpersonate = 0x400,
48 | NoCustomMarshal = 0x2000,
49 | DisableAAA = 0x1000
50 | }
51 |
52 | [DllImport("ole32.dll")]
53 | public static extern int CoInitializeSecurity(IntPtr pVoid, int cAuthSvc, IntPtr asAuthSvc, IntPtr pReserved1,
54 | RpcAuthnLevel level, RpcImpLevel impers, IntPtr pAuthList, EoAuthnCap dwCapabilities, IntPtr pReserved3);
55 |
56 | static void Main(string[] args)
57 | {
58 | var hres1 = CoSetProxyBlanket(null, 0, 0, null, 0, 0, IntPtr.Zero, 0); // Noncompliant
59 |
60 | var hres2 = CoInitializeSecurity(IntPtr.Zero, -1, IntPtr.Zero, IntPtr.Zero, RpcAuthnLevel.None,
61 | RpcImpLevel.Impersonate, IntPtr.Zero, EoAuthnCap.None, IntPtr.Zero); // Noncompliant
62 | }
63 |
64 | See
65 |
69 |
70 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules.Description/S4211.html:
--------------------------------------------------------------------------------
1 | Transparency attributes, SecurityCriticalAttribute
and SecuritySafeCriticalAttribute
are used to identify code that
2 | performs security-critical operations. The second one indicates that it is safe to call this code from transparent, while the first one does not.
3 | Since the transparency attributes of code elements with larger scope take precedence over transparency attributes of code elements that are contained
4 | in the first element a class, for instance, with a SecurityCriticalAttribute
can not contain a method with a
5 | SecuritySafeCriticalAttribute
.
6 | This rule raises an issue when a member is marked with a System.Security
security attribute that has a different transparency than the
7 | security attribute of a container of the member.
8 | Noncompliant Code Example
9 |
10 | using System;
11 | using System.Security;
12 |
13 | namespace MyLibrary
14 | {
15 |
16 | [SecurityCritical]
17 | public class Foo
18 | {
19 | [SecuritySafeCritical] // Noncompliant
20 | public void Bar()
21 | {
22 | }
23 | }
24 | }
25 |
26 | Compliant Solution
27 |
28 | using System;
29 | using System.Security;
30 |
31 | namespace MyLibrary
32 | {
33 |
34 | [SecurityCritical]
35 | public class Foo
36 | {
37 | public void Bar()
38 | {
39 | }
40 | }
41 | }
42 |
43 | See
44 |
48 |
49 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules.Description/S4212.html:
--------------------------------------------------------------------------------
1 | Because serialization constructors allocate and initialize objects, security checks that are present on regular constructors must also be present
2 | on a serialization constructor. Failure to do so would allow callers that could not otherwise create an instance to use the serialization constructor
3 | to do this.
4 | This rule raises an issue when a type implements the System.Runtime.Serialization.ISerializable
interface, is not a delegate or
5 | interface, is declared in an assembly that allows partially trusted callers and has a constructor that takes a
6 | System.Runtime.Serialization.SerializationInfo
object and a System.Runtime.Serialization.StreamingContext
object which is
7 | not secured by a security check, but one or more of the regular constructors in the type is secured.
8 | Noncompliant Code Example
9 |
10 | using System;
11 | using System.IO;
12 | using System.Runtime.Serialization;
13 | using System.Runtime.Serialization.Formatters.Binary;
14 | using System.Security;
15 | using System.Security.Permissions;
16 |
17 | [assembly: AllowPartiallyTrustedCallersAttribute()]
18 | namespace MyLibrary
19 | {
20 | [Serializable]
21 | public class Foo : ISerializable
22 | {
23 | private int n;
24 |
25 | [FileIOPermissionAttribute(SecurityAction.Demand, Unrestricted = true)]
26 | public Foo()
27 | {
28 | n = -1;
29 | }
30 |
31 | protected Foo(SerializationInfo info, StreamingContext context) // Noncompliant
32 | {
33 | n = (int)info.GetValue("n", typeof(int));
34 | }
35 |
36 | void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
37 | {
38 | info.AddValue("n", n);
39 | }
40 | }
41 | }
42 |
43 | Compliant Solution
44 |
45 | using System;
46 | using System.IO;
47 | using System.Runtime.Serialization;
48 | using System.Runtime.Serialization.Formatters.Binary;
49 | using System.Security;
50 | using System.Security.Permissions;
51 |
52 | [assembly: AllowPartiallyTrustedCallersAttribute()]
53 | namespace MyLibrary
54 | {
55 | [Serializable]
56 | public class Foo : ISerializable
57 | {
58 | private int n;
59 |
60 | [FileIOPermissionAttribute(SecurityAction.Demand, Unrestricted = true)]
61 | public Foo()
62 | {
63 | n = -1;
64 | }
65 |
66 | [FileIOPermissionAttribute(SecurityAction.Demand, Unrestricted = true)]
67 | protected Foo(SerializationInfo info, StreamingContext context)
68 | {
69 | n = (int)info.GetValue("n", typeof(int));
70 | }
71 |
72 | void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
73 | {
74 | info.AddValue("n", n);
75 | }
76 | }
77 | }
78 |
79 | See
80 |
84 |
85 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules.Description/S4426.html:
--------------------------------------------------------------------------------
1 | When generating cryptograpic keys (or key pairs), it is important to use a key length that provides enough entropy against brute-force attacks. For
2 | the RSA algorithm the key should be at
3 | least 2048 bits long.
4 | This rule raises an issue when a RSA key-pair generator is initialized with too small a length parameter.
5 | Noncompliant Code Example
6 |
7 | using System;
8 | using System.Security.Cryptography;
9 |
10 | namespace MyLibrary
11 | {
12 | public class MyCryptoClass
13 | {
14 | static void Main()
15 | {
16 | RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(1024); // Noncompliant
17 | // ...
18 | }
19 | }
20 | }
21 |
22 | Compliant Solution
23 |
24 | using System;
25 | using System.Security.Cryptography;
26 |
27 | namespace MyLibrary
28 | {
29 | public class MyCryptoClass
30 | {
31 | static void Main()
32 | {
33 | RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(2048);
34 | // ...
35 | }
36 | }
37 | }
38 |
39 | See
40 |
51 |
52 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules.Description/S4432.html:
--------------------------------------------------------------------------------
1 | Encryption algorithms can be used with various modes. Some combinations are not secured:
2 |
3 | - Electronic Codebook (ECB) mode: Under a given key, any given plaintext block always gets encrypted to the same ciphertext block. Thus, it does
4 | not hide data patterns well. In some senses, it doesn't provide serious message confidentiality, and it is not recommended for use in cryptographic
5 | protocols at all.
6 | - Cipher Block Chaining (CBC) with PKCS#5 padding (or PKCS#7) is susceptible to padding oracle attacks. CBC + PKCS#7 can be used if combined with
7 | an authenticity check (HMAC-SHA256 for example) on the cipher text.
8 |
9 | In both cases, Galois/Counter Mode (GCM) with no padding should be preferred. As the .NET framework doesn't provide this natively, the use of a
10 | certified third party lib is recommended.
11 | This rule raises an issue when any of the following CipherMode is detected: ECB, CBC, OFB, CFB, CTS.
12 | Noncompliant Code Example
13 |
14 | AesManaged aes = new AesManaged
15 | {
16 | KeySize = 128,
17 | BlockSize = 128,
18 | Mode = CipherMode.OFB, // Noncompliant
19 | Padding = PaddingMode.PKCS7
20 | };
21 |
22 | See
23 |
34 |
35 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules.Description/S4433.html:
--------------------------------------------------------------------------------
1 | An un-authenticated LDAP connection can lead to transactions without access control. Authentication, and with it, access control, are the last line
2 | of defense against LDAP injections and should not be disabled.
3 | This rule raises an issue when an LDAP connection is created with AuthenticationTypes.Anonymous
or
4 | AuthenticationTypes.None
.
5 | Noncompliant Code Example
6 |
7 | DirectoryEntry myDirectoryEntry = new DirectoryEntry(adPath);
8 | myDirectoryEntry.AuthenticationType = AuthenticationTypes.None; // Noncompliant
9 |
10 | DirectoryEntry myDirectoryEntry = new DirectoryEntry(adPath, "u", "p", AuthenticationTypes.None); // Noncompliant
11 |
12 | Compliant Solution
13 |
14 | DirectoryEntry myDirectoryEntry = new DirectoryEntry(myADSPath); // Compliant; default DirectoryEntry.AuthenticationType property value is "Secure" since .NET Framework 2.0
15 |
16 | DirectoryEntry myDirectoryEntry = new DirectoryEntry(myADSPath, "u", "p", AuthenticationTypes.Secure);
17 |
18 | See
19 |
25 |
26 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules.Description/S4507.html:
--------------------------------------------------------------------------------
1 | Delivering code in production with debug features activated is security-sensitive. It has led in the past to the following vulnerabilities:
2 |
7 | An application's debug features enable developers to find bugs more easily. It often gives access to detailed information on both the system
8 | running the application and users. Sometime it even enables the execution of custom commands. Thus deploying on production servers an application
9 | which has debug features activated is extremely dangerous.
10 | Ask Yourself Whether
11 |
12 | - the code or configuration enabling the application debug features is deployed on production servers.
13 | - the application runs by default with debug features activated.
14 |
15 | You are at risk if you answered yes to any of these questions.
16 | Recommended Secure Coding Practices
17 | Do not enable debug features on production servers.
18 | The .Net Core framework offers multiple features which help during debug.
19 | Microsoft.AspNetCore.Builder.IApplicationBuilder.UseDeveloperExceptionPage
and
20 | Microsoft.AspNetCore.Builder.IApplicationBuilder.UseDatabaseErrorPage
are two of them. Make sure that those features are disabled in
21 | production.
22 | Use if (env.IsDevelopment())
to disable debug code.
23 | Sensitive Code Example
24 | This rule raises issues when the following .Net Core methods are called:
25 | Microsoft.AspNetCore.Builder.IApplicationBuilder.UseDeveloperExceptionPage
,
26 | Microsoft.AspNetCore.Builder.IApplicationBuilder.UseDatabaseErrorPage
. No Issue is raised when those calls are disabled by if
27 | (env.IsDevelopment())
.
28 |
29 | using Microsoft.AspNetCore.Builder;
30 | using Microsoft.AspNetCore.Hosting;
31 |
32 | namespace mvcApp
33 | {
34 | public class Startup2
35 | {
36 | public void Configure(IApplicationBuilder app, IHostingEnvironment env)
37 | {
38 | if (env.IsDevelopment())
39 | {
40 | // The following calls are ok because they are disabled in production
41 | app.UseDeveloperExceptionPage();
42 | app.UseDatabaseErrorPage();
43 | }
44 | // Those calls are Sensitive because it seems that they will run in production
45 | app.UseDeveloperExceptionPage(); // Sensitive
46 | app.UseDatabaseErrorPage(); // Sensitive
47 | }
48 | }
49 | }
50 |
51 |
52 | Exceptions
53 | This rule does not analyze configuration files. Make sure that debug mode is not enabled by default in those files.
54 | See
55 |
61 |
62 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules.Description/S4564.html:
--------------------------------------------------------------------------------
1 | ASP.Net has a feature to validate HTTP requests to prevent potentially dangerous content to perform a cross-site scripting (XSS) attack. There is
2 | no reason to disable this mechanism even if other checks to prevent XXS attacks are in place.
3 | This rule raises an issue if a method with parameters is marked with System.Web.Mvc.HttpPostAttribute
and not
4 | System.Web.Mvc.ValidateInputAttribute(true)
.
5 | Noncompliant Code Example
6 |
7 | public class FooBarController : Controller
8 | {
9 | [HttpPost] // Noncompliant
10 | [ValidateInput(false)]
11 | public ActionResult Purchase(string input)
12 | {
13 | return Foo(input);
14 | }
15 |
16 | [HttpPost] // Noncompliant
17 | public ActionResult PurchaseSomethingElse(string input)
18 | {
19 | return Foo(input);
20 | }
21 | }
22 |
23 | Compliant Solution
24 |
25 | public class FooBarController : Controller
26 | {
27 | [HttpPost]
28 | [ValidateInput(true)] // Compliant
29 | public ActionResult Purchase(string input)
30 | {
31 | return Foo(input);
32 | }
33 | }
34 |
35 | Exceptions
36 | Parameterless methods marked with System.Web.Mvc.HttpPostAttribute
will not trigger this issue.
37 | See
38 |
46 |
47 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules.Description/S4790.html:
--------------------------------------------------------------------------------
1 | Hashing data is security-sensitive. It has led in the past to the following vulnerabilities:
2 |
7 | Cryptographic hash functions are used to uniquely identify information without storing their original form. When not done properly, an attacker can
8 | steal the original information by guessing it (ex: with a rainbow table), or replace the
9 | original data with another one having the same hash.
10 | This rule flags code that initiates hashing.
11 | Ask Yourself Whether
12 |
13 | - the hashed value is used in a security context.
14 | - the hashing algorithm you are using is known to have vulnerabilities.
15 | - salts are not automatically generated and applied by the hashing function.
16 |
17 | - any generated salts are cryptographically weak or not credential-specific.
18 |
19 | You are at risk if you answered yes to the first question and any of the following ones.
20 | Recommended Secure Coding Practices
21 |
22 | - for security related purposes, use only hashing algorithms which are currently known to be strong. Avoid using algorithms like MD5 and SHA1
24 | completely in security contexts.
25 | - do not define your own hashing- or salt algorithms as they will most probably have flaws.
26 | - do not use algorithms that compute too quickly, like SHA256, as it must remain beyond modern hardware capabilities to perform brute force and
27 | dictionary based attacks.
28 | - use a hashing algorithm that generate its own salts as part of the hashing. If you generate your own salts, make sure that a cryptographically
29 | strong salt algorithm is used, that generated salts are credential-specific, and finally, that the salt is applied correctly before the hashing.
30 |
31 | - save both the salt and the hashed value in the relevant database record; during future validation operations, the salt and hash can then be
32 | retrieved from the database. The hash is recalculated with the stored salt and the value being validated, and the result compared to the stored
33 | hash.
34 | - the strength of hashing algorithms often decreases over time as hardware capabilities increase. Check regularly that the algorithms you are
35 | using are still considered secure. If needed, rehash your data using a stronger algorithm.
36 |
37 | Sensitive Code Example
38 |
39 | using System.Security.Cryptography;
40 |
41 | void ComputeHash()
42 | {
43 | // Review all instantiations of classes that inherit from HashAlgorithm, for example:
44 | HashAlgorithm hashAlgo = HashAlgorithm.Create(); // Sensitive
45 | HashAlgorithm hashAlgo2 = HashAlgorithm.Create("SHA1"); // Sensitive
46 | SHA1 sha = new SHA1CryptoServiceProvider(); // Sensitive
47 | MD5 md5 = new MD5CryptoServiceProvider(); // Sensitive
48 | // ...
49 | }
50 |
51 | class MyHashAlgorithm : HashAlgorithm // Sensitive
52 | {
53 | // ...
54 | }
55 |
56 | See
57 |
68 |
69 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules.Description/S4818.html:
--------------------------------------------------------------------------------
1 | Using sockets is security-sensitive. It has led in the past to the following vulnerabilities:
2 |
7 | Sockets are vulnerable in multiple ways:
8 |
9 | - They enable a software to interact with the outside world. As this world is full of attackers it is necessary to check that they cannot receive
10 | sensitive information or inject dangerous input.
11 | - The number of sockets is limited and can be exhausted. Which makes the application unresponsive to users who need additional sockets.
12 |
13 | This rules flags code that creates sockets. It matches only the direct use of sockets, not use through frameworks or high-level APIs such as the
14 | use of http connections.
15 | Ask Yourself Whether
16 |
17 | - sockets are created without any limit every time a user performs an action.
18 | - input received from sockets is used without being sanitized.
19 | - sensitive data is sent via sockets without being encrypted.
20 |
21 | You are at risk if you answered yes to any of these questions.
22 | Recommended Secure Coding Practices
23 |
24 | - In many cases there is no need to open a socket yourself. Use instead libraries and existing protocols.
25 | - Encrypt all data sent if it is sensitive. Usually it is better to encrypt it even if the data is not sensitive as it might change later.
26 | - Sanitize any input read from the socket.
27 | - Limit the number of sockets a given user can create. Close the sockets as soon as possible.
28 |
29 | Sensitive Code Example
30 |
31 | using System.Net.Sockets;
32 |
33 | class TestSocket
34 | {
35 | public static void Run()
36 | {
37 | // Sensitive
38 | Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
39 |
40 | // TcpClient and UdpClient simply abstract the details of creating a Socket
41 | TcpClient client = new TcpClient("example.com", 80); // Sensitive
42 | UdpClient listener = new UdpClient(80); // Sensitive
43 | }
44 | }
45 |
46 | See
47 |
56 |
57 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules.Description/S4823.html:
--------------------------------------------------------------------------------
1 | Using command line arguments is security-sensitive. It has led in the past to the following vulnerabilities:
2 |
7 | Command line arguments can be dangerous just like any other user input. They should never be used without being first validated and sanitized.
8 | Remember also that any user can retrieve the list of processes running on a system, which makes the arguments provided to them visible. Thus
9 | passing sensitive information via command line arguments should be considered as insecure.
10 | This rule raises an issue when on every program entry points (main
methods) when command line arguments are used. The goal is to guide
11 | security code reviews.
12 | Ask Yourself Whether
13 |
14 | - any of the command line arguments are used without being sanitized first.
15 | - your application accepts sensitive information via command line arguments.
16 |
17 | If you answered yes to any of these questions you are at risk.
18 | Recommended Secure Coding Practices
19 | Sanitize all command line arguments before using them.
20 | Any user or application can list running processes and see the command line arguments they were started with. There are safer ways of providing
21 | sensitive information to an application than exposing them in the command line. It is common to write them on the process' standard input, or give the
22 | path to a file containing the information.
23 | Sensitive Code Example
24 |
25 | namespace MyNamespace
26 | {
27 | class Program
28 | {
29 | static void Main(string[] args) // Sensitive if there is a reference to "args" in the method.
30 | {
31 | string myarg = args[0];
32 | // ...
33 | }
34 | }
35 | }
36 |
37 | See
38 |
44 |
45 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules.Description/S4829.html:
--------------------------------------------------------------------------------
1 | Reading Standard Input is security-sensitive. It has led in the past to the following vulnerabilities:
2 |
6 | It is common for attackers to craft inputs enabling them to exploit software vulnerabilities. Thus any data read from the standard input (stdin)
7 | can be dangerous and should be validated.
8 | This rule flags code that reads from the standard input.
9 | Ask Yourself Whether
10 |
11 | - data read from the standard input is not sanitized before being used.
12 |
13 | You are at risk if you answered yes to this question.
14 | Recommended Secure Coding Practices
15 | Sanitize all data read from the standard input before using it.
16 | Sensitive Code Example
17 |
18 | using System;
19 | public class C
20 | {
21 | public void Main()
22 | {
23 | Console.In; // Sensitive
24 | var code = Console.Read(); // Sensitive
25 | var keyInfo = Console.ReadKey(...); // Sensitive
26 | var text = Console.ReadLine(); // Sensitive
27 | Console.OpenStandardInput(...); // Sensitive
28 | }
29 | }
30 |
31 | Exceptions
32 | This rule does not raise issues when the return value of the Console.Read
Console.ReadKey
or
33 | Console.ReadLine
methods is ignored.
34 |
35 | using System;
36 | public class C
37 | {
38 | public void Main()
39 | {
40 | Console.ReadKey(...); // Return value is ignored
41 | Console.ReadLine(); // Return value is ignored
42 | }
43 | }
44 |
45 | See:
46 |
49 |
50 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules.Description/S4834.html:
--------------------------------------------------------------------------------
1 | Controlling permissions is security-sensitive. It has led in the past to the following vulnerabilities:
2 |
7 | Attackers can only damage what they have access to. Thus limiting their access is a good way to prevent them from wreaking havoc, but it has to be
8 | done properly.
9 | This rule flags code that controls the access to resources and actions or configures this access. The goal is to guide security code reviews.
10 | Ask Yourself Whether
11 |
12 | - at least one accessed action or resource is security-sensitive.
13 | - there is no access control in place or it does not cover all sensitive actions and resources.
14 | - users have permissions they don't need.
15 | - the access control is based on a user input or on some other unsafe data.
16 | - permissions are difficult to remove or take a long time to be updated.
17 |
18 | You are at risk if you answered yes to the first question and any of the following ones.
19 | Recommended Secure Coding Practices
20 | The first step is to restrict all sensitive actions to authenticated users.
21 | Each user should have the lowest privileges possible. The access control granularity should match the sensitivity of each resource or action. The
22 | more sensitive it is, the less people should have access to it.
23 | Do not base the access control on a user input or on a value which might have been tampered with. For example, the developer should not read a
24 | user's permissions from an HTTP cookie as it can be modified client-side.
25 | Check that the access to each action and resource is properly restricted.
26 | Enable administrators to swiftly remove permissions when necessary. This enables them to reduce the time an attacker can have access to your
27 | systems when a breach occurs.
28 | Log and monitor refused access requests as they can reveal an attack.
29 | Sensitive Code Example
30 |
31 | using System.Threading;
32 | using System.Security.Permissions;
33 | using System.Security.Principal;
34 | using System.IdentityModel.Tokens;
35 |
36 | class SecurityPrincipalDemo
37 | {
38 | class MyIdentity : IIdentity // Sensitive, custom IIdentity implementations should be reviewed
39 | {
40 | // ...
41 | }
42 |
43 | class MyPrincipal : IPrincipal // Sensitive, custom IPrincipal implementations should be reviewed
44 | {
45 | // ...
46 | }
47 | [System.Security.Permissions.PrincipalPermission(SecurityAction.Demand, Role = "Administrators")] // Sensitive. The access restrictions enforced by this attribute should be reviewed.
48 | static void CheckAdministrator()
49 | {
50 | WindowsIdentity MyIdentity = WindowsIdentity.GetCurrent(); // Sensitive
51 | HttpContext.User = ...; // Sensitive: review all reference (set and get) to System.Web HttpContext.User
52 | AppDomain domain = AppDomain.CurrentDomain;
53 | domain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal); // Sensitive
54 | MyIdentity identity = new MyIdentity(); // Sensitive
55 | MyPrincipal MyPrincipal = new MyPrincipal(MyIdentity); // Sensitive
56 | Thread.CurrentPrincipal = MyPrincipal; // Sensitive
57 | domain.SetThreadPrincipal(MyPrincipal); // Sensitive
58 |
59 | // All instantiation of PrincipalPermission should be reviewed.
60 | PrincipalPermission principalPerm = new PrincipalPermission(null, "Administrators"); // Sensitive
61 | principalPerm.Demand();
62 |
63 | SecurityTokenHandler handler = ...;
64 | // Sensitive: this creates an identity.
65 | ReadOnlyCollection<ClaimsIdentity> identities = handler.ValidateToken(…);
66 | }
67 |
68 | // Sensitive: review how this function uses the identity and principal.
69 | void modifyPrincipal(MyIdentity identity, MyPrincipal principal)
70 | {
71 | // ...
72 | }
73 | }
74 |
75 | See
76 |
80 |
81 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules.Description/S5042.html:
--------------------------------------------------------------------------------
1 | Expanding archive files is security-sensitive. For example, expanding archive files has led in the past to the following vulnerabilities:
2 |
6 | Applications that expand archive files (zip, tar, jar, war, 7z, ...) should verify the path where the archive's files are expanded and not trust
7 | blindly the content of the archive. Archive's files should not be expanded outside of the root directory where the archive is supposed to be expanded.
8 | Also, applications should control the size of the expanded data to not be a victim of Zip Bomb attack. Failure to do so could allow an attacker to use
9 | a specially crafted archive that holds directory traversal paths (e.g. ../../attacker.sh) or the attacker could overload the file system, processors
10 | or memory of the operating system where the archive is expanded making the target OS completely unusable.
11 | This rule raises an issue when code handle archives. The goal is to guide security code reviews.
12 | Ask Yourself Whether
13 |
14 | - there is no validation of the name of the archive entry
15 | - there is no validation of the effective path where the archive entry is going to be expanded
16 | - there is no validation of the size of the expanded archive entry
17 | - there is no validation of the ratio between the compressed and uncompressed archive entry
18 |
19 | You are at risk if you answered yes to any of those questions.
20 |
21 | Recommended Secure Coding Practices
22 | Validate the full path of the extracted file against the full path of the directory where files are expanded.
23 |
24 | - the canonical path of the expanded file must start with the canonical path of the directory where files are extracted.
25 | - the name of the archive entry must not contain "..", i.e. reference to a parent directory.
26 |
27 | Stop extracting the archive if any of its entries has been tainted with a directory traversal path.
28 | Define and control the ratio between compressed and uncompress bytes.
29 | Define and control the maximum allowed expanded file size.
30 | Count the number of file entries extracted from the archive and abort the extraction if their number is greater than a predefined threshold.
31 | Sensitive Code Example
32 |
33 | foreach (ZipArchiveEntry entry in archive.Entries)
34 | {
35 | // entry.FullName could contain parent directory references ".." and the destinationPath variable could become outside of the desired path
36 | string destinationPath = Path.GetFullPath(Path.Combine(path, entry.FullName));
37 |
38 | entry.ExtractToFile(destinationPath); // Sensitive, extracts the entry in a file
39 |
40 | Stream stream;
41 | stream = entry.Open(); // Sensitive, the entry is about to be extracted
42 | }
43 |
44 | See
45 |
53 |
54 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules/Hotspots/S1313_HardcodedIpAddress.fs:
--------------------------------------------------------------------------------
1 | module SonarAnalyzer.FSharp.Rules.S1313_HardcodedIpAddress
2 |
3 | open SonarAnalyzer.FSharp
4 | open SonarAnalyzer.FSharp.RuleHelpers
5 | open FSharpAst
6 | open System.Net
7 |
8 | // =================================================
9 | // #1313 Using hardcoded IP addresses is security-sensitive
10 | // https://rules.sonarsource.com/csharp/type/Security%20Hotspot/RSPEC-1313
11 | // =================================================
12 |
13 | module Private =
14 |
15 | []
16 | let DiagnosticId = "S1313"
17 | let messageFormat = "Make sure using this hardcoded IP address '{0}' is safe here."
18 | let rule = DiagnosticDescriptor.Create(DiagnosticId, messageFormat, RspecStrings.ResourceManager)
19 |
20 | exception EarlyReturn
21 |
22 | let checkWithEarlyReturn f node =
23 | try
24 | f node
25 | with
26 | | :? EarlyReturn ->
27 | None
28 |
29 |
30 | let parseIpAddress str =
31 | match System.Net.IPAddress.TryParse(str) with
32 | | false,_ ->
33 | None
34 | | true, address ->
35 | Some address
36 |
37 | let isIp4Address (address:IPAddress) =
38 | address.AddressFamily = System.Net.Sockets.AddressFamily.InterNetwork
39 |
40 | let isValidIpAddress literalValue =
41 | option {
42 | let! address = parseIpAddress literalValue
43 | if (isIp4Address address) &&
44 | // must have 4 parts
45 | literalValue.Split('.').Length <> 4 then
46 | return false
47 | else
48 | return true
49 | // if address parsing fails, return false
50 | } |> Option.defaultValue false
51 |
52 |
53 | let ignoredNames = ["VERSION"; "ASSEMBLY"]
54 |
55 | let isIgnoredMemberName (ctx:TastContext) =
56 | option {
57 | // if there is a containing call, get it, else drop through and return false later
58 | let! _,node = tryContainingCall ctx
59 | let isContainedInMember name =
60 | node.Member.CompiledName.ToUpperInvariant().Contains(name)
61 | // if any matches found, return true
62 | return ignoredNames |> List.exists isContainedInMember
63 | } |> Option.defaultValue false
64 |
65 | let isIgnoredClassName (ctx:TastContext) =
66 | option {
67 | // if there is a containing call, get it, else drop through and return false later
68 | let! _,node = tryContainingNewObjectExpr ctx
69 | let! entity = node.Ctor.DeclaringEntity
70 | let isContainedInClassName name =
71 | entity.CompiledName.ToUpperInvariant().Contains(name)
72 | // if any matches found, return true
73 | return ignoredNames |> List.exists isContainedInClassName
74 | } |> Option.defaultValue false
75 |
76 |
77 | let checkNode (ctx:TastContext) (node:Tast.ConstantExpr) =
78 | if node.Type |> isType WellKnownType.string |> not then raise EarlyReturn
79 |
80 | let literalValue = (string node.Value) // .Trim() // null is converted to "" by `string` so this is safe
81 | // Note Trim() is not used in the C# version
82 |
83 | let allowedValues = [""; "::"; "127.0.0.1"]
84 | if allowedValues |> List.contains literalValue then raise EarlyReturn
85 |
86 | if not (isValidIpAddress literalValue) then raise EarlyReturn
87 |
88 | // check for ignored names such as "ASSEMBLY"
89 | if isIgnoredClassName ctx then raise EarlyReturn
90 | if isIgnoredMemberName ctx then raise EarlyReturn
91 |
92 | // OK if used in an attribute
93 | // NB attributes are not normally visited in the AST. Must be asked for explicitly.
94 |
95 | Diagnostic.Create(rule, node.Location, literalValue) |> Some
96 |
97 | open Private
98 |
99 | /// The implementation of the rule
100 | []
101 | let Rule : Rule = fun ctx ->
102 |
103 | // only trigger for constants
104 | ctx.Try()
105 | |> Option.bind (checkWithEarlyReturn (checkNode ctx))
106 |
107 |
108 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules/Hotspots/S2245_DoNotUseRandom.fs:
--------------------------------------------------------------------------------
1 | module SonarAnalyzer.FSharp.Rules.S2245_DoNotUseRandom
2 |
3 | open SonarAnalyzer.FSharp
4 | open SonarAnalyzer.FSharp.RuleHelpers
5 | open FSharpAst
6 |
7 | // =================================================
8 | // #2245 Using pseudorandom number generators (PRNGs) is security-sensitive
9 | // https://rules.sonarsource.com/csharp/type/Security%20Hotspot/RSPEC-2245
10 | // =================================================
11 |
12 | module Private =
13 |
14 | []
15 | let DiagnosticId = "S2245";
16 | let messageFormat = "Make sure that using this pseudorandom number generator is safe here.";
17 | let rule = DiagnosticDescriptor.Create(DiagnosticId, messageFormat, RspecStrings.ResourceManager)
18 |
19 | /// Checks to see if the ctor for System.Random is ever called.
20 | let checkForRandomConstructor (ctx: TastContext) =
21 | let ctorForRandom : Tast.MemberDescriptor =
22 | {
23 | DeclaringEntity = Some { AccessPath = "System"; DisplayName = "Random"; CompiledName = "Random" }
24 | CompiledName = ".ctor"
25 | DisplayName = ".ctor"
26 | }
27 |
28 | option {
29 | let! call = ctx.Try()
30 | if call.Ctor = ctorForRandom then
31 | return! Diagnostic.Create(rule, call.Location, call.Ctor.CompiledName) |> Some
32 | else
33 | return! None
34 | }
35 |
36 | open Private
37 |
38 | /// The implementation of the rule
39 | []
40 | let Rule : Rule = fun ctx ->
41 | let rule =
42 | checkForRandomConstructor
43 | rule ctx
44 |
45 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules/Hotspots/S2255_UsingCookies.fs:
--------------------------------------------------------------------------------
1 | module SonarAnalyzer.FSharp.Rules.S2255_UsingCookies
2 |
3 | open SonarAnalyzer.FSharp
4 | open SonarAnalyzer.FSharp.RuleHelpers
5 | open FSharpAst
6 | open System.Net
7 |
8 | // =================================================
9 | // #2255 Writing cookies is security-sensitive
10 | // https://rules.sonarsource.com/csharp/type/Security%20Hotspot/RSPEC-2255
11 | // =================================================
12 |
13 | module Private =
14 |
15 | []
16 | let DiagnosticId = "S2255";
17 | let messageFormat = "Make sure that this cookie is written safely.";
18 | let rule = DiagnosticDescriptor.Create(DiagnosticId, messageFormat, RspecStrings.ResourceManager)
19 |
20 | exception EarlyReturn
21 |
22 | open Private
23 |
24 | /// The implementation of the rule
25 | []
26 | let Rule : Rule = fun ctx ->
27 | None
28 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules/Hotspots/S3011_BypassingAccessibility.fs:
--------------------------------------------------------------------------------
1 | module SonarAnalyzer.FSharp.Rules.S3011_BypassingAccessibility
2 |
3 | open SonarAnalyzer.FSharp
4 | open SonarAnalyzer.FSharp.RuleHelpers
5 | open FSharpAst
6 |
7 | // =================================================
8 | // #3011 Changing or bypassing accessibility is security-sensitive
9 | // https://rules.sonarsource.com/csharp/type/Security%20Hotspot/RSPEC-3011
10 | // =================================================
11 |
12 | module Private =
13 |
14 | []
15 | let DiagnosticId = "S3011";
16 | let messageFormat = "Make sure that this accessibility bypass is safe here.";
17 | let rule = DiagnosticDescriptor.Create(DiagnosticId, messageFormat, RspecStrings.ResourceManager)
18 |
19 | /// Checks to see if the constant for BindingFlags.NonPublic is ever used.
20 | let checkForNonPublicFlag (ctx: TastContext) =
21 | let isBindingFlags (t:FSharpAst.Tast.NamedType) =
22 | let bindingFlagDescriptor : Tast.NamedTypeDescriptor =
23 | {
24 | AccessPath = "System.Reflection"
25 | CompiledName = "BindingFlags"
26 | DisplayName = "BindingFlags"
27 | }
28 | t.Descriptor = bindingFlagDescriptor
29 |
30 | let isNonPublic (constExpr:Tast.ConstantExpr) =
31 | constExpr.Value = (32 |> box)
32 |
33 | option {
34 | let! call = ctx.Try()
35 | match call.Type with
36 | | FSharpAst.Tast.NamedType t ->
37 | let isFailure = isBindingFlags t && isNonPublic call
38 |
39 | if isFailure then
40 | return! Diagnostic.Create(rule, call.Location, t.Descriptor.CompiledName) |> Some
41 | else
42 | return! None
43 | | _ -> return! None
44 | }
45 |
46 | open Private
47 |
48 | /// The implementation of the rule
49 | []
50 | let Rule : Rule = fun ctx ->
51 | let rule =
52 | checkForNonPublicFlag
53 | rule ctx
54 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules/Hotspots/S4507_DeliveringDebugFeaturesInProduction.fs:
--------------------------------------------------------------------------------
1 | module SonarAnalyzer.FSharp.Rules.S4507_DeliveringDebugFeaturesInProduction
2 |
3 | open SonarAnalyzer.FSharp
4 | open SonarAnalyzer.FSharp.RuleHelpers
5 | open FSharpAst
6 | open System.Net
7 |
8 | // =================================================
9 | // #4507 Delivering code in production with debug features activated is security-sensitive
10 | // https://rules.sonarsource.com/csharp/type/Security%20Hotspot/RSPEC-4507
11 | // =================================================
12 |
13 |
14 | module Private =
15 |
16 | []
17 | let DiagnosticId = "S4507";
18 | let messageFormat = "Make sure this debug feature is deactivated before delivering the code in production.";
19 | let rule = DiagnosticDescriptor.Create(DiagnosticId, messageFormat, RspecStrings.ResourceManager)
20 |
21 | exception EarlyReturn
22 |
23 | open Private
24 |
25 | /// The implementation of the rule
26 | []
27 | let Rule : Rule = fun ctx ->
28 | None
29 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules/Hotspots/S4787_EncryptingData.fs:
--------------------------------------------------------------------------------
1 | module SonarAnalyzer.FSharp.Rules.S4787_EncryptingData
2 |
3 | open SonarAnalyzer.FSharp
4 | open SonarAnalyzer.FSharp.RuleHelpers
5 | open FSharpAst
6 | open System.Net
7 |
8 | // =================================================
9 | // #4787 Encrypting data is security-sensitive
10 | // https://rules.sonarsource.com/csharp/type/Security%20Hotspot/RSPEC-4787
11 | // =================================================
12 |
13 | module Private =
14 |
15 | []
16 | let DiagnosticId = "S4787";
17 | let messageFormat = "Make sure that encrypting data is safe here.";
18 | let rule = DiagnosticDescriptor.Create(DiagnosticId, messageFormat, RspecStrings.ResourceManager)
19 |
20 | exception EarlyReturn
21 |
22 | open Private
23 |
24 | /// The implementation of the rule
25 | []
26 | let Rule : Rule = fun ctx ->
27 | None
28 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules/Hotspots/S4790_CreatingHashAlgorithms.fs:
--------------------------------------------------------------------------------
1 | module SonarAnalyzer.FSharp.Rules.S4790_CreatingHashAlgorithms
2 |
3 | open SonarAnalyzer.FSharp
4 | open SonarAnalyzer.FSharp.RuleHelpers
5 | open FSharpAst
6 | open System.Net
7 |
8 | // =================================================
9 | // #4790 Hashing data is security-sensitive
10 | // https://rules.sonarsource.com/csharp/type/Security%20Hotspot/RSPEC-4790
11 | // =================================================
12 |
13 | module Private =
14 |
15 | []
16 | let DiagnosticId = "S4790";
17 | let messageFormat = "Make sure that hashing data is safe here.";
18 | let rule = DiagnosticDescriptor.Create(DiagnosticId, messageFormat, RspecStrings.ResourceManager)
19 |
20 | exception EarlyReturn
21 |
22 | open Private
23 |
24 | /// The implementation of the rule
25 | []
26 | let Rule : Rule = fun ctx ->
27 | None
28 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules/Hotspots/S4792_ConfiguringLoggers.fs:
--------------------------------------------------------------------------------
1 | module SonarAnalyzer.FSharp.Rules.S4792_ConfiguringLoggers
2 |
3 | open SonarAnalyzer.FSharp
4 | open SonarAnalyzer.FSharp.RuleHelpers
5 | open FSharpAst
6 | open System.Net
7 |
8 | // =================================================
9 | // #4792 Configuring loggers is security-sensitive
10 | // https://rules.sonarsource.com/csharp/type/Security%20Hotspot/RSPEC-4792
11 | // =================================================
12 |
13 | module Private =
14 |
15 | []
16 | let DiagnosticId = "S4792";
17 | let messageFormat = "Make sure that this logger's configuration is safe.";
18 | let rule = DiagnosticDescriptor.Create(DiagnosticId, messageFormat, RspecStrings.ResourceManager)
19 |
20 | exception EarlyReturn
21 |
22 | open Private
23 |
24 | /// The implementation of the rule
25 | []
26 | let Rule : Rule = fun ctx ->
27 | None
28 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules/Hotspots/S4818_SocketsCreation.fs:
--------------------------------------------------------------------------------
1 | module SonarAnalyzer.FSharp.Rules.S4818_SocketsCreation
2 |
3 | open SonarAnalyzer.FSharp
4 | open SonarAnalyzer.FSharp.RuleHelpers
5 | open FSharpAst
6 | open System.Net
7 |
8 | // =================================================
9 | // #4818 Using Sockets is security-sensitive
10 | // https://rules.sonarsource.com/csharp/type/Security%20Hotspot/RSPEC-4818
11 | // =================================================
12 |
13 |
14 | module Private =
15 |
16 | []
17 | let DiagnosticId = "S4818";
18 | let messageFormat = "Make sure that sockets are used safely here.";
19 | let rule = DiagnosticDescriptor.Create(DiagnosticId, messageFormat, RspecStrings.ResourceManager)
20 |
21 | exception EarlyReturn
22 |
23 | open Private
24 |
25 | /// The implementation of the rule
26 | []
27 | let Rule : Rule = fun ctx ->
28 | None
29 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules/Hotspots/S4823_UsingCommandLineArguments.fs:
--------------------------------------------------------------------------------
1 | module SonarAnalyzer.FSharp.Rules.S4823_UsingCommandLineArguments
2 |
3 | open SonarAnalyzer.FSharp
4 | open SonarAnalyzer.FSharp.RuleHelpers
5 | open FSharpAst
6 | open System.Net
7 |
8 | // =================================================
9 | // #4823 Using command line arguments is security-sensitive
10 | // https://rules.sonarsource.com/csharp/type/Security%20Hotspot/RSPEC-4823
11 | // =================================================
12 |
13 | module Private =
14 |
15 | []
16 | let DiagnosticId = "S4823";
17 | let messageFormat = "Make sure that command line arguments are used safely here.";
18 | let rule = DiagnosticDescriptor.Create(DiagnosticId, messageFormat, RspecStrings.ResourceManager)
19 |
20 | exception EarlyReturn
21 |
22 | open Private
23 |
24 | /// The implementation of the rule
25 | []
26 | let Rule : Rule = fun ctx ->
27 | None
28 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules/Hotspots/S4829_ReadingStandardInput.fs:
--------------------------------------------------------------------------------
1 | module SonarAnalyzer.FSharp.Rules.S4829_ReadingStandardInput
2 |
3 | open SonarAnalyzer.FSharp
4 | open SonarAnalyzer.FSharp.RuleHelpers
5 | open FSharpAst
6 | open System.Net
7 |
8 | // =================================================
9 | // #4829 Reading the Standard Input is security-sensitive
10 | // https://rules.sonarsource.com/csharp/type/Security%20Hotspot/RSPEC-4829
11 | // =================================================
12 |
13 | module Private =
14 |
15 | []
16 | let DiagnosticId = "S4829";
17 | let messageFormat = "Make sure that reading the standard input is safe here.";
18 | let rule = DiagnosticDescriptor.Create(DiagnosticId, messageFormat, RspecStrings.ResourceManager)
19 |
20 | exception EarlyReturn
21 |
22 | open Private
23 |
24 | /// The implementation of the rule
25 | []
26 | let Rule : Rule = fun ctx ->
27 | None
28 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules/Hotspots/S4834_ControllingPermissions.fs:
--------------------------------------------------------------------------------
1 | module SonarAnalyzer.FSharp.Rules.S4834_ControllingPermissions
2 |
3 | open SonarAnalyzer.FSharp
4 | open SonarAnalyzer.FSharp.RuleHelpers
5 | open FSharpAst
6 | open System.Net
7 |
8 | // =================================================
9 | // #4834 Controlling permissions is security-sensitive
10 | // https://rules.sonarsource.com/csharp/type/Security%20Hotspot/RSPEC-4834
11 | // =================================================
12 |
13 | module Private =
14 |
15 | []
16 | let DiagnosticId = "S4834";
17 | let messageFormat = "Make sure that permissions are controlled safely here.";
18 | let rule = DiagnosticDescriptor.Create(DiagnosticId, messageFormat, RspecStrings.ResourceManager)
19 |
20 | exception EarlyReturn
21 |
22 | open Private
23 |
24 | /// The implementation of the rule
25 | []
26 | let Rule : Rule = fun ctx ->
27 | None
28 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/Rules/Hotspots/S5042_ExpandingArchiveFiles.fs:
--------------------------------------------------------------------------------
1 | module SonarAnalyzer.FSharp.Rules.S5042_ExpandingArchiveFiles
2 |
3 | open SonarAnalyzer.FSharp
4 | open SonarAnalyzer.FSharp.RuleHelpers
5 | open FSharpAst
6 | open System.Net
7 |
8 | // =================================================
9 | // #5042 Expanding archive files is security-sensitive
10 | // https://rules.sonarsource.com/csharp/type/Security%20Hotspot/RSPEC-5042
11 | // =================================================
12 |
13 | module Private =
14 |
15 | []
16 | let DiagnosticId = "S5042";
17 | let messageFormat = "Make sure that decompressing this archive file is safe.";
18 | let rule = DiagnosticDescriptor.Create(DiagnosticId, messageFormat, RspecStrings.ResourceManager)
19 |
20 | exception EarlyReturn
21 |
22 | open Private
23 |
24 | /// The implementation of the rule
25 | []
26 | let Rule : Rule = fun ctx ->
27 | None
28 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/src/SonarAnalyzer.FSharp/SonarAnalyzer.FSharp.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/tests/FSharpAst.UnitTest/FSharpAst.UnitTest.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 | true
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/tests/SonarAnalyzer.FSharp.UnitTest/NoncompliantLocationsTest.fs:
--------------------------------------------------------------------------------
1 | module SonarAnalyzer.FSharp.UnitTest.NoncompliantLocationsTest
2 |
3 | open NUnit.Framework
4 |
5 | // check that the NonCompliantLocations logic is working
6 |
7 | []
8 | let ``check that noncompliant location parsing works correctly``() =
9 | let lines = [
10 | @"1. // Compliant"
11 | @"2. normal code // Noncompliant"
12 | @"2. // commented out code // Noncompliant"
13 | @"3. Noncompliant() // a call not a comment"
14 | ]
15 | let actual = Verifier.getNoncompliantLocations lines |> sprintf "%A"
16 | let expected = [2] |> sprintf "%A"
17 | Assert.AreEqual(expected,actual)
18 |
19 |
20 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/tests/SonarAnalyzer.FSharp.UnitTest/RspecStringsTest.fs:
--------------------------------------------------------------------------------
1 | module SonarAnalyzer.FSharp.UnitTest.RspecStringsTest
2 |
3 | open SonarAnalyzer.FSharp
4 | open NUnit.Framework
5 |
6 | // check that the RspecStrings resources has been embedded and access logic is working
7 |
8 | []
9 | let getStringSucceeds() =
10 | let rm = RspecStrings.ResourceManager
11 | let str = rm.GetString("S1313_Title")
12 | Assert.IsNotNull(str)
13 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/tests/SonarAnalyzer.FSharp.UnitTest/RuleManagerTest.fs:
--------------------------------------------------------------------------------
1 | module SonarAnalyzer.FSharp.UnitTest.RuleFinderTest
2 |
3 | open SonarAnalyzer.FSharp
4 | open NUnit.Framework
5 | open FSharpAst
6 |
7 | // check that the Rule manager logic work
8 |
9 | /// Check that all rules loaded in the assembly actually run witout error
10 | []
11 | let ``check all AvailableRules can run without error``() =
12 |
13 | let availableRules = RuleManager.getAvailableRules()
14 | if Seq.length availableRules = 0 then
15 | Assert.Fail("Expect non empty list of rules")
16 |
17 | /// create a dummy context to run each rule on
18 | let dummyNode : Tast.ImplementationFile = {Name= "dummy"; Decls=[]}
19 | let ctx : TastContext = {Filename=dummyNode.Name; Node=dummyNode; Ancestors=[]}
20 |
21 | let failedRules = ResizeArray()
22 | for availableRule in availableRules do
23 | let ruleId = availableRule.RuleId
24 | try
25 | let _result = availableRule.Rule ctx
26 | ()
27 | with
28 | | ex ->
29 | let msg = sprintf "Rule %s failed with exception '%s'\n%s\n===============" ruleId ex.Message ex.StackTrace
30 | failedRules.Add msg
31 |
32 | if failedRules.Count > 0 then
33 | let msg = String.concat "\n" failedRules
34 | Assert.Fail(msg)
35 |
36 | /// Check that all rules loaded in the assembly can be converted into RuleDetails
37 | /// and that their associated resources are available.
38 | []
39 | let ``check all RuleDetails can be created``() =
40 |
41 | let availableRules = RuleManager.getAvailableRules()
42 | if Seq.length availableRules = 0 then
43 | Assert.Fail("Expect non empty list of rules")
44 |
45 | let failedRules = ResizeArray()
46 | for availableRule in availableRules do
47 | try
48 | let _ruleDetail = RuleManager.toRuleDetail availableRule
49 | ()
50 | with
51 | | ex ->
52 | let msg = sprintf "Rule %s failed with exception '%s'\n%s\n===============" availableRule.RuleId ex.Message ex.StackTrace
53 | failedRules.Add msg
54 |
55 | if failedRules.Count > 0 then
56 | let msg = String.concat "\n" failedRules
57 | Assert.Fail(msg)
58 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/tests/SonarAnalyzer.FSharp.UnitTest/SourceFileTests.fs:
--------------------------------------------------------------------------------
1 | module SonarAnalyzer.FSharp.UnitTest.SourceFileTests
2 |
3 | open System
4 | open NUnit.Framework
5 | open SonarAnalyzer.FSharp
6 |
7 | []
8 | let S1313_HardcodedIpAddress() =
9 | let rule = Rules.S1313_HardcodedIpAddress.Rule
10 | Verifier.verify @"TestCases/S1313_HardcodedIpAddress.fs" rule
11 |
12 | []
13 | let S2077_ExecutingSqlQueries() =
14 | let rule = Rules.S2077_ExecutingSqlQueries.Rule
15 | Verifier.verify @"TestCases/S2077_ExecutingSqlQueries.fs" rule
16 |
17 | []
18 | let S2092_CookieShouldBeSecure() =
19 | let rule = Rules.S2092_CookieShouldBeSecure.Rule
20 | Verifier.verify @"TestCases/S2092_CookieShouldBeSecure.fs" rule
21 |
22 | []
23 | let S2245_DoNotUseRandom() =
24 | let rule = Rules.S2245_DoNotUseRandom.Rule
25 | Verifier.verify @"TestCases/S2245_DoNotUseRandom.fs" rule
26 |
27 | []
28 | []//Requires NuGet
29 | let S2255_UsingCookies() =
30 | let rule = Rules.S2255_UsingCookies.Rule
31 | Verifier.verify @"TestCases/S2255_UsingCookies.fs" rule
32 |
33 | []
34 | let S3011_BypassingAccessibility() =
35 | let rule = Rules.S3011_BypassingAccessibility.Rule
36 | Verifier.verify @"TestCases/S3011_BypassingAccessibility.fs" rule
37 |
38 | []
39 | []//Requires NuGet
40 | let S4507_DeliveringDebugFeaturesInProduction() =
41 | let rule = Rules.S4507_DeliveringDebugFeaturesInProduction.Rule
42 | Verifier.verify @"TestCases/S4507_DeliveringDebugFeaturesInProduction.fs" rule
43 |
44 | []
45 | let S4784_UsingRegularExpressions() =
46 | let rule = Rules.S4784_UsingRegularExpressions.Rule
47 | Verifier.verify @"TestCases/S4784_UsingRegularExpressions.fs" rule
48 |
49 | []
50 | []
51 | let S4787_EncryptingData() =
52 | let rule = Rules.S4787_EncryptingData.Rule
53 | Verifier.verify @"TestCases/S4787_EncryptingData.fs" rule
54 |
55 | []
56 | []
57 | let S4790_CreatingHashAlgorithms() =
58 | let rule = Rules.S4790_CreatingHashAlgorithms.Rule
59 | Verifier.verify @"TestCases/S4790_CreatingHashAlgorithms.fs" rule
60 |
61 | []
62 | []//Requires NuGet
63 | let S4792_ConfiguringLoggers_AspNetCore() =
64 | let rule = Rules.S4792_ConfiguringLoggers.Rule
65 | Verifier.verify @"TestCases/S4792_ConfiguringLoggers_AspNetCore.fs" rule
66 |
67 | []
68 | []//Requires NuGet
69 | let S4792_ConfiguringLoggers_Serilog() =
70 | let rule = Rules.S4792_ConfiguringLoggers.Rule
71 | Verifier.verify @"TestCases/S4792_ConfiguringLoggers_Serilog.fs" rule
72 |
73 | []
74 | []
75 | let S4818_SocketsCreation() =
76 | let rule = Rules.S4818_SocketsCreation.Rule
77 | Verifier.verify @"TestCases/S4818_SocketsCreation.fs" rule
78 |
79 | []
80 | []
81 | let S4823_UsingCommandLineArguments() =
82 | let rule = Rules.S4823_UsingCommandLineArguments.Rule
83 | Verifier.verify @"TestCases/S4823_UsingCommandLineArguments.fs" rule
84 |
85 | []
86 | []
87 | let S4829_ReadingStandardInput() =
88 | let rule = Rules.S4829_ReadingStandardInput.Rule
89 | Verifier.verify @"TestCases/S4829_ReadingStandardInput.fs" rule
90 |
91 | []
92 | []//Requires NuGet
93 | let S4834_ControllingPermissions() =
94 | let rule = Rules.S4834_ControllingPermissions.Rule
95 | Verifier.verify @"TestCases/S4834_ControllingPermissions.fs" rule
96 |
97 | []
98 | []
99 | let S5042_ExpandingArchiveFiles() =
100 | let rule = Rules.S5042_ExpandingArchiveFiles.Rule
101 | Verifier.verify @"TestCases/S5042_ExpandingArchiveFiles.fs" rule
102 |
103 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/tests/SonarAnalyzer.FSharp.UnitTest/TestCases/S1313_HardcodedIpAddress.fs:
--------------------------------------------------------------------------------
1 | module SonarAnalyzer.FSharp.UnitTest.TestCases.S1313_HardcodedIpAddress
2 |
3 | /// Dummy class for testing
4 | type AnyAssemblyClass(s:string) = class end
5 |
6 | /// Dummy class for testing
7 | type SomeAttribute(s:string) =
8 | inherit System.Attribute()
9 |
10 | /// Dummy function for testing
11 | let writeAssemblyInfo(assemblyName:string, version:string, author:string, description:string, title:string) =
12 | ()
13 |
14 |
15 | [] // this is mainly for assembly versions
16 | let hardcodedIpAddress() =
17 |
18 | let ip1 = "192.168.0.1" // Noncompliant {{Make sure using this hardcoded IP address '192.168.0.1' is safe here.}}
19 | // ^^^^^^^^^^^^^
20 |
21 | let ip2 = "300.0.0.0" // Compliant, not a valid IP
22 | let ip3 = "127.0.0.1" // Compliant, this is an exception in the rule (see: https://github.com/SonarSource/sonar-csharp/issues/1540)
23 | let ip4 = " 127.0.0.0 " // Compliant
24 | let ip5 = @" ""127.0.0.0"" " // Compliant
25 |
26 | let ip6 = "2001:db8:1234:ffff:ffff:ffff:ffff:ffff" // Noncompliant
27 | let ip7 = "::/0" // Compliant, not recognized as IPv6 address
28 | let ip8 = "::" // Compliant, this is an exception in the rule
29 |
30 | let ip9 = "2" // Compliant, should not be recognized as 0.0.0.2
31 |
32 | let v = System.Version("127.0.0.0") //Compliant
33 | let a = AnyAssemblyClass("127.0.0.0") //Compliant
34 |
35 | //Compliant
36 | writeAssemblyInfo("Project","1.2.0.0","Thomas","Content","Package")
37 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/tests/SonarAnalyzer.FSharp.UnitTest/TestCases/S1313_HardcodedIpAddress.fsi:
--------------------------------------------------------------------------------
1 | module SonarAnalyzer.FSharp.UnitTest.TestCases.S1313_HardcodedIpAddress
2 |
3 | (*
4 | This tests whether FSI files can be parsed too without error
5 | *)
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/tests/SonarAnalyzer.FSharp.UnitTest/TestCases/S2092_CookieShouldBeSecure.fs:
--------------------------------------------------------------------------------
1 | module SonarAnalyzer.FSharp.UnitTest.TestCases.S2092_CookieShouldBeSecure
2 |
3 | open System
4 |
5 | type HttpCookie(str:string) =
6 | member val Secure = false with get, set
7 | member val HttpOnly = false with get, set
8 |
9 | type Program() =
10 |
11 | let mutable field1 = HttpCookie("c") // Noncompliant
12 | let mutable field2 = None
13 |
14 | member val Property1 = HttpCookie("c") with get, set // Noncompliant
15 | member val Property2 = None with get, set
16 |
17 | member this.CtorSetsAllowedValue() =
18 | // none
19 | ()
20 |
21 | member this.CtorSetsNotAllowedValue() =
22 | HttpCookie("c") |> ignore // Noncompliant {{Make sure creating this cookie without setting the 'Secure' property is safe here.}}
23 |
24 | member this.InitializerSetsAllowedValue() =
25 | HttpCookie("c", Secure = true) |> ignore
26 |
27 | member this.InitializerSetsNotAllowedValue() =
28 | HttpCookie("c", Secure = false) |> ignore // Noncompliant
29 | // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
30 | HttpCookie("c") |> ignore // Noncompliant
31 | HttpCookie("c", HttpOnly = true) |> ignore // Noncompliant
32 |
33 | member this.PropertySetsNotAllowedValue() =
34 | let c = new HttpCookie("c", Secure = true)
35 | c.Secure <- false // Noncompliant
36 | // ^^^^^^^^^^^^^^^^
37 |
38 | field1.Secure <- false // Noncompliant
39 | //this.field1.Secure <- false // Noncompliant
40 |
41 | //Property1.Secure <- false // Noncompliant
42 | this.Property1.Secure <- false // Noncompliant
43 |
44 | member this.PropertySetsAllowedValue(foo:bool) =
45 | let c1 = HttpCookie("c") // Compliant, Secure is set below
46 | c1.Secure <- true
47 |
48 | field1 <- HttpCookie("c") // Compliant, Secure is set below
49 | field1.Secure <- true
50 |
51 | field2 <- Some (HttpCookie("c")) // Compliant, Secure is set below
52 | field2.Value.Secure <- true
53 |
54 | this.Property1 <- HttpCookie("c") // Compliant, Secure is set below
55 | this.Property1.Secure <- true
56 |
57 | this.Property2 <- Some (HttpCookie("c")) // Compliant, Secure is set below
58 | this.Property2.Value.Secure <- true
59 |
60 | //let c2 = HttpCookie("c") // Noncompliant, Secure is set conditionally
61 | //if foo then
62 | // c2.Secure <- true
63 |
64 | let c3 = HttpCookie("c") // Compliant, Secure is set after the if
65 | if foo then
66 | // do something
67 | ()
68 | c3.Secure <- true
69 |
70 | let mutable c4 : HttpCookie = Unchecked.defaultof
71 | //if foo then
72 | // c4 <- HttpCookie("c") // Noncompliant, Secure is not set in the same scope
73 | c4.Secure <- true
74 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/tests/SonarAnalyzer.FSharp.UnitTest/TestCases/S2245_DoNotUseRandom.fs:
--------------------------------------------------------------------------------
1 | module SonarAnalyzer.FSharp.UnitTest.TestCases.S2245_DoNotUseRandom
2 |
3 | open System
4 | open System.Security.Cryptography
5 |
6 | module Main =
7 |
8 | let r1 = Random() // Noncompliant {{Make sure that using this pseudorandom number generator is safe here.}}
9 | // ^^^^^^^^^^^^
10 | let r2 = Random(1) // Noncompliant
11 |
12 | let r3 = EventArgs() // Compliant, not Random
13 |
14 | let r4 = RandomNumberGenerator.Create() // Compliant, using cryptographically strong RNG
15 |
16 | ()
17 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/tests/SonarAnalyzer.FSharp.UnitTest/TestCases/S2255_UsingCookies.fs:
--------------------------------------------------------------------------------
1 | module SonarAnalyzer.FSharp.UnitTest.TestCases.S2255_UsingCookies
2 |
3 | open Microsoft.AspNetCore.Http
4 | open Microsoft.Extensions.Primitives
5 |
6 | module Program =
7 |
8 | let responses(response:HttpResponse ) =
9 |
10 | // Response headers
11 | response.Headers.Add("Set-Cookie", StringValues "") // Noncompliant
12 | response.Headers.["Set-Cookie"] <- StringValues "" // Noncompliant
13 | let value = response.Headers.["Set-Cookie"] // Compliant
14 |
15 | // Not the Set-Cookie header
16 | response.Headers.Add("something", StringValues "")
17 | response.Headers.["something"] <- value
18 | let value = response.Headers.["something"]
19 |
20 | // Response headers as variable
21 | let responseHeaders = response.Headers
22 | responseHeaders.Add("Set-Cookie", StringValues "") // Noncompliant
23 | responseHeaders.["Set-Cookie"] <- StringValues "" // Noncompliant
24 | let value = responseHeaders.["Set-Cookie"] // Compliant
25 |
26 | responseHeaders.Remove("Set-Cookie") |> ignore // Compliant
27 | responseHeaders.Remove("") |> ignore // Compliant
28 |
29 | // Response cookies as property
30 | response.Cookies.Append("", "") // Noncompliant
31 | response.Cookies.Append("", "", CookieOptions() ) // Noncompliant
32 |
33 | // Response cookies as variable
34 | let responseCookies = response.Cookies
35 | responseCookies.Append("", "") // Noncompliant
36 | responseCookies.Append("", "", CookieOptions() ) // Noncompliant
37 |
38 | responseCookies.Delete("") // Compliant
39 |
40 | let requests(request:HttpRequest )=
41 |
42 | let value = StringValues ""
43 |
44 | // Request headers
45 | request.Headers.Add("Set-Cookie", StringValues "") // Noncompliant
46 | request.Headers.["Set-Cookie"] <- value // Noncompliant
47 | let value = request.Headers.["Set-Cookie"] // Compliant
48 |
49 | // Not the Set-Cookie header
50 | request.Headers.Add("something", StringValues "")
51 | request.Headers.["something"] <- value
52 | let value = request.Headers.["something"]
53 |
54 | // Request headers as variable
55 | let requestHeaders = request.Headers
56 | requestHeaders.Add("Set-Cookie", StringValues "") // Noncompliant
57 | requestHeaders.["Set-Cookie"] <- value // Noncompliant
58 | let value = requestHeaders.["Set-Cookie"] // Compliant
59 |
60 | requestHeaders.Remove("Set-Cookie") |> ignore // Compliant
61 | requestHeaders.Remove("") |> ignore // Compliant
62 |
63 | // Request cookies as property
64 | let value = request.Cookies.[""] // Compliant
65 | let v = request.Cookies.TryGetValue("") // Compliant
66 |
67 | // Request cookies as variable
68 | let requestCookies = request.Cookies
69 | let value = requestCookies.[""] // Compliant
70 | let v = requestCookies.TryGetValue("") // Compliant
71 |
72 | ()
73 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/tests/SonarAnalyzer.FSharp.UnitTest/TestCases/S3011_BypassingAccessibility.fs:
--------------------------------------------------------------------------------
1 | module SonarAnalyzer.FSharp.UnitTest.TestCases.S3011_BypassingAccessibility
2 |
3 | open System
4 | open System.Reflection
5 |
6 | module Program =
7 |
8 | let test() =
9 | // RSPEC: https://jira.sonarsource.com/browse/RSPEC-3011
10 | let dynClass = Type.GetType("MyInternalClass")
11 | // Questionable. Using BindingFlags.NonPublic will return non-public members
12 | let bindingAttr = BindingFlags.NonPublic ||| BindingFlags.Static // Noncompliant
13 | // ^^^^^^^^^^^^^^^^^^^^^^ {{Make sure that this accessibility bypass is safe here.}}
14 | let dynMethod = dynClass.GetMethod("mymethod", bindingAttr)
15 | let result = dynMethod.Invoke(dynClass, null)
16 |
17 | ()
18 |
19 |
20 | let additionalChecks(t:System.Type) : BindingFlags =
21 | // Using other binding attributes should be ok
22 | let bindingAttr =
23 | BindingFlags.Static ||| BindingFlags.CreateInstance ||| BindingFlags.DeclaredOnly |||
24 | BindingFlags.ExactBinding ||| BindingFlags.GetField ||| BindingFlags.InvokeMethod // et cetera...
25 | let dynMeth = t.GetMember("mymethod", bindingAttr)
26 |
27 | // We don't detect casts to the forbidden value
28 | let nonPublic : BindingFlags = enum 32
29 | let dynMeth = t.GetMember("mymethod", nonPublic)
30 |
31 | let v = Enum.TryParse("NonPublic")
32 | let dynMeth = t.GetMember("mymethod", nonPublic)
33 |
34 | let bindingAttr = (((BindingFlags.NonPublic)) ||| BindingFlags.Static) // Noncompliant
35 | // ^^^^^^^^^^^^^^^^^^^^^^
36 | let dynMeth = t.GetMember("mymethod", (BindingFlags.NonPublic)) // Noncompliant
37 | // ^^^^^^^^^^^^^^^^^^^^^^
38 | let v = (int)BindingFlags.NonPublic // Noncompliant
39 | BindingFlags.NonPublic // Noncompliant
40 |
41 | let defaultAccess = BindingFlags.OptionalParamBinding ||| BindingFlags.NonPublic // Noncompliant
42 | // ^^^^^^^^^^^^^^^^^^^^^^
43 |
44 | let private access1 = BindingFlags.NonPublic // Noncompliant
45 |
46 | let access2 = BindingFlags.NonPublic // Noncompliant
47 | let getBindingFlags() = BindingFlags.NonPublic // Noncompliant
48 |
49 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/tests/SonarAnalyzer.FSharp.UnitTest/TestCases/S4507_DeliveringDebugFeaturesInProduction.fs:
--------------------------------------------------------------------------------
1 | module SonarAnalyzer.FSharp.UnitTest.TestCases.S4507_DeliveringDebugFeaturesInProduction
2 |
3 | open Microsoft.AspNetCore.Builder
4 | open Microsoft.AspNetCore.Hosting
5 |
6 | module Startup =
7 |
8 | let Configure(app:IApplicationBuilder, env:IHostingEnvironment) =
9 | // Invoking as extension methods
10 | if (env.IsDevelopment()) then
11 | app.UseDeveloperExceptionPage() |> ignore // Compliant
12 | app.UseDatabaseErrorPage() |> ignore // Compliant
13 |
14 | // Invoking as static methods
15 | if (HostingEnvironmentExtensions.IsDevelopment(env)) then
16 | DeveloperExceptionPageExtensions.UseDeveloperExceptionPage(app) |> ignore // Compliant
17 | DatabaseErrorPageExtensions.UseDatabaseErrorPage(app) |> ignore // Compliant
18 |
19 | // Not in development
20 | if not (env.IsDevelopment()) then
21 | DeveloperExceptionPageExtensions.UseDeveloperExceptionPage(app) |> ignore // Noncompliant
22 | DatabaseErrorPageExtensions.UseDatabaseErrorPage(app) |> ignore // Noncompliant
23 |
24 | // Custom conditions are deliberately ignored
25 | let isDevelopment = env.IsDevelopment()
26 | if (isDevelopment) then
27 | app.UseDeveloperExceptionPage() |> ignore // Noncompliant, False Positive
28 | app.UseDatabaseErrorPage() |> ignore // Noncompliant, False Positive
29 |
30 | // These are called unconditionally
31 | app.UseDeveloperExceptionPage() |> ignore // Noncompliant
32 | app.UseDatabaseErrorPage() |> ignore // Noncompliant
33 | DeveloperExceptionPageExtensions.UseDeveloperExceptionPage(app) |> ignore // Noncompliant
34 | DatabaseErrorPageExtensions.UseDatabaseErrorPage(app) // Noncompliant
35 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/tests/SonarAnalyzer.FSharp.UnitTest/TestCases/S4787_EncryptingData.fs:
--------------------------------------------------------------------------------
1 | module SonarAnalyzer.FSharp.UnitTest.TestCases.S4787_EncryptingData
2 |
3 |
4 | open System
5 | open System.Security.Cryptography
6 |
7 | // RSPEC example: https://jira.sonarsource.com/browse/RSPEC-4938
8 | type MyClass() =
9 |
10 | member this.Main() =
11 | let data :Byte[] = [| 1uy; 1uy; 1uy |]
12 |
13 | let myRSA = RSA.Create()
14 | let padding = RSAEncryptionPadding.CreateOaep(HashAlgorithmName.SHA1)
15 |
16 | // Review all base RSA class' Encrypt/Decrypt calls
17 | myRSA.Encrypt(data, padding) |> ignore // Noncompliant
18 | // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ {{Make sure that encrypting data is safe here.}}
19 | myRSA.EncryptValue(data) |> ignore // Noncompliant
20 | myRSA.Decrypt(data, padding) |> ignore // Noncompliant
21 | myRSA.DecryptValue(data) |> ignore // Noncompliant
22 |
23 | let myRSAC = new RSACryptoServiceProvider()
24 | // Review the use of any TryEncrypt/TryDecrypt and specific Encrypt/Decrypt of RSA subclasses.
25 | myRSAC.Encrypt(data, false) |> ignore // Noncompliant
26 | myRSAC.Decrypt(data, false) |> ignore // Noncompliant
27 |
28 |
29 | // Note: TryEncrypt/TryDecrypt are only in .NET Core 2.1+
30 | // myRSAC.TryEncrypt(data, Span.Empty, padding, out written) // Non compliant
31 | // myRSAC.TryDecrypt(data, Span.Empty, padding, out written) // Non compliant
32 |
33 | let rgbKey : byte[] = [| 1uy; 2uy; 3uy |]
34 | let rgbIV : byte[] = [| 4uy; 5uy; 6uy |]
35 | let rijn = SymmetricAlgorithm.Create()
36 | // Review the creation of Encryptors from any SymmetricAlgorithm instance.
37 | rijn.CreateEncryptor() |> ignore // Noncompliant
38 | // ^^^^^^^^^^^^^^^^^^^^^^ {{Make sure that encrypting data is safe here.}}
39 | rijn.CreateEncryptor(rgbKey, rgbIV) |> ignore // Noncompliant
40 | rijn.CreateDecryptor() |> ignore // Noncompliant
41 | rijn.CreateDecryptor(rgbKey, rgbIV) |> ignore // Noncompliant
42 |
43 |
44 | type MyAsymmetricCrypto() = // Noncompliant
45 | inherit System.Security.Cryptography.AsymmetricAlgorithm()
46 |
47 |
48 | type MySymmetricCrypto() = // Noncompliant
49 | inherit System.Security.Cryptography.SymmetricAlgorithm()
50 |
51 | override this.CreateDecryptor(rgbKey, rgbIV) = null
52 | override this.CreateEncryptor(rgbKey, rgbIV) = null
53 | override this.GenerateIV() = ()
54 | override this.GenerateKey() = ()
55 |
56 | type MyRSA() =
57 | inherit System.Security.Cryptography.RSA() // Noncompliant
58 |
59 | // Dummy methods with the same names as the additional methods added in Net Core 2.1.
60 | member this.TryEncrypt() = ()
61 | member this.TryEncrypt(dummyMethod) = ()
62 | member this.TryDecrypt() = ()
63 | member this.TryDecrypt(dummyMethod) = ()
64 |
65 | member this.OtherMethod() = ()
66 |
67 | // Abstract methods
68 | override this.ExportParameters(includePrivateParameters) = new RSAParameters()
69 | override this.ImportParameters(parameters) = ()
70 |
71 |
72 | type Class2() =
73 |
74 | member this.AdditionalTests(data:Byte[], padding:RSAEncryptionPadding) =
75 | let customAsymProvider = new MyRSA()
76 |
77 | // Should raise on derived asymmetric classes
78 | customAsymProvider.Encrypt(data, padding) |> ignore // Noncompliant
79 | customAsymProvider.EncryptValue(data) |> ignore // Noncompliant
80 | customAsymProvider.Decrypt(data, padding) |> ignore // Noncompliant
81 | customAsymProvider.DecryptValue(data) |> ignore // Noncompliant
82 |
83 | // Should raise on the Try* methods added in NET Core 2.1
84 | // Note: this test is cheating - we can't currently referencing the
85 | // real 2.1 assemblies since the test project is targetting an older
86 | // NET Framework, so we're testing against a custom subclass
87 | // to which we've added the new method names.
88 | customAsymProvider.TryEncrypt() // Noncompliant
89 | customAsymProvider.TryEncrypt(null) // Noncompliant
90 | customAsymProvider.TryDecrypt() // Noncompliant
91 | customAsymProvider.TryDecrypt(null) // Noncompliant
92 |
93 | customAsymProvider.OtherMethod()
94 |
95 | // Should raise on derived symmetric classes
96 | let customSymProvider = new MySymmetricCrypto()
97 | customSymProvider.CreateEncryptor() |> ignore // Noncompliant
98 | customSymProvider.CreateDecryptor() |> ignore // Noncompliant
99 |
100 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/tests/SonarAnalyzer.FSharp.UnitTest/TestCases/S4790_CreatingHashAlgorithms.fs:
--------------------------------------------------------------------------------
1 | module rec SonarAnalyzer.FSharp.UnitTest.TestCases.S4790_CreatingHashAlgorithms
2 |
3 | open System.Security.Cryptography
4 |
5 | type TestClass() =
6 |
7 | // RSPEC 4790: https://jira.sonarsource.com/browse/RSPEC-4790
8 | member this.ComputeHash() =
9 | // Review all instantiations of classes that inherit from HashAlgorithm, for example:
10 | let hashAlgo = HashAlgorithm.Create() // Noncompliant
11 | // ^^^^^^^^^^^^^^^^^^^^^^ {{Make sure that hashing data is safe here.}}
12 | let hashAlgo2 = HashAlgorithm.Create("SHA1") // Noncompliant
13 | // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ {{Make sure that hashing data is safe here.}}
14 |
15 | let sha = new SHA1CryptoServiceProvider() // Noncompliant
16 | // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ {{Make sure that hashing data is safe here.}}
17 |
18 | let md5 = new MD5CryptoServiceProvider() // Noncompliant
19 | // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ {{Make sure that hashing data is safe here.}}
20 | ()
21 |
22 | member this.AdditionalTests(sha1:SHA1CryptoServiceProvider ) =
23 | use myHash = new MyHashAlgorithm() // Noncompliant
24 | use myHash = new MyHashAlgorithm(123) // Noncompliant
25 |
26 | let myHash = MyHashAlgorithm.Create() // Noncompliant
27 | let myHash = MyHashAlgorithm.Create(42) // Noncompliant
28 |
29 | let myHash = MyHashAlgorithm.CreateHash() // compliant - method name is not Create
30 | let myHash = MyHashAlgorithm.DoCreate() // compliant - method name is not Create
31 |
32 | // Other methods are not checked
33 | let hash = sha1.ComputeHash(null:byte[])
34 | let hash = sha1.Hash
35 | let canReuse = sha1.CanReuseTransform
36 | sha1.Clear()
37 |
38 | type MyHashAlgorithm(data:int) =
39 | inherit HashAlgorithm() // Noncompliant
40 | // ^^^^^^^^^^^^^
41 | new () = new MyHashAlgorithm(1)
42 | static member Create() : MyHashAlgorithm = failwith "not implemented"
43 | static member Create(data) : MyHashAlgorithm = failwith "not implemented"
44 |
45 | static member CreateHash() : MyHashAlgorithm = failwith "not implemented"
46 | static member DoCreate() :MyHashAlgorithm = failwith "not implemented"
47 |
48 | override this.Initialize() = ()
49 | override this.HashCore(array, ibStart, cbSize) = ()
50 | override this.HashFinal() = failwith "not implemented"
51 |
52 |
53 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/tests/SonarAnalyzer.FSharp.UnitTest/TestCases/S4792_ConfiguringLoggers_Serilog.fs:
--------------------------------------------------------------------------------
1 | module rec SonarAnalyzer.FSharp.UnitTest.TestCases.S4792_ConfiguringLoggers_Serilog
2 |
3 | open Serilog
4 | open Serilog.Core
5 |
6 |
7 | type SerilogLogging() =
8 |
9 | // RSPEC-4792: https://jira.sonarsource.com/browse/RSPEC-4792
10 | member this.Foo() =
11 | new Serilog.LoggerConfiguration() // Noncompliant
12 | // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ {{Make sure that this logger's configuration is safe.}}
13 |
14 | member this.AdditionalTests() =
15 | let config = LoggerConfiguration() // Noncompliant
16 | let config = MyConfiguration() // Noncompliant
17 |
18 | // Using the logger shouldn't raise issues
19 | let levelSwitch = new LoggingLevelSwitch()
20 | levelSwitch.MinimumLevel <- Serilog.Events.LogEventLevel.Warning
21 |
22 | let newLog =
23 | config.MinimumLevel.ControlledBy(levelSwitch)
24 | .WriteTo.Console()
25 | .CreateLogger()
26 |
27 | Log.Logger <- newLog
28 | Log.Information("logged info")
29 | Log.CloseAndFlush()
30 |
31 | type MyConfiguration() =
32 | inherit LoggerConfiguration()
33 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/tests/SonarAnalyzer.FSharp.UnitTest/TestCases/S4818_SocketsCreation.fs:
--------------------------------------------------------------------------------
1 | module rec SonarAnalyzer.FSharp.UnitTest.TestCases.S4818_SocketsCreation
2 |
3 | open System.Net.Sockets
4 |
5 | type TestSocket() =
6 |
7 | // RSpec example: https://jira.sonarsource.com/browse/RSPEC-4944
8 | static member Run() =
9 | let socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) // Noncompliant
10 | // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ {{Make sure that sockets are used safely here.}}
11 |
12 | // TcpClient and UdpClient simply abstract the details of creating a Socket
13 | let client = new TcpClient("example.com", 80) // Noncompliant
14 | // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ {{Make sure that sockets are used safely here.}}
15 |
16 | let listener = new UdpClient(80) // Noncompliant
17 | // ^^^^^^^^^^^^^^^^^ {{Make sure that sockets are used safely here.}}
18 | ()
19 |
20 | member this.Tests(socket:Socket, tcp:TcpClient, udp:UdpClient) =
21 | // Ok to call other methods and properties
22 | socket.Accept() |> ignore
23 | let isAvailable = tcp.Available
24 | udp.DontFragment <- true
25 |
26 | // Creating of subclasses is not checked
27 | let s = new MySocket()
28 | let s = new MyTcpClient()
29 | let s = new MyUdpClient()
30 | ()
31 |
32 | type MySocket() =
33 | inherit Socket(new SocketInformation())
34 |
35 | type MyTcpClient() =
36 | inherit TcpClient()
37 |
38 | type MyUdpClient() =
39 | inherit UdpClient()
40 |
41 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/tests/SonarAnalyzer.FSharp.UnitTest/TestCases/S4823_UsingCommandLineArguments.fs:
--------------------------------------------------------------------------------
1 | module SonarAnalyzer.FSharp.UnitTest.TestCases.S4823_UsingCommandLineArguments
2 |
3 | open System
4 |
5 | type Program1() =
6 |
7 | static member Main([] args:string[]) = // Noncompliant {{Make sure that command line arguments are used safely here.}}
8 | // ^^^^
9 | Console.WriteLine(args.[0])
10 |
11 | type Program2() =
12 |
13 | static member Main([] args:string[]) = // Compliant, args is not used
14 | ()
15 |
16 | static member Main(arg:string ) = // Compliant, doesn't conform to signature for a Main method
17 | Console.WriteLine(arg)
18 |
19 | static member Main(x:int, [] args:string[]) = // Compliant, doesn't conform to signature for a Main method
20 | Console.WriteLine(args)
21 |
22 | type Program3() =
23 | static let staticArgs : string[] = [||]
24 |
25 | static member Main([] args:string[]) = // Compliant, args is not used
26 | Console.WriteLine(staticArgs)
27 |
28 | type Program4() =
29 | static member Main([] args:string[]) : string = // Compliant, doesn't conform to signature for a Main method
30 | Console.WriteLine(args)
31 | ""
32 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/tests/SonarAnalyzer.FSharp.UnitTest/TestCases/S4829_ReadingStandardInput.fs:
--------------------------------------------------------------------------------
1 | module SonarAnalyzer.FSharp.UnitTest.TestCases.S4829_ReadingStandardInput
2 |
3 | open System
4 | type Con = System.Console
5 |
6 | type MyConsole() =
7 | static member Read() = 1
8 | static member ReadKey() = 1
9 | static member In =
10 | new System.IO.StringReader("") :> System.IO.TextReader
11 |
12 | type Program() =
13 |
14 | member this.Method() =
15 | let code = System.Console.Read() // Noncompliant {{Make sure that reading the standard input is safe here.}}
16 | // ^^^^^^^^^^^^^^^^^^^^^
17 | let code = Con.Read() // Noncompliant
18 |
19 | let value = Console.ReadLine() // Noncompliant
20 | let code = Console.Read() // Noncompliant
21 | let key = Console.ReadKey() // Noncompliant
22 | let key = Console.ReadKey(true) // Noncompliant
23 |
24 | Console.Read() |> ignore // Compliant, value is ignored
25 | Console.ReadLine() |> ignore // Compliant, value is ignored
26 | Console.ReadKey() |> ignore // Compliant, value is ignored
27 | Console.ReadKey(true) |> ignore // Compliant, value is ignored
28 |
29 | Console.OpenStandardInput() |> ignore // Noncompliant
30 | Console.OpenStandardInput(100) |> ignore // Noncompliant
31 |
32 | let x = System.Console.In // Noncompliant
33 | // ^^^^^^^^^^^^^^^^^
34 | let x = Console.In // Noncompliant
35 | let x = Con.In // Noncompliant
36 | Console.In.Read() |> ignore // Noncompliant
37 |
38 | // Other Console methods
39 | Console.Write(1)
40 | Console.WriteLine(1)
41 | // Other classes
42 | MyConsole.Read() |> ignore
43 | MyConsole.In.Read() |> ignore
44 | ()
45 |
46 |
47 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/tests/SonarAnalyzer.FSharp.UnitTest/TestCases/S4834_ControllingPermissions.fs:
--------------------------------------------------------------------------------
1 | module rec SonarAnalyzer.FSharp.UnitTest.TestCases.S4834_ControllingPermissions
2 |
3 | open System
4 | open Microsoft.IdentityModel.Tokens
5 | open System.Security.Permissions
6 | open System.Security.Principal
7 | open System.Threading
8 | open System.Web
9 |
10 | module Program =
11 | open Microsoft.AspNetCore.Http
12 |
13 | type MyIdentity() =
14 |
15 | interface IIdentity with // Noncompliant {{Make sure that permissions are controlled safely here.}}
16 | // ^^^^^^^^^
17 | member this.Name = raise (NotImplementedException())
18 | member this.AuthenticationType = raise (NotImplementedException())
19 | member this.IsAuthenticated = raise (NotImplementedException())
20 |
21 |
22 | type MyPrincipal() =
23 | interface IPrincipal with // Noncompliant
24 | member this.Identity = raise (NotImplementedException())
25 | member this.IsInRole(role) = raise (NotImplementedException())
26 |
27 | // Indirectly implementing IIdentity
28 | type MyWindowsIdentity() =
29 | inherit WindowsIdentity("") // Noncompliant
30 |
31 | []
32 | let SecuredMethod() = () // Noncompliant, decorated with PrincipalPermission
33 | // ^^^^^^^^^^^^^
34 |
35 | let ValidateSecurityToken(handler:SecurityTokenHandler, securityToken:SecurityToken) =
36 | //handler.ValidateToken(securityToken) // Noncompliant
37 | ()
38 |
39 | let CreatingPermissions() =
40 | WindowsIdentity.GetCurrent() |> ignore // Noncompliant
41 | // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
42 |
43 | // All instantiations of PrincipalPermission
44 | let principalPermission = new PrincipalPermission(PermissionState.None) // Noncompliant
45 | let principalPermission = new PrincipalPermission("", "") // Noncompliant
46 | let principalPermission = new PrincipalPermission("", "", true) // Noncompliant
47 | ()
48 |
49 | let HttpContextUser(httpContext:HttpContext) =
50 | let user = httpContext.User // Noncompliant
51 | // ^^^^^^^^^^^^^^^^
52 | httpContext.User <- user // Noncompliant
53 | // ^^^^^^^^^^^^^^^^
54 |
55 | let AppDomainSecurity(appDomain:AppDomain, principal:IPrincipal) = // Noncompliant, IPrincipal parameter, see another section with tests
56 | appDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal) // Noncompliant
57 | // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
58 | appDomain.SetThreadPrincipal(principal) // Noncompliant
59 | appDomain.ExecuteAssembly("") // Compliant, not one of the tracked methods
60 |
61 | let ThreadSecurity(principal:IPrincipal) = // Noncompliant, IPrincipal parameter, see another section with tests
62 | Thread.CurrentPrincipal <- principal // Noncompliant
63 | // ^^^^^^^^^^^^^^^^^^^^^^^
64 | let principal = Thread.CurrentPrincipal // Noncompliant
65 | ()
66 |
67 |
68 | let CreatingPrincipalAndIdentity(windowsIdentity:WindowsIdentity) = // Noncompliant, IIdentity parameter, see another section with tests
69 | let identity = new MyIdentity() // Noncompliant, creation of type that implements IIdentity
70 | // ^^^^^^^^^^^^^^^^
71 | let identity = new WindowsIdentity("") // Noncompliant
72 | let principal = new MyPrincipal() // Noncompliant, creation of type that implements IPrincipal
73 | let principal = new WindowsPrincipal(windowsIdentity) // Noncompliant
74 | ()
75 |
76 | // Method declarations that accept IIdentity or IPrincipal
77 | let AcceptIdentity(identity:MyIdentity ) = () // Noncompliant
78 | // ^^^^^^^^^^^^^^
79 | let AcceptIdentity2(identity:IIdentity) = () // Noncompliant
80 | let AcceptPrincipal(principal:MyPrincipal) = () // Noncompliant
81 | let AcceptPrincipal2(principal:IPrincipal) = () // Noncompliant
82 |
83 |
84 | type Properties() =
85 |
86 | let mutable identity: IIdentity = null
87 |
88 | member this.Identity
89 | with get() = identity
90 | and set value = // Compliant, we do not raise for property accessors
91 | identity <- value
92 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/tests/SonarAnalyzer.FSharp.UnitTest/TestCases/S5042_ExpandingArchiveFiles.fs:
--------------------------------------------------------------------------------
1 | module SonarAnalyzer.FSharp.UnitTest.TestCases.S5042_ExpandingArchiveFiles
2 |
3 | open System
4 | open System.IO
5 | open System.IO.Compression
6 | open System.Linq
7 |
8 | type Class1() =
9 |
10 | member this.ExtractArchive(archive:ZipArchive) =
11 | for entry in archive.Entries do
12 | entry.ExtractToFile("") // Noncompliant
13 | // ^^^^^^^^^^^^^^^^^^^^^^^
14 |
15 | for i = 0 to archive.Entries.Count - 1 do
16 | archive.Entries.[i].ExtractToFile("") // Noncompliant
17 |
18 | archive.Entries.ToList()
19 | |> Seq.iter (fun e -> e.ExtractToFile("")) // Noncompliant
20 | // ^^^^^^^^^^^^^^^^^^^
21 |
22 | member this.ExtractEntry(entry:ZipArchiveEntry) =
23 | entry.ExtractToFile("") // Noncompliant
24 | entry.ExtractToFile("", true) // Noncompliant
25 |
26 | ZipFileExtensions.ExtractToFile(entry, "") // Noncompliant
27 | ZipFileExtensions.ExtractToFile(entry, "", true) // Noncompliant
28 |
29 | let stream = entry.Open() // Noncompliant
30 |
31 | entry.Delete() // Compliant, method is not tracked
32 |
33 | let fullName = entry.FullName // Compliant, properties are not tracked
34 |
35 | this.ExtractToFile(entry) // Compliant, method is not tracked
36 |
37 | //this.Invoke(ZipFileExtensions.ExtractToFile) // Compliant, not an invocation, but could be considered as FN
38 |
39 | member this.ExtractToFile(entry:ZipArchiveEntry) = ()
40 |
41 | member this.Invoke(action: Action ) = ()
42 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/tests/SonarAnalyzer.FSharp.UnitTest/Verifier.fs:
--------------------------------------------------------------------------------
1 | module SonarAnalyzer.FSharp.UnitTest.Verifier
2 |
3 | (*
4 | Run a rule on a file and verify that the errors detected by the rule
5 | match the errors we expect.
6 |
7 | To get the locations of the expected errors, parse the file
8 | looking for lines tagged with a "// Noncompliant" comment.
9 | *)
10 |
11 |
12 | open System
13 | open System.Text.RegularExpressions
14 | open NUnit.Framework
15 |
16 | let getNoncompliantLocations lines =
17 |
18 | // The pattern means match "//Noncompliant"
19 | // but "" can't contain "/".
20 | // This prevents commented-out lines from showing up.
21 | let regexPattern = @"^[^/]*//\s*Noncompliant"
22 |
23 | let getLocation lineNo lineText =
24 | let m = Regex.Match(lineText, regexPattern)
25 | if (m.Success) then
26 | Some (lineNo + 1)
27 | else
28 | None
29 |
30 | lines
31 | |> List.mapi getLocation
32 | |> List.choose id
33 |
34 | (*
35 | getNoncompliantLocations [
36 | @"1. // Compliant"
37 | @"2. normal code // Noncompliant"
38 | @"2. // commented out code // Noncompliant"
39 | @"3. Noncompliant() // a call not a comment"
40 | ]
41 | *)
42 |
43 | let applyRule fileName rule =
44 | let config = FSharpAst.TransformerConfig.Default
45 | let tast = FSharpAst.FileApi.translateFile config fileName
46 | []
47 |
48 | let verify fileName rule =
49 | try
50 | let lines = IO.File.ReadAllLines fileName |> List.ofArray
51 | let expectedLocations =
52 | getNoncompliantLocations lines
53 | |> sprintf "Line numbers %A" // convert to a string for easier testing
54 | let actualLocations =
55 | SonarAnalyzer.FSharp.RuleRunner.analyzeFileWithRules [rule] fileName
56 | |> List.map (fun diag -> diag.Location.StartLine)
57 | |> List.distinct |> List.sort
58 | |> sprintf "Line numbers %A" // convert to a string for easier testing
59 | Assert.AreEqual(expectedLocations, actualLocations)
60 | with
61 | | ex ->
62 | Assert.Fail(sprintf "[%s] %s" fileName ex.Message)
63 | reraise()
64 |
--------------------------------------------------------------------------------
/SonarAnalyzer.FSharp/zip-assembly.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | zip-assembly
5 |
6 | zip
7 |
8 | false
9 |
10 |
11 | src/FsSonarRunner/publish
12 | \
13 |
14 | win-x86/**
15 | linux-x86/**
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | # appveyor.yml reference see https://www.appveyor.com/docs/appveyor-yml/
2 |
3 | #---------------------------------#
4 | # general configuration #
5 | #---------------------------------#
6 |
7 | # version format
8 | version: 1.0.0.{build}
9 |
10 | #---------------------------------#
11 | # environment configuration #
12 | #---------------------------------#
13 |
14 | # Build worker image (VM template)
15 | image: Visual Studio 2017
16 |
17 | # build cache to preserve files/folders between builds
18 | cache:
19 | - C:\Users\appveyor\.m2
20 |
21 | # scripts that run after cloning repository
22 | install:
23 | - cmd: mvn versions:set -DnewVersion=%APPVEYOR_BUILD_VERSION%
24 |
25 | # .NET Core project files patching
26 | dotnet_csproj:
27 | patch: true
28 | file: '**\*.csproj;**\*.props;**\*.fsproj;**\*.xml'
29 | version: '{version}'
30 | package_version: '{version}'
31 | assembly_version: '{version}'
32 | file_version: '{version}'
33 | informational_version: '{version}'
34 |
35 | #---------------------------------#
36 | # build configuration #
37 | #---------------------------------#
38 |
39 | # build Configuration, i.e. Debug, Release, etc.
40 | configuration: Release
41 |
42 | # Build settings, not to be confused with "before_build" and "after_build".
43 | # "project" is relative to the original build directory and not influenced by directory changes in "before_build".
44 | #build:
45 |
46 | # scripts to run before build
47 | #before_build:
48 |
49 | # to run your custom scripts instead of automatic MSBuild
50 | build_script:
51 | - mvn -Dconfiguration=%CONFIGURATION% clean install
52 | # - mvn -Dconfiguration=%CONFIGURATION% -P sonar -Dsonar.branch.name=%APPVEYOR_REPO_BRANCH% sonar:sonar
53 |
54 | # scripts to run after build (working directory and environment changes are persisted from the previous steps)
55 | #after_build:
56 |
57 | # scripts to run *after* solution is built and *before* automatic packaging occurs (web apps, NuGet packages, Azure Cloud Services)
58 | #before_package:
59 |
60 | #---------------------------------#
61 | # tests configuration #
62 | #---------------------------------#
63 |
64 | # to run tests against only selected assemblies and/or categories
65 | #test:
66 |
67 | # scripts to run before tests (working directory and environment changes are persisted from the previous steps such as "before_build")
68 | #before_test:
69 |
70 | # to run your custom scripts instead of automatic tests
71 | #test_script:
72 |
73 | # scripts to run after tests
74 | after_test:
75 | - ps: |
76 | $url = "https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)"
77 | $wc = New-Object 'System.Net.WebClient'
78 | $files = Get-ChildItem -Recurse .\TEST-*.xml
79 | foreach ($f in $files) {
80 | $wc.UploadFile($url, (Resolve-Path $f))
81 | }
82 |
83 | # to disable automatic tests
84 | #test: off
85 |
86 | #---------------------------------#
87 | # artifacts configuration #
88 | #---------------------------------#
89 |
90 | artifacts:
91 | - path: 'sonar-fsharpsecurity-plugin/target/*.jar'
92 | name: sonar-fsharpsecurity-plugin
93 |
94 | #---------------------------------#
95 | # deployment configuration #
96 | #---------------------------------#
97 |
98 | #---------------------------------#
99 | # global handlers #
100 | #---------------------------------#
101 |
102 | # on successful build
103 | #on_success:
104 |
105 | # on build failure
106 | #on_failure:
107 |
108 | # after build failure or success
109 | #on_finish:
110 |
111 | #---------------------------------#
112 | # notifications #
113 | #---------------------------------#
114 |
--------------------------------------------------------------------------------
/docs/contributing-analyzer.md:
--------------------------------------------------------------------------------
1 | # Building, Testing and Debugging the F# Analyzer
2 |
3 | The F# analyzer can be run as a standalone executable, outside of Sonar.
4 |
5 | This can be useful for local testing before committing, or as a commit hook, etc.
6 |
7 | ## Getting the code
8 |
9 | * Clone [this repository](https://github.com/swlaschin/sonar-fsharpsecurity-plugin.git)
10 |
11 | ## To build and test
12 |
13 | The root of the F# code is `./SonarAnalyzer.FSharp`
14 |
15 | So to build, change to that directory and build as normal.
16 |
17 | ```
18 | cd .\SonarAnalyzer.FSharp
19 | dotnet build SonarAnalyzer.FSharp.sln
20 | ```
21 |
22 | To run the F# unit tests:
23 |
24 | ```
25 | dotnet test SonarAnalyzer.FSharp.sln
26 | ```
27 |
28 | ## To run against F# code without using the Sonar server
29 |
30 | The main executable is `FsSonarRunner`, so to run it on its own:
31 |
32 | ```
33 | cd .\SonarAnalyzer.FSharp\src\FsSonarRunner
34 | dotnet run
35 | ```
36 |
37 | This will show the available options.
38 |
39 | As a demonstration, try running it on the test cases which are part of the test suite
40 |
41 | ```
42 | cd .\SonarAnalyzer.FSharp\src\FsSonarRunner
43 | dotnet run -- -d ..\..\tests\SonarAnalyzer.FSharp.UnitTest\TestCases
44 | ```
45 |
46 | To convert the dotnet executable into a standalone:
47 |
48 | ```
49 | cd .\SonarAnalyzer.FSharp\src\FsSonarRunner
50 | dotnet publish --output publish/ --runtime
51 | ```
52 |
53 | where `` is `win-x64`, `linux-x64`, etc
54 |
55 |
56 | ## Contributing
57 |
58 | Please see [Contributing Code](../CONTRIBUTING.md) for details on contributing changes back to the code.
59 |
60 |
--------------------------------------------------------------------------------
/docs/contributing-plugin.md:
--------------------------------------------------------------------------------
1 | # Building, Testing and Debugging the SonarQube plugin
2 |
3 | This page documents how to develop with the Java side of the plugin.
4 |
5 | See [here for building, testing and debugging the F# analyzer](contributing-analyzer.md).
6 |
7 | ## How the plugin works
8 |
9 | The plugin is a mix of Java (under `sonar-fsharpsecurity-plugin`) and F# (under `SonarAnalyzer.FSharp`).
10 |
11 | The plugin itself is written in Java and is loaded when `sonar-scanner` is used. The way it works is that Sonar provides a number of abstract classes which
12 | the plugin then implements.
13 |
14 | * Sonar asks the plugin what file extensions it wants. The plugin returns `.fs` and `.fsproj`, etc.
15 | * Sonar asks the plugin what rules it supports. In this case, the plugin returns the list using an XML file generated by the F# executable called `FsSolarRunner.exe` (using the `--export` option).
16 | * The Sonar server may know that the user has asked for some of these rules not to run (to avoid the same errors over and over again)
17 | * Sonar then passes the rules and the files into the plugin
18 | * The plugin analyzes those files. In this case the plugin:
19 | * writes those rules and files to an XML file
20 | * then executes the F# executable (`FsSolarRunner.exe`) passing that input file as parameter
21 | * the F# exe dumps out the results as files.
22 | * the plugin then reads these files in to memory
23 | * Finally the plugin returns them to the Solar framework which stores them in the database associated with the server.
24 |
25 | ## Installing Java and Maven on Windows
26 |
27 | To install on Windows, I recommend using the [Chocolatey package manager](https://chocolatey.org/).
28 |
29 | You'll need to install:
30 |
31 | * [OpenJDK](https://chocolatey.org/packages/openjdk) using `choco install openjdk`
32 | * For building, you'll also need [Maven](https://chocolatey.org/packages/maven) using `choco install maven`
33 |
34 | ## Installing Java and Maven on other platforms
35 |
36 | Use your preferred package manager, such as `apt-get`.
37 |
38 |
39 | ## Getting the code
40 |
41 | * Clone [this repository](https://github.com/swlaschin/sonar-fsharpsecurity-plugin.git)
42 | * Download sub-modules `git submodule update --init --recursive`
43 |
44 | ## To build and test
45 |
46 | To build the Java plugin .jar file (from the root):
47 |
48 | ```
49 | mvn clean install
50 | ```
51 |
52 | To run the Java unit tests:
53 |
54 | ```
55 | mvn clean test
56 | ```
57 |
58 | To create the .jar file that can be copied to the SonarQube plugins directory:
59 |
60 | ```
61 | mvn clean package
62 | ```
63 |
64 | The .jar file is output to `\sonar-fsharpsecurity-plugin\target`
65 |
66 | To run the same script that AppVeyor uses:
67 |
68 | ```
69 | mvn -Dconfiguration=Release clean install
70 | ```
71 |
72 | ## Developing with VS Code
73 |
74 | Install:
75 |
76 | * Language Support for Java by Red Hat -- [redhat.java](https://marketplace.visualstudio.com/items?itemName=redhat.java)
77 | * Microsoft Debugger for Java -- [vscjava.vscode-java-debug](https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-debug)
78 |
79 | To debug a plugin, see [the instructions on the SonarQube site](https://docs.sonarqube.org/latest/extend/developing-plugin/)
80 |
81 | ## Developing with Eclipse or IntelliJ
82 |
83 | When working with Eclipse or IntelliJ please follow the [sonar guidelines](https://github.com/SonarSource/sonar-developer-toolset)
84 |
85 | ## Understanding the Sonar Plugin API
86 |
87 | See http://javadocs.sonarsource.org/7.9.1/apidocs/
88 |
89 |
90 | ## Contributing
91 |
92 | Please see [Contributing Code](../CONTRIBUTING.md) for details on contributing changes back to the code.
93 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | swlaschin.sonarqube.fsharp
9 | sonar-fsharpsecurity-analyzer
10 | 0.0.0.1
11 | pom
12 |
13 | org.sonarsource.parent
14 | parent
15 | 52
16 |
17 |
18 | SonarAnalyzer.FSharp
19 | sonar-fsharpsecurity-plugin
20 |
21 |
22 | Debug
23 | 0.8.4
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | org.sonarsource.scanner.maven
32 | sonar-maven-plugin
33 | 3.6.0.1398
34 |
35 |
36 | verify
37 |
38 | sonar
39 |
40 |
41 |
42 |
43 |
44 | org.jacoco
45 | jacoco-maven-plugin
46 | ${jacoco.version}
47 |
48 |
49 |
50 |
51 |
52 | org.jacoco
53 | jacoco-maven-plugin
54 |
55 |
56 |
57 | prepare-agent
58 |
59 |
60 |
61 | report
62 |
63 | report
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | SonarQube F# Security Analyzer Plugin
73 | SonarQube F# Security Analyzer Plugin
74 | https://github.com/swlaschin/sonar-fsharpsecurity-plugin
75 | 2015
76 |
77 |
78 | GNU Lesser General Public License, version 3
79 | https://opensource.org/licenses/LGPL-3.0
80 | manual
81 |
82 |
83 |
84 | swlaschin
85 | https://github.com/swlaschin
86 |
87 |
88 |
89 |
90 | swlaschin
91 | Scott Wlaschin
92 | swlaschin
93 | https://github.com/swlaschin
94 |
95 | owner
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | GitHub
106 | https://github.com/swlaschin/sonar-fsharpsecurity-plugin/issues
107 |
108 |
109 | AppVeyor
110 | https://ci.appveyor.com/project/swlaschin/sonar-fsharpsecurity-plugin
111 |
112 |
113 | scm:git:https://github.com/swlaschin/sonar-fsharpsecurity-plugin.git
114 | https://github.com/swlaschin/sonar-fsharpsecurity-plugin
115 |
116 |
117 |
118 | sonar
119 |
120 | https://sonarcloud.io
121 | sonar-fsharpsecurity-plugin
122 |
123 |
124 |
125 |
126 |
127 |
--------------------------------------------------------------------------------
/sonar-fsharpsecurity-plugin/src/license-header.txt:
--------------------------------------------------------------------------------
1 | Sonar FSharpSecurity Plugin, open source software quality management tool.
2 |
3 | Sonar FSharpSecurity Plugin is free software; you can redistribute it and/or
4 | modify it under the terms of the GNU Lesser General Public
5 | License as published by the Free Software Foundation; either
6 | version 3 of the License, or (at your option) any later version.
7 |
8 | Sonar FSharpSecurity Plugin is distributed in the hope that it will be useful,
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 | Lesser General Public License for more details.
--------------------------------------------------------------------------------
/sonar-fsharpsecurity-plugin/src/main/java/org/sonar/plugins/fsharp/FSharpIssue.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Sonar FSharpSecurity Plugin, open source software quality management tool.
3 | *
4 | * Sonar FSharpSecurity Plugin is free software; you can redistribute it and/or
5 | * modify it under the terms of the GNU Lesser General Public
6 | * License as published by the Free Software Foundation; either
7 | * version 3 of the License, or (at your option) any later version.
8 | *
9 | * Sonar FSharpSecurity Plugin is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | * Lesser General Public License for more details.
13 | */
14 |
15 | package org.sonar.plugins.fsharp;
16 |
17 | /*
18 | Represents an issue detected by the scanner.
19 |
20 | Designed to be easily compatible with http://javadocs.sonarsource.org/7.9.1/apidocs/org/sonar/api/batch/sensor/issue/NewIssue.html
21 | */
22 | public class FSharpIssue {
23 |
24 | private final String ruleKey;
25 | private final String message;
26 | private final String absoluteFilePath;
27 | private final int startLine;
28 | private final int startLineOffset;
29 | private final int endLine;
30 | private final int endLineOffset;
31 |
32 | public FSharpIssue(String ruleKey, String message, String absoluteFilePath, int startLine, int startLineOffset, int endLine, int endLineOffset) {
33 | this.ruleKey = ruleKey;
34 | this.message = message;
35 | this.absoluteFilePath = absoluteFilePath;
36 | this.startLine = startLine;
37 | this.startLineOffset = startLineOffset;
38 | this.endLine = endLine;
39 | this.endLineOffset = endLineOffset;
40 | }
41 |
42 | public String ruleKey() {
43 | return ruleKey;
44 | }
45 |
46 | public String message() {
47 | return message;
48 | }
49 |
50 | public String absoluteFilePath() {
51 | return absoluteFilePath;
52 | }
53 |
54 | public int startLine() {
55 | return startLine;
56 | }
57 |
58 | public int startLineOffset() {
59 | return startLineOffset;
60 | }
61 |
62 | public int endLine() {
63 | return endLine;
64 | }
65 |
66 | public int endLineOffset() {
67 | return endLineOffset;
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/sonar-fsharpsecurity-plugin/src/main/java/org/sonar/plugins/fsharp/FSharpLanguage.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Sonar FSharpSecurity Plugin, open source software quality management tool.
3 | *
4 | * Sonar FSharpSecurity Plugin is free software; you can redistribute it and/or
5 | * modify it under the terms of the GNU Lesser General Public
6 | * License as published by the Free Software Foundation; either
7 | * version 3 of the License, or (at your option) any later version.
8 | *
9 | * Sonar FSharpSecurity Plugin is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | * Lesser General Public License for more details.
13 | */
14 | package org.sonar.plugins.fsharp;
15 |
16 | import org.apache.commons.lang.StringUtils;
17 | import org.sonar.api.config.Configuration;
18 | import org.sonar.api.resources.AbstractLanguage;
19 | import java.util.Optional;
20 |
21 | /*
22 | This class defines properties of the FSharp Language, such as FILE_SUFFIXES
23 | (which is actually a constant defined in the top-level plugin class FSharpPlugin)
24 | */
25 |
26 | public class FSharpLanguage extends AbstractLanguage {
27 |
28 | private final Configuration configuration;
29 |
30 | public FSharpLanguage(Configuration configuration) {
31 | super(FSharpPlugin.LANGUAGE_KEY, FSharpPlugin.LANGUAGE_NAME);
32 | this.configuration = configuration;
33 | }
34 |
35 | @Override
36 | public boolean equals(Object obj) {
37 | if (!super.equals(obj)) {
38 | return false;
39 | }
40 |
41 | FSharpLanguage fobj = (FSharpLanguage) obj;
42 | // added field is tested
43 | return configuration.equals(fobj.configuration);
44 | }
45 |
46 | @Override
47 | public int hashCode() {
48 | int result = super.hashCode();
49 | result = 31 * result + configuration.hashCode();
50 | return result;
51 | }
52 |
53 | @Override
54 | public String[] getFileSuffixes() {
55 | String suffixesStr = configuration.get(FSharpPlugin.FILE_SUFFIXES_KEY).orElse(FSharpPlugin.FILE_SUFFIXES_DEFVALUE);
56 | String[] suffixes = StringUtils.split(suffixesStr, ",");
57 | return suffixes;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/sonar-fsharpsecurity-plugin/src/main/java/org/sonar/plugins/fsharp/FSharpPlugin.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Sonar FSharpSecurity Plugin, open source software quality management tool.
3 | *
4 | * Sonar FSharpSecurity Plugin is free software; you can redistribute it and/or
5 | * modify it under the terms of the GNU Lesser General Public
6 | * License as published by the Free Software Foundation; either
7 | * version 3 of the License, or (at your option) any later version.
8 | *
9 | * Sonar FSharpSecurity Plugin is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | * Lesser General Public License for more details.
13 | */
14 | package org.sonar.plugins.fsharp;
15 |
16 | import org.sonar.api.Properties;
17 | import org.sonar.api.Property;
18 |
19 | import org.sonar.api.Plugin;
20 |
21 | /*
22 | This class is the top-level plugin class, called by SonarScanner.
23 | It installs some extensions, listed below.
24 |
25 | All code is in this project is adaptedwith gratitude from
26 | https://github.com/jmecsoftware/sonar-fsharp-plugin
27 | https://github.com/SonarSource/sonar-csharp
28 | */
29 |
30 |
31 | @Properties({
32 | @Property(
33 | key = FSharpPlugin.FILE_SUFFIXES_KEY,
34 | defaultValue = FSharpPlugin.FILE_SUFFIXES_DEFVALUE,
35 | name = "File suffixes",
36 | description = "Comma-separated list of suffixes of files to analyze.",
37 | project = true, global = true
38 | )
39 | })
40 | public class FSharpPlugin implements Plugin {
41 |
42 | public static final String LANGUAGE_KEY = "fs";
43 | public static final String LANGUAGE_NAME = "F#";
44 |
45 | public static final String FILE_SUFFIXES_KEY = "sonar.fs.file.suffixes";
46 | public static final String FILE_SUFFIXES_DEFVALUE = ".fs,.fsx,.fsi";
47 |
48 | public static final String FSHARP_WAY_PROFILE = "Sonar way";
49 |
50 | public static final String REPOSITORY_KEY = "fsharpsecurity";
51 | public static final String REPOSITORY_NAME = "SonarQube";
52 |
53 | @Override
54 | public void define(Context context) {
55 | context.addExtension(FSharpLanguage.class); // the F# language properties
56 | context.addExtension(FSharpSonarRulesDefinition.class); // the list of rules available
57 | context.addExtension(FSharpSonarWayProfile.class); // the quality profile
58 | context.addExtension(FsSonarRunnerExtractor.class); // a utility to unzip the F# executable
59 | context.addExtension(FSharpSensor.class); // the main analyzer
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/sonar-fsharpsecurity-plugin/src/main/java/org/sonar/plugins/fsharp/FSharpSonarRulesDefinition.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Sonar FSharpSecurity Plugin, open source software quality management tool.
3 | *
4 | * Sonar FSharpSecurity Plugin is free software; you can redistribute it and/or
5 | * modify it under the terms of the GNU Lesser General Public
6 | * License as published by the Free Software Foundation; either
7 | * version 3 of the License, or (at your option) any later version.
8 | *
9 | * Sonar FSharpSecurity Plugin is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | * Lesser General Public License for more details.
13 | */
14 |
15 |
16 | // based on plugins from from https://github.com/SonarSource
17 | package org.sonar.plugins.fsharp;
18 |
19 | import java.io.InputStream;
20 | import java.nio.charset.StandardCharsets;
21 | import org.sonar.api.rule.Severity;
22 | import org.sonar.api.server.rule.RuleParamType;
23 | import org.sonar.api.server.rule.RulesDefinition;
24 | import org.sonar.api.server.rule.RulesDefinitionXmlLoader;
25 | import org.sonar.api.utils.log.Logger;
26 | import org.sonar.api.utils.log.Loggers;
27 |
28 | /*
29 | This class loads the available rules into the repository.
30 | The rules come from a XML resource file.
31 |
32 |
33 | IMPORTANT: the schema must match the one defined by the F# code in FsSonarRunner/RuleDefinitionDto
34 | and also as defined by Sonar at http://javadocs.sonarsource.org/7.9.1/apidocs/org/sonar/api/server/rule/RulesDefinitionXmlLoader.html
35 |
36 | */
37 |
38 | public class FSharpSonarRulesDefinition implements RulesDefinition {
39 | private static final String PATH_TO_RULES_XML = "/rules.xml";
40 | private static final Logger LOG = Loggers.get(FSharpSonarRulesDefinition.class);
41 |
42 | private void defineRulesForLanguage(Context context, String repositoryKey, String repositoryName, String languageKey, String filename) {
43 | NewRepository repository = context.createRepository(repositoryKey, languageKey).setName(repositoryName);
44 | LOG.info("Reading rules definition. File: '" + filename + "' repositoryKey:" + repositoryKey + " repositoryName:" + repositoryName + " languageKey:" + languageKey);
45 |
46 | InputStream rulesXml = this.getClass().getResourceAsStream(filename);
47 | if (rulesXml != null) {
48 | RulesDefinitionXmlLoader rulesLoader = new RulesDefinitionXmlLoader();
49 | rulesLoader.load(repository, rulesXml, StandardCharsets.UTF_8.name());
50 | }
51 | else {
52 | LOG.error("No resource found for rules definition");
53 | }
54 |
55 | repository.done();
56 | }
57 |
58 | @Override
59 | public void define(Context context) {
60 | defineFromFile(context,PATH_TO_RULES_XML);
61 | }
62 |
63 | public void defineFromFile(Context context, String filename) {
64 | defineRulesForLanguage(context, FSharpPlugin.REPOSITORY_KEY, FSharpPlugin.REPOSITORY_NAME, FSharpPlugin.LANGUAGE_KEY,filename);
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/sonar-fsharpsecurity-plugin/src/main/java/org/sonar/plugins/fsharp/FSharpSonarWayProfile.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Sonar FSharpSecurity Plugin, open source software quality management tool.
3 | *
4 | * Sonar FSharpSecurity Plugin is free software; you can redistribute it and/or
5 | * modify it under the terms of the GNU Lesser General Public
6 | * License as published by the Free Software Foundation; either
7 | * version 3 of the License, or (at your option) any later version.
8 | *
9 | * Sonar FSharpSecurity Plugin is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | * Lesser General Public License for more details.
13 | */
14 |
15 |
16 | package org.sonar.plugins.fsharp;
17 |
18 | import java.util.List;
19 | import java.util.ArrayList;
20 | import java.io.File;
21 | import java.io.InputStream;
22 | import java.nio.file.Files;
23 | import java.nio.file.Path;
24 | import java.nio.file.Paths;
25 | import java.nio.charset.StandardCharsets;
26 | import java.net.URL;
27 | import java.io.IOException;
28 | import java.io.BufferedReader;
29 | import java.io.InputStreamReader;
30 | import java.util.stream.Stream;
31 | import java.util.stream.Collectors;
32 | import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition;
33 | import org.sonar.api.utils.log.Logger;
34 | import org.sonar.api.utils.log.Loggers;
35 |
36 |
37 | /*
38 | This class defines a Quality Profile, which captures which rules are available and what the severities are.
39 | See https://docs.sonarqube.org/latest/instance-administration/quality-profiles/
40 |
41 | The default (non-overridable) profile is called "Sonar Way" and is provided by the plugin.
42 | */
43 |
44 |
45 | public class FSharpSonarWayProfile implements BuiltInQualityProfilesDefinition {
46 | private static final String PATH_TO_PROFILE_TXT = "/profile.txt";
47 | public static final Logger LOG = Loggers.get(FSharpSonarWayProfile.class);
48 |
49 | @Override
50 | public void define(Context context) {
51 | URL profileUrl = getClass().getResource(PATH_TO_PROFILE_TXT);
52 | defineFromUrl(context,profileUrl);
53 | }
54 |
55 | public void defineFromUrl(Context context, URL profileUrl) {
56 | LOG.info("FSharpSonarWayProfile: reading profile " + profileUrl.toString());
57 | NewBuiltInQualityProfile profile = context.createBuiltInQualityProfile(FSharpPlugin.FSHARP_WAY_PROFILE, FSharpPlugin.LANGUAGE_KEY);
58 | profile.setDefault(true);
59 |
60 | List ruleKeys = importRuleKeysFromUrl(profileUrl);
61 | ruleKeys.forEach( (ruleKey) -> profile.activateRule(FSharpPlugin.REPOSITORY_KEY, ruleKey) );
62 |
63 | profile.done();
64 | }
65 |
66 | public List importRuleKeysFromUrl(URL profileUrl) {
67 | List ruleKeys = new ArrayList();
68 |
69 | try (InputStream stream = profileUrl.openStream()) {
70 | ruleKeys = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8)).lines().collect(Collectors.toList());
71 | LOG.info("FSharpSonarWayProfile: #rules found: " + Integer.toString(ruleKeys.size()) );
72 | } catch (IOException e) {
73 | LOG.error("Unable to read profile stream: {} => {}", profileUrl.toString(), e.getMessage());
74 | }
75 |
76 | return ruleKeys;
77 | }
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/sonar-fsharpsecurity-plugin/src/main/java/org/sonar/plugins/fsharp/FsSonarRunnerExtractor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Sonar FSharpSecurity Plugin, open source software quality management tool.
3 | *
4 | * Sonar FSharpSecurity Plugin is free software; you can redistribute it and/or
5 | * modify it under the terms of the GNU Lesser General Public
6 | * License as published by the Free Software Foundation; either
7 | * version 3 of the License, or (at your option) any later version.
8 | *
9 | * Sonar FSharpSecurity Plugin is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | * Lesser General Public License for more details.
13 | */
14 |
15 | package org.sonar.plugins.fsharp;
16 |
17 | import org.sonar.api.batch.InstantiationStrategy;
18 |
19 | import java.io.File;
20 | import java.io.IOException;
21 | import java.io.InputStream;
22 | import java.nio.file.Files;
23 | import org.sonar.api.batch.ScannerSide;
24 | import org.sonar.api.utils.log.Logger;
25 | import org.sonar.api.utils.log.Loggers;
26 | import org.sonar.plugins.fsharp.utils.OSInfo;
27 | import org.sonar.plugins.fsharp.utils.UnZip;
28 |
29 | /*
30 | Extract the FsSolarRunner from the zip file
31 | */
32 |
33 | // adapted from https://github.com/SonarSource/sonar-csharp
34 | @InstantiationStrategy(InstantiationStrategy.PER_BATCH)
35 | @ScannerSide()
36 | public class FsSonarRunnerExtractor {
37 | public static final Logger LOG = Loggers.get(FsSonarRunnerExtractor.class);
38 | private static final String SONARQUBE_ANALYZER_EXE = "FsSonarRunner";
39 | private static final String SONARQUBE_ANALYZER_ZIP = "SonarAnalyzer.FSharp.zip";
40 |
41 | private File file = null;
42 |
43 | public File executableFile(String workDir) throws IOException {
44 | // once loaded, file is cached between calls
45 | if (file == null) {
46 | String filePath;
47 | switch (OSInfo.getOs()) {
48 | case WINDOWS:
49 | filePath = "win-x86" + File.separator + SONARQUBE_ANALYZER_EXE + ".exe";
50 | break;
51 | case LINUX:
52 | filePath = "linux-x86" + File.separator + SONARQUBE_ANALYZER_EXE;
53 | break;
54 | default:
55 | String msg = "Operation system `" + OSInfo.getOs().toString() + "`not supported";
56 | LOG.error(msg);
57 | throw new UnsupportedOperationException(msg);
58 | }
59 |
60 | file = unzipAnalyzerFile(filePath, workDir);
61 | if (!file.canExecute() && !file.setExecutable(true)) {
62 | LOG.error("Could not set executable permission");
63 | }
64 | }
65 |
66 | return file;
67 | }
68 |
69 | private File unzipAnalyzerFile(String fileName, String workDir) throws IOException {
70 | File toolWorkingDir = new File(workDir, "ProjectTools");
71 | File zipFile = new File(workDir, SONARQUBE_ANALYZER_ZIP);
72 |
73 | if (zipFile.exists()) {
74 | return new File(toolWorkingDir, fileName);
75 | }
76 |
77 | try {
78 | try (InputStream is = getClass().getResourceAsStream("/" + SONARQUBE_ANALYZER_ZIP)) {
79 | Files.copy(is, zipFile.toPath());
80 | }
81 |
82 | UnZip unZip = new UnZip();
83 | unZip.unZipIt(zipFile.getAbsolutePath(), toolWorkingDir.getAbsolutePath());
84 | return new File(toolWorkingDir, fileName);
85 | } catch (IOException e) {
86 | LOG.error("Unable to unzip File: {} => {}", fileName, e.getMessage());
87 | throw e;
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/sonar-fsharpsecurity-plugin/src/main/java/org/sonar/plugins/fsharp/utils/OSInfo.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Sonar FSharpSecurity Plugin, open source software quality management tool.
3 | *
4 | * Sonar FSharpSecurity Plugin is free software; you can redistribute it and/or
5 | * modify it under the terms of the GNU Lesser General Public
6 | * License as published by the Free Software Foundation; either
7 | * version 3 of the License, or (at your option) any later version.
8 | *
9 | * Sonar FSharpSecurity Plugin is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | * Lesser General Public License for more details.
13 | */
14 |
15 | // copied with thanks from https://github.com/jmecsoftware/sonar-fsharp-plugin
16 |
17 | package org.sonar.plugins.fsharp.utils;
18 |
19 | import java.io.IOException;
20 | import java.util.Locale;
21 |
22 | /**
23 | * Detect OS Name and Version Java
24 | *
25 | * Java is Platform independent and can run everywhere. Knowing this,
26 | * it can be worth knowing in what operation system the application is
27 | * running. To detect the current operation system, you can use the
28 | * OSInfo class below, which retrieves the information from the
29 | * system properties and returns an OS enum that holds the name and
30 | * version of the operating system the application is running on.
31 | *
32 | * @see original code at MemoryNotFound.org
33 | */
34 | public class OSInfo {
35 |
36 | public enum OS {
37 | WINDOWS,
38 | UNIX,
39 | POSIX_UNIX,
40 | MAC,
41 | LINUX,
42 | OTHER;
43 |
44 | private String version;
45 |
46 | public String getVersion() {
47 | return version;
48 | }
49 |
50 | private void setVersion(String version) {
51 | this.version = version;
52 | }
53 | }
54 |
55 | private static OS os = OS.OTHER;
56 |
57 | static {
58 | try {
59 | String osName = System.getProperty("os.name");
60 | if (osName == null) {
61 | throw new IOException("os.name not found");
62 | }
63 | osName = osName.toLowerCase(Locale.ENGLISH);
64 | if (osName.contains("windows")) {
65 | os = OS.WINDOWS;
66 | } else if (osName.contains("linux")) {
67 | os = OS.LINUX;
68 | } else if (osName.contains("mpe/ix")
69 | || osName.contains("freebsd")
70 | || osName.contains("irix")
71 | || osName.contains("digital unix")
72 | || osName.contains("unix")) {
73 | os = OS.UNIX;
74 | } else if (osName.contains("mac os")) {
75 | os = OS.MAC;
76 | } else if (osName.contains("sun os")
77 | || osName.contains("sunos")
78 | || osName.contains("solaris")) {
79 | os = OS.POSIX_UNIX;
80 | } else if (osName.contains("hp-ux")
81 | || osName.contains("aix")) {
82 | os = OS.POSIX_UNIX;
83 | } else {
84 | os = OS.OTHER;
85 | }
86 |
87 | } catch (Exception ex) {
88 | os = OS.OTHER;
89 | } finally {
90 | os.setVersion(System.getProperty("os.version"));
91 | }
92 | }
93 |
94 | public static OS getOs() {
95 | return os;
96 | }
97 | }
--------------------------------------------------------------------------------
/sonar-fsharpsecurity-plugin/src/main/resources/profile.txt:
--------------------------------------------------------------------------------
1 | S5042
2 | S4834
3 | S4829
4 | S4823
5 | S4818
6 | S4792
7 | S4790
8 | S4787
9 | S4784
10 | S4507
11 | S3011
12 | S2255
13 | S2245
14 | S2092
15 | S2077
16 | S1313
17 |
--------------------------------------------------------------------------------
/sonar-fsharpsecurity-plugin/src/test/java/org/sonar/plugins/fsharp/FSharpAnalysisResultImporterTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Sonar FSharpSecurity Plugin, open source software quality management tool.
3 | *
4 | * Sonar FSharpSecurity Plugin is free software; you can redistribute it and/or
5 | * modify it under the terms of the GNU Lesser General Public
6 | * License as published by the Free Software Foundation; either
7 | * version 3 of the License, or (at your option) any later version.
8 | *
9 | * Sonar FSharpSecurity Plugin is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | * Lesser General Public License for more details.
13 | */
14 |
15 | package org.sonar.plugins.fsharp;
16 |
17 | import java.io.BufferedWriter;
18 | import java.io.File;
19 | import java.io.FileInputStream;
20 | import java.io.FileWriter;
21 | import java.io.IOException;
22 | import java.io.InputStreamReader;
23 | import java.nio.charset.StandardCharsets;
24 | import java.nio.file.Paths;
25 | import java.util.Base64;
26 | import java.util.Collection;
27 | import java.util.HashMap;
28 | import java.util.HashSet;
29 | import java.util.Map;
30 | import java.util.Map.Entry;
31 | import java.util.List;
32 | import java.util.ArrayList;
33 |
34 | import static org.junit.jupiter.api.Assertions.assertEquals;
35 | import static org.junit.jupiter.api.Assertions.assertNotNull;
36 | import static org.junit.jupiter.api.Assertions.assertTrue;
37 |
38 | import org.junit.jupiter.api.Disabled;
39 | import org.junit.jupiter.api.Test;
40 |
41 | import org.sonar.api.utils.log.Logger;
42 | import org.sonar.api.utils.log.LogTester;
43 | import org.sonar.api.utils.log.LoggerLevel;
44 |
45 | public class FSharpAnalysisResultImporterTest {
46 |
47 | private static final LogTester logTester = new LogTester();
48 | private static final String analysisOutput = "/sonarDiagnosticsExample.xml";
49 |
50 | @Test
51 | public void moreThanOneIssueLoaded() {
52 | // Arrange
53 | logTester.setLevel(LoggerLevel.DEBUG);
54 | File file = new File(getClass().getResource(analysisOutput).getFile());
55 |
56 | // Act
57 | List issues = new FSharpAnalysisResultImporter().parse(file);
58 |
59 | // Assert
60 | boolean moreThanOneRule = issues.size() > 0;
61 | assertTrue(moreThanOneRule, "Expecting more than one issue from the example file");
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/sonar-fsharpsecurity-plugin/src/test/java/org/sonar/plugins/fsharp/FSharpLanguageTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Sonar FSharpSecurity Plugin, open source software quality management tool.
3 | *
4 | * Sonar FSharpSecurity Plugin is free software; you can redistribute it and/or
5 | * modify it under the terms of the GNU Lesser General Public
6 | * License as published by the Free Software Foundation; either
7 | * version 3 of the License, or (at your option) any later version.
8 | *
9 | * Sonar FSharpSecurity Plugin is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | * Lesser General Public License for more details.
13 | */
14 |
15 | package org.sonar.plugins.fsharp;
16 |
17 | import static org.junit.jupiter.api.Assertions.assertEquals;
18 | import static org.junit.jupiter.api.Assertions.assertFalse;
19 | import static org.junit.jupiter.api.Assertions.assertNotEquals;
20 | import static org.junit.jupiter.api.Assertions.assertTrue;
21 |
22 | import org.apache.commons.lang.StringUtils;
23 | import org.junit.jupiter.api.Test;
24 | import org.sonar.api.config.Configuration;
25 | import org.sonar.api.config.internal.MapSettings;
26 |
27 | /*
28 | Check that the FSharp Language class overrides equals/hash etc
29 | */
30 |
31 |
32 | public class FSharpLanguageTest {
33 | @Test
34 | public void baseClassConstructorCall() {
35 | Configuration configuration = (new MapSettings()).asConfig();
36 | FSharpLanguage fsharp = new FSharpLanguage(configuration);
37 |
38 | assertEquals(FSharpPlugin.LANGUAGE_KEY, fsharp.getKey(), "Language Key");
39 | assertEquals(FSharpPlugin.LANGUAGE_NAME, fsharp.getName(), "Language Name");
40 | }
41 |
42 | @Test
43 | public void hashcode_equals_true() {
44 | Configuration configuration = (new MapSettings()).asConfig();
45 | FSharpLanguage fsharp1 = new FSharpLanguage(configuration);
46 | FSharpLanguage fsharp2 = new FSharpLanguage(configuration);
47 |
48 | boolean areEqual = fsharp1.equals(fsharp2);
49 |
50 | assertTrue(areEqual);
51 | assertEquals(fsharp1.hashCode(), fsharp2.hashCode());
52 | }
53 |
54 | @Test
55 | public void hashcode_equals_false() {
56 | Configuration configuration1 = (new MapSettings()).asConfig();
57 | Configuration configuration2 = (new MapSettings()).asConfig();
58 | FSharpLanguage fsharp1 = new FSharpLanguage(configuration1);
59 | FSharpLanguage fsharp2 = new FSharpLanguage(configuration2);
60 |
61 | boolean areEqual = fsharp1.equals(fsharp2);
62 |
63 | assertFalse(areEqual);
64 | assertNotEquals(fsharp1.hashCode(), fsharp2.hashCode());
65 | }
66 |
67 | @Test
68 | public void fileSuffixes_default() {
69 | Configuration configuration = (new MapSettings()).asConfig();
70 | FSharpLanguage fsharp = new FSharpLanguage(configuration);
71 |
72 | String[] suffixes = fsharp.getFileSuffixes();
73 |
74 | assertEquals(3, suffixes.length);
75 | for (String suffix : suffixes) {
76 | assertTrue(FSharpPlugin.FILE_SUFFIXES_DEFVALUE.contains(suffix), "`" + suffix + "` not found");
77 | }
78 | }
79 |
80 | @Test
81 | public void fileSuffixes_userDefined() {
82 | String keys = ".fs,.fsx";
83 | int no_keys = 1 + StringUtils.countMatches(keys, ",");
84 | MapSettings settings = new MapSettings();
85 | settings.setProperty(FSharpPlugin.FILE_SUFFIXES_KEY, keys);
86 | Configuration configuration = settings.asConfig();
87 | FSharpLanguage fsharp = new FSharpLanguage(configuration);
88 |
89 | String[] suffixes = fsharp.getFileSuffixes();
90 |
91 | assertEquals(no_keys, suffixes.length);
92 | for (String suffix : suffixes) {
93 | assertTrue(keys.contains(suffix), "`" + suffix + "` not found");
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/sonar-fsharpsecurity-plugin/src/test/java/org/sonar/plugins/fsharp/FSharpPluginTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Sonar FSharpSecurity Plugin, open source software quality management tool.
3 | *
4 | * Sonar FSharpSecurity Plugin is free software; you can redistribute it and/or
5 | * modify it under the terms of the GNU Lesser General Public
6 | * License as published by the Free Software Foundation; either
7 | * version 3 of the License, or (at your option) any later version.
8 | *
9 | * Sonar FSharpSecurity Plugin is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | * Lesser General Public License for more details.
13 | */
14 |
15 | package org.sonar.plugins.fsharp;
16 |
17 | import static org.junit.jupiter.api.Assertions.assertEquals;
18 | import static org.mockito.Mockito.mock;
19 |
20 | import org.junit.jupiter.api.Test;
21 | import org.sonar.api.Plugin;
22 | import org.sonar.api.SonarRuntime;
23 |
24 |
25 | /*
26 | Check that 5 extensions are loaded in FSharpPlugin
27 | */
28 |
29 | public class FSharpPluginTest {
30 | @Test
31 | public void addExtensions_expectedNumber() {
32 | // Arrange
33 | Plugin.Context context = new Plugin.Context(mock(SonarRuntime.class));
34 | FSharpPlugin plugin = new FSharpPlugin();
35 |
36 | // Act
37 | plugin.define(context);
38 |
39 | // Assert
40 | assertEquals(5, context.getExtensions().size());
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/sonar-fsharpsecurity-plugin/src/test/java/org/sonar/plugins/fsharp/FSharpSensorTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Sonar FSharpSecurity Plugin, open source software quality management tool.
3 | *
4 | * Sonar FSharpSecurity Plugin is free software; you can redistribute it and/or
5 | * modify it under the terms of the GNU Lesser General Public
6 | * License as published by the Free Software Foundation; either
7 | * version 3 of the License, or (at your option) any later version.
8 | *
9 | * Sonar FSharpSecurity Plugin is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | * Lesser General Public License for more details.
13 | */
14 |
15 | package org.sonar.plugins.fsharp;
16 |
17 | import static org.junit.jupiter.api.Assertions.assertEquals;
18 | import static org.junit.jupiter.api.Assertions.assertTrue;
19 | import static org.mockito.Mockito.mock;
20 |
21 | import org.junit.jupiter.api.Test;
22 | import org.sonar.api.batch.fs.FileSystem;
23 | import org.sonar.api.batch.sensor.Sensor;
24 | import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor;
25 | import org.sonar.api.issue.NoSonarFilter;
26 | import org.sonar.api.measures.FileLinesContextFactory;
27 |
28 | import org.sonar.api.utils.log.Logger;
29 | import org.sonar.api.utils.log.LogTester;
30 | import org.sonar.api.utils.log.LoggerLevel;
31 |
32 | /*
33 | Check that the analyzer works.
34 |
35 | The real unit tests are in the F# code, so we just need to check that it can be called without crashing.
36 | */
37 |
38 | public class FSharpSensorTest {
39 |
40 | private static final LogTester logTester = new LogTester();
41 |
42 |
43 | @Test
44 | public void describe_languageAndKey_asExpected() {
45 | // Arrange
46 | logTester.setLevel(LoggerLevel.DEBUG);
47 | FsSonarRunnerExtractor extractor = new FsSonarRunnerExtractor();
48 | FileSystem fs = mock(FileSystem.class);
49 | FileLinesContextFactory fileLinesContextFactory = mock(FileLinesContextFactory.class);
50 | NoSonarFilter noSonarFilter = new NoSonarFilter();
51 | Sensor sensor = new FSharpSensor(extractor, fs, fileLinesContextFactory, noSonarFilter);
52 |
53 | DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor();
54 |
55 | // Act
56 | sensor.describe(descriptor);
57 |
58 | // Assert
59 | assertEquals(FSharpPlugin.LANGUAGE_NAME, descriptor.name());
60 | assertEquals(1, descriptor.languages().size());
61 | assertTrue(descriptor.languages().contains(FSharpPlugin.LANGUAGE_KEY), "LANGUAGE_KEY not found");
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/sonar-fsharpsecurity-plugin/src/test/java/org/sonar/plugins/fsharp/FSharpSonarRulesDefinitionTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Sonar FSharpSecurity Plugin, open source software quality management tool.
3 | *
4 | * Sonar FSharpSecurity Plugin is free software; you can redistribute it and/or
5 | * modify it under the terms of the GNU Lesser General Public
6 | * License as published by the Free Software Foundation; either
7 | * version 3 of the License, or (at your option) any later version.
8 | *
9 | * Sonar FSharpSecurity Plugin is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | * Lesser General Public License for more details.
13 | */
14 |
15 | package org.sonar.plugins.fsharp;
16 |
17 | import static org.junit.jupiter.api.Assertions.assertEquals;
18 | import static org.junit.jupiter.api.Assertions.assertNotNull;
19 | import static org.junit.jupiter.api.Assertions.assertTrue;
20 |
21 | import org.junit.jupiter.api.Disabled;
22 | import org.junit.jupiter.api.Test;
23 | import org.sonar.api.server.rule.RulesDefinition.Context;
24 |
25 | import org.sonar.api.utils.log.Logger;
26 | import org.sonar.api.utils.log.LogTester;
27 | import org.sonar.api.utils.log.LoggerLevel;
28 |
29 | public class FSharpSonarRulesDefinitionTest {
30 | private static final String rulesFile = "/rulesExample.xml";
31 | private static final LogTester logTester = new LogTester();
32 |
33 | @Test
34 | public void repositories_exactlyOne() {
35 | // Arrange
36 | logTester.setLevel(LoggerLevel.DEBUG);
37 | Context context = new Context();
38 | assertEquals(0, context.repositories().size());
39 |
40 | // Act
41 | new FSharpSonarRulesDefinition().defineFromFile(context,rulesFile);
42 |
43 | // Assert
44 | assertEquals(1, context.repositories().size());
45 | }
46 |
47 | @Test
48 | public void repository_expectedNameAndKey() {
49 | // Arrange
50 | logTester.setLevel(LoggerLevel.DEBUG);
51 | Context context = new Context();
52 |
53 | // Act
54 | new FSharpSonarRulesDefinition().defineFromFile(context,rulesFile);
55 |
56 | // Assert
57 | assertEquals(FSharpPlugin.REPOSITORY_NAME, context.repositories().get(0).name());
58 | assertNotNull(context.repository(FSharpPlugin.REPOSITORY_KEY));
59 | }
60 |
61 | @Test
62 | public void moreThanOneRuleLoaded() {
63 | // Arrange
64 | logTester.setLevel(LoggerLevel.DEBUG);
65 | Context context = new Context();
66 | assertEquals(0, context.repositories().size());
67 |
68 | // Act
69 | new FSharpSonarRulesDefinition().defineFromFile(context,rulesFile);
70 |
71 | // Assert
72 | boolean moreThanOneRule = context.repository(FSharpPlugin.REPOSITORY_KEY).rules().size() > 0;
73 | assertTrue(moreThanOneRule, "Expecting more than one rule");
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/sonar-fsharpsecurity-plugin/src/test/java/org/sonar/plugins/fsharp/FSharpSonarWayProfileTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Sonar FSharpSecurity Plugin, open source software quality management tool.
3 | *
4 | * Sonar FSharpSecurity Plugin is free software; you can redistribute it and/or
5 | * modify it under the terms of the GNU Lesser General Public
6 | * License as published by the Free Software Foundation; either
7 | * version 3 of the License, or (at your option) any later version.
8 | *
9 | * Sonar FSharpSecurity Plugin is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | * Lesser General Public License for more details.
13 | */
14 |
15 | package org.sonar.plugins.fsharp;
16 |
17 | import java.io.BufferedWriter;
18 | import java.io.File;
19 | import java.io.FileInputStream;
20 | import java.io.FileWriter;
21 | import java.io.IOException;
22 | import java.io.InputStreamReader;
23 | import java.nio.charset.StandardCharsets;
24 | import java.nio.file.Paths;
25 | import java.util.Base64;
26 | import java.util.Collection;
27 | import java.util.HashMap;
28 | import java.util.HashSet;
29 | import java.util.Map;
30 | import java.util.Map.Entry;
31 | import java.util.List;
32 | import java.util.ArrayList;
33 | import java.net.URL;
34 |
35 | import static org.junit.jupiter.api.Assertions.assertEquals;
36 | import static org.junit.jupiter.api.Assertions.assertNotNull;
37 | import static org.junit.jupiter.api.Assertions.assertTrue;
38 |
39 | import org.junit.jupiter.api.Disabled;
40 | import org.junit.jupiter.api.Test;
41 |
42 | import org.sonar.api.utils.log.Logger;
43 | import org.sonar.api.utils.log.LogTester;
44 | import org.sonar.api.utils.log.LoggerLevel;
45 |
46 | import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition;
47 | import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition.NewBuiltInQualityProfileImpl;
48 |
49 | public class FSharpSonarWayProfileTest {
50 |
51 | private static final LogTester logTester = new LogTester();
52 | private static final String PROFILE_EXAMPLE_PATH = "/profileExample.txt";
53 |
54 | @Test
55 | public void moreThanOneRuleLoaded() {
56 | // Arrange
57 | logTester.setLevel(LoggerLevel.DEBUG);
58 | URL profileUrl = getClass().getResource(PROFILE_EXAMPLE_PATH);
59 |
60 | // Act
61 | List ruleKeys = new FSharpSonarWayProfile().importRuleKeysFromUrl(profileUrl);
62 |
63 | // Assert
64 | boolean moreThanOneRule = ruleKeys.size() > 0;
65 | assertTrue(moreThanOneRule, "Expecting more than one issue from the example file");
66 | }
67 |
68 | @Test
69 | public void checkProfileWorks() {
70 | // Arrange
71 | logTester.setLevel(LoggerLevel.DEBUG);
72 |
73 | URL profileUrl = getClass().getResource(PROFILE_EXAMPLE_PATH);
74 |
75 | BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context();
76 |
77 | // Act
78 | new FSharpSonarWayProfile().defineFromUrl(context, profileUrl);
79 |
80 | // Assert
81 | // just make sure it doesn't crash
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/sonar-fsharpsecurity-plugin/src/test/resources/profileExample.txt:
--------------------------------------------------------------------------------
1 | S5042
2 | S4834
3 | S4829
4 | S4823
5 | S4818
6 | S4792
7 | S4790
8 | S4787
9 | S4784
10 | S4507
11 | S3011
12 | S2255
13 | S2245
14 | S2092
15 | S2077
16 | S1313
17 |
--------------------------------------------------------------------------------
/sonar-fsharpsecurity-plugin/src/test/resources/rulesExample.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | S5042
5 | Expanding archive files is security-sensitive
6 | Expanding archive files is security-sensitive. For example, expanding archive files has led in the past to the following vulnerabilities: