From 9765a2e04c984fbad99c7794efa0c41a1cad042c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 14 Jun 2021 04:55:32 +0000 Subject: [PATCH 01/15] Bump bctls-jdk15on from 1.68 to 1.69 Bumps [bctls-jdk15on](https://github.com/bcgit/bc-java) from 1.68 to 1.69. - [Release notes](https://github.com/bcgit/bc-java/releases) - [Changelog](https://github.com/bcgit/bc-java/blob/master/docs/releasenotes.html) - [Commits](https://github.com/bcgit/bc-java/commits) Signed-off-by: dependabot-preview[bot] --- CryptoAnalysis/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CryptoAnalysis/pom.xml b/CryptoAnalysis/pom.xml index 996704ad6..c257a0d96 100644 --- a/CryptoAnalysis/pom.xml +++ b/CryptoAnalysis/pom.xml @@ -271,7 +271,7 @@ org.bouncycastle bctls-jdk15on - 1.68 + 1.69 org.slf4j From 067acc7d2b26816e1cdb224c866a138ceb6f1412 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 11:33:47 +0000 Subject: [PATCH 02/15] Bump bcprov-jdk15on from 1.68 to 1.70 Bumps [bcprov-jdk15on](https://github.com/bcgit/bc-java) from 1.68 to 1.70. - [Release notes](https://github.com/bcgit/bc-java/releases) - [Changelog](https://github.com/bcgit/bc-java/blob/master/docs/releasenotes.html) - [Commits](https://github.com/bcgit/bc-java/commits) --- updated-dependencies: - dependency-name: org.bouncycastle:bcprov-jdk15on dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- CryptoAnalysis/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CryptoAnalysis/pom.xml b/CryptoAnalysis/pom.xml index 8b517c2d8..405778fa2 100644 --- a/CryptoAnalysis/pom.xml +++ b/CryptoAnalysis/pom.xml @@ -260,7 +260,7 @@ org.bouncycastle bcprov-jdk15on - 1.68 + 1.70 test From 6db5f314e2ccc96bfccdd5e6ba221240bba82221 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Oct 2022 09:21:11 +0100 Subject: [PATCH 03/15] Bump gson from 2.9.1 to 2.10 (#414) Bumps [gson](https://github.com/google/gson) from 2.9.1 to 2.10. - [Release notes](https://github.com/google/gson/releases) - [Changelog](https://github.com/google/gson/blob/master/CHANGELOG.md) - [Commits](https://github.com/google/gson/compare/gson-parent-2.9.1...gson-parent-2.10) --- updated-dependencies: - dependency-name: com.google.code.gson:gson dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- CryptoAnalysis/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CryptoAnalysis/pom.xml b/CryptoAnalysis/pom.xml index 7bc021396..a24d1e4e2 100644 --- a/CryptoAnalysis/pom.xml +++ b/CryptoAnalysis/pom.xml @@ -284,7 +284,7 @@ com.google.code.gson gson - 2.9.1 + 2.10 de.darmstadt.tu.crossing.CrySL From a93427b441f3173b99a538fc501d7dc1f37baac0 Mon Sep 17 00:00:00 2001 From: Sebastian Leuer Date: Mon, 31 Oct 2022 10:36:14 +0100 Subject: [PATCH 04/15] update some deps --- CryptoAnalysis/pom.xml | 13 ++- .../crypto/cryslhandler/CrySLModelReader.java | 98 ++++--------------- pom.xml | 1 + 3 files changed, 32 insertions(+), 80 deletions(-) diff --git a/CryptoAnalysis/pom.xml b/CryptoAnalysis/pom.xml index 8b517c2d8..e588d3989 100644 --- a/CryptoAnalysis/pom.xml +++ b/CryptoAnalysis/pom.xml @@ -30,7 +30,7 @@ org.apache.maven.plugins maven-dependency-plugin - 3.1.1 + 3.3.0 unpack @@ -49,7 +49,7 @@ org.apache.maven.plugins maven-dependency-plugin - 3.1.2 + 3.3.0 unpack @@ -251,10 +251,17 @@ 4.13.2 test + + + com.google.inject + guice + 5.1.0 + org.apache.maven.plugins maven-invoker-plugin - 3.2.1 + 3.3.0 test diff --git a/CryptoAnalysis/src/main/java/crypto/cryslhandler/CrySLModelReader.java b/CryptoAnalysis/src/main/java/crypto/cryslhandler/CrySLModelReader.java index 5290720ec..9aa3ea19f 100644 --- a/CryptoAnalysis/src/main/java/crypto/cryslhandler/CrySLModelReader.java +++ b/CryptoAnalysis/src/main/java/crypto/cryslhandler/CrySLModelReader.java @@ -1,99 +1,43 @@ package crypto.cryslhandler; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.AbstractMap.SimpleEntry; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - import com.google.common.base.CharMatcher; import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import com.google.common.io.Files; import com.google.inject.Injector; -import org.eclipse.emf.common.util.EList; -import org.eclipse.emf.common.util.URI; -import org.eclipse.emf.ecore.EObject; -import org.eclipse.emf.ecore.resource.Resource; -import org.eclipse.xtext.common.types.JvmExecutable; -import org.eclipse.xtext.common.types.JvmFormalParameter; -import org.eclipse.xtext.common.types.access.impl.ClasspathTypeProvider; -import org.eclipse.xtext.resource.XtextResource; -import org.eclipse.xtext.resource.XtextResourceSet; - - import crypto.exceptions.CryptoAnalysisException; -import com.google.common.base.CharMatcher; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.inject.Injector; import crypto.interfaces.ICrySLPredicateParameter; import crypto.interfaces.ISLConstraint; -import crypto.rules.CrySLArithmeticConstraint; +import crypto.rules.*; import crypto.rules.CrySLArithmeticConstraint.ArithOp; -import crypto.rules.CrySLComparisonConstraint; import crypto.rules.CrySLComparisonConstraint.CompOp; -import crypto.rules.CrySLCondPredicate; -import crypto.rules.CrySLConstraint; import crypto.rules.CrySLConstraint.LogOps; -import crypto.rules.CrySLForbiddenMethod; -import crypto.rules.CrySLMethod; -import crypto.rules.CrySLObject; -import crypto.rules.CrySLPredicate; -import crypto.rules.CrySLRule; -import crypto.rules.CrySLSplitter; -import crypto.rules.CrySLValueConstraint; -import crypto.rules.ParEqualsPredicate; -import crypto.rules.StateMachineGraph; -import crypto.rules.StateNode; -import crypto.rules.TransitionEdge; import de.darmstadt.tu.crossing.CrySLStandaloneSetup; import de.darmstadt.tu.crossing.constraints.CrySLArithmeticOperator; import de.darmstadt.tu.crossing.constraints.CrySLComparisonOperator; import de.darmstadt.tu.crossing.constraints.CrySLLogicalOperator; -import de.darmstadt.tu.crossing.crySL.ArithmeticExpression; -import de.darmstadt.tu.crossing.crySL.ArithmeticOperator; -import de.darmstadt.tu.crossing.crySL.ArrayElements; -import de.darmstadt.tu.crossing.crySL.ComparingOperator; -import de.darmstadt.tu.crossing.crySL.ComparisonExpression; -import de.darmstadt.tu.crossing.crySL.Constraint; -import de.darmstadt.tu.crossing.crySL.DestroysBlock; -import de.darmstadt.tu.crossing.crySL.Domainmodel; -import de.darmstadt.tu.crossing.crySL.EnsuresBlock; -import de.darmstadt.tu.crossing.crySL.Event; -import de.darmstadt.tu.crossing.crySL.Expression; -import de.darmstadt.tu.crossing.crySL.ForbMethod; -import de.darmstadt.tu.crossing.crySL.ForbiddenBlock; -import de.darmstadt.tu.crossing.crySL.Literal; -import de.darmstadt.tu.crossing.crySL.LiteralExpression; -import de.darmstadt.tu.crossing.crySL.LogicalImply; -import de.darmstadt.tu.crossing.crySL.LogicalOperator; import de.darmstadt.tu.crossing.crySL.Object; -import de.darmstadt.tu.crossing.crySL.ObjectDecl; -import de.darmstadt.tu.crossing.crySL.Order; -import de.darmstadt.tu.crossing.crySL.PreDefinedPredicates; -import de.darmstadt.tu.crossing.crySL.Pred; -import de.darmstadt.tu.crossing.crySL.PredLit; -import de.darmstadt.tu.crossing.crySL.ReqPred; -import de.darmstadt.tu.crossing.crySL.RequiredBlock; -import de.darmstadt.tu.crossing.crySL.SimpleOrder; -import de.darmstadt.tu.crossing.crySL.SuPar; -import de.darmstadt.tu.crossing.crySL.SuParList; -import de.darmstadt.tu.crossing.crySL.SuperType; -import de.darmstadt.tu.crossing.crySL.UnaryPreExpression; -import de.darmstadt.tu.crossing.crySL.UseBlock; +import de.darmstadt.tu.crossing.crySL.*; import de.darmstadt.tu.crossing.crySL.impl.DomainmodelImpl; import de.darmstadt.tu.crossing.crySL.impl.ObjectImpl; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.xtext.common.types.JvmExecutable; +import org.eclipse.xtext.common.types.JvmFormalParameter; +import org.eclipse.xtext.common.types.access.impl.ClasspathTypeProvider; +import org.eclipse.xtext.resource.XtextResource; +import org.eclipse.xtext.resource.XtextResourceSet; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.AbstractMap.SimpleEntry; +import java.util.*; +import java.util.Map.Entry; public class CrySLModelReader { diff --git a/pom.xml b/pom.xml index ef729045d..57b4c16e6 100644 --- a/pom.xml +++ b/pom.xml @@ -60,6 +60,7 @@ org.apache.maven.plugins maven-source-plugin + 3.2.1 attach-sources From bc85da6cb9af4039218e4e98e8b8a05e877a2a13 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Oct 2022 10:53:45 +0100 Subject: [PATCH 05/15] Bump flatten-maven-plugin from 1.2.2 to 1.3.0 (#411) Bumps [flatten-maven-plugin](https://github.com/mojohaus/flatten-maven-plugin) from 1.2.2 to 1.3.0. - [Release notes](https://github.com/mojohaus/flatten-maven-plugin/releases) - [Commits](https://github.com/mojohaus/flatten-maven-plugin/compare/1.2.2...flatten-maven-plugin-1.3.0) --- updated-dependencies: - dependency-name: org.codehaus.mojo:flatten-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 57b4c16e6..a67903a97 100644 --- a/pom.xml +++ b/pom.xml @@ -36,7 +36,7 @@ org.codehaus.mojo flatten-maven-plugin - 1.2.2 + 1.3.0 true From 814a10b26e24d7edc02e5ba262bdb006d6ab4821 Mon Sep 17 00:00:00 2001 From: Sebastian Leuer Date: Mon, 31 Oct 2022 11:03:25 +0100 Subject: [PATCH 06/15] version to 2.8.0-SNAPSHOT --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a67903a97..71cbb346a 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ - 2.7.3 + 2.8.0-SNAPSHOT UTF-8 3.3.0 From d34dba20110f646e2559fcaee08a617ab51489c8 Mon Sep 17 00:00:00 2001 From: Sebastian Leuer Date: Mon, 31 Oct 2022 11:11:59 +0100 Subject: [PATCH 07/15] ignore test artifacts --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 3946c6fd3..0f048ac6b 100644 --- a/.gitignore +++ b/.gitignore @@ -202,4 +202,5 @@ buildNumber.properties # End of https://www.toptal.com/developers/gitignore/api/java,maven,eclipse,intellij+all -.flattened-pom.xml \ No newline at end of file +.flattened-pom.xml +/shippable/testresults/ From 883943c0f18c6ef4d8620b152d2cb125cb199298 Mon Sep 17 00:00:00 2001 From: smeyer198 Date: Fri, 13 Jan 2023 10:54:57 +0100 Subject: [PATCH 08/15] Refactor reporters -All reporters output the same information -Add a flag to include or exclude analysis statistics -Add a new CSV reporter to output more detailed information --- .../java/crypto/HeadlessCryptoScanner.java | 90 +++-- .../analysis/CryptoScannerSettings.java | 117 ++++-- .../java/crypto/reporting/CSVReporter.java | 332 ++++-------------- .../crypto/reporting/CSVSummaryReporter.java | 202 +++++++++++ .../crypto/reporting/CommandLineReporter.java | 36 +- .../crypto/reporting/ReportStatistics.java | 79 +++++ .../main/java/crypto/reporting/Reporter.java | 109 ++++++ .../java/crypto/reporting/ReporterHelper.java | 41 ++- .../java/crypto/reporting/SARIFConfig.java | 13 +- .../java/crypto/reporting/SARIFHelper.java | 35 +- .../java/crypto/reporting/SARIFReporter.java | 61 ++-- .../java/crypto/reporting/TXTReporter.java | 42 ++- 12 files changed, 755 insertions(+), 402 deletions(-) create mode 100644 CryptoAnalysis/src/main/java/crypto/reporting/CSVSummaryReporter.java create mode 100644 CryptoAnalysis/src/main/java/crypto/reporting/ReportStatistics.java create mode 100644 CryptoAnalysis/src/main/java/crypto/reporting/Reporter.java diff --git a/CryptoAnalysis/src/main/java/crypto/HeadlessCryptoScanner.java b/CryptoAnalysis/src/main/java/crypto/HeadlessCryptoScanner.java index b689d6357..3dd703fd7 100644 --- a/CryptoAnalysis/src/main/java/crypto/HeadlessCryptoScanner.java +++ b/CryptoAnalysis/src/main/java/crypto/HeadlessCryptoScanner.java @@ -1,10 +1,12 @@ package crypto; import java.io.File; +import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.TimeUnit; import com.google.common.base.Stopwatch; import com.google.common.collect.Lists; @@ -27,6 +29,7 @@ import crypto.preanalysis.SeedFactory; import crypto.providerdetection.ProviderDetection; import crypto.reporting.CSVReporter; +import crypto.reporting.CSVSummaryReporter; import crypto.reporting.CommandLineReporter; import crypto.reporting.ErrorMarkerListener; import crypto.reporting.SARIFReporter; @@ -163,9 +166,9 @@ private void analyse() { public String toString() { String s = "HeadllessCryptoScanner: \n"; - s += "\tSoftwareIdentifier: "+ softwareIdentifier() +"\n"; - s += "\tApplicationClassPath: "+ applicationClassPath() +"\n"; - s += "\tSootClassPath: "+ sootClassPath() +"\n\n"; + s += "\tSoftwareIdentifier: " + softwareIdentifier() + "\n"; + s += "\tApplicationClassPath: " + applicationClassPath() + "\n"; + s += "\tSootClassPath: " + sootClassPath() + "\n\n"; return s; } @@ -178,25 +181,54 @@ protected void internalTransform(String phaseName, Map options) BoomerangPretransformer.v().apply(); ObservableDynamicICFG observableDynamicICFG = new ObservableDynamicICFG(false); List rules = HeadlessCryptoScanner.rules; + + long callgraphConstructionTime = callGraphWatch.elapsed(TimeUnit.MILLISECONDS); + + final CrySLResultsReporter reporter = new CrySLResultsReporter(); ErrorMarkerListener fileReporter; - if(reportFormat()!= null) { - switch (reportFormat()) { - case SARIF: - fileReporter = new SARIFReporter(getOutputFolder(), rules); - break; - case CSV: - fileReporter = new CSVReporter(getOutputFolder(), softwareIdentifier(), rules, callGraphWatch.elapsed(TimeUnit.MILLISECONDS)); - break; - default: - fileReporter = new TXTReporter(getOutputFolder(), rules); + + Set formats = reportFormats(); + + if (formats.size() > 0) { + for (ReportFormat format : formats) { + switch (format) { + case CMD: + fileReporter = new CommandLineReporter(softwareIdentifier(), rules, callgraphConstructionTime, includeStatistics()); + reporter.addReportListener(fileReporter); + break; + case TXT: + fileReporter = new TXTReporter(getOutputFolder(), softwareIdentifier(), rules, callgraphConstructionTime, includeStatistics()); + reporter.addReportListener(fileReporter); + break; + case SARIF: + fileReporter = new SARIFReporter(getOutputFolder(), softwareIdentifier(), rules, callgraphConstructionTime, includeStatistics()); + reporter.addReportListener(fileReporter); + break; + case CSV: + fileReporter = new CSVReporter(getOutputFolder(), softwareIdentifier(), rules, callgraphConstructionTime, includeStatistics()); + reporter.addReportListener(fileReporter); + break; + case CSV_SUMMARY: + fileReporter = new CSVSummaryReporter(getOutputFolder(), softwareIdentifier(), rules, callgraphConstructionTime, includeStatistics()); + reporter.addReportListener(fileReporter); + break; + default: + fileReporter = new CommandLineReporter(softwareIdentifier(), rules, callgraphConstructionTime, includeStatistics()); + reporter.addReportListener(fileReporter); + } } + } else { + // default to command line reporter + fileReporter = new CommandLineReporter(softwareIdentifier(), rules, callgraphConstructionTime, includeStatistics()); + reporter.addReportListener(fileReporter); } - else { - fileReporter = new CommandLineReporter(rules); + + if(getAdditionalListener() != null) { + reporter.addReportListener(getAdditionalListener()); } - final CrySLResultsReporter reporter = new CrySLResultsReporter(); - if(getAdditionalListener() != null) - reporter.addReportListener(getAdditionalListener()); + + //reporter.addReportListener(fileReporter); + CryptoScanner scanner = new CryptoScanner() { @Override @@ -215,23 +247,27 @@ public Debugger debugger(IDEALSeedSolver if(getOutputFolder() == null) { LOGGER.error("The visualization requires the --reportDir option."); } - File vizFile = new File(getOutputFolder()+"/viz/ObjectId#"+seed.getObjectId()+".json"); + + File vizFile = new File(getOutputFolder() + "/viz/ObjectId#" + seed.getObjectId() + ".json"); vizFile.getParentFile().mkdirs(); + return new IDEVizDebugger<>(vizFile, icfg()); } return super.debugger(solver, seed); } }; - reporter.addReportListener(fileReporter); + //reporter.addReportListener(fileReporter); if (providerDetection()) { ProviderDetection providerDetection = new ProviderDetection(); if(rulesetRootPath == null) { - rulesetRootPath = System.getProperty("user.dir")+File.separator+"src"+File.separator+"main"+File.separator+"resources"; + rulesetRootPath = System.getProperty("user.dir") + File.separator + "src" + File.separator + "main" + File.separator + "resources"; } + String detectedProvider = providerDetection.doAnalysis(observableDynamicICFG, rulesetRootPath); + if(detectedProvider != null) { rules.clear(); switch(settings.getRulesetPathType()) { @@ -366,15 +402,23 @@ protected boolean isPreAnalysis() { protected boolean enableVisualization(){ return settings.isVisualization(); } - + protected ReportFormat reportFormat() { - return settings.getReportFormat(); + return null; + } + + protected Set reportFormats() { + return settings.getReportFormats(); } protected boolean providerDetection() { return settings.isProviderDetectionAnalysis(); } + protected boolean includeStatistics() { + return settings.isIncludeStatistics(); + } + private static String pathToJCE() { // When whole program mode is disabled, the classpath misses jce.jar return System.getProperty("java.home") + File.separator + "lib" + File.separator + "jce.jar"; diff --git a/CryptoAnalysis/src/main/java/crypto/analysis/CryptoScannerSettings.java b/CryptoAnalysis/src/main/java/crypto/analysis/CryptoScannerSettings.java index 52d3b216f..40c1b8307 100644 --- a/CryptoAnalysis/src/main/java/crypto/analysis/CryptoScannerSettings.java +++ b/CryptoAnalysis/src/main/java/crypto/analysis/CryptoScannerSettings.java @@ -1,5 +1,8 @@ package crypto.analysis; +import java.util.HashSet; +import java.util.Set; + import crypto.exceptions.CryptoAnalysisParserException; public class CryptoScannerSettings { @@ -12,10 +15,11 @@ public class CryptoScannerSettings { private String applicationPath = null; private String softwareIdentifier = ""; private String reportDirectory = null; - private ReportFormat reportFormat = null; + private Set reportFormats; private boolean preAnalysis; private boolean visualization; private boolean providerDetectionAnalysis; + private boolean includeStatistics; public CryptoScannerSettings() { setControlGraph(ControlGraph.CHA); @@ -23,6 +27,9 @@ public CryptoScannerSettings() { setPreAnalysis(false); setVisualization(false); setProviderDetectionAnalysis(false); + setIncludeStatistics(true); + + reportFormats = new HashSet<>(); } public ControlGraph getControlGraph() { @@ -88,13 +95,9 @@ public String getReportDirectory() { public void setReportDirectory(String reportDirectory) { this.reportDirectory = reportDirectory; } - - public ReportFormat getReportFormat() { - return reportFormat; - } - - public void setReportFormat(ReportFormat reportFormat) { - this.reportFormat = reportFormat; + + public Set getReportFormats() { + return reportFormats; } public boolean isPreAnalysis() { @@ -121,12 +124,22 @@ public void setProviderDetectionAnalysis(boolean providerDetectionAnalysis) { this.providerDetectionAnalysis = providerDetectionAnalysis; } + public boolean isIncludeStatistics() { + return includeStatistics; + } + + public void setIncludeStatistics(boolean includeStatistics) { + this.includeStatistics = includeStatistics; + } + public void parseSettingsFromCLI(String[] settings) throws CryptoAnalysisParserException { int mandatorySettings = 0; + if(settings == null) { showErrorMessage(); } - for(int i=0; i crypto.HeadlessCryptoScanner \\\r\n"+ - " --rulesDir \\\r\n" + - " --appPath \n"; + + "The default command for running CryptoAnalysis is: \n" + + "java -cp crypto.HeadlessCryptoScanner \\\r\n" + + " --rulesDir \\\r\n" + + " --appPath \n"; throw new CryptoAnalysisParserException(errorMessage); } private static void showErrorMessage(String arg) throws CryptoAnalysisParserException { - String errorMessage = "An error occured while trying to parse the CLI argument: "+arg+".\n" - +"The default command for running CryptoAnalysis is: \n" - + "java -cp crypto.HeadlessCryptoScanner \\\r\n" + - " --rulesDir \\\r\n" + - " --appPath \n" + String errorMessage = "An error occured while trying to parse the CLI argument: " + arg + ".\n" + + "The default command for running CryptoAnalysis is: \n" + + "java -cp crypto.HeadlessCryptoScanner \\\r\n" + + " --rulesDir \\\r\n" + + " --appPath \n" + "\nAdditional arguments that can be used are:\n" + "--cg \n" + "--sootPath \n" + "--identifier \n" + "--reportPath \n" - + "--reportFormat \n" + + "--reportFormat \n" + "--preanalysis (enables pre-analysis)\n" + "--visualization (enables the visualization, but also requires --reportPath option to be set)\n" - + "--providerDetection (enables provider detection analysis)\n"; + + "--providerDetection (enables provider detection analysis)\n" + + "--dstats (disable the statistic information in the reports)\n"; throw new CryptoAnalysisParserException(errorMessage); } diff --git a/CryptoAnalysis/src/main/java/crypto/reporting/CSVReporter.java b/CryptoAnalysis/src/main/java/crypto/reporting/CSVReporter.java index dd56dd05f..9d9f8df46 100644 --- a/CryptoAnalysis/src/main/java/crypto/reporting/CSVReporter.java +++ b/CryptoAnalysis/src/main/java/crypto/reporting/CSVReporter.java @@ -3,299 +3,117 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; -import java.util.Collection; +import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.Map.Entry; -import java.util.Set; -import java.util.concurrent.TimeUnit; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import com.google.common.base.Joiner; -import com.google.common.base.Stopwatch; -import com.google.common.collect.HashBasedTable; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Multimap; -import com.google.common.collect.Sets; -import com.google.common.collect.Table; -import com.google.common.collect.Table.Cell; -import boomerang.BackwardQuery; -import boomerang.Query; -import boomerang.jimple.Statement; -import boomerang.jimple.Val; -import boomerang.results.ForwardBoomerangResults; -import crypto.analysis.AnalysisSeedWithSpecification; -import crypto.analysis.EnsuredCrySLPredicate; -import crypto.analysis.IAnalysisSeed; + +import java.util.Set; + import crypto.analysis.errors.AbstractError; -import crypto.analysis.errors.ConstraintError; -import crypto.analysis.errors.ForbiddenMethodError; -import crypto.analysis.errors.HardCodedError; -import crypto.analysis.errors.ImpreciseValueExtractionError; -import crypto.analysis.errors.IncompleteOperationError; -import crypto.analysis.errors.NeverTypeOfError; -import crypto.analysis.errors.RequiredPredicateError; -import crypto.analysis.errors.TypestateError; -import crypto.extractparameter.CallSiteWithParamIndex; -import crypto.extractparameter.ExtractedValue; -import crypto.interfaces.ISLConstraint; -import crypto.rules.CrySLPredicate; import crypto.rules.CrySLRule; -import soot.MethodOrMethodContext; -import soot.Scene; +import soot.SootClass; import soot.SootMethod; -import soot.jimple.toolkits.callgraph.ReachableMethods; -import soot.util.queue.QueueReader; -import sync.pds.solver.nodes.Node; -import typestate.TransitionFunction; -public class CSVReporter extends ErrorMarkerListener { +public class CSVReporter extends Reporter { private static final Logger LOGGER = LoggerFactory.getLogger(CSVReporter.class); private static final String CSV_SEPARATOR = ";"; - private Set errors = Sets.newHashSet(); - private int seeds; - private List headers = Lists.newArrayList(); - private Map headersToValues = Maps.newHashMap(); - private List rules; - private Set dataflowReachableMethods = Sets.newHashSet(); - private Stopwatch analysisTime = Stopwatch.createUnstarted(); - - /** - * Path of directory of analysis reports - */ - private File reportDir; - /** - * name of the analysis report - */ private static final String REPORT_NAME = "CryptoAnalysis-Report.csv"; - /** - * the headers of CSV report - */ - private enum Headers{ - SoftwareID,SeedObjectCount,CallGraphTime_ms,CryptoAnalysisTime_ms,CallGraphReachableMethods, - CallGraphReachableMethods_ActiveBodies,DataflowVisitedMethod + + private List headers; + private List contents; + + private enum Headers { + ErrorID, ErrorType, ViolatingClass, Class, Method, LineNumber, Statement, Message } - - /** - * Creates {@link CSVReporter} a constructor with reportDir, softwareId, rules and callGraphConstructionTime as parameter - * - * @param reportDir a {@link String} path giving the location of the report directory - * @param softwareId {@link Format} An identifier used to label output files in CSV report format - * @param rules {@link CrySLRule} the rules with which the project is analyzed - * @param callGraphConstructionTime {@link long} call graph construction time in ms - */ - public CSVReporter(String reportDir, String softwareId, List rules, long callGraphConstructionTime) { - this.reportDir = (reportDir != null ? new File(reportDir) : new File(System.getProperty("user.dir"))); - this.rules = rules; - ReachableMethods reachableMethods = Scene.v().getReachableMethods(); - QueueReader listener = reachableMethods.listener(); - Set visited = Sets.newHashSet(); - int callgraphReachableMethodsWithActiveBodies = 0; - while (listener.hasNext()) { - MethodOrMethodContext next = listener.next(); - visited.add(next.method()); - if (next.method().hasActiveBody()) { - callgraphReachableMethodsWithActiveBodies++; - } - } - int callgraphReachableMethods = visited.size(); - for(Headers h : Headers.values()){ - headers.add(h.toString()); - } - put(Headers.SoftwareID,softwareId); - put(Headers.CallGraphTime_ms,callGraphConstructionTime); - put(Headers.CallGraphReachableMethods,callgraphReachableMethods); - put(Headers.CallGraphReachableMethods_ActiveBodies,callgraphReachableMethodsWithActiveBodies); - addDynamicHeader(ConstraintError.class.getSimpleName()); - addDynamicHeader(NeverTypeOfError.class.getSimpleName()); - addDynamicHeader(HardCodedError.class.getSimpleName()); - addDynamicHeader(TypestateError.class.getSimpleName()); - addDynamicHeader(RequiredPredicateError.class.getSimpleName()); - addDynamicHeader(IncompleteOperationError.class.getSimpleName()); - addDynamicHeader(ImpreciseValueExtractionError.class.getSimpleName()); - addDynamicHeader(ForbiddenMethodError.class.getSimpleName()); + + private enum StatisticHeaders { + SoftwareID, SeedObjectCount, CryptoAnalysisTime_ms, CallGraphTime_ms, CallGraphReachableMethods, + CallGraphReachableMethods_ActiveBodies, DataflowVisitedMethod } - private void addDynamicHeader(String name) { - headers.add(name+"_sum"); - for(CrySLRule r : rules){ - headers.add(name+"_"+r.getClassName()); + public CSVReporter(String reportDir, String softwareId, List rules, long callGraphConstructionTime, boolean includeStatistics) { + super((reportDir != null ? new File(reportDir) : new File(System.getProperty("user.dir"))), softwareId, rules, callGraphConstructionTime, includeStatistics); + + headers = new ArrayList<>(); + contents = new ArrayList<>(); + + for (Headers h : Headers.values()) { + headers.add(h.toString()); } - } - - @Override - public void beforeAnalysis() { - analysisTime.start(); - } - - @Override - public void afterAnalysis() { - analysisTime.stop(); - put(Headers.DataflowVisitedMethod, dataflowReachableMethods.size()); - put(Headers.CryptoAnalysisTime_ms, analysisTime.elapsed(TimeUnit.MILLISECONDS)); - put(Headers.SeedObjectCount, seeds); - Table errorTable = HashBasedTable.create(); - for(AbstractError err : errors){ - Integer integer = errorTable.get(err.getClass(), err.getRule()); - if(integer == null){ - integer = 0; + if (includeStatistics()) { + for (StatisticHeaders h : StatisticHeaders.values()) { + headers.add(h.toString()); } - integer++; - errorTable.put(err.getClass(), err.getRule(),integer); } + } - - for(Cell c : errorTable.cellSet()){ - put(c.getRowKey().getSimpleName() + "_" + c.getColumnKey().getClassName(), c.getValue()); - } + @Override + public void handleAnalysisResults() { + int idCount = 0; - Map errorsAccumulated = Maps.newHashMap(); - for(Cell c : errorTable.cellSet()){ - Integer integer = errorsAccumulated.get(c.getRowKey()); - if(integer == null){ - integer = 0; + for (SootClass c : this.errorMarkers.rowKeySet()) { + String className = c.getName(); + + for (Entry> e : this.errorMarkers.row(c).entrySet()) { + String methodName = e.getKey().getSubSignature(); + + for (AbstractError marker : e.getValue()) { + String errorType = marker.getClass().getSimpleName(); + String violatingClass = marker.getRule().getClassName(); + String errorMessage = marker.toErrorMarkerString(); + int lineNumber = marker.getErrorLocation().getUnit().get().getJavaSourceStartLineNumber(); + String statement = marker.getErrorLocation().getUnit().get().toString(); + + String line = idCount + CSV_SEPARATOR + errorType + CSV_SEPARATOR + violatingClass + CSV_SEPARATOR + className + + CSV_SEPARATOR + methodName + CSV_SEPARATOR + lineNumber + CSV_SEPARATOR + statement + CSV_SEPARATOR + errorMessage; + + contents.add(line); + + idCount++; + } } - integer += c.getValue(); - errorsAccumulated.put(c.getRowKey(),integer); - } - - for(Entry c : errorsAccumulated.entrySet()){ - put(c.getKey().getSimpleName() + "_sum", c.getValue()); } writeToFile(); } - + private void writeToFile() { try { - FileWriter writer = new FileWriter(reportDir + File.separator+ REPORT_NAME); + FileWriter writer = new FileWriter(getOutputFolder() + File.separator + REPORT_NAME); + + // write headers writer.write(Joiner.on(CSV_SEPARATOR).join(headers) + "\n"); - List line = Lists.newArrayList(); - for(String h : headers){ - String string = headersToValues.get(h); - if(string == null){ - string = ""; - } - line.add(string); + + // write errors line by line + for (String line : this.contents) { + writer.write(line + "\n"); } - writer.write(Joiner.on(CSV_SEPARATOR).join(line) + "\n"); - writer.write("\n"+SARIFConfig.ANALYSISTOOL_NAME_VALUE+"\n"); - String version = getClass().getPackage().getImplementationVersion(); - if(version == null) { - version = "Version is not known"; + + if (includeStatistics()) { + // Additional analysis statistics + writer.write("\nAdditional analysis statistics:\n"); + writer.write(String.format("SoftwareID: %s\n", statistics.getSoftwareID())); + writer.write(String.format("SeedObjectCount: %d\n", statistics.getSeedObjectCount())); + writer.write(String.format("CryptoAnalysisTime: %d\n", statistics.getAnalysisTime())); + writer.write(String.format("CallgraphConstructionTime: %d\n", statistics.getCallgraphTime())); + writer.write(String.format("CallgraphReachableMethods: %d\n", statistics.getCallgraphReachableMethods())); + writer.write(String.format("CallgraphReachableMethodsWithActiveBodies: %d\n", statistics.getCallgraphReachableMethodsWithActiveBodies())); + writer.write(String.format("DataflowVisitedMethods: %d\n", statistics.getDataflowVisitedMethods())); } - writer.write(version); + writer.close(); - LOGGER.info("CSV Report generated to file : "+ reportDir.getAbsolutePath() + File.separator+ REPORT_NAME); + LOGGER.info("CSV Report generated to file : " + getOutputFolder().getAbsolutePath() + File.separator+ REPORT_NAME); } catch (IOException e) { - LOGGER.error("Could not write to " + reportDir.getAbsolutePath() + File.separator+ REPORT_NAME, e); + LOGGER.error("Could not write to " + getOutputFolder().getAbsolutePath() + File.separator + REPORT_NAME, e); } } - private void put(String key, Object val) { - if (!headers.contains(key)) { - LOGGER.error("Did not create a header to this value " + key); - } else { - if(val == null){ - LOGGER.info(key+" is null"); - } - else { - headersToValues.put(key, val.toString()); - } - } - } - private void put(Headers key, Object val) { - put(key.toString(),val); - } - - @Override - public void beforeConstraintCheck(AnalysisSeedWithSpecification analysisSeedWithSpecification) { - - } - - @Override - public void afterConstraintCheck(AnalysisSeedWithSpecification analysisSeedWithSpecification) { - - } - - @Override - public void beforePredicateCheck(AnalysisSeedWithSpecification analysisSeedWithSpecification) { - - } - - @Override - public void afterPredicateCheck(AnalysisSeedWithSpecification analysisSeedWithSpecification) { - - } - - @Override - public void seedStarted(IAnalysisSeed analysisSeedWithSpecification) { - // TODO Auto-generated method stub - - } - - @Override - public void boomerangQueryStarted(Query seed, BackwardQuery q) { - } - - @Override - public void boomerangQueryFinished(Query seed, BackwardQuery q) { - - } - - @Override - public void reportError(AbstractError error) { - errors.add(error); - } - - @Override - public void ensuredPredicates(Table> existingPredicates, - Table> expectedPredicates, - Table> missingPredicates) { - - } - - @Override - public void checkedConstraints(AnalysisSeedWithSpecification analysisSeedWithSpecification, - Collection relConstraints) { - } - - @Override - public void onSeedTimeout(Node seed) { - - } - - @Override - public void onSeedFinished(IAnalysisSeed seed, ForwardBoomerangResults forwardResults) { - dataflowReachableMethods.addAll(forwardResults.getStats().getCallVisitedMethods()); - } - - - @Override - public void collectedValues(AnalysisSeedWithSpecification seed, - Multimap collectedValues) { - - } - - @Override - public void discoveredSeed(IAnalysisSeed curr) { - seeds++; - } - - @Override - public void onSecureObjectFound(IAnalysisSeed analysisObject) { - // TODO Auto-generated method stub - - } - - @Override - public void addProgress(int processedSeeds, int workListsize) { - // TODO Auto-generated method stub - - } - } diff --git a/CryptoAnalysis/src/main/java/crypto/reporting/CSVSummaryReporter.java b/CryptoAnalysis/src/main/java/crypto/reporting/CSVSummaryReporter.java new file mode 100644 index 000000000..b08eb59d7 --- /dev/null +++ b/CryptoAnalysis/src/main/java/crypto/reporting/CSVSummaryReporter.java @@ -0,0 +1,202 @@ +package crypto.reporting; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.google.common.base.Joiner; +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.common.collect.Table; +import com.google.common.collect.Table.Cell; +import crypto.analysis.errors.AbstractError; +import crypto.analysis.errors.ConstraintError; +import crypto.analysis.errors.ForbiddenMethodError; +import crypto.analysis.errors.HardCodedError; +import crypto.analysis.errors.ImpreciseValueExtractionError; +import crypto.analysis.errors.IncompleteOperationError; +import crypto.analysis.errors.NeverTypeOfError; +import crypto.analysis.errors.RequiredPredicateError; +import crypto.analysis.errors.TypestateError; +import crypto.rules.CrySLRule; + +public class CSVSummaryReporter extends Reporter { + + private static final Logger LOGGER = LoggerFactory.getLogger(CSVSummaryReporter.class); + + private static final String CSV_SEPARATOR = ";"; + private Set errors = Sets.newHashSet(); + private List headers = Lists.newArrayList(); + private Map headersToValues = Maps.newHashMap(); + + /** + * name of the analysis report + */ + private static final String REPORT_NAME = "CryptoAnalysis-Report-Summary.csv"; + /** + * the headers of CSV report + */ + private enum Headers { + SoftwareID, SeedObjectCount, CryptoAnalysisTime_ms, CallGraphTime_ms, CallGraphReachableMethods, + CallGraphReachableMethods_ActiveBodies, DataflowVisitedMethod + } + + /** + * Creates {@link CSVSummaryReporter} a constructor with reportDir, softwareId, rules and callGraphConstructionTime as parameter + * + * @param reportDir a {@link String} path giving the location of the report directory + * @param softwareId {@link Format} An identifier used to label output files in CSV report format + * @param rules {@link CrySLRule} the rules with which the project is analyzed + * @param callGraphConstructionTime {@link long} call graph construction time in ms + */ + public CSVSummaryReporter(String reportDir, String softwareId, List rules, long callGraphConstructionTime, boolean includeStatistics) { + super((reportDir != null ? new File(reportDir) : new File(System.getProperty("user.dir"))), softwareId, rules, callGraphConstructionTime, includeStatistics); + + // include statistics only if wanted + if (includeStatistics()) { + // Create headers for the statistics + for (Headers h : Headers.values()) { + headers.add(h.toString()); + } + + put(Headers.SoftwareID, getStatistics().getSoftwareID()); + put(Headers.CallGraphTime_ms, getStatistics().getCallgraphTime()); + put(Headers.CallGraphReachableMethods, getStatistics().getCallgraphReachableMethods()); + put(Headers.CallGraphReachableMethods_ActiveBodies, getStatistics().getCallgraphReachableMethodsWithActiveBodies()); + } + + // Create headers for the errors depending on the used set of rules + addDynamicHeader(ConstraintError.class.getSimpleName()); + addDynamicHeader(NeverTypeOfError.class.getSimpleName()); + addDynamicHeader(HardCodedError.class.getSimpleName()); + addDynamicHeader(TypestateError.class.getSimpleName()); + addDynamicHeader(RequiredPredicateError.class.getSimpleName()); + addDynamicHeader(IncompleteOperationError.class.getSimpleName()); + addDynamicHeader(ImpreciseValueExtractionError.class.getSimpleName()); + addDynamicHeader(ForbiddenMethodError.class.getSimpleName()); + } + + /** + * Create headers for all specified rules with the format _ + * (e.g. ConstraintError_java.security.AlgorithmParameterGenerator). + * + * @param name Name of the error class + */ + private void addDynamicHeader(String name) { + headers.add(name + "_sum"); + + for (CrySLRule r : getRules()) { + headers.add(name + "_" + r.getClassName()); + } + } + + private void put(Headers key, Object val) { + put(key.toString(), val); + } + + private void put(String key, Object val) { + if (!headers.contains(key)) { + LOGGER.error("Did not create a header to this value " + key); + } else { + if (val == null) { + LOGGER.info(key + " is null"); + } else { + headersToValues.put(key, val.toString()); + } + } + } + + @Override + public void handleAnalysisResults() { + if (includeStatistics()) { + put(Headers.DataflowVisitedMethod, getStatistics().getDataflowVisitedMethods()); + put(Headers.CryptoAnalysisTime_ms, getStatistics().getAnalysisTime()); + put(Headers.SeedObjectCount, getStatistics().getSeedObjectCount()); + } + + // Count the number of each error class + Table errorTable = HashBasedTable.create(); + + for (AbstractError err : errors) { + Integer integer = errorTable.get(err.getClass(), err.getRule()); + + if(integer == null) { + integer = 0; + } + + integer++; + errorTable.put(err.getClass(), err.getRule(), integer); + } + + // Set the corresponding error headers to the number of occurred errors + for (Cell c : errorTable.cellSet()) { + put(c.getRowKey().getSimpleName() + "_" + c.getColumnKey().getClassName(), c.getValue()); + } + + Map errorsAccumulated = Maps.newHashMap(); + + for (Cell c : errorTable.cellSet()) { + Integer integer = errorsAccumulated.get(c.getRowKey()); + + if(integer == null) { + integer = 0; + } + + integer += c.getValue(); + errorsAccumulated.put(c.getRowKey(), integer); + } + + for (Entry c : errorsAccumulated.entrySet()) { + put(c.getKey().getSimpleName() + "_sum", c.getValue()); + } + + writeToFile(); + } + + private void writeToFile() { + try { + FileWriter writer = new FileWriter(getOutputFolder() + File.separator + REPORT_NAME); + writer.write(Joiner.on(CSV_SEPARATOR).join(headers) + "\n"); + + List line = Lists.newArrayList(); + + for (String h : headers) { + String string = headersToValues.get(h); + + if (string == null) { + string = ""; + } + + line.add(string); + } + + writer.write(Joiner.on(CSV_SEPARATOR).join(line) + "\n"); + writer.write("\n" + SARIFConfig.ANALYSISTOOL_NAME_VALUE + "\n"); + + String version = getClass().getPackage().getImplementationVersion(); + + if (version == null) { + version = "Version is not known"; + } + + writer.write(version); + writer.close(); + LOGGER.info("CSV Report generated to file : " + getOutputFolder().getAbsolutePath() + File.separator + REPORT_NAME); + } catch (IOException e) { + LOGGER.error("Could not write to " + getOutputFolder().getAbsolutePath() + File.separator + REPORT_NAME, e); + } + } + + @Override + public void reportError(AbstractError error) { + errors.add(error); + } + +} diff --git a/CryptoAnalysis/src/main/java/crypto/reporting/CommandLineReporter.java b/CryptoAnalysis/src/main/java/crypto/reporting/CommandLineReporter.java index 027545219..c52eccacf 100644 --- a/CryptoAnalysis/src/main/java/crypto/reporting/CommandLineReporter.java +++ b/CryptoAnalysis/src/main/java/crypto/reporting/CommandLineReporter.java @@ -1,25 +1,16 @@ package crypto.reporting; import java.io.File; -import java.util.Collection; -import java.util.HashSet; import java.util.List; - -import crypto.analysis.IAnalysisSeed; import crypto.rules.CrySLRule; -public class CommandLineReporter extends ErrorMarkerListener { +public class CommandLineReporter extends Reporter { - private File outputFolder; - private List rules; - private Collection objects = new HashSet<>(); - /** * The analysis report */ private String analysisReport; - /** * Creates {@link CommandLineReporter} a constructor with reportDir and rules as parameter * @@ -27,26 +18,29 @@ public class CommandLineReporter extends ErrorMarkerListener { * @param rules {@link CrySLRule} the rules with which the project is analyzed */ public CommandLineReporter(String reportDir, List rules) { - this.outputFolder = (reportDir != null ? new File(reportDir) : null); - this.rules = rules; + super((reportDir != null ? new File(reportDir) : null), "", rules, -1, false); } /** - * Creates {@link CommandLineReporter} a constructor with reportDir and rules as parameter + * Creates {@link CommandLineReporter} a constructor with the softwareID, the rules and the + * callgraph construction time as parameter * + * @param softwareID Identifier for the software * @param rules {@link CrySLRule} the rules with which the project is analyzed + * @param callgraphConstructionTime Time for the callgraph construction in milliseconds */ - public CommandLineReporter(List rules) { - this.rules = rules; + public CommandLineReporter(String softwareID, List rules, long callgraphConstructionTime, boolean includeStatistics) { + super(null, softwareID, rules, callgraphConstructionTime, includeStatistics); } @Override - public void discoveredSeed(IAnalysisSeed object) { - this.objects.add(object); - } - @Override - public void afterAnalysis() { - this.analysisReport = ReporterHelper.generateReport(this.rules, this.objects, this.secureObjects, this.errorMarkers, this.errorMarkerCount); + public void handleAnalysisResults() { + if (includeStatistics()) { + this.analysisReport = ReporterHelper.generateReport(getRules(), getObjects(), this.secureObjects, this.errorMarkers, this.errorMarkerCount, getStatistics()); + } else { + this.analysisReport = ReporterHelper.generateReport(getRules(), getObjects(), this.secureObjects, this.errorMarkers, this.errorMarkerCount, null); + } + System.out.println(analysisReport); } } diff --git a/CryptoAnalysis/src/main/java/crypto/reporting/ReportStatistics.java b/CryptoAnalysis/src/main/java/crypto/reporting/ReportStatistics.java new file mode 100644 index 000000000..694b5345a --- /dev/null +++ b/CryptoAnalysis/src/main/java/crypto/reporting/ReportStatistics.java @@ -0,0 +1,79 @@ +package crypto.reporting; + +public class ReportStatistics { + + private String softwareID; + private int seedObjectCount; + private long analysisTime; + private long callgraphTime; + private int callgraphReachableMethods; + private int callgraphReachableMethodsWithActiveBodies; + private int dataflowVisitedMethods; + + public ReportStatistics() { + this.softwareID = "Unknown"; + this.seedObjectCount = -1; + this.analysisTime = -1; + this.callgraphTime = -1; + this.callgraphReachableMethods = -1; + this.callgraphReachableMethodsWithActiveBodies = -1; + this.dataflowVisitedMethods = -1; + } + + public void setSoftwareID(String softwareID) { + this.softwareID = softwareID; + } + + public String getSoftwareID() { + return softwareID; + } + + public void setSeedObjectCount(int seedObjectCount) { + this.seedObjectCount = seedObjectCount; + } + + public int getSeedObjectCount() { + return seedObjectCount; + } + + public void setAnalysisTime(long analysisTime) { + this.analysisTime = analysisTime; + } + + public long getAnalysisTime() { + return analysisTime; + } + + public void setCallgraphTime(long callgraphTime) { + this.callgraphTime = callgraphTime; + } + + public long getCallgraphTime() { + return callgraphTime; + } + + public void setCallgraphReachableMethods(int callgraphReachableMethods) { + this.callgraphReachableMethods = callgraphReachableMethods; + } + + public int getCallgraphReachableMethods() { + return callgraphReachableMethods; + } + + public void setCallgraphReachableMethodsWithActiveBodies(int callgraphReachableMethodsWithActiveBodies) { + this.callgraphReachableMethodsWithActiveBodies = callgraphReachableMethodsWithActiveBodies; + } + + public int getCallgraphReachableMethodsWithActiveBodies() { + return callgraphReachableMethodsWithActiveBodies; + } + + public void setDataflowVisitedMethods(int dataflowVisitedMethods) { + this.dataflowVisitedMethods = dataflowVisitedMethods; + } + + public int getDataflowVisitedMethods() { + return dataflowVisitedMethods; + } + +} diff --git a/CryptoAnalysis/src/main/java/crypto/reporting/Reporter.java b/CryptoAnalysis/src/main/java/crypto/reporting/Reporter.java new file mode 100644 index 000000000..86bbb48eb --- /dev/null +++ b/CryptoAnalysis/src/main/java/crypto/reporting/Reporter.java @@ -0,0 +1,109 @@ +package crypto.reporting; + +import java.io.File; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import com.google.common.base.Stopwatch; + +import boomerang.results.ForwardBoomerangResults; +import crypto.analysis.IAnalysisSeed; +import crypto.rules.CrySLRule; +import soot.MethodOrMethodContext; +import soot.Scene; +import soot.SootMethod; +import soot.jimple.toolkits.callgraph.ReachableMethods; +import soot.util.queue.QueueReader; +import typestate.TransitionFunction; + +public abstract class Reporter extends ErrorMarkerListener { + + private File outputFolder; + private List rules; + private boolean includeStatistics; + + protected final ReportStatistics statistics = new ReportStatistics(); + protected final Stopwatch analysisWatch = Stopwatch.createUnstarted(); + protected final Collection objects = new HashSet<>(); + protected final Set dataflowReachableMethods = new HashSet<>(); + + public Reporter(File outputFolder, String softwareID, List rules, long callgraphConstructionTime, boolean includeStatistics) { + this.outputFolder = outputFolder; + this.rules = rules; + this.includeStatistics = includeStatistics; + + this.statistics.setSoftwareID(softwareID); + this.statistics.setCallgraphTime(callgraphConstructionTime); + + // Compute reachable methods and visited methods + ReachableMethods reachableMethods = Scene.v().getReachableMethods(); + QueueReader listener = reachableMethods.listener(); + Set visited = new HashSet<>(); + + int callgraphReachableMethodsWithActiveBodies = 0; + + while (listener.hasNext()) { + MethodOrMethodContext next = listener.next(); + visited.add(next.method()); + + if (next.method().hasActiveBody()) { + callgraphReachableMethodsWithActiveBodies++; + } + } + + this.statistics.setCallgraphReachableMethods(visited.size()); + this.statistics.setCallgraphReachableMethodsWithActiveBodies(callgraphReachableMethodsWithActiveBodies); + } + + public File getOutputFolder() { + return outputFolder; + } + + public List getRules() { + return rules; + } + + public boolean includeStatistics() { + return includeStatistics; + } + + public ReportStatistics getStatistics() { + return statistics; + } + + public Collection getObjects() { + return objects; + } + + @Override + public void beforeAnalysis() { + this.analysisWatch.start(); + } + + @Override + public void discoveredSeed(IAnalysisSeed object) { + this.objects.add(object); + } + + @Override + public void onSeedFinished(IAnalysisSeed seed, ForwardBoomerangResults forwardResults) { + this.dataflowReachableMethods.addAll(forwardResults.getStats().getCallVisitedMethods()); + } + + @Override + public void afterAnalysis() { + this.analysisWatch.stop(); + + this.statistics.setSeedObjectCount(this.objects.size()); + this.statistics.setAnalysisTime(this.analysisWatch.elapsed(TimeUnit.MILLISECONDS)); + this.statistics.setDataflowVisitedMethods(this.dataflowReachableMethods.size()); + + handleAnalysisResults(); + } + + public abstract void handleAnalysisResults(); + +} diff --git a/CryptoAnalysis/src/main/java/crypto/reporting/ReporterHelper.java b/CryptoAnalysis/src/main/java/crypto/reporting/ReporterHelper.java index ae29a60e0..b31e3e5be 100644 --- a/CryptoAnalysis/src/main/java/crypto/reporting/ReporterHelper.java +++ b/CryptoAnalysis/src/main/java/crypto/reporting/ReporterHelper.java @@ -29,7 +29,7 @@ public class ReporterHelper{ */ public static String generateReport(List rules, Collection objects, List secureObjects, Table> errorMarkers, - Map errorMarkerCount){ + Map errorMarkerCount, ReportStatistics statistics) { String report = ""; report += "Ruleset: \n"; @@ -50,37 +50,60 @@ public static String generateReport(List rules, Collection> e : errorMarkers.row(c).entrySet()) { report += String.format("\n\t in Method: %s\n", e.getKey().getSubSignature()); + for (AbstractError marker : e.getValue()) { - report += String.format("\t\t%s violating CrySL rule for %s", marker.getClass().getSimpleName() ,marker.getRule().getClassName()); + report += String.format("\t\t%s violating CrySL rule for %s", marker.getClass().getSimpleName(), marker.getRule().getClassName()); + if(marker instanceof ErrorWithObjectAllocation) { report += String.format(" (on Object #%s)\n", ((ErrorWithObjectAllocation) marker).getObjectLocation().getObjectId()); } else { report += "\n"; } + report += String.format("\t\t\t%s\n", marker.toErrorMarkerString()); - report += String.format("\t\t\tat statement: %s\n\n", marker.getErrorLocation().getUnit().get()); + report += String.format("\t\t\tat statement: %s\n", marker.getErrorLocation().getUnit().get()); + report += String.format("\t\t\tat line: %d\n\n", marker.getErrorLocation().getUnit().get().getJavaSourceStartLineNumber()); } } + report += "\n"; } + report += "======================= CryptoAnalysis Summary ==========================\n"; report += String.format("\tNumber of CrySL rules: %s\n", rules.size()); report += String.format("\tNumber of Objects Analyzed: %s\n", objects.size()); - if(errorMarkers.rowKeySet().isEmpty()){ + + if (errorMarkers.rowKeySet().isEmpty()) { report += "No violation of any of the rules found.\n"; - } else{ + } else { report += "\n\tCryptoAnalysis found the following violations. For details see description above.\n"; - for(Entry e : errorMarkerCount.entrySet()){ - report += String.format("\t%s: %s\n", e.getKey().getSimpleName(),e.getValue()); + + for (Entry e : errorMarkerCount.entrySet()) { + report += String.format("\t%s: %s\n", e.getKey().getSimpleName(), e.getValue()); } } - report += "====================================================================="; + + if (statistics != null) { + // Additional analysis statistics + report += "\n\tAdditional analysis statistics:\n"; + report += String.format("\t\tSoftwareID: %s\n", statistics.getSoftwareID()); + report += String.format("\t\tSeedObjectCount: %d\n", statistics.getSeedObjectCount()); + report += String.format("\t\tCryptoAnalysisTime (in ms): %d\n", statistics.getAnalysisTime()); + report += String.format("\t\tCallgraphConstructionTime (in ms): %d\n", statistics.getCallgraphTime()); + report += String.format("\t\tCallgraphReachableMethods: %d\n", statistics.getCallgraphReachableMethods()); + report += String.format("\t\tCallgraphReachableMethodsWithActiveBodies: %d\n", statistics.getCallgraphReachableMethodsWithActiveBodies()); + report += String.format("\t\tDataflowVisitedMethods: %d\n", statistics.getDataflowVisitedMethods()); + } + + report += "========================================================================="; + return report; } diff --git a/CryptoAnalysis/src/main/java/crypto/reporting/SARIFConfig.java b/CryptoAnalysis/src/main/java/crypto/reporting/SARIFConfig.java index 6f9c6f7a9..2e5fdc2a3 100644 --- a/CryptoAnalysis/src/main/java/crypto/reporting/SARIFConfig.java +++ b/CryptoAnalysis/src/main/java/crypto/reporting/SARIFConfig.java @@ -1,6 +1,7 @@ package crypto.reporting; public class SARIFConfig { + public static final String VERSION = "version"; public static final String SARIF_VERSION = "sarifVersion"; public static final String SARIF_VERSION_NUMBER = "2.0.0"; @@ -35,8 +36,9 @@ public class SARIFConfig { public static final String URI_KEY = "uri"; public static final String REGION_KEY = "region"; public static final String START_LINE_KEY = "startLine"; + public static final String METHOD_KEY = "method"; + public static final String STATEMENT_KEY = "statement"; public static final String FULLY_QUALIFIED_LOGICAL_NAME_KEY = "fullyQualifiedLogicalName"; -// public static final String public static final String RESOURCES_KEY = "resources"; public static final String RULES_KEY = "rules"; @@ -63,4 +65,13 @@ public class SARIFConfig { public static final String INSTANCE_OF_ERROR_KEY = "InstanceOfError"; public static final String INSTANCE_OF_ERROR_VALUE = "Reported when a value was found to not be of a certain instance."; + public static final String STATISTICS_KEY = "statistics"; + public static final String SOFTWAREID_KEY = "SoftwareID"; + public static final String SEEDOBJECTCOUNT_KEY = "SeedObjectCount"; + public static final String ANALYSISTIME_KEY = "CryptoAnalysisTime"; + public static final String CALLGRAPHTIME_KEY = "CallgraphConstructionTime"; + public static final String CALLGRAPHREACHABLEMETHODS_KEY = "CallgraphReachableMethods"; + public static final String CALLGRAPGREACHABLEMETHODSWITHACTIVEBODIES_KEY = "CallgraphRechableMethodsWithActiveBodies"; + public static final String DATAFLOWVISITEDMETHODS_KEY = "DataflowVisitedMethods"; + } diff --git a/CryptoAnalysis/src/main/java/crypto/reporting/SARIFHelper.java b/CryptoAnalysis/src/main/java/crypto/reporting/SARIFHelper.java index 4459466cb..ac52628a1 100644 --- a/CryptoAnalysis/src/main/java/crypto/reporting/SARIFHelper.java +++ b/CryptoAnalysis/src/main/java/crypto/reporting/SARIFHelper.java @@ -49,17 +49,21 @@ private JSONObject getRuns() { public JSONObject getToolInfo() { JSONObject tool = new JSONObject(); + tool.put(SARIFConfig.ANALYSISTOOL_NAME_KEY, SARIFConfig.ANALYSISTOOL_NAME_VALUE); tool.put(SARIFConfig.VERSION, getClass().getPackage().getImplementationVersion()); - tool.put(SARIFConfig.SEMANTIC_VERSION_KEY,getClass().getPackage().getImplementationVersion()); + tool.put(SARIFConfig.SEMANTIC_VERSION_KEY, getClass().getPackage().getImplementationVersion()); tool.put(SARIFConfig.LANGUAGE_KEY, SARIFConfig.LANGUAGE_VALUE); + return tool; } public JSONObject getMessage(String text, String richText) { JSONObject message = new JSONObject(); + message.put(SARIFConfig.TEXT_KEY, text); message.put(SARIFConfig.RICH_TEXT_KEY, richText); + return message; } @@ -67,27 +71,48 @@ public String getFileName(SootClass c) { return sourceLocater == null ? c.getName().replace(".", "/") + ".java" : sourceLocater.getAbsolutePath(c); } - public JSONArray getLocations(SootClass c, String methodName, int lineNumber) { + public JSONArray getLocations(SootClass c, String methodName, int lineNumber, String method, String statement) { JSONArray locations = new JSONArray(); JSONObject location = new JSONObject(); - JSONObject startLine = new JSONObject(); - startLine.put(SARIFConfig.START_LINE_KEY, lineNumber); + JSONObject region = new JSONObject(); + region.put(SARIFConfig.START_LINE_KEY, lineNumber); + region.put(SARIFConfig.METHOD_KEY, method); + region.put(SARIFConfig.STATEMENT_KEY, statement); + JSONObject uri = new JSONObject(); uri.put(SARIFConfig.URI_KEY, getFileName(c)); + JSONObject physicalLocation = new JSONObject(); physicalLocation.put(SARIFConfig.FILE_LOCATION_KEY, uri); - physicalLocation.put(SARIFConfig.REGION_KEY, startLine); + physicalLocation.put(SARIFConfig.REGION_KEY, region); location.put(SARIFConfig.PHYSICAL_LOCATION_KEY, physicalLocation); + String fullyQualifiedLogicalName = c.getName().replace(".", "::") + "::" + methodName; location.put(SARIFConfig.FULLY_QUALIFIED_LOGICAL_NAME_KEY, fullyQualifiedLogicalName); locations.add(location); + return locations; } public String getRuleDescription(String ruleId) { return this.rulesMap.get(ruleId); } + + public JSONObject getStatisticsInfo(ReportStatistics statistics) { + JSONObject statisticField = new JSONObject(); + + statisticField.put(SARIFConfig.SOFTWAREID_KEY, statistics.getSoftwareID()); + statisticField.put(SARIFConfig.SEEDOBJECTCOUNT_KEY, statistics.getSeedObjectCount()); + statisticField.put(SARIFConfig.ANALYSISTIME_KEY, statistics.getAnalysisTime()); + statisticField.put(SARIFConfig.CALLGRAPHTIME_KEY, statistics.getCallgraphTime()); + statisticField.put(SARIFConfig.CALLGRAPHREACHABLEMETHODS_KEY, statistics.getCallgraphReachableMethods()); + statisticField.put(SARIFConfig.CALLGRAPGREACHABLEMETHODSWITHACTIVEBODIES_KEY, statistics.getCallgraphReachableMethodsWithActiveBodies()); + statisticField.put(SARIFConfig.DATAFLOWVISITEDMETHODS_KEY, statistics.getDataflowVisitedMethods()); + + return statisticField; + } + } diff --git a/CryptoAnalysis/src/main/java/crypto/reporting/SARIFReporter.java b/CryptoAnalysis/src/main/java/crypto/reporting/SARIFReporter.java index e141fe84f..528644cfc 100644 --- a/CryptoAnalysis/src/main/java/crypto/reporting/SARIFReporter.java +++ b/CryptoAnalysis/src/main/java/crypto/reporting/SARIFReporter.java @@ -3,9 +3,7 @@ import java.io.File; import java.nio.file.Paths; import java.io.IOException; -import java.util.Collection; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -20,7 +18,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; -import crypto.analysis.IAnalysisSeed; import crypto.analysis.errors.AbstractError; import crypto.rules.CrySLRule; import soot.SootClass; @@ -36,14 +33,13 @@ @SuppressWarnings("unchecked") -public class SARIFReporter extends ErrorMarkerListener { +public class SARIFReporter extends Reporter { private static final Logger LOGGER = LoggerFactory.getLogger(SARIFReporter.class); - private File outputFolder; - // private List rules; - private Collection objects = new HashSet<>(); - private JSONObject files = new JSONObject(), resources = new JSONObject(), rules = new JSONObject(); + private JSONObject files = new JSONObject(); + private JSONObject resources = new JSONObject(); + private JSONObject rules = new JSONObject(); private JSONArray results = new JSONArray(); private SARIFHelper sarifHelper; private Map errorCountMap; @@ -52,17 +48,14 @@ public class SARIFReporter extends ErrorMarkerListener { */ private static final String REPORT_NAME = "CryptoAnalysis-Report.json"; - public SARIFReporter(String reportDir, List rules) { - this.outputFolder = (reportDir != null ? new File(reportDir) : new File(System.getProperty("user.dir"))); + public SARIFReporter(String reportDir, String softwareId, List rules, long callgraphConstructionTime, boolean includeStatistics) { + super((reportDir != null ? new File(reportDir) : new File(System.getProperty("user.dir"))), softwareId, rules, callgraphConstructionTime, includeStatistics); + this.sarifHelper = new SARIFHelper(); this.errorCountMap = new HashMap(); initializeMap(); } - public SARIFReporter(String string, List rules, SourceCodeLocater sourceLocater) { - this(string, rules); - this.sarifHelper = new SARIFHelper(sourceLocater); - } private void initializeMap() { this.errorCountMap.put(SARIFConfig.CONSTRAINT_ERROR_KEY, 0); this.errorCountMap.put(SARIFConfig.NEVER_TYPE_OF_ERROR_KEY, 0); @@ -84,53 +77,63 @@ private void addFile(SootClass c) { private String addRules(String errorType) { String finalErrorType = errorType; + if (this.rules.containsKey(errorType)) { int count = this.errorCountMap.get(errorType); count++; finalErrorType = errorType.concat("-".concat(Integer.toString(count))); this.errorCountMap.put(errorType, count); } + JSONObject ruleInfo = new JSONObject(); JSONObject fullDescription = new JSONObject(); + fullDescription.put(SARIFConfig.TEXT_KEY, this.sarifHelper.getRuleDescription(errorType)); ruleInfo.put(SARIFConfig.RULES_ID_KEY, errorType); ruleInfo.put(SARIFConfig.FULL_DESCRIPTION_KEY, fullDescription); this.rules.put(finalErrorType, ruleInfo); + return finalErrorType; } - private void addResults(String errorType, SootClass c, String methodName, int lineNumber, String text, + private void addResults(String errorType, SootClass c, String methodName, int lineNumber, String method, String statement, String text, String richText) { JSONObject result = new JSONObject(); String finalErrorType = addRules(errorType); + result.put(SARIFConfig.RULE_ID_KEY, finalErrorType); result.put(SARIFConfig.MESSAGE_KEY, this.sarifHelper.getMessage(text, richText)); - result.put(SARIFConfig.LOCATIONS_KEY, this.sarifHelper.getLocations(c, methodName, lineNumber)); + result.put(SARIFConfig.LOCATIONS_KEY, this.sarifHelper.getLocations(c, methodName, lineNumber, method, statement)); this.results.add(result); } private JSONObject makeSARIF() { this.resources.put(SARIFConfig.RULES_KEY, this.rules); + JSONObject sarif = new JSONObject(); sarif.put(SARIFConfig.SARIF_VERSION, SARIFConfig.SARIF_VERSION_NUMBER); + JSONArray runs = new JSONArray(); JSONObject run = new JSONObject(); + run.put(SARIFConfig.TOOL_KEY, this.sarifHelper.getToolInfo()); + + if (includeStatistics()) { + run.put(SARIFConfig.STATISTICS_KEY, this.sarifHelper.getStatisticsInfo(getStatistics())); + } + run.put(SARIFConfig.FILES_KEY, this.files); run.put(SARIFConfig.RESULTS_KEY, this.results); run.put(SARIFConfig.RESOURCES_KEY, this.resources); runs.add(run); + sarif.put(SARIFConfig.RUNS_KEY, runs); + return sarif; } @Override - public void discoveredSeed(IAnalysisSeed object) { - this.objects.add(object); - } - - @Override - public void afterAnalysis() { + public void handleAnalysisResults() { for (SootClass c : this.errorMarkers.rowKeySet()) { addFile(c); @@ -141,18 +144,24 @@ public void afterAnalysis() { marker.getClass().getSimpleName(), marker.getRule().getClassName()); String text = String.format("%s.", marker.toErrorMarkerString()); int lineNumber = marker.getErrorLocation().getUnit().get().getJavaSourceStartLineNumber(); - this.addResults(errorType, c, e.getKey().getName(), lineNumber, text, richText); + String method = e.getKey().getSubSignature(); + String statement = marker.getErrorLocation().getUnit().get().toString(); + this.addResults(errorType, c, e.getKey().getName(), lineNumber, method, statement, text, richText); } } } + JSONObject sarif = makeSARIF(); + try { ObjectMapper mapper = new ObjectMapper(); ObjectWriter writer = mapper.writer(new DefaultPrettyPrinter()); - writer.writeValue(Paths.get(outputFolder + File.separator + REPORT_NAME).toFile(), sarif); - LOGGER.info("SARIF Report generated to file : "+ outputFolder + File.separator + REPORT_NAME); + writer.writeValue(Paths.get(getOutputFolder() + File.separator + REPORT_NAME).toFile(), sarif); + + LOGGER.info("SARIF Report generated to file : " + getOutputFolder() + File.separator + REPORT_NAME); } catch (IOException e) { - LOGGER.error("Could not write to file: "+outputFolder.getAbsolutePath() + File.separator+ REPORT_NAME, e); + LOGGER.error("Could not write to file: " + getOutputFolder().getAbsolutePath() + File.separator + REPORT_NAME, e); } } + } diff --git a/CryptoAnalysis/src/main/java/crypto/reporting/TXTReporter.java b/CryptoAnalysis/src/main/java/crypto/reporting/TXTReporter.java index 116984fc3..b741e5afb 100644 --- a/CryptoAnalysis/src/main/java/crypto/reporting/TXTReporter.java +++ b/CryptoAnalysis/src/main/java/crypto/reporting/TXTReporter.java @@ -6,23 +6,19 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; -import java.util.Collection; -import java.util.HashSet; import java.util.List; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import crypto.analysis.IAnalysisSeed; import crypto.rules.CrySLRule; import soot.Printer; import soot.SootClass; import soot.util.EscapedWriter; -public class TXTReporter extends ErrorMarkerListener{ +public class TXTReporter extends Reporter { - private File outputFolder; - private List rules; - private Collection objects = new HashSet<>(); private static final Logger LOG = LoggerFactory.getLogger(TXTReporter.class); + /** * The report of the analysis */ @@ -35,38 +31,40 @@ public class TXTReporter extends ErrorMarkerListener{ /** * Creates {@link TXTReporter} a constructor with reportDir and rules as parameter * - * @param reportDir a {@link String} path giving the location of the report directory + * @param reportDir A {@link String} path giving the location of the report directory * @param rules {@link CrySLRule} the rules with which the project is analyzed */ - public TXTReporter(String reportDir, List rules) { - this.outputFolder = (reportDir != null ? new File(reportDir) : new File(System.getProperty("user.dir"))); - this.rules = rules; + public TXTReporter(String reportDir, String softwareID, List rules, long callgraphConstructionTime, boolean includeStatistics) { + super((reportDir != null ? new File(reportDir) : new File(System.getProperty("user.dir"))), softwareID, rules, callgraphConstructionTime, includeStatistics); } @Override - public void discoveredSeed(IAnalysisSeed object) { - this.objects.add(object); - } - @Override - public void afterAnalysis() { - this.analysisReport = ReporterHelper.generateReport(rules, objects, this.secureObjects, this.errorMarkers, this.errorMarkerCount); + public void handleAnalysisResults() { + if (includeStatistics()) { + this.analysisReport = ReporterHelper.generateReport(getRules(), getObjects(), this.secureObjects, this.errorMarkers, this.errorMarkerCount, getStatistics()); + } else { + this.analysisReport = ReporterHelper.generateReport(getRules(), getObjects(), this.secureObjects, this.errorMarkers, this.errorMarkerCount, null); + } try { - FileWriter writer = new FileWriter(outputFolder + File.separator + REPORT_NAME); + FileWriter writer = new FileWriter(getOutputFolder() + File.separator + REPORT_NAME); writer.write(this.analysisReport); writer.close(); + for (SootClass c : this.errorMarkers.rowKeySet()) { - FileOutputStream streamOut = new FileOutputStream(new File(outputFolder + File.separator +c.toString()+".jimple")); + FileOutputStream streamOut = new FileOutputStream(new File(getOutputFolder() + File.separator + c.toString() + ".jimple")); PrintWriter writerOut = new PrintWriter(new EscapedWriter(new OutputStreamWriter(streamOut))); Printer.v().printTo(c, writerOut); + writerOut.flush(); streamOut.close(); writerOut.close(); } - LOG.info("Text Report generated to file : "+ outputFolder.getAbsolutePath() + File.separator + REPORT_NAME); + + LOG.info("Text Report generated to file : "+ getOutputFolder().getAbsolutePath() + File.separator + REPORT_NAME); } catch (IOException e) { - LOG.error("Could not write to file " + outputFolder.getAbsolutePath() + File.separator+ REPORT_NAME, e); + LOG.error("Could not write to file " + getOutputFolder().getAbsolutePath() + File.separator+ REPORT_NAME, e); } - } + } From 499ec5f1f1eda493c3a3597dea47b712774c882f Mon Sep 17 00:00:00 2001 From: smeyer198 Date: Fri, 13 Jan 2023 13:16:28 +0100 Subject: [PATCH 09/15] Update and add tests for reporter changes --- .../java/crypto/HeadlessCryptoScanner.java | 17 ++--- .../tests/headless/AbstractHeadlessTest.java | 20 ++++-- .../java/tests/headless/ReportFormatTest.java | 66 ++++++++++++++++++- 3 files changed, 83 insertions(+), 20 deletions(-) diff --git a/CryptoAnalysis/src/main/java/crypto/HeadlessCryptoScanner.java b/CryptoAnalysis/src/main/java/crypto/HeadlessCryptoScanner.java index 3dd703fd7..7596939b5 100644 --- a/CryptoAnalysis/src/main/java/crypto/HeadlessCryptoScanner.java +++ b/CryptoAnalysis/src/main/java/crypto/HeadlessCryptoScanner.java @@ -1,7 +1,6 @@ package crypto; import java.io.File; -import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; @@ -31,7 +30,7 @@ import crypto.reporting.CSVReporter; import crypto.reporting.CSVSummaryReporter; import crypto.reporting.CommandLineReporter; -import crypto.reporting.ErrorMarkerListener; +import crypto.reporting.Reporter; import crypto.reporting.SARIFReporter; import crypto.reporting.TXTReporter; import crypto.rules.CrySLRule; @@ -165,7 +164,7 @@ private void analyse() { } public String toString() { - String s = "HeadllessCryptoScanner: \n"; + String s = "HeadlessCryptoScanner: \n"; s += "\tSoftwareIdentifier: " + softwareIdentifier() + "\n"; s += "\tApplicationClassPath: " + applicationClassPath() + "\n"; s += "\tSootClassPath: " + sootClassPath() + "\n\n"; @@ -185,8 +184,8 @@ protected void internalTransform(String phaseName, Map options) long callgraphConstructionTime = callGraphWatch.elapsed(TimeUnit.MILLISECONDS); final CrySLResultsReporter reporter = new CrySLResultsReporter(); - ErrorMarkerListener fileReporter; - + Reporter fileReporter; + Set formats = reportFormats(); if (formats.size() > 0) { @@ -227,8 +226,6 @@ protected void internalTransform(String phaseName, Map options) reporter.addReportListener(getAdditionalListener()); } - //reporter.addReportListener(fileReporter); - CryptoScanner scanner = new CryptoScanner() { @Override @@ -257,8 +254,6 @@ public Debugger debugger(IDEALSeedSolver } }; - //reporter.addReportListener(fileReporter); - if (providerDetection()) { ProviderDetection providerDetection = new ProviderDetection(); @@ -403,10 +398,6 @@ protected boolean enableVisualization(){ return settings.isVisualization(); } - protected ReportFormat reportFormat() { - return null; - } - protected Set reportFormats() { return settings.getReportFormats(); } diff --git a/CryptoAnalysis/src/test/java/tests/headless/AbstractHeadlessTest.java b/CryptoAnalysis/src/test/java/tests/headless/AbstractHeadlessTest.java index e5697e9d1..f93efb5ba 100644 --- a/CryptoAnalysis/src/test/java/tests/headless/AbstractHeadlessTest.java +++ b/CryptoAnalysis/src/test/java/tests/headless/AbstractHeadlessTest.java @@ -2,6 +2,7 @@ import java.io.File; import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.Set; import org.junit.Before; @@ -56,10 +57,21 @@ public abstract class AbstractHeadlessTest { private static boolean PROVIDER_DETECTION = true; private CrySLAnalysisListener errorCountingAnalysisListener; private Table, Integer> errorMarkerCountPerErrorTypeAndMethod = HashBasedTable.create(); - private static ReportFormat reportFormat = null; + private static Set reportFormats = new HashSet<>(); public static void setReportFormat(ReportFormat reportFormat) { - AbstractHeadlessTest.reportFormat = reportFormat; + // use this method to add exactly one report format + AbstractHeadlessTest.reportFormats.clear(); + AbstractHeadlessTest.reportFormats.add(reportFormat); + } + + public static void setReportFormat(ReportFormat ...formats) { + // use this method to add multiple report formats + AbstractHeadlessTest.reportFormats.clear(); + + for (ReportFormat format : formats) { + AbstractHeadlessTest.reportFormats.add(format); + } } public static void setVISUALIZATION(boolean vISUALIZATION) { @@ -129,8 +141,8 @@ protected boolean providerDetection() { } @Override - protected ReportFormat reportFormat(){ - return VISUALIZATION ? reportFormat : null; + protected Set reportFormats(){ + return VISUALIZATION ? reportFormats : new HashSet<>(); } }; return scanner; diff --git a/CryptoAnalysis/src/test/java/tests/headless/ReportFormatTest.java b/CryptoAnalysis/src/test/java/tests/headless/ReportFormatTest.java index 4b7a82dd2..607c8afb8 100644 --- a/CryptoAnalysis/src/test/java/tests/headless/ReportFormatTest.java +++ b/CryptoAnalysis/src/test/java/tests/headless/ReportFormatTest.java @@ -13,9 +13,10 @@ public class ReportFormatTest extends AbstractHeadlessTest{ private static final String rootPath = "cognicrypt-output/"; - private static final String txtReportPath = rootPath+"CryptoAnalysis-Report.txt"; - private static final String csvReportPath = rootPath+"CryptoAnalysis-Report.csv"; - private static final String sarifReportPath = rootPath+"CryptoAnalysis-Report.json"; + private static final String txtReportPath = rootPath + "CryptoAnalysis-Report.txt"; + private static final String csvReportPath = rootPath + "CryptoAnalysis-Report.csv"; + private static final String csvSummaryReportPath = rootPath + "CryptoAnalysis-Report-Summary.csv"; + private static final String sarifReportPath = rootPath + "CryptoAnalysis-Report.json"; @Test public void TXTReportCreationTest() { @@ -47,6 +48,24 @@ public void CSVReportCreationTest() { Assert.assertTrue(report.exists()); } + @Test + public void CSVSummaryCreationTest() { + File report = new File(csvSummaryReportPath); + + if (report.exists()) { + report.delete(); + } + + String mavenProjectPath = new File("../CryptoAnalysisTargets/ReportFormatExample").getAbsolutePath(); + MavenProject mavenProject = createAndCompile(mavenProjectPath); + setReportFormat(ReportFormat.CSV_SUMMARY); + setVISUALIZATION(true); + + HeadlessCryptoScanner scanner = createScanner(mavenProject); + scanner.exec(); + Assert.assertTrue(report.exists()); + } + @Test public void SARIFReportCreationTest() { File report = new File(sarifReportPath); @@ -62,6 +81,47 @@ public void SARIFReportCreationTest() { Assert.assertTrue(report.exists()); } + @Test + public void multipleFormatsCreationTest() { + File txtReport = new File(txtReportPath); + + if (txtReport.exists()) { + txtReport.delete(); + } + + File csvReport = new File(csvReportPath); + + if (csvReport.exists()) { + csvReport.delete(); + } + + File csvSummaryReport = new File(csvSummaryReportPath); + + if (csvSummaryReport.exists()) { + csvSummaryReport.delete(); + } + + File sarifReport = new File(sarifReportPath); + + if (sarifReport.exists()) { + sarifReport.delete(); + } + + String mavenProjectPath = new File("../CryptoAnalysisTargets/ReportFormatExample").getAbsolutePath(); + MavenProject mavenProject = createAndCompile(mavenProjectPath); + + setReportFormat(ReportFormat.CMD, ReportFormat.TXT, ReportFormat.CSV, ReportFormat.CSV_SUMMARY, ReportFormat.SARIF); + setVISUALIZATION(true); + + HeadlessCryptoScanner scanner = createScanner(mavenProject); + scanner.exec(); + + Assert.assertTrue(txtReport.exists()); + Assert.assertTrue(csvReport.exists()); + Assert.assertTrue(csvSummaryReport.exists()); + Assert.assertTrue(sarifReport.exists()); + } + @After public void tearDown() { try { From c7cc6a51402216e3edd15b74fcca84e77bd316ff Mon Sep 17 00:00:00 2001 From: smeyer198 Date: Wed, 18 Jan 2023 10:28:10 +0100 Subject: [PATCH 10/15] Add docs to the refactored reporter parts --- .../java/crypto/HeadlessCryptoScanner.java | 2 +- .../analysis/CryptoScannerSettings.java | 14 +++++- .../java/crypto/reporting/CSVReporter.java | 47 ++++++++++++++----- .../crypto/reporting/CSVSummaryReporter.java | 30 +++++++----- .../crypto/reporting/CommandLineReporter.java | 24 ++++++---- .../crypto/reporting/ReportStatistics.java | 20 +++++++- .../main/java/crypto/reporting/Reporter.java | 34 ++++++++++++++ .../java/crypto/reporting/ReporterHelper.java | 30 +++++++----- .../java/crypto/reporting/SARIFConfig.java | 1 + .../java/crypto/reporting/SARIFReporter.java | 25 ++++++---- .../java/crypto/reporting/TXTReporter.java | 25 ++++++---- 11 files changed, 188 insertions(+), 64 deletions(-) diff --git a/CryptoAnalysis/src/main/java/crypto/HeadlessCryptoScanner.java b/CryptoAnalysis/src/main/java/crypto/HeadlessCryptoScanner.java index 7596939b5..97858c7ec 100644 --- a/CryptoAnalysis/src/main/java/crypto/HeadlessCryptoScanner.java +++ b/CryptoAnalysis/src/main/java/crypto/HeadlessCryptoScanner.java @@ -217,7 +217,7 @@ protected void internalTransform(String phaseName, Map options) } } } else { - // default to command line reporter + // if the --reportformat flag is not set or no format is specified, use the command line reporter as default fileReporter = new CommandLineReporter(softwareIdentifier(), rules, callgraphConstructionTime, includeStatistics()); reporter.addReportListener(fileReporter); } diff --git a/CryptoAnalysis/src/main/java/crypto/analysis/CryptoScannerSettings.java b/CryptoAnalysis/src/main/java/crypto/analysis/CryptoScannerSettings.java index 40c1b8307..2c60c3a09 100644 --- a/CryptoAnalysis/src/main/java/crypto/analysis/CryptoScannerSettings.java +++ b/CryptoAnalysis/src/main/java/crypto/analysis/CryptoScannerSettings.java @@ -239,13 +239,25 @@ private void parseControlGraphValue(String value) throws CryptoAnalysisParserExc } } + /** + * This method parses the specified report formats and returns the number of given report formats until + * the next flag from the command line is found (e.g. "--reportformat CMD TXT CSV --" will store + * the formats CMD, TXT and CSV and return the value 3). + * + * @param settings The command line input. + * @param position The position of the --reportformat flag in the command line input + * @return numFormats The number of specified formats. If a format is given twice, it is also + * counted twice. + * @throws CryptoAnalysisParserException if a reportformat value is not supported + */ + private int parseReportFormatValues(String[] settings, int position) throws CryptoAnalysisParserException { // settings should be the command line input and position the index of --reportFormat int numFormats = 0; for (int i = position + 1; i < settings.length; i++) { - // new argument + // new argument is specified if (settings[i].startsWith("-")) { break; } diff --git a/CryptoAnalysis/src/main/java/crypto/reporting/CSVReporter.java b/CryptoAnalysis/src/main/java/crypto/reporting/CSVReporter.java index 9d9f8df46..a510a30b0 100644 --- a/CryptoAnalysis/src/main/java/crypto/reporting/CSVReporter.java +++ b/CryptoAnalysis/src/main/java/crypto/reporting/CSVReporter.java @@ -19,6 +19,15 @@ import soot.SootClass; import soot.SootMethod; +/** + * This class extends the class {@link Reporter} by generating an analysis report and write it into a + * csv file. + * + * Compared to the {@link CSVSummaryReporter}, this reporter writes each error from the analysis into + * a single line. If the statistics are enabled, each line is extended by the corresponding statistic + * fields. Since the statistics are computed for the whole analysis, each value for the different fields + * are the same in all lines. + */ public class CSVReporter extends Reporter { private static final Logger LOGGER = LoggerFactory.getLogger(CSVReporter.class); @@ -29,15 +38,33 @@ public class CSVReporter extends Reporter { private List headers; private List contents; + /** Headers for the errors. These headers are always part of the analysis report. */ private enum Headers { ErrorID, ErrorType, ViolatingClass, Class, Method, LineNumber, Statement, Message } + /** + * Headers for the statistics. These headers are only part of the analysis report, if + * the corresponding parameter in the constructor is set to true. + */ private enum StatisticHeaders { SoftwareID, SeedObjectCount, CryptoAnalysisTime_ms, CallGraphTime_ms, CallGraphReachableMethods, CallGraphReachableMethods_ActiveBodies, DataflowVisitedMethod } + /** + * Subclass of {@link Reporter}. Creates an instance of {@link CSVReporter}, which + * can be used to create a csv file containing the analysis report. + * + * @param reportDir A {@link String} path giving the location of the report directory. + * The reportPath should end without an ending file separator. + * @param softwareID A {@link String} for the analyzed software. + * @param rules A {@link List} of {@link CrySLRule} containing the rules the program is analyzed with. + * @param callgraphConstructionTime The time in milliseconds for the construction of the callgraph. + * @param includeStatistics Set this value to true, if the analysis report should contain some + * analysis statistics (e.g. the callgraph construction time). If this value is set + * to false, no statistics will be output. + */ public CSVReporter(String reportDir, String softwareId, List rules, long callGraphConstructionTime, boolean includeStatistics) { super((reportDir != null ? new File(reportDir) : new File(System.getProperty("user.dir"))), softwareId, rules, callGraphConstructionTime, includeStatistics); @@ -75,6 +102,14 @@ public void handleAnalysisResults() { String line = idCount + CSV_SEPARATOR + errorType + CSV_SEPARATOR + violatingClass + CSV_SEPARATOR + className + CSV_SEPARATOR + methodName + CSV_SEPARATOR + lineNumber + CSV_SEPARATOR + statement + CSV_SEPARATOR + errorMessage; + // Add the statistics to every single line of the report + if (includeStatistics()) { + line += CSV_SEPARATOR + getStatistics().getSoftwareID() + CSV_SEPARATOR + getStatistics().getSeedObjectCount() + CSV_SEPARATOR + + getStatistics().getAnalysisTime() + CSV_SEPARATOR + getStatistics().getCallgraphTime() + + CSV_SEPARATOR + getStatistics().getCallgraphReachableMethods() + CSV_SEPARATOR + getStatistics().getCallgraphReachableMethodsWithActiveBodies() + + CSV_SEPARATOR + getStatistics().getDataflowVisitedMethods(); + } + contents.add(line); idCount++; @@ -97,18 +132,6 @@ private void writeToFile() { writer.write(line + "\n"); } - if (includeStatistics()) { - // Additional analysis statistics - writer.write("\nAdditional analysis statistics:\n"); - writer.write(String.format("SoftwareID: %s\n", statistics.getSoftwareID())); - writer.write(String.format("SeedObjectCount: %d\n", statistics.getSeedObjectCount())); - writer.write(String.format("CryptoAnalysisTime: %d\n", statistics.getAnalysisTime())); - writer.write(String.format("CallgraphConstructionTime: %d\n", statistics.getCallgraphTime())); - writer.write(String.format("CallgraphReachableMethods: %d\n", statistics.getCallgraphReachableMethods())); - writer.write(String.format("CallgraphReachableMethodsWithActiveBodies: %d\n", statistics.getCallgraphReachableMethodsWithActiveBodies())); - writer.write(String.format("DataflowVisitedMethods: %d\n", statistics.getDataflowVisitedMethods())); - } - writer.close(); LOGGER.info("CSV Report generated to file : " + getOutputFolder().getAbsolutePath() + File.separator+ REPORT_NAME); } catch (IOException e) { diff --git a/CryptoAnalysis/src/main/java/crypto/reporting/CSVSummaryReporter.java b/CryptoAnalysis/src/main/java/crypto/reporting/CSVSummaryReporter.java index b08eb59d7..abb5b9422 100644 --- a/CryptoAnalysis/src/main/java/crypto/reporting/CSVSummaryReporter.java +++ b/CryptoAnalysis/src/main/java/crypto/reporting/CSVSummaryReporter.java @@ -27,6 +27,12 @@ import crypto.analysis.errors.TypestateError; import crypto.rules.CrySLRule; +/** + * This class extends the class {@link Reporter} by generating a summary of the analysis and write it into a + * csv file. Compared to the {@link CSVReporter} this reporter will not output any information about the concrete + * errors found in the analysis. The summary will only contain the number of error types and in which classes from + * the rule set the errors were found. + */ public class CSVSummaryReporter extends Reporter { private static final Logger LOGGER = LoggerFactory.getLogger(CSVSummaryReporter.class); @@ -36,25 +42,27 @@ public class CSVSummaryReporter extends Reporter { private List headers = Lists.newArrayList(); private Map headersToValues = Maps.newHashMap(); - /** - * name of the analysis report - */ + /** Name of the analysis report */ private static final String REPORT_NAME = "CryptoAnalysis-Report-Summary.csv"; - /** - * the headers of CSV report - */ + + /** The headers of CSV report */ private enum Headers { SoftwareID, SeedObjectCount, CryptoAnalysisTime_ms, CallGraphTime_ms, CallGraphReachableMethods, CallGraphReachableMethods_ActiveBodies, DataflowVisitedMethod } /** - * Creates {@link CSVSummaryReporter} a constructor with reportDir, softwareId, rules and callGraphConstructionTime as parameter + * Subclass of {@link Reporter}. Creates an instance of {@link CSVSummaryReporter}, which + * can be used to create a csv file containing a summary of the analysis. * - * @param reportDir a {@link String} path giving the location of the report directory - * @param softwareId {@link Format} An identifier used to label output files in CSV report format - * @param rules {@link CrySLRule} the rules with which the project is analyzed - * @param callGraphConstructionTime {@link long} call graph construction time in ms + * @param reportDir A {@link String} path giving the location of the report directory. + * The reportPath should end without an ending file separator. + * @param softwareID A {@link String} for the analyzed software. + * @param rules A {@link List} of {@link CrySLRule} containing the rules the program is analyzed with. + * @param callgraphConstructionTime The time in milliseconds for the construction of the callgraph. + * @param includeStatistics Set this value to true, if the analysis report should contain some + * analysis statistics (e.g. the callgraph construction time). If this value is set + * to false, no statistics will be output. */ public CSVSummaryReporter(String reportDir, String softwareId, List rules, long callGraphConstructionTime, boolean includeStatistics) { super((reportDir != null ? new File(reportDir) : new File(System.getProperty("user.dir"))), softwareId, rules, callGraphConstructionTime, includeStatistics); diff --git a/CryptoAnalysis/src/main/java/crypto/reporting/CommandLineReporter.java b/CryptoAnalysis/src/main/java/crypto/reporting/CommandLineReporter.java index c52eccacf..bad0f2d54 100644 --- a/CryptoAnalysis/src/main/java/crypto/reporting/CommandLineReporter.java +++ b/CryptoAnalysis/src/main/java/crypto/reporting/CommandLineReporter.java @@ -4,15 +4,16 @@ import java.util.List; import crypto.rules.CrySLRule; +/** + * This class extends the class {@link Reporter} by generating an analysis report and print it to the command line. + */ public class CommandLineReporter extends Reporter { - /** - * The analysis report - */ + /**The analysis report */ private String analysisReport; /** - * Creates {@link CommandLineReporter} a constructor with reportDir and rules as parameter + * Subclass of {@link Reporter}. Creates an instance of {@link CommandLineReporter} with reportDir and rules as parameter * * @param reportDir a {@link String} path giving the location of the report directory * @param rules {@link CrySLRule} the rules with which the project is analyzed @@ -22,12 +23,17 @@ public CommandLineReporter(String reportDir, List rules) { } /** - * Creates {@link CommandLineReporter} a constructor with the softwareID, the rules and the - * callgraph construction time as parameter + * Subclass of {@link Reporter}. Creates an instance of {@link CommandLineReporter}, which + * can be used to print an analysis report to stdout. * - * @param softwareID Identifier for the software - * @param rules {@link CrySLRule} the rules with which the project is analyzed - * @param callgraphConstructionTime Time for the callgraph construction in milliseconds + * @param reportDir A {@link String} path giving the location of the report directory. + * The reportPath should end without an ending file separator. + * @param softwareID A {@link String} for the analyzed software. + * @param rules A {@link List} of {@link CrySLRule} containing the rules the program is analyzed with. + * @param callgraphConstructionTime The time in milliseconds for the construction of the callgraph. + * @param includeStatistics Set this value to true, if the analysis report should contain some + * analysis statistics (e.g. the callgraph construction time). If this value is set + * to false, no statistics will be output. */ public CommandLineReporter(String softwareID, List rules, long callgraphConstructionTime, boolean includeStatistics) { super(null, softwareID, rules, callgraphConstructionTime, includeStatistics); diff --git a/CryptoAnalysis/src/main/java/crypto/reporting/ReportStatistics.java b/CryptoAnalysis/src/main/java/crypto/reporting/ReportStatistics.java index 694b5345a..e014c5f4c 100644 --- a/CryptoAnalysis/src/main/java/crypto/reporting/ReportStatistics.java +++ b/CryptoAnalysis/src/main/java/crypto/reporting/ReportStatistics.java @@ -1,5 +1,18 @@ package crypto.reporting; +/** + * This class is used by the class {@link Reporter} to store all statistics, which are relevant for the analysis. + * + * Currently the following statistics are supported: + * - softwareID: Identifier of the analyzed software. This value can be set by using the --identifier flag. + * - seedObjectCount: Number of seed objects. + * - analysisTime: The time in milliseconds for the actual analysis (e.g. without the initialization of the analysis + * and the construction of the callgraph) + * - callgraphTime: The time in milliseconds to construct the callgraph. + * - callgraphReachableMethods: The number of reachable methods in the callgraph. + * - callgraphReachableMethodsWithActiveBodies: The number of reachable methods with active bodies in the callgraph. + * - dataflowVisitedMethods: The number of visited methods in the dataflows. + */ public class ReportStatistics { private String softwareID; @@ -10,8 +23,13 @@ public class ReportStatistics { private int callgraphReachableMethodsWithActiveBodies; private int dataflowVisitedMethods; + /** + * Creates an instance to store all relevant statistics for an analysis. The softwareID is initialized with + * an empty string and all numeric variables are initialized with -1. The corresponding set methods should be + * used to update the statistic values. + */ public ReportStatistics() { - this.softwareID = "Unknown"; + this.softwareID = ""; this.seedObjectCount = -1; this.analysisTime = -1; this.callgraphTime = -1; diff --git a/CryptoAnalysis/src/main/java/crypto/reporting/Reporter.java b/CryptoAnalysis/src/main/java/crypto/reporting/Reporter.java index 86bbb48eb..adb25eeed 100644 --- a/CryptoAnalysis/src/main/java/crypto/reporting/Reporter.java +++ b/CryptoAnalysis/src/main/java/crypto/reporting/Reporter.java @@ -19,17 +19,46 @@ import soot.util.queue.QueueReader; import typestate.TransitionFunction; +/** + * Superclass for all reporters. + * + * This class is used to define and implement the basic parts, which all reporter should be able to support. This includes + * the computation of all statistics for the analysis and the basic logic for methods defined in the {@link ICrySLResultsListener}. + * + * This class is abstract. Subclasses have to call the constructor and overwrite the method handleAnalysisResults(), which is called + * after the analysis is finished. + */ public abstract class Reporter extends ErrorMarkerListener { private File outputFolder; private List rules; private boolean includeStatistics; + /** An instance of {@link ReportStatistics} to store all relevant analysis statistics */ protected final ReportStatistics statistics = new ReportStatistics(); + + /** The stopwatch to measure to time for the actual analysis */ protected final Stopwatch analysisWatch = Stopwatch.createUnstarted(); + + /** A {@link Collection} to store and count all analyzed objects */ protected final Collection objects = new HashSet<>(); + + /** A {@link Set} to store and count all reachable methods in the dataflow */ protected final Set dataflowReachableMethods = new HashSet<>(); + /** + * The constructor to initialize all attributes. Since this class is abstract, all subclasses + * have to call this constructor. + * + * @param reportDir A {@link String} path giving the location of the report directory. + * The reportPath should end without an ending file separator. + * @param softwareID A {@link String} for the analyzed software. + * @param rules A {@link List} of {@link CrySLRule} containing the rules the program is analyzed with. + * @param callgraphConstructionTime The time in milliseconds for the construction of the callgraph. + * @param includeStatistics Set this value to true, if the analysis report should contain some + * analysis statistics (e.g. the callgraph construction time). If this value is set + * to false, no statistics will be output. + */ public Reporter(File outputFolder, String softwareID, List rules, long callgraphConstructionTime, boolean includeStatistics) { this.outputFolder = outputFolder; this.rules = rules; @@ -104,6 +133,11 @@ public void afterAnalysis() { handleAnalysisResults(); } + /** + * This method is called after the analysis is finished and all statistics have been computed. A subclass + * can override this method to extend the actions after the analysis, e.g. creating an analysis report + * and write it into a file. + */ public abstract void handleAnalysisResults(); } diff --git a/CryptoAnalysis/src/main/java/crypto/reporting/ReporterHelper.java b/CryptoAnalysis/src/main/java/crypto/reporting/ReporterHelper.java index b31e3e5be..f7db834ea 100644 --- a/CryptoAnalysis/src/main/java/crypto/reporting/ReporterHelper.java +++ b/CryptoAnalysis/src/main/java/crypto/reporting/ReporterHelper.java @@ -15,17 +15,25 @@ import soot.SootClass; import soot.SootMethod; -public class ReporterHelper{ +/** + * This class is used to generate a report as a {@link String} for multiple other classes. This class is + * used by the {@link CommandLineReporter} and {@link TXTReporter}. + */ +public class ReporterHelper { - /** Generates analysis report content for {@link CommandLineReporter} CommandLineReporter and {@link TXTReporter} TXTReporter - * @param rules a {@link List} with {@link CrySLRule} rules - * @param objects a{@link Collection} with {@link IAnalysisSeed} objects - * @param secureObjects a {@link List} with {@link IAnalysisSeed} secureObjects - * @param errorMarkers a {@link Table} containing {@link SootClass},{@link SootMethod} - * and a {@link Set} of {@link AbstractError} of the errors found during analysis - * @param errorMarkerCount a {@link Map} containing {@link Class} class of error and - * {@link Integer} number of errors - * @return report {@link String} of the analysis + /** Generates an analysis report content for the {@link CommandLineReporter} and {@link TXTReporter}. + * + * @param rules A {@link List} with {@link CrySLRule} rules, which were used in the analysis + * @param objects A {@link Collection} with {@link IAnalysisSeed} objects + * @param secureObjects A {@link List} with {@link IAnalysisSeed} secureObjects + * @param errorMarkers A {@link Table} containing {@link SootClass}, {@link SootMethod} + * and a {@link Set} of {@link AbstractError} of the errors found during analysis + * @param errorMarkerCount A {@link Map} containing {@link Class} class of error and + * {@link Integer} number of errors + * @param statistics An instance of the class {@link ReportStatistics}, which holds all relevant statistics + * for the analysis. If no statistics should be included in the analysis, the value null + * should be passed. + * @return report The formatted analysis report as {@link String} */ public static String generateReport(List rules, Collection objects, List secureObjects, Table> errorMarkers, @@ -90,8 +98,8 @@ public static String generateReport(List rules, Collection errorCountMap; - /** - * name of the analysis report - */ + /** name of the analysis report */ private static final String REPORT_NAME = "CryptoAnalysis-Report.json"; + /** + * Subclass of {@link Reporter}. Creates an instance of {@link SARIFReporter}, which + * can be used to create a json file containing the analysis report in the SARIF format. + * + * @param reportDir A {@link String} path giving the location of the report directory. + * The reportPath should end without an ending file separator. + * @param softwareID A {@link String} for the analyzed software. + * @param rules A {@link List} of {@link CrySLRule} containing the rules the program is analyzed with. + * @param callgraphConstructionTime The time in milliseconds for the construction of the callgraph. + * @param includeStatistics Set this value to true, if the analysis report should contain some + * analysis statistics (e.g. the callgraph construction time). If this value is set + * to false, no statistics will be output. + */ public SARIFReporter(String reportDir, String softwareId, List rules, long callgraphConstructionTime, boolean includeStatistics) { super((reportDir != null ? new File(reportDir) : new File(System.getProperty("user.dir"))), softwareId, rules, callgraphConstructionTime, includeStatistics); diff --git a/CryptoAnalysis/src/main/java/crypto/reporting/TXTReporter.java b/CryptoAnalysis/src/main/java/crypto/reporting/TXTReporter.java index b741e5afb..4a8d71996 100644 --- a/CryptoAnalysis/src/main/java/crypto/reporting/TXTReporter.java +++ b/CryptoAnalysis/src/main/java/crypto/reporting/TXTReporter.java @@ -15,24 +15,31 @@ import soot.SootClass; import soot.util.EscapedWriter; +/** + * This class extends the class {@link Reporter} by generating a report and writing it into a text file. + */ public class TXTReporter extends Reporter { private static final Logger LOG = LoggerFactory.getLogger(TXTReporter.class); - /** - * The report of the analysis - */ + /** The report of the analysis */ private String analysisReport; - /** - * name of the analysis report - */ + + /** name of the analysis report */ private static final String REPORT_NAME = "CryptoAnalysis-Report.txt"; /** - * Creates {@link TXTReporter} a constructor with reportDir and rules as parameter + * Subclass of {@link Reporter}. Creates an instance of {@link TXTReporter}, which + * can be used to create a text file containing the analysis report. * - * @param reportDir A {@link String} path giving the location of the report directory - * @param rules {@link CrySLRule} the rules with which the project is analyzed + * @param reportDir A {@link String} path giving the location of the report directory. + * The reportPath should end without an ending file separator. + * @param softwareID A {@link String} for the analyzed software. + * @param rules A {@link List} of {@link CrySLRule} containing the rules the program is analyzed with. + * @param callgraphConstructionTime The time in milliseconds for the construction of the callgraph. + * @param includeStatistics Set this value to true, if the analysis report should contain some + * analysis statistics (e.g. the callgraph construction time). If this value is set + * to false, no statistics will be output. */ public TXTReporter(String reportDir, String softwareID, List rules, long callgraphConstructionTime, boolean includeStatistics) { super((reportDir != null ? new File(reportDir) : new File(System.getProperty("user.dir"))), softwareID, rules, callgraphConstructionTime, includeStatistics); From d344f40d85fbd3f71c346448aaaefd4ae9fb5d0f Mon Sep 17 00:00:00 2001 From: smeyer198 Date: Wed, 18 Jan 2023 14:13:25 +0100 Subject: [PATCH 11/15] Update README.md to include reporter changes --- README.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1cfac9e75..71194dd57 100644 --- a/README.md +++ b/README.md @@ -49,18 +49,19 @@ Other additional arguments that can be used are as follows: --cg (possible values are CHA, SPARK, SPARKLIB) --sootPath --identifier ---reportPath ---reportFormat (possible values are TXT, SARIF, CSV) +--reportPath +--reportFormat (possible values are CMD, TXT, SARIF, CSV, CSV_SUMMARY) --preanalysis (enables pre-analysis) --visualization (enables the visualization, but also requires --reportPath option to be set) --providerDetection (enables provider detection analysis) +--dstats (disable the output of the analysis statistics in the reports) ``` Note, depending on the analyzed application, the analysis may require a lot of memory and a large stack size. Remember to set the necessary heap size (e.g. -Xmx8g) and stack size (e.g. -Xss60m). ## Report and Error Types -In the standard option, CogniCryptSAST outputs a report to the console. CogniCryptSAST reporst misuses when the code is not compliant with the CrySL rules. For each misuse CogniCryptSAST reports the class and the method the misuse is contained in. There are multiple misuse types: +CogniCryptSAST reports misuses when the code is not compliant with the CrySL rules. For each misuse CogniCryptSAST reports the class and the method the misuse is contained in. There are multiple misuse types: * **ConstraintError**: A constraint of a CrySL rule is violated, e.g., a key is generated with the wrong key size. * **NeverTypeOfError**: Reported when a value was found to be of a certain reference type: For example, a character array containing a password should never be converted from a `String`. (see `KeyStore` rule [here](https://github.com/CROSSINGTUD/Crypto-API-Rules/blob/master/src/de/darmstadt/tu/crossing/KeyStore.cryptsl)). @@ -71,7 +72,14 @@ In the standard option, CogniCryptSAST outputs a report to the consol * **RequiredPredicateError**: An object A expects an object B to have been used correctly (CrySL blocks REQUIRES and ENSURES). For example a `Cipher` object requires a `SecretKey` object to be correctly and securely generated. * **IncompleteOperationError**: The usage of an object may be incomplete: For example a `Cipher`object may be initialized but never used for en- or decryption, this may render the code dead. This error heavily depends on the computed call graph (CHA by default). -When the option `--reportPath ` is chosen, CogniCryptSAST writes the report to the file `CogniCrypt-Report.txt` and additionally outputs the .jimple files of the classes where misuses where found in. Jimple is an intermediate representation close to the syntax of Java. +CogniCryptSAST supports different report formats, which can be set by using `--reportformat` option. The supported formats are: +- `CMD`: The report is printed to the command line. The content is equivalent to the format from the `TXT` option. +- `TXT`: The report is written to the text file `CryptoAnalysis-Report.txt`. The content is equivalent to the format from the `CMD` option. Additionally, the .jimple files of the classes, where misuses were found in, are output. Jimple is an intermediate representation close to the syntax of Java. +- `SARIF`: The report is written to the JSON file `CryptoAnalysis-Report.json`. The content is formatted in the SARIF format. +- `CSV`: The report is written to the CSV file `CryptoAnalysis-Report.csv`. The content is formatted in the CSV format. +- `CSV_SUMMARY`: The report is written to the file `CryptoAnalysis-Report-Summary.csv` and contains a summary of the analysis results. Compared to the `CSV` format, this format does not provide concrete information about the errors, it only lists the amount of each misuse type. This option was previously implemented by the `CSV` option, which has been changed to provide more detailed information about the errors in the CSV format. + +If the `--reportformat` option is not specified, CogniCryptSAST defaults to the `CMD` option. It also allows the usage of multiple different formats for the same analysis (e.g. `--reportformat CMD TXT CSV` creates a report, which is printed to the command line and is written to a text and CSV file). If the option `--reportPath ` is set, the reports are created in the specified directory. ## Updating CrySL Rules From baa6dff449f67271163419b50a186373d39fcb06 Mon Sep 17 00:00:00 2001 From: smeyer198 Date: Sun, 5 Nov 2023 21:40:14 +0100 Subject: [PATCH 12/15] Adapt repository URLs in pom files --- .gitignore | 1 + CryptoAnalysis-Android/pom.xml | 63 +++++++++++----------------------- CryptoAnalysis/pom.xml | 31 +++-------------- pom.xml | 29 +++------------- 4 files changed, 31 insertions(+), 93 deletions(-) diff --git a/.gitignore b/.gitignore index 0f048ac6b..9d6c3ee1b 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ bin/ tmp/ *.tmp +*.temp *.bak *.swp *~.nib diff --git a/CryptoAnalysis-Android/pom.xml b/CryptoAnalysis-Android/pom.xml index f2bc6aa01..a3e27ea75 100644 --- a/CryptoAnalysis-Android/pom.xml +++ b/CryptoAnalysis-Android/pom.xml @@ -8,12 +8,12 @@ de.fraunhofer.iem CryptoAnalysis-Parent - ${revision} + 2.8.0 ../pom.xml - 2.7.1 + 2.12.0 @@ -69,53 +69,30 @@ CryptoAnalysis - de.tud.sse - soot-infoflow - ${flowDroidVersion} - - - de.tud.sse - soot-infoflow-android - ${flowDroidVersion} - - - de.tud.sse - soot-infoflow-cmd - ${flowDroidVersion} - + de.fraunhofer.sit.sse.flowdroid + soot-infoflow + ${flowDroidVersion} + + + de.fraunhofer.sit.sse.flowdroid + soot-infoflow-summaries + ${flowDroidVersion} + + + de.fraunhofer.sit.sse.flowdroid + soot-infoflow-android + ${flowDroidVersion} + - soot-snapshot - Soot snapshot repository - https://soot-build.cs.uni-paderborn.de/nexus/repository/soot-snapshot/ - default + ossrh + https://s01.oss.sonatype.org/content/repositories/snapshots/ - true - soot-release - Soot release repository - https://soot-build.cs.uni-paderborn.de/nexus/repository/soot-release/ - default + ossrh + https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ - - - - soot-snapshot - soot snapshots - https://soot-build.cs.uni-paderborn.de/nexus/repository/soot-snapshot/ - - false - - - - - soot-release - soot release - https://soot-build.cs.uni-paderborn.de/nexus/repository/soot-release/ - - - diff --git a/CryptoAnalysis/pom.xml b/CryptoAnalysis/pom.xml index b21125486..ba24c3d68 100644 --- a/CryptoAnalysis/pom.xml +++ b/CryptoAnalysis/pom.xml @@ -8,7 +8,7 @@ de.fraunhofer.iem CryptoAnalysis-Parent - ${revision} + 2.8.0 ../pom.xml @@ -336,33 +336,12 @@ - soot-snapshot - Soot snapshot repository - https://soot-build.cs.uni-paderborn.de/nexus/repository/soot-snapshot/ - default + ossrh + https://s01.oss.sonatype.org/content/repositories/snapshots/ - true - soot-release - Soot release repository - https://soot-build.cs.uni-paderborn.de/nexus/repository/soot-release/ - default + ossrh + https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ - - - - soot-snapshot - soot snapshots - https://soot-build.cs.uni-paderborn.de/nexus/repository/soot-snapshot/ - - false - - - - soot-release - soot release - https://soot-build.cs.uni-paderborn.de/nexus/repository/soot-release/ - - diff --git a/pom.xml b/pom.xml index 71cbb346a..fa8a02214 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ de.fraunhofer.iem CryptoAnalysis-Parent - ${revision} + 2.8.0 pom CryptoAnalysis-parent @@ -17,7 +17,6 @@ - 2.8.0-SNAPSHOT UTF-8 3.3.0 @@ -88,32 +87,14 @@ - - - soot-snapshot - soot snapshot - https://soot-build.cs.uni-paderborn.de/nexus/repository/soot-snapshot/ - - false - - - - soot-release - soot release - https://soot-build.cs.uni-paderborn.de/nexus/repository/soot-release/ - - false - - - - soot-snapshot - https://soot-build.cs.uni-paderborn.de/nexus/repository/soot-snapshot/ + ossrh + https://s01.oss.sonatype.org/content/repositories/snapshots/ - soot-release - https://soot-build.cs.uni-paderborn.de/nexus/repository/soot-release/ + ossrh + https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ From f29b21804f878dba65036a8054a8ec417eb4682e Mon Sep 17 00:00:00 2001 From: smeyer198 Date: Mon, 6 Nov 2023 10:10:23 +0100 Subject: [PATCH 13/15] Update crysl version to 2.0.2 --- CryptoAnalysis/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CryptoAnalysis/pom.xml b/CryptoAnalysis/pom.xml index ba24c3d68..3ff6bae37 100644 --- a/CryptoAnalysis/pom.xml +++ b/CryptoAnalysis/pom.xml @@ -296,7 +296,7 @@ de.darmstadt.tu.crossing.CrySL de.darmstadt.tu.crossing.CrySL - 2.0.1 + 2.0.2 org.eclipse.xtext From be02985d14f40b778e2dcb0940001bb55e667c38 Mon Sep 17 00:00:00 2001 From: smeyer198 Date: Mon, 6 Nov 2023 14:25:51 +0100 Subject: [PATCH 14/15] Add deployment profile and workflow --- .github/workflows/deploy.yml | 34 +++++++++++++++++++ pom.xml | 65 ++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 .github/workflows/deploy.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 000000000..42e4e1204 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,34 @@ +name: Deploy CryptoAnalysis + +on: [workflow_dispatch] + +jobs: + deployment: + runs-on: ubuntu-latest + name: CryptoAnalysis deployment + steps: + - name: Checkout source code + uses: actions/checkout@v3 + # Sets up Java version + - name: Set up Java + uses: actions/setup-java@v3 + with: + distribution: 'adopt' + java-package: 'jdk' + java-version: '8' + server-id: 'ossrh' # must match the serverId configured for the nexus-staging-maven-plugin + server-username: OSSRH_USERNAME # Env var that holds your OSSRH user name + server-password: OSSRH_PASSWORD # Env var that holds your OSSRH user pw + gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} # Substituted with the value stored in the referenced secret + gpg-passphrase: SIGN_KEY_PASS # Env var that holds the key's passphrase + # Sets up Maven version + - name: Set up Maven + uses: stCarolas/setup-maven@v4.5 + with: + maven-version: 3.6.3 + - name: Build & Deploy CryptoAnalysis + run: mvn -B -U clean deploy -Pdeployment -DskipTests + env: + SIGN_KEY_PASS: ${{ secrets.GPG_PRIVATE_KEY_PASSPHRASE }} + OSSRH_USERNAME: ${{ secrets.SONATYPE_USER }} + OSSRH_PASSWORD: ${{ secrets.SONATYPE_PW }} \ No newline at end of file diff --git a/pom.xml b/pom.xml index fa8a02214..e13ca729d 100644 --- a/pom.xml +++ b/pom.xml @@ -21,6 +21,71 @@ 3.3.0 + + + + deployment + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.13 + true + + ossrh + https://s01.oss.sonatype.org + true + + + + org.apache.maven.plugins + maven-gpg-plugin + 3.0.1 + + + sign-artifacts + verify + + sign + + + + --pinentry-mode + loopback + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.6.0 + + + attach-javadoc + + jar + + + + + + org.apache.maven.plugins + maven-release-plugin + 3.0.1 + + @{project.version} + + + + + + + From a0a0278f6c15484e64d80cdb04fc81be3fd2e38d Mon Sep 17 00:00:00 2001 From: smeyer198 Date: Mon, 6 Nov 2023 14:33:39 +0100 Subject: [PATCH 15/15] Add fields for Sonatype server --- pom.xml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/pom.xml b/pom.xml index e13ca729d..da2e4d3f7 100644 --- a/pom.xml +++ b/pom.xml @@ -11,6 +11,28 @@ pom CryptoAnalysis-parent + + + Eclipse Public License - v2.0 + https://www.eclipse.org/legal/epl-2.0/ + + + + + CogniCrypt + CogniCrypt + cognicrypt@eim.upb.de + + + + scm:git:git@github.com:CROSSINGTUD/CryptoAnalysis.git + scm:git:ssh://github.com:CROSSINGTUD/CryptoAnalysis.git + https://github.com/CROSSINGTUD/CryptoAnalysis + + + CogniCrypt_SAST: CrySL-to-Static Analysis Compiler + https://github.com/CROSSINGTUD/CryptoAnalysis + CryptoAnalysis CryptoAnalysis-Android