diff --git a/README.md b/README.md index 5afd173c..49ff2732 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ A number of **parsers** have been implemented. Some **parsers** can parse output | [_CloudFormation Linter_](https://github.com/aws-cloudformation/cfn-lint) | `JUNIT` | `cfn-lint . -f junit --output-file report-junit.xml` | [_CodeClimate_](https://codeclimate.com/) | `CODECLIMATE` | | [_CodeNarc_](http://codenarc.sourceforge.net/) | `CODENARC` | +| [_Coverity_](https://scan.coverity.com/) | `COVERITY` | | [_Dart_](https://dart.dev/) | `MACHINE` | With `dart analyze --format=machine` | [_Dependency Check_](https://jeremylong.github.io/DependencyCheck/) | `SARIF` | Using `--format SARIF` | [_Detekt_](https://github.com/arturbosch/detekt) | `CHECKSTYLE` | With `--output-format xml`. @@ -89,7 +90,7 @@ A number of **parsers** have been implemented. Some **parsers** can parse output | [_YAMLLint_](https://yamllint.readthedocs.io/en/stable/index.html) | `YAMLLINT` | With `-f parsable` | [_ZPTLint_](https://pypi.python.org/pypi/zptlint) | `ZPTLINT` | -51 parsers and 78 reporters. +52 parsers and 79 reporters. Missing a format? Open an issue [here](https://github.com/tomasbjerre/violations-lib/issues)! diff --git a/build.gradle b/build.gradle index b9745b45..58f377ff 100644 --- a/build.gradle +++ b/build.gradle @@ -21,21 +21,36 @@ project.ext.buildConfig = [ updateReadme: true ], staticCodeAnalysis: [ - maxViolations: 6 + maxViolations: 5 ], ] apply from: project.buildscript.classLoader.getResource('main.gradle').toURI() apply plugin: 'jsonschema2pojo' -jsonSchema2Pojo { - source = files("${sourceSets.main.output.resourcesDir}/json") - targetDirectory = file("src/gen/java") - targetPackage = 'se.bjurr.violations.lib.model.generated.sarif' - generateBuilders = true - annotationStyle = 'none' - includeGeneratedAnnotation = false - removeOldOutput = true +[ + [ + removeOldOutput: true, + from: "${rootDir}/src/main/resources/json/sarif-schema.json", + to: "se.bjurr.violations.lib.model.generated.sarif" + ], + [ + removeOldOutput: false, + from: "${rootDir}/src/main/resources/json/coverity-schema.json", + to: "se.bjurr.violations.lib.model.generated.coverity" + ] +].each { codeGen -> + logger.lifecycle("Generating ${codeGen.from} to ${codeGen.to}") + + jsonSchema2Pojo { + source = files(codeGen.from) + targetDirectory = file("src/gen/java") + targetPackage = codeGen.to + generateBuilders = true + annotationStyle = 'none' + includeGeneratedAnnotation = false + removeOldOutput = codeGen.removeOldOutput + } } spotlessJava.dependsOn generateJsonSchema2Pojo @@ -48,6 +63,6 @@ dependencies { testImplementation 'junit:junit:4.13.2' testImplementation 'org.assertj:assertj-core:3.25.3' testImplementation 'uk.co.jemos.podam:podam:8.0.1.RELEASE' - testImplementation 'com.approvaltests:approvaltests:22.4.0' - testImplementation 'com.networknt:json-schema-validator:1.3.3' + testImplementation 'com.approvaltests:approvaltests:23.0.0' + testImplementation 'com.networknt:json-schema-validator:1.4.0' } diff --git a/src/main/java/se/bjurr/violations/lib/parsers/AnsibleLaterParser.java b/src/main/java/se/bjurr/violations/lib/parsers/AnsibleLaterParser.java index e8e8d383..2c107ffe 100644 --- a/src/main/java/se/bjurr/violations/lib/parsers/AnsibleLaterParser.java +++ b/src/main/java/se/bjurr/violations/lib/parsers/AnsibleLaterParser.java @@ -15,7 +15,7 @@ public class AnsibleLaterParser implements ViolationsParser { - public class AnsibleLaterEntry { + public static class AnsibleLaterEntry { public String asctime; public String levelname; public String message; diff --git a/src/main/java/se/bjurr/violations/lib/parsers/CoverityParser.java b/src/main/java/se/bjurr/violations/lib/parsers/CoverityParser.java new file mode 100644 index 00000000..027d4e6e --- /dev/null +++ b/src/main/java/se/bjurr/violations/lib/parsers/CoverityParser.java @@ -0,0 +1,50 @@ +package se.bjurr.violations.lib.parsers; + +import static se.bjurr.violations.lib.model.SEVERITY.ERROR; +import static se.bjurr.violations.lib.model.SEVERITY.WARN; +import static se.bjurr.violations.lib.model.Violation.violationBuilder; + +import com.google.gson.Gson; +import java.util.Set; +import java.util.TreeSet; +import se.bjurr.violations.lib.ViolationsLogger; +import se.bjurr.violations.lib.model.SEVERITY; +import se.bjurr.violations.lib.model.Violation; +import se.bjurr.violations.lib.model.generated.coverity.CoveritySchema; +import se.bjurr.violations.lib.model.generated.coverity.Issue; +import se.bjurr.violations.lib.reports.Parser; + +public class CoverityParser implements ViolationsParser { + + @Override + public Set parseReportOutput( + final String string, final ViolationsLogger violationsLogger) throws Exception { + final CoveritySchema coverityReport = new Gson().fromJson(string, CoveritySchema.class); + + final Set violations = new TreeSet<>(); + for (final Issue issue : coverityReport.getIssues()) { + + violations.add( + violationBuilder() // + .setFile(issue.getMainEventFilePathname()) // + .setMessage( + issue.getCheckerProperties().getSubcategoryLocalEffect() + + "\n" + + issue.getCheckerProperties().getSubcategoryLocalEffect()) // + .setParser(Parser.COVERITY) // + .setCategory(issue.getCheckerProperties().getCategory()) + .setRule(issue.getType() + "/" + issue.getSubtype()) // + .setSeverity(this.toSeverity(issue.getCheckerProperties().getImpact())) // + .setStartLine(issue.getMainEventLineNumber()) // + .build()); + } + return violations; + } + + private SEVERITY toSeverity(final String from) { + if (from.equalsIgnoreCase("Medium")) { + return WARN; + } + return ERROR; + } +} diff --git a/src/main/java/se/bjurr/violations/lib/reports/Parser.java b/src/main/java/se/bjurr/violations/lib/reports/Parser.java index 9ad7b04e..46f6621a 100644 --- a/src/main/java/se/bjurr/violations/lib/reports/Parser.java +++ b/src/main/java/se/bjurr/violations/lib/reports/Parser.java @@ -14,6 +14,7 @@ import se.bjurr.violations.lib.parsers.CheckStyleParser; import se.bjurr.violations.lib.parsers.CodeClimateParser; import se.bjurr.violations.lib.parsers.CodeNarcParser; +import se.bjurr.violations.lib.parsers.CoverityParser; import se.bjurr.violations.lib.parsers.CppLintParser; import se.bjurr.violations.lib.parsers.DocFXParser; import se.bjurr.violations.lib.parsers.FindbugsParser; @@ -64,6 +65,7 @@ public enum Parser { CHECKSTYLE(new CheckStyleParser()), // CODENARC(new CodeNarcParser()), // CLANG(new CLangParser()), // + COVERITY(new CoverityParser()), // CPD(new CPDParser()), // CPPCHECK(new CPPCheckParser()), // CPPLINT(new CppLintParser()), // diff --git a/src/main/java/se/bjurr/violations/lib/reports/Reporter.java b/src/main/java/se/bjurr/violations/lib/reports/Reporter.java index 8e988bad..25717ffd 100644 --- a/src/main/java/se/bjurr/violations/lib/reports/Reporter.java +++ b/src/main/java/se/bjurr/violations/lib/reports/Reporter.java @@ -17,6 +17,7 @@ public enum Reporter { "https://github.com/PyCQA/bandit", "With `bandit -r examples/ -f custom -o bandit.out --msg-template \"{abspath}:{line}: {severity}: {test_id}: {msg}\"`"), CLANG("CLang", Parser.CLANG, "https://clang-analyzer.llvm.org/", ""), + COVERITY("Coverity", Parser.COVERITY, "https://scan.coverity.com/", ""), CFN( "CloudFormation Linter", Parser.JUNIT, diff --git a/src/main/resources/json/coverity-schema.json b/src/main/resources/json/coverity-schema.json new file mode 100644 index 00000000..d9484c26 --- /dev/null +++ b/src/main/resources/json/coverity-schema.json @@ -0,0 +1,196 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "formatVersion": { + "type": "integer" + }, + "suppressedIssueCount": { + "type": "integer" + }, + "issues": { + "type": "array", + "items": { + "$ref": "#/definitions/issue" + } + }, + "desktopAnalysisSettings": { + "type": "null" + }, + "error": { + "type": "null" + }, + "warnings": { + "type": "array", + "items": {} + } + }, + "definitions": { + "issue": { + "type": "object", + "properties": { + "mergeKey": { + "type": "string" + }, + "occurrenceCountForMK": { + "type": "integer" + }, + "occurrenceNumberInMK": { + "type": "integer" + }, + "referenceOccurrenceCountForMK": { + "type": "null" + }, + "checkerName": { + "type": "string" + }, + "subcategory": { + "type": "string" + }, + "type": { + "type": "string" + }, + "subtype": { + "type": "string" + }, + "code-language": { + "type": "string" + }, + "extra": { + "type": "string" + }, + "domain": { + "type": "string" + }, + "language": { + "type": "string" + }, + "mainEventFilePathname": { + "type": "string" + }, + "strippedMainEventFilePathname": { + "type": "string" + }, + "mainEventLineNumber": { + "type": "integer" + }, + "properties": { + "type": "object" + }, + "functionDisplayName": { + "type": "string" + }, + "functionMangledName": { + "type": "string" + }, + "localStatus": { + "type": "null" + }, + "ordered": { + "type": "boolean" + }, + "stateOnServer": { + "type": "null" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/event" + } + }, + "checkerProperties": { + "$ref": "#/definitions/checkerProperty" + } + } + }, + "event": { + "type": "object", + "properties": { + "covLStrEventDescription": { + "type": "string" + }, + "eventDescription": { + "type": "string" + }, + "eventNumber": { + "type": "integer" + }, + "eventTreePosition": { + "type": "string" + }, + "eventSet": { + "type": "integer" + }, + "eventTag": { + "type": "string" + }, + "filePathname": { + "type": "string" + }, + "strippedFilePathname": { + "type": "string" + }, + "lineNumber": { + "type": "integer" + }, + "main": { + "type": "boolean" + }, + "moreInformationId": { + "type": "null" + }, + "remediation": { + "type": "boolean" + }, + "events": { + "type": "null" + } + } + }, + "checkerProperty": { + "type": "object", + "properties": { + "category": { + "type": "string" + }, + "categoryDescription": { + "type": "string" + }, + "cweCategory": { + "type": "string" + }, + "issueKinds": { + "type": "array", + "items": { + "$ref": "#/definitions/issueKind" + } + }, + "eventSetCaptions": { + "type": "array", + "items": {} + }, + "impact": { + "type": "string" + }, + "impactDescription": { + "type": "string" + }, + "subcategoryLocalEffect": { + "type": "string" + }, + "subcategoryShortDescription": { + "type": "string" + }, + "subcategoryLongDescription": { + "type": "string" + } + } + }, + "issueKind": { + "type": "string" + } + } +} diff --git a/src/test/java/se/bjurr/violations/lib/CoverityTest.java b/src/test/java/se/bjurr/violations/lib/CoverityTest.java new file mode 100644 index 00000000..7d9351fc --- /dev/null +++ b/src/test/java/se/bjurr/violations/lib/CoverityTest.java @@ -0,0 +1,45 @@ +package se.bjurr.violations.lib; + +import static org.assertj.core.api.Assertions.assertThat; +import static se.bjurr.violations.lib.TestUtils.getRootFolder; +import static se.bjurr.violations.lib.ViolationsApi.violationsApi; +import static se.bjurr.violations.lib.model.SEVERITY.WARN; + +import java.util.ArrayList; +import java.util.Set; +import org.junit.Test; +import se.bjurr.violations.lib.model.Violation; +import se.bjurr.violations.lib.reports.Parser; + +public class CoverityTest { + + @Test + public void testThatViolationsCanBeParsed() { + final String rootFolder = getRootFolder(); + + final Set actual = + violationsApi() // + .withPattern(".*/coverity/example1\\.json$") // + .inFolder(rootFolder) // + .findAll(Parser.COVERITY) // + .violations(); + + assertThat(actual) // + .hasSize(1); + + final Violation violation0 = new ArrayList<>(actual).get(0); + assertThat(violation0.getMessage()) // + .isEqualTo( + "The expression's value is always zero; construct may indicate an inadvertent logic error.\nThe expression's value is always zero; construct may indicate an inadvertent logic error."); + assertThat(violation0.getFile()) // + .isEqualTo("C:/Workspace/workspace/Build_jenkins_development/somefile.cs"); + assertThat(violation0.getSeverity()) // + .isEqualTo(WARN); + assertThat(violation0.getCategory()) // + .isEqualTo("Integer handling issues"); + assertThat(violation0.getRule()) // + .isEqualTo("constant_expression_result/bit_and_with_zero"); + assertThat(violation0.getStartLine()) // + .isEqualTo(79); + } +} diff --git a/src/test/resources/coverity/example1.json b/src/test/resources/coverity/example1.json new file mode 100644 index 00000000..58f14396 --- /dev/null +++ b/src/test/resources/coverity/example1.json @@ -0,0 +1,62 @@ +{ + "type": "Coverity issues", + "formatVersion": 7, + "suppressedIssueCount": 0, + "issues": [ + { + "mergeKey": "884ed7531feed32eb916d9038a3b9bd6", + "occurrenceCountForMK": 1, + "occurrenceNumberInMK": 1, + "referenceOccurrenceCountForMK": null, + "checkerName": "CONSTANT_EXPRESSION_RESULT", + "subcategory": "bit_and_with_zero", + "type": "constant_expression_result", + "subtype": "bit_and_with_zero", + "code-language": "c#", + "extra": "status", + "domain": "STATIC_CS", + "language": "C#", + "mainEventFilePathname": "C:\\Workspace\\workspace\\Build_jenkins_development\\somefile.cs", + "strippedMainEventFilePathname": "\\workspace\\Build_jenkins_development\\Architecture\\somefile.cs", + "mainEventLineNumber": 79, + "properties": {}, + "functionDisplayName": "somename", + "functionMangledName": "somename", + "localStatus": null, + "ordered": false, + "events": [ + { + "covLStrEventDescription": "{CovLStrv2{{t{{0} is always 0.}{{code{status & System.Printing.PrintJobStatus.None}}}}{t{ This occurs as a value.}}}}", + "eventDescription": "\"status & System.Printing.PrintJobStatus.None\" is always 0. This occurs as a value.", + "eventNumber": 1, + "eventTreePosition": "1", + "eventSet": 0, + "eventTag": "bit_and_with_zero", + "filePathname": "C:\\Workspace\\workspace\\Build_jenkins_development\\somefile.cs", + "strippedFilePathname": "\\workspace\\Build_jenkins_development\\Architecture\\somefile.cs", + "lineNumber": 79, + "main": true, + "moreInformationId": null, + "remediation": false, + "events": null + } + ], + "stateOnServer": null, + "checkerProperties": { + "category": "Integer handling issues", + "categoryDescription": "Integer handling issues", + "cweCategory": "569", + "issueKinds": ["QUALITY"], + "eventSetCaptions": [], + "impact": "Medium", + "impactDescription": "Medium", + "subcategoryLocalEffect": "The expression's value is always zero; construct may indicate an inadvertent logic error.", + "subcategoryShortDescription": "Bitwise-and with zero", + "subcategoryLongDescription": "Bitwise-and ('&') operation applied to zero always produces zero" + } + } + ], + "desktopAnalysisSettings": null, + "error": null, + "warnings": [] +} diff --git a/src/test/resources/findbugs/spotbugs-report.xml b/src/test/resources/findbugs/spotbugs-report.xml new file mode 100644 index 00000000..55af6c15 --- /dev/null +++ b/src/test/resources/findbugs/spotbugs-report.xml @@ -0,0 +1,133 @@ + + + + + + + Method appears to call the same method on the same object redundantly + Method removed.CurrentSupplierContactResolverImpl.resolveContact(removed) appears to call the same method on the same object redundantly + + + At CurrentSupplierContactResolverImpl.java:[lines 51-93] + + In class removed.CurrentSupplierContactResolverImpl + + + + In method removed.CurrentSupplierContactResolverImpl.resolveContact(removed) + + + At CurrentSupplierContactResolverImpl.java:[line 92] + + + Value requireNonNull(Ljava/lang/Object;)Ljava/lang/Object; + + + + Method buries logic to the right (indented) more than it needs to be + Method removed.FacadeReadOnlyHandler.lambda$resolveDefaultValue$3(Field, XFWDefaultValue) buries logic to the right (indented) more than it needs to be + + + At FacadeReadOnlyHandler.java:[lines 52-257] + + In class removed.FacadeReadOnlyHandler + + + + In method removed.FacadeReadOnlyHandler.lambda$resolveDefaultValue$3(Field, XFWDefaultValue) + + + At FacadeReadOnlyHandler.java:[lines 169-175] + + + + Method throws exception with static message string + Method removed.FacadeReadOnlyHandler.checkFacadeReadOnly(BeforeStoreEvent) throws exception with static message string + + + At FacadeReadOnlyHandler.java:[lines 52-257] + + In class removed.FacadeReadOnlyHandler + + + + In method removed.FacadeReadOnlyHandler.checkFacadeReadOnly(BeforeStoreEvent) + + + At FacadeReadOnlyHandler.java:[line 255] + + + + Performance + + + Dodgy code + + + Method buries logic to the right (indented) more than it needs to be +
+ + <p>Looks for relatively large <code>if</code> blocks of code, where you unconditionally return from them, + and then follow that with an unconditional return of a small block. This places the bulk of the logic to the right indentation-wise, + making it more difficult to read than needed. It would be better to invert the logic of the if block, and immediately return, + allowing the bulk of the logic to be move to the left for easier reading.</p> + +
+
+ + Method throws exception with static message string +
+ + <p>This method creates and throws an exception using a static string as the exceptions message. + Without any specific context of this particular exception invocation, such as the values of parameters, + key member variables, or local variables, it may be difficult to infer how this exception occurred. Consider + adding context to the exception message.</p> + +
+
+ + Method appears to call the same method on the same object redundantly +
+ + <p>This method makes two consecutive calls to the same method, using the same constant + parameters, on the same instance, without any intervening changes to the objects. If this + method does not make changes to the object, which it appears it doesn't, then making + two calls is just a waste. These method calls could be combined by assigning the + result into a temporary variable, and using the variable the second time.</p> + +
+
+ + Burying Logic + + + Possibly Redundant Method Calls + + + Weak Exception Messaging + + + + + + + + + + + + + + + + + + + + + + + + + +
\ No newline at end of file