Skip to content

Commit 5fbe55e

Browse files
committed
Add support for scoring ReSharper and Roslynator for CSharp. Enhance
scoring/CWE mapping for Cppcheck, Fortify, Snyk.
1 parent e446ae5 commit 5fbe55e

File tree

7 files changed

+871
-77
lines changed

7 files changed

+871
-77
lines changed

library/src/main/java/org/owasp/benchmarkutils/helpers/Categories.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ private void load(InputStream xmlFileStream)
238238
if (parentOfNodeList.getLength() > 0) {
239239
String[] parentOfList = parentOfNodeList.item(0).getTextContent().split(",");
240240
for (String parentOfString : parentOfList) {
241-
Integer parentOfInt = Integer.valueOf(parentOfString);
241+
Integer parentOfInt = Integer.valueOf(parentOfString.trim());
242242
if (!parentOf.add(parentOfInt)) {
243243
System.err.println(
244244
"FATAL ERROR: file "

plugin/src/main/java/org/owasp/benchmarkutils/score/parsers/CppcheckXMLReader.java

Lines changed: 250 additions & 29 deletions
Large diffs are not rendered by default.

plugin/src/main/java/org/owasp/benchmarkutils/score/parsers/FortifyReader.java

Lines changed: 221 additions & 47 deletions
Large diffs are not rendered by default.
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
/**
2+
* OWASP Benchmark Project
3+
*
4+
* <p>This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
5+
* details, please see <a
6+
* href="https://owasp.org/www-project-benchmark/">https://owasp.org/www-project-benchmark/</a>.
7+
*
8+
* <p>The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
9+
* of the GNU General Public License as published by the Free Software Foundation, version 2.
10+
*
11+
* <p>The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
12+
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
13+
* PURPOSE. See the GNU General Public License for more details.
14+
*
15+
* @author Dave Wichers
16+
* @created 2025
17+
*/
18+
package org.owasp.benchmarkutils.score.parsers;
19+
20+
import org.json.JSONArray;
21+
import org.json.JSONObject;
22+
import org.owasp.benchmarkutils.score.CweNumber;
23+
import org.owasp.benchmarkutils.score.ResultFile;
24+
import org.owasp.benchmarkutils.score.TestCaseResult;
25+
import org.owasp.benchmarkutils.score.TestSuiteResults;
26+
27+
public class ReSharperReader extends Reader {
28+
29+
@Override
30+
public boolean canRead(ResultFile resultFile) {
31+
return resultFile.isJson()
32+
&& resultFile.line(1).contains("schemastore.azurewebsites.net")
33+
&& resultFile.json().has("runs");
34+
}
35+
36+
@Override
37+
public TestSuiteResults parse(ResultFile resultFile) throws Exception {
38+
TestSuiteResults tr =
39+
new TestSuiteResults("ReSharper", false, TestSuiteResults.ToolType.SAST);
40+
41+
JSONArray runs = resultFile.json().getJSONArray("runs");
42+
JSONArray results = runs.getJSONObject(0).getJSONArray("results");
43+
// tr.setToolVersion(resultFile.json().getString("version"));
44+
45+
// results
46+
for (int i = 0; i < results.length(); i++) {
47+
TestCaseResult tcr = parseReSharperFindings(results.getJSONObject(i));
48+
if (tcr != null) {
49+
tr.put(tcr);
50+
}
51+
}
52+
return tr;
53+
}
54+
55+
private TestCaseResult parseReSharperFindings(JSONObject result) {
56+
try {
57+
String ruleId = result.getString("ruleId"); // Name of rule
58+
String level =
59+
result.getString("level"); // Severity level of finding (note, warning, ...)
60+
String ruleExplanation = result.getJSONObject("message").getString("text");
61+
62+
String uri =
63+
result.getJSONArray("locations")
64+
.getJSONObject(0)
65+
.getJSONObject("physicalLocation")
66+
.getJSONObject("artifactLocation")
67+
.getString("uri");
68+
69+
// String className = result.getString("filename");
70+
// className = (className.substring(className.lastIndexOf('/') +
71+
// 1)).split("\\.")[0];
72+
if (isTestCaseFile(uri)) {
73+
TestCaseResult tcr = new TestCaseResult();
74+
tcr.setActualResultTestID(TestSuiteResults.getFileNameNoPath(uri));
75+
76+
// Figure out CWE
77+
int cweNum = cweLookup(ruleId, level, ruleExplanation, uri);
78+
if (cweNum != CweNumber.DONTCARE) {
79+
tcr.setCWE(cweNum);
80+
return tcr;
81+
}
82+
}
83+
84+
} catch (Exception ex) {
85+
ex.printStackTrace();
86+
}
87+
88+
return null;
89+
}
90+
91+
/**
92+
* This method maps ReSharper rules to their corresponding CWE, or CweNumber.DONTCARE, if we
93+
* don't care about it.
94+
*/
95+
private int cweLookup(
96+
String ruleid, String severityLevel, String ruleExplanation, String filename) {
97+
switch (ruleid) {
98+
case "ArrangeModifiersOrder": // Inconsistent modifiers declaration order
99+
case "CheckNamespace": // Namespace does not correspond to file location
100+
case "ClassNeverInstantiated.Global": // Class 'FOO' is never instantiated
101+
case "CollectionNeverQueried.Local": // Content of collection 'FOO' is only updated but
102+
// never used
103+
case "ConvertIfStatementToConditionalTernaryExpression": // Convert into '?:' expression
104+
case "ConvertIfStatementToNullCoalescingExpression": // Convert into '??' expression
105+
case "FieldCanBeMadeReadOnly.Local": // Field can be made readonly
106+
case "ForCanBeConvertedToForeach": // For-loop can be converted into foreach-loop
107+
case "InconsistentNaming": // Name 'FOO' does not match rule 'Static readonly fields
108+
// (private)'
109+
case "JoinDeclarationAndInitializer": // Join declaration and assignment
110+
case "PossibleIntendedRethrow": // Exception rethrow possibly intended
111+
case "RedundantNameQualifier": // Qualifier is redundant
112+
case "RedundantUsingDirective": // Using directive not required and can be safely
113+
case "RedundantVerbatimStringPrefix": // Redundant verbatim string prefix
114+
// removed
115+
case "TooWideLocalVariableScope": // Local variable 'FOO' can be declared in inner scope
116+
case "UnassignedReadonlyField.Compiler": // Readonly field 'FOO' is never assigned
117+
case "UnusedMemberInSuper.Global": // Only overrides of method 'FOO' are used
118+
case "UnusedType.Global": // Class is never used
119+
case "UseObjectOrCollectionInitializer": // Use object or collection initializer (to
120+
// improve readability)
121+
case "UseStringInterpolation": // Use string interpolation expression
122+
return CweNumber.DONTCARE;
123+
124+
case "AssignNullToNotNullAttribute": // Possible 'null' assignment to non-nullable
125+
case "ExpressionIsAlwaysNull": // Expression is always null
126+
case "PossibleNullReferenceException": // Possible 'System.NullReferenceException'
127+
// entity
128+
case "ReplaceWithStringIsNullOrEmpty": // Replace with '!String.IsNullOrEmpty'
129+
return 476; // CWE-476: NULL Pointer Dereference
130+
case "ConditionIsAlwaysTrueOrFalse":
131+
{
132+
if ("Expression is always false".equals(ruleExplanation))
133+
return 570; // CWE-570 Expression is Always False
134+
else if ("Expression is always true".equals(ruleExplanation))
135+
return 571; // CWE-571 Expression is Always True
136+
else {
137+
System.err.println(
138+
"WARNING: Unmapped rule explanation of '"
139+
+ ruleExplanation
140+
+ "' for ruleid id: ConditionIsAlwaysTrueOrFalse");
141+
}
142+
} // Intentionally fall thru to EqualExpressionComparison
143+
case "EqualExpressionComparison": // Similar expressions comparison
144+
return 571; // // CWE-571 Expression is Always True
145+
146+
case "BadChildStatementIndent": // Line indent is not restored to the previous level
147+
// around child statement
148+
case "CSharpWarnings::CS0642": // Possible mistaken empty statement
149+
case "MisleadingBodyLikeStatement": // Statement can be confused with previous
150+
// statement's body
151+
return 483; // CWE-483 Incorrect Block Delimitation
152+
153+
case "CSharpWarnings::CS0162": // Code is unreachable
154+
case "EmptyForStatement": // Empty 'for' loop is redundant
155+
case "EmptyStatement": // Empty statement is redundant
156+
case "HeuristicUnreachableCode": // Case/Code is heuristically unreachable
157+
case "MathAbsMethodIsRedundant": // Math.Abs() argument is always non-negative
158+
case "RedundantBoolCompare": // Comparison with true is redundant
159+
case "RedundantCast": // Type cast is redundant
160+
case "RedundantDefaultMemberInitializer": // Initializing field by default value is
161+
case "RedundantJumpStatement": // Redundant control flow jump statement
162+
case "RedundantStringFormatCall": // Redundant 'String.Format()' call
163+
// redundant
164+
case "UnusedField.Compiler": // Field 'FOO' is never used
165+
case "UnusedMember.Global": // Constant/Field/Method 'FOO' is never used
166+
case "UnusedMember.Local": // Constant/Field/Method 'FOO' is never used
167+
case "UselessBinaryOperation": // Addition or subtraction of 0 in every execution path,
168+
// which is useless
169+
return 561; // CWE-561 Dead Code
170+
171+
case "CSharpWarnings::CS0618": // 'CS0618: Constructor
172+
// 'System.Net.Sockets.TcpListener.TcpListener(int)' is
173+
// obsolete: 'This method has been deprecated. Please use
174+
// TcpListener(IPAddress localaddr, int port) instead.
175+
// http://go.microsoft.com/fwlink/?linkid=14202'
176+
return 477; // CWE-477 Use of Obsolete Function
177+
case "CSharpWarnings::CS0665": // Assignment in conditional expression; did you mean to
178+
// use '==' instead of '='?
179+
return 481; // CWE-481 Assigning instead of Comparing
180+
181+
case "CSharpWarnings::CS1717": // Assignment made to same variable; did you mean to
182+
// assign something else?
183+
return 481; // CWE-481 Assigning instead of Comparing
184+
185+
case "FormatStringProblem": // Formatting is specified, but the argument is not
186+
// 'IFormattable'
187+
return 440; // CWE-440 Expected Behavior Violation
188+
case "FunctionNeverReturns": // Function never returns
189+
return 770; // CWE-770 Allocation of Resources Without Limits or Throttling
190+
case "FunctionRecursiveOnAllPaths": // Method is recursive on all execution paths
191+
return 674; // CWE-674 Uncontrolled Recursion
192+
case "InconsistentOrderOfLocks": // The expression is used in several lock statements
193+
// with inconsistent execution order, forming a cycle
194+
return 833; // CWE-833 Deadlock
195+
case "IntDivisionByZero": // Division by zero in at least one execution path
196+
return 369; // CWE-369 Divide by Zero
197+
case "IntVariableOverflowInUncheckedContext": // Possible overflow in unchecked context
198+
return 190; // CWE-190 Integer Overflow or Wraparound
199+
200+
case "MemberCanBePrivate.Global": // Method 'FOO' can be made private
201+
case "MemberCanBeProtected.Global": // Method 'FOO' can be made protected
202+
return 668; // CWE-668 Exposure of Resource to Wrong Sphere
203+
204+
case "NotAccessedField.Compiler": // Field 'FOO' is assigned but its value is never used
205+
case "NotAccessedVariable": // Local variable 'data' is only assigned but its value is
206+
// never used
207+
case "NotAccessedVariable.Compiler": // Local variable 'FOO' is assigned but its value
208+
// is never used
209+
case "RedundantAssignment": // Value assigned is not used in any execution path
210+
case "RedundantToStringCall": // Redundant 'Object.ToString()' call
211+
case "StructuredMessageTemplateProblem": // Argument is not used in message template
212+
case "UnusedParameter.Global": // Parameter 'FOO' is never used
213+
case "UnusedParameter.Local": // Parameter 'FOO' is never used
214+
case "UnusedVariable": // Local variable 'FOO' is never used
215+
case "UnusedVariable.Compiler": // Local variable 'FOO' is never used
216+
return 563; // CWE-563 Assignment to Variable without Use
217+
218+
case "ReturnValueOfPureMethodIsNotUsed": // Return value of pure method is not used
219+
return 252; // CWE-252 Unchecked Return Value
220+
case "SuspiciousTypeConversion.Global": // Suspicious comparison: there is no type in
221+
// the solution which is inherited from both
222+
// 'System.Net.IPHostEntry' and 'string'
223+
return 510; // CWE-510 Trapdoor
224+
default:
225+
System.err.println(
226+
"WARNING: no CWE value provided for ruleid id: '"
227+
+ ruleid
228+
+ "' with rule explanation: '"
229+
+ ruleExplanation
230+
+ "' and severity: '"
231+
+ severityLevel
232+
+ "' for file: "
233+
+ TestSuiteResults.getFileNameNoPath(filename));
234+
}
235+
return CweNumber.UNMAPPED;
236+
}
237+
}

plugin/src/main/java/org/owasp/benchmarkutils/score/parsers/Reader.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,9 @@ public static List<Reader> allReaders() {
100100
new PTAIReader(),
101101
new QualysWASReader(),
102102
new Rapid7Reader(),
103+
new ReSharperReader(),
103104
new ReshiftReader(),
105+
new RoslynatorXMLReader(),
104106
new ScnrReader(),
105107
new SeekerReader(),
106108
new SemgrepReader(),

0 commit comments

Comments
 (0)