From d052b9dbfcb3c1a23d1b0e32a41a3448e5dd8247 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Tue, 19 Dec 2023 13:03:01 +0100 Subject: [PATCH 01/25] add methods for interacting with GitHub Actions --- .../reporting/GitHubAnnotationReporter.java | 155 ++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 CryptoAnalysis/src/main/java/crypto/reporting/GitHubAnnotationReporter.java diff --git a/CryptoAnalysis/src/main/java/crypto/reporting/GitHubAnnotationReporter.java b/CryptoAnalysis/src/main/java/crypto/reporting/GitHubAnnotationReporter.java new file mode 100644 index 000000000..afee943e2 --- /dev/null +++ b/CryptoAnalysis/src/main/java/crypto/reporting/GitHubAnnotationReporter.java @@ -0,0 +1,155 @@ +package crypto.reporting; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.Locale; +import java.util.Objects; +import java.util.StringJoiner; + +public class GitHubAnnotationReporter { + /** + * Gets an input variable when running as a GitHub Action. Input values are read from environment + * variables with the format specified here. + * This method automatically handles converting the input name to the correct environment variable + * name. + * + * @param name the name of the input parameter + * @return the value of the input, or null if the input doesn't exist + */ + private static String getInput(String name) { + name = name.replaceAll(" ", "_"); + name = "INPUT_" + name.toUpperCase(Locale.ROOT); + + return System.getenv(name); + } + + /** + * Sets a summary + * for this GitHub Actions step. Supports GitHub flavored + * Markdown. + * + * @param summary the content of the summary + */ + private static void setSummary(String summary) { + String filePath = System.getenv("GITHUB_STEP_SUMMARY"); + try { + Files.write(Paths.get(filePath), summary.getBytes(StandardCharsets.UTF_8), StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.SYNC); + } catch (NullPointerException | IOException e) { + throw new IllegalStateException("Exception while trying to write to GITHUB_STEP_SUMMARY file. Make sure you are running inside GitHub Actions.", e); + } + } + + /** + * This class is used to output GitHub Actions Annotations. + * + *

It should be used when executing inside a GitHub Actions workflow. It will print out commands + * to the standard output that get interpreted as annotations by the GitHub Actions runner. The + * corresponding documentation + * is not very exhaustive, so some parts of this class were created by looking at the official typescript package and the + * actions runner source. + */ + private static class ActionsAnnotation { + /** + * Prints a GitHub Actions annotation. + * + * @param message the message that this annotation displays + * @param title optional; the title of the annotation + * @param file optional; the file in which the annotation should be displayed + * @param startLine optional; the line at which the annotation should be displayed, starting at 1 + * @param endLine optional; the end (inclusive) of the multi-line annotation; {@code startLine} + * needs to be set as well when setting this value + * @param startColumn optional; the column at which the annotation should be displayed, starting + * at 1; {@code startLine} needs to be set as well when setting this value; not supported for + * multi-line annotations. This means {@code endLine} needs to have the same value of {@code + * startLine} or be not set at all. + * @param endColumn optional; the end (inclusive) column at which the annotation should be + * displayed + * @throws IllegalArgumentException when the requirements specified above are not met + */ + public static void printAnnotation(@Nonnull String message, @Nullable String title, @Nullable String file, @Nullable Integer startLine, @Nullable Integer endLine, @Nullable Integer startColumn, @Nullable Integer endColumn) throws IllegalArgumentException { + // Implementing the validation logic from the GitHub Actions runner found at: + // https://github.com/actions/runner/blob/21b49c542cdb8caf3c6217db6286fdefecb5f025/src/Runner.Worker/ActionCommandManager.cs#L672 + // We re-implement this here so it shows up as an exception instead of the annotation being + // modified by the + // runner and only having a debug log message when running the action. + Objects.requireNonNull(message); + + if (endLine != null && startLine == null) { + throw new IllegalArgumentException("Invalid annotation. `startLine` needs to be set if `endLine` was provided."); + } + if (endColumn != null && startColumn == null) { + throw new IllegalArgumentException("Invalid annotation. `startColumn` needs to be set if `endColumn` was provided."); + } + if (startColumn != null && startLine == null) { + throw new IllegalArgumentException("Invalid annotation. `startLine` needs to be set if `startColumn` was provided."); + } + if (endLine != null && !Objects.equals(startLine, endLine) && startColumn != null) { + throw new IllegalArgumentException("Invalid annotation. `startColumn` can't be set when `startLine` and `endLine` have different values."); + } + if (startLine != null && endLine != null && startLine > endLine) { + throw new IllegalArgumentException("Invalid annotation. `startLine` can't have a higher value than `endLine`."); + } + if (startColumn != null && endColumn != null && startColumn > endColumn) { + throw new IllegalArgumentException("Invalid annotation. `startColumn` can't have a higher value than `endColumn`."); + } + + print(message, title, file, startLine, endLine, startColumn, endColumn); + } + + /** + * Prints the annotation to the standard output. The properties and the message are escaped + * properly. + */ + private static void print(@Nonnull String message, @Nullable String title, @Nullable String file, @Nullable Integer startLine, @Nullable Integer endLine, @Nullable Integer startColumn, @Nullable Integer endColumn) { + StringBuilder builder = new StringBuilder(); + + builder.append("::").append("error").append(' '); + + StringJoiner properties = new StringJoiner(","); + if (title != null) { + properties.add("title=" + escapeParameter(title)); + } + if (file != null) { + properties.add("file=" + escapeParameter(file)); + } + if (startLine != null) { + properties.add("line=" + escapeParameter(String.valueOf(startLine))); + } + if (endLine != null) { + properties.add("endLine=" + escapeParameter(String.valueOf(endLine))); + } + if (startColumn != null) { + properties.add("col=" + escapeParameter(String.valueOf(startColumn))); + } + if (endColumn != null) { + properties.add("endColumn=" + escapeParameter(String.valueOf(endColumn))); + } + builder.append(properties); + + builder.append("::").append(escapeMessage(message)); + + System.out.println(builder); + } + + private static String escapeParameter(String property) { + // not documented, but works and is used by the official toolkit + // https://github.com/actions/toolkit/blob/a6bf8726aa7b78d4fc8111359cca5d538527b239/packages/core/src/command.ts#L87 + return property.replaceAll("%", "%25").replaceAll("\r", "%0D").replaceAll("\n", "%0A").replaceAll(":", "%3A").replaceAll(",", "%2C"); + } + + private static String escapeMessage(String data) { + // not documented, but works and is used by the official toolkit + // https://github.com/actions/toolkit/blob/a6bf8726aa7b78d4fc8111359cca5d538527b239/packages/core/src/command.ts#L80 + return data.replaceAll("%", "%25").replaceAll("\r", "%0D").replaceAll("\n", "%0A"); + } + } +} From 5edff4317bbe555a5dca548e10f88c5a39a07af4 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Tue, 19 Dec 2023 13:04:11 +0100 Subject: [PATCH 02/25] add a GitHub Actions annotation reporter --- .../java/crypto/HeadlessCryptoScanner.java | 11 +- .../analysis/CryptoScannerSettings.java | 31 ++--- .../reporting/GitHubAnnotationReporter.java | 109 +++++++++++++++++- 3 files changed, 133 insertions(+), 18 deletions(-) diff --git a/CryptoAnalysis/src/main/java/crypto/HeadlessCryptoScanner.java b/CryptoAnalysis/src/main/java/crypto/HeadlessCryptoScanner.java index a98139b5c..92130ee65 100644 --- a/CryptoAnalysis/src/main/java/crypto/HeadlessCryptoScanner.java +++ b/CryptoAnalysis/src/main/java/crypto/HeadlessCryptoScanner.java @@ -22,6 +22,7 @@ import crypto.reporting.CSVReporter; import crypto.reporting.CSVSummaryReporter; import crypto.reporting.CommandLineReporter; +import crypto.reporting.GitHubAnnotationReporter; import crypto.reporting.Reporter; import crypto.reporting.SARIFReporter; import crypto.reporting.TXTReporter; @@ -218,6 +219,10 @@ protected void internalTransform(String phaseName, Map options) fileReporter = new CSVSummaryReporter(getOutputFolder(), softwareIdentifier(), rules, callgraphConstructionTime, includeStatistics()); reporter.addReportListener(fileReporter); break; + case GITHUB_ANNOTATION: + fileReporter = new GitHubAnnotationReporter(softwareIdentifier(), rules, callgraphConstructionTime, includeStatistics()); + reporter.addReportListener(fileReporter); + break; default: fileReporter = new CommandLineReporter(softwareIdentifier(), rules, callgraphConstructionTime, includeStatistics()); reporter.addReportListener(fileReporter); @@ -259,7 +264,7 @@ public Debugger debugger(IDEALSeedSolver } return super.debugger(solver, seed); } - + @Override public Collection getForbiddenPredicates() { return forbiddenPredicates(); @@ -269,7 +274,7 @@ public Collection getForbiddenPredicates() { public Collection getIgnoredSections() { return ignoredSections(); } - + }; if (providerDetection()) { @@ -435,7 +440,7 @@ protected Collection forbiddenPredicates() { protected Collection ignoredSections() { return settings.getIgnoredSections(); } - + 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 f44c32a30..23ad86647 100644 --- a/CryptoAnalysis/src/main/java/crypto/analysis/CryptoScannerSettings.java +++ b/CryptoAnalysis/src/main/java/crypto/analysis/CryptoScannerSettings.java @@ -31,17 +31,17 @@ public class CryptoScannerSettings implements Callable { + "using a ZIP file, please make sure that the path ends with '.zip'", required = true) private String rulesDir = null; - + @CommandLine.Option( names = {"--cg"}, description = "The call graph to resolve method calls. Possible values are CHA, SPARK and SPARKLIB (default: CHA)") private String cg = null; - + @CommandLine.Option( names = {"--sootPath"}, description = "The absolute path of the whole project") private String sootPath = ""; - + @CommandLine.Option( names = {"--identifier"}, description = "An identifier for the analysis to label output files") @@ -51,29 +51,29 @@ public class CryptoScannerSettings implements Callable { names = {"--reportPath"}, description = "Path for a directory to write the reports into") private String reportPath = null; - + @CommandLine.Option( names = {"--reportFormat"}, split = ",", description = "The format of the report. Possible values are CMD, TXT, SARIF, CSV and CSV_SUMMARY (default: CMD)." + " Multiple formats should be split with a comma (e.g. CMD,TXT,CSV)") private String[] reportFormat = null; - + @CommandLine.Option( names = {"--preanalysis"}, description = "Enable a preanalysis") private boolean preanalysis = false; - + @CommandLine.Option( names = {"--visualization"}, description = "Enable visualization") private boolean visualization = false; - + @CommandLine.Option( names = {"--providerdetection"}, description = "Enable provider detection") private boolean providerdetection = false; - + @CommandLine.Option( names = {"--dstats"}, description = "Disable the output of analysis statistics in the reports") @@ -96,7 +96,7 @@ public class CryptoScannerSettings implements Callable { + "Note that constructors are methods that can be specified with ''." ) private String ignoreSectionsPath = null; - + private ControlGraph controlGraph; private RulesetPathType rulesetPathType; private Set reportFormats; @@ -108,7 +108,7 @@ public enum ControlGraph { } public enum ReportFormat { - CMD, TXT, SARIF, CSV, CSV_SUMMARY + CMD, TXT, SARIF, CSV, CSV_SUMMARY, GITHUB_ANNOTATION } public enum RulesetPathType { @@ -133,7 +133,7 @@ public void parseSettingsFromCLI(String[] settings) throws CryptoAnalysisParserE } else { this.rulesetPathType = RulesetPathType.DIR; } - + if (cg != null) { parseControlGraphValue(cg); } @@ -149,7 +149,7 @@ public void parseSettingsFromCLI(String[] settings) throws CryptoAnalysisParserE if (ignoreSectionsPath != null) { parseIgnoredSections(ignoreSectionsPath); } - + if (exitCode != ExitCode.OK) { throw new CryptoAnalysisParserException("Error while parsing the CLI arguments"); } @@ -253,16 +253,19 @@ private void parseReportFormatValues(String[] settings) throws CryptoAnalysisPar case "csv_summary": reportFormats.add(ReportFormat.CSV_SUMMARY); break; + case "github_annotation": + reportFormats.add(ReportFormat.GITHUB_ANNOTATION); + break; default: throw new CryptoAnalysisParserException("Incorrect value " + reportFormatValue + " for --reportFormat option. " + "Available options are: CMD, TXT, SARIF, CSV and CSV_SUMMARY.\n"); } } } - + private boolean isZipFile(String path) throws CryptoAnalysisParserException { File file = new File(path); - + // Copied from https://stackoverflow.com/questions/33934178/how-to-identify-a-zip-file-in-java int fileSignature = 0; diff --git a/CryptoAnalysis/src/main/java/crypto/reporting/GitHubAnnotationReporter.java b/CryptoAnalysis/src/main/java/crypto/reporting/GitHubAnnotationReporter.java index afee943e2..83ddb4663 100644 --- a/CryptoAnalysis/src/main/java/crypto/reporting/GitHubAnnotationReporter.java +++ b/CryptoAnalysis/src/main/java/crypto/reporting/GitHubAnnotationReporter.java @@ -1,17 +1,124 @@ package crypto.reporting; +import com.google.common.collect.Table; +import crypto.analysis.errors.AbstractError; +import crypto.rules.CrySLRule; +import soot.SootClass; +import soot.SootMethod; +import soot.tagkit.Host; + import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; +import java.util.List; import java.util.Locale; import java.util.Objects; +import java.util.Set; import java.util.StringJoiner; -public class GitHubAnnotationReporter { +public class GitHubAnnotationReporter extends Reporter { + /** + * Path to relate paths in the analyzed jar and the source tree. + *

+ * Example: + * Relating the class {@code "example.IncompleOperationErrorExample"} + * to the source file {@code "CryptoAnalysisTargets/CogniCryptDemoExample/src/main/java/example/IncompleOperationErrorExample.java"} + * requires a {@code basePath} of {@code "CryptoAnalysisTargets/CogniCryptDemoExample/src/main/java"}. + */ + private final String basePath; + + /** + * The constructor to initialize all attributes. Since this class is abstract, all subclasses + * have to call this constructor. + * TODO describe + * + * @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 GitHubAnnotationReporter(String softwareID, List rules, long callgraphConstructionTime, boolean includeStatistics) { + super(null, softwareID, rules, callgraphConstructionTime, includeStatistics); + + basePath = getInput("basePath"); + } + + @Override + public void handleAnalysisResults() { + // report errors on individual lines + for (Table.Cell> cell : errorMarkers.cellSet()) { + SootClass clazz = cell.getRowKey(); + Path path = classToSourcePath(clazz); + + boolean sourceExists = Files.exists(path); // TODO log a warning when a source file could not be found (and recommend checking the `basePath` again + + for (AbstractError error : cell.getValue()) { + String title = error.getClass().getSimpleName() + " violating CrySL rule for " + error.getRule().getClassName(); + + Integer line = error.getErrorLocation().getUnit().transform(Host::getJavaSourceStartLineNumber).or(-1); + if (line == -1) { + line = null; + } + + Integer column = error.getErrorLocation().getUnit().transform(Host::getJavaSourceStartColumnNumber).or(-1); + if (column == -1) { + column = null; + } + + if (sourceExists) { + ActionsAnnotation.printAnnotation(error.toErrorMarkerString(), title, path.toString(), line, null, column, null); + } else { + // fall back to a "global" annotation when the corresponding source file could not be found + StringBuilder message = new StringBuilder(error.toErrorMarkerString()); + + if (line != null) { + message.append(System.lineSeparator()).append("at line ").append(line); + } + + if (column != null) { + message.append(System.lineSeparator()).append("at column ").append(column); + } + + ActionsAnnotation.printAnnotation(message.toString(), title, null, null, null, null, null); + } + } + } + + // report summary + StringBuilder summary = new StringBuilder(); + + summary.append(String.format("Number of CrySL rules: %s\n", getRules().size())); + summary.append(String.format("Number of Objects Analyzed: %s\n", getObjects().size())); + summary.append(String.format("Number of violations: %s\n", errorMarkerCount.values().stream().reduce(0, Integer::sum))); + + if (includeStatistics() && statistics != null) { + // add statistics to summary + summary.append("\nAdditional analysis statistics:\n"); + summary.append(String.format("SoftwareID: %s\n", statistics.getSoftwareID())); + summary.append(String.format("SeedObjectCount: %d\n", statistics.getSeedObjectCount())); + summary.append(String.format("CryptoAnalysisTime (in ms): %d\n", statistics.getAnalysisTime())); + summary.append(String.format("CallgraphConstructionTime (in ms): %d\n", statistics.getCallgraphTime())); + summary.append(String.format("CallgraphReachableMethods: %d\n", statistics.getCallgraphReachableMethods())); + summary.append(String.format("CallgraphReachableMethodsWithActiveBodies: %d\n", statistics.getCallgraphReachableMethodsWithActiveBodies())); + summary.append(String.format("DataflowVisitedMethods: %d\n", statistics.getDataflowVisitedMethods())); + } + + setSummary(summary.toString()); + } + + private Path classToSourcePath(SootClass clazz) { + return Paths.get(basePath, clazz.getName().replace('.', File.separatorChar) + + ".java"); + } + /** * Gets an input variable when running as a GitHub Action. Input values are read from environment * variables with the format specified Date: Tue, 19 Dec 2023 13:04:43 +0100 Subject: [PATCH 03/25] prepare using the project as a GitHub Action --- .github/action.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/action.yml diff --git a/.github/action.yml b/.github/action.yml new file mode 100644 index 000000000..7d722db60 --- /dev/null +++ b/.github/action.yml @@ -0,0 +1,18 @@ +name: CogniCrypt - Static Spplication Security Testing +description: CogniCrypt takes rules written in the specification language CrySL as input, and performs a static analysis based on the specification of the rules. +inputs: + basePath: + description: "Path to relate paths in the analyzed jar and the source tree. Class \"com.example\" is searched for at \"basePath/com/example\"." + rulesDir: + description: "TODO" + required: true + appPath: + description: "TODO" + required: true + +runs: + using: "composite" + steps: + # TODO download jar + - run: java -cp CryptoAnalysis-3.0.0-jar-with-dependencies.jar crypto.HeadlessCryptoScanner --rulesDir {{ inputs.rulesDir }} --appPath {{ inputs.appPath }} --reportFormat github_annotation + shell: bash From 904915f861528a364ce8d18d65fb91ebf1dba56e Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Tue, 19 Dec 2023 13:10:03 +0100 Subject: [PATCH 04/25] try to run action --- .github/workflows/test.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..cbd9a0b7f --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,20 @@ +name: Test Internal Action + +on: push + +jobs: + internal_action: + runs-on: ubuntu-latest + name: Test CryptoAnalysis Action + 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: '11' + - name: Run CogniCrypt + uses: Timbals/CryptoAnalysis@annotations From af2371393912025d4d7f81930a6dde8e8fe83533 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Tue, 19 Dec 2023 13:13:22 +0100 Subject: [PATCH 05/25] move action to root --- .github/action.yml => action.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/action.yml => action.yml (100%) diff --git a/.github/action.yml b/action.yml similarity index 100% rename from .github/action.yml rename to action.yml From bf7b5f08d322d43f5fa2eb5d7131150aac5f8d6c Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Tue, 19 Dec 2023 13:15:24 +0100 Subject: [PATCH 06/25] specify rulesDir and appPath --- .github/workflows/test.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cbd9a0b7f..c61202f29 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,3 +18,6 @@ jobs: java-version: '11' - name: Run CogniCrypt uses: Timbals/CryptoAnalysis@annotations + with: + rulesDir: "$(pwd)/JCA-CrySL-rules" + appPath: "$(pwd)/CryptoAnalysisTargets/CogniCryptDemoExample/Examples.jar" From f9e617e47e99ee08f83da90a6e214dd4fe7509e8 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Tue, 19 Dec 2023 13:17:02 +0100 Subject: [PATCH 07/25] add missing dollars to access inputs --- action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/action.yml b/action.yml index 7d722db60..aeb4a110e 100644 --- a/action.yml +++ b/action.yml @@ -14,5 +14,5 @@ runs: using: "composite" steps: # TODO download jar - - run: java -cp CryptoAnalysis-3.0.0-jar-with-dependencies.jar crypto.HeadlessCryptoScanner --rulesDir {{ inputs.rulesDir }} --appPath {{ inputs.appPath }} --reportFormat github_annotation + - run: java -cp CryptoAnalysis-3.0.0-jar-with-dependencies.jar crypto.HeadlessCryptoScanner --rulesDir ${{ inputs.rulesDir }} --appPath ${{ inputs.appPath }} --reportFormat github_annotation shell: bash From 6917963a3dc8cdc1b15d75cc0b77e645a56fc4b1 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Tue, 19 Dec 2023 13:46:24 +0100 Subject: [PATCH 08/25] specify base path --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c61202f29..5825aa708 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,3 +21,4 @@ jobs: with: rulesDir: "$(pwd)/JCA-CrySL-rules" appPath: "$(pwd)/CryptoAnalysisTargets/CogniCryptDemoExample/Examples.jar" + basePath: "CryptoAnalysisTargets/CogniCryptDemoExample/src/main/java" From c80cba4f731fbd70c9cf9bc7bcb0bd8fe8fe2817 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Tue, 19 Dec 2023 14:06:58 +0100 Subject: [PATCH 09/25] pass base path through --- action.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/action.yml b/action.yml index aeb4a110e..76d6a8c76 100644 --- a/action.yml +++ b/action.yml @@ -16,3 +16,6 @@ runs: # TODO download jar - run: java -cp CryptoAnalysis-3.0.0-jar-with-dependencies.jar crypto.HeadlessCryptoScanner --rulesDir ${{ inputs.rulesDir }} --appPath ${{ inputs.appPath }} --reportFormat github_annotation shell: bash + # workaround for https://github.com/actions/runner/issues/665 + env: + INPUT_BASEPATH: ${{ inputs.basePath }} \ No newline at end of file From 99e58f09d85b6480490c2c5d32a793a4602263b7 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Tue, 19 Dec 2023 19:33:52 +0100 Subject: [PATCH 10/25] fail when a violation is found --- .../src/main/java/crypto/HeadlessCryptoScanner.java | 5 ++++- .../java/crypto/reporting/GitHubAnnotationReporter.java | 8 +++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CryptoAnalysis/src/main/java/crypto/HeadlessCryptoScanner.java b/CryptoAnalysis/src/main/java/crypto/HeadlessCryptoScanner.java index 92130ee65..3723693d0 100644 --- a/CryptoAnalysis/src/main/java/crypto/HeadlessCryptoScanner.java +++ b/CryptoAnalysis/src/main/java/crypto/HeadlessCryptoScanner.java @@ -64,10 +64,13 @@ public abstract class HeadlessCryptoScanner { private static String rulesetRootPath; private static final CrySLRuleReader ruleReader = new CrySLRuleReader(); private boolean hasSeeds; - + + public static int exitCode = 0; + public static void main(String[] args) { HeadlessCryptoScanner scanner = createFromCLISettings(args); scanner.exec(); + System.exit(exitCode); } public static HeadlessCryptoScanner createFromCLISettings(String[] args) { diff --git a/CryptoAnalysis/src/main/java/crypto/reporting/GitHubAnnotationReporter.java b/CryptoAnalysis/src/main/java/crypto/reporting/GitHubAnnotationReporter.java index 83ddb4663..20dfcbf46 100644 --- a/CryptoAnalysis/src/main/java/crypto/reporting/GitHubAnnotationReporter.java +++ b/CryptoAnalysis/src/main/java/crypto/reporting/GitHubAnnotationReporter.java @@ -1,6 +1,7 @@ package crypto.reporting; import com.google.common.collect.Table; +import crypto.HeadlessCryptoScanner; import crypto.analysis.errors.AbstractError; import crypto.rules.CrySLRule; import soot.SootClass; @@ -97,7 +98,8 @@ public void handleAnalysisResults() { summary.append(String.format("Number of CrySL rules: %s\n", getRules().size())); summary.append(String.format("Number of Objects Analyzed: %s\n", getObjects().size())); - summary.append(String.format("Number of violations: %s\n", errorMarkerCount.values().stream().reduce(0, Integer::sum))); + int errorCount = errorMarkerCount.values().stream().reduce(0, Integer::sum); + summary.append(String.format("Number of violations: %s\n", errorCount)); if (includeStatistics() && statistics != null) { // add statistics to summary @@ -112,6 +114,10 @@ public void handleAnalysisResults() { } setSummary(summary.toString()); + + if (errorCount != 0) { + HeadlessCryptoScanner.exitCode = 1; + } } private Path classToSourcePath(SootClass clazz) { From f45940bbd1ea6b31edeec483e8559bdab0204489 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Tue, 19 Dec 2023 19:55:48 +0100 Subject: [PATCH 11/25] add other CLI options to the action --- action.yml | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/action.yml b/action.yml index 76d6a8c76..51e636353 100644 --- a/action.yml +++ b/action.yml @@ -9,13 +9,43 @@ inputs: appPath: description: "TODO" required: true + cg: + description: "Selection of call graph for analysis (possible values are CHA, SPARK, SPARKLIB)" + sootPath: + description: "Absolute path of the whole project" + identifier: + description: "Identifier for labeling output files" + reportPath: + description: "Directory Location for cryptoanalysis reports" + reportFormat: + description: "format of crytoanalysis reports (possible values are CMD, TXT, SARIF, CSV, CSV_SUMMARY)" + preanalysis: + description: "Enables pre-analysis" + visualization : + description: "Enables the visualization, but also requires reportPath option to be set" + providerDetection : + description: "Enables provider detection analysis" + dstats: + description: "Disables the output of the analysis statistics in the reports" runs: using: "composite" steps: # TODO download jar - - run: java -cp CryptoAnalysis-3.0.0-jar-with-dependencies.jar crypto.HeadlessCryptoScanner --rulesDir ${{ inputs.rulesDir }} --appPath ${{ inputs.appPath }} --reportFormat github_annotation + - run: | + java -cp CryptoAnalysis-3.0.2-jar-with-dependencies.jar crypto.HeadlessCryptoScanner \ + --rulesDir ${{ inputs.rulesDir }} \ + --appPath ${{ inputs.appPath }} \ + --reportFormat github_annotation ${{ inputs.reportFormat }} \ + ${{ inputs.cg && format('{0} {1}', '--cg', inputs.cg) || '' }} \ + ${{ inputs.sootPath && format('{0} {1}', '--sootPath', inputs.sootPath) || '' }} \ + ${{ inputs.identifier && format('{0} {1}', '--identifier', inputs.identifier) || '' }} \ + ${{ inputs.reportPath && format('{0} {1}', '--reportPath', inputs.reportPath) || '' }} \ + ${{ inputs.preanalysis == 'true' && '--preanalysis' || '' }} \ + ${{ inputs.visualization == 'true' && '--visualization ' || '' }} \ + ${{ inputs.providerDetection == 'true' && '--providerDetection ' || '' }} \ + ${{ inputs.dstats == 'true' && '--dstats' || '' }} shell: bash # workaround for https://github.com/actions/runner/issues/665 env: - INPUT_BASEPATH: ${{ inputs.basePath }} \ No newline at end of file + INPUT_BASEPATH: ${{ inputs.basePath }} From 201945cc9032289e9e7fe6eb7d1ca7721c59ff9c Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Tue, 19 Dec 2023 20:19:06 +0100 Subject: [PATCH 12/25] compile CryptoAnalysis jar --- action.yml | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/action.yml b/action.yml index 51e636353..803a06564 100644 --- a/action.yml +++ b/action.yml @@ -31,9 +31,37 @@ inputs: runs: using: "composite" steps: - # TODO download jar + - name: Set up Java + uses: actions/setup-java@v3 + with: + distribution: 'adopt' + java-package: jdk + java-version: '11' + # TODO cache compiled jar + # TODO download pre-compiled jar instead (when available) + - name: Set up Maven + uses: stCarolas/setup-maven@v4.5 + with: + maven-version: 3.6.3 + - name: Checkout source code + uses: actions/checkout@v3 + with: + repository: ${{ env.GH_ACTION_REPOSITORY }} + ref: ${{ env.GH_ACTION_REF }} + path: 'CryptoAnalysisBuild_98e04be6' # random string at the end to avoid any conflicts with the actual repo + env: + # workaround for https://github.com/actions/runner/issues/2473 + GH_ACTION_REPOSITORY: ${{ github.action_repository }} + GH_ACTION_REF: ${{ github.action_ref }} + - name: Compile + shell: bash + run: mvn package -DskipTests=true + working-directory: 'CryptoAnalysisBuild_98e04be6' + - name: Copy JAR to convenient path + shell: bash + run: cp CryptoAnalysisBuild_98e04be6/CryptoAnalysis/build/CryptoAnalysis-*-jar-with-dependencies.jar CryptoAnalysisBuild_98e04be6/CryptoAnalysis.jar - run: | - java -cp CryptoAnalysis-3.0.2-jar-with-dependencies.jar crypto.HeadlessCryptoScanner \ + java -cp CryptoAnalysisBuild_98e04be6/CryptoAnalysis.jar crypto.HeadlessCryptoScanner \ --rulesDir ${{ inputs.rulesDir }} \ --appPath ${{ inputs.appPath }} \ --reportFormat github_annotation ${{ inputs.reportFormat }} \ From 65a4e5edef8aa9c28de6fa0e677d9c72235be3c8 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Tue, 19 Dec 2023 21:00:46 +0100 Subject: [PATCH 13/25] cache compiled jar for action --- action.yml | 56 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 12 deletions(-) diff --git a/action.yml b/action.yml index 803a06564..f36a858f3 100644 --- a/action.yml +++ b/action.yml @@ -37,29 +37,61 @@ runs: distribution: 'adopt' java-package: jdk java-version: '11' - # TODO cache compiled jar - # TODO download pre-compiled jar instead (when available) - - name: Set up Maven - uses: stCarolas/setup-maven@v4.5 - with: - maven-version: 3.6.3 + - name: Checkout source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: ${{ env.GH_ACTION_REPOSITORY }} ref: ${{ env.GH_ACTION_REF }} - path: 'CryptoAnalysisBuild_98e04be6' # random string at the end to avoid any conflicts with the actual repo + path: CryptoAnalysisBuild_98e04be6 # random string at the end to avoid any conflicts with the actual repo + env: + # workaround for https://github.com/actions/runner/issues/2473 + GH_ACTION_REPOSITORY: ${{ github.action_repository }} + GH_ACTION_REF: ${{ github.action_ref }} + + - name: Calculate cache key + id: cache-key + shell: bash + working-directory: CryptoAnalysisBuild_98e04be6 + run: echo "cache-key=CryptoAnalysis-$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + - name: Restore CryptoAnalysis Cache + id: cache + uses: actions/cache/restore@v3 + with: + path: CryptoAnalysisBuild_98e04be6/CryptoAnalysis.jar + key: ${{ steps.cache-key.outputs.cache-key }} env: # workaround for https://github.com/actions/runner/issues/2473 - GH_ACTION_REPOSITORY: ${{ github.action_repository }} - GH_ACTION_REF: ${{ github.action_ref }} - - name: Compile + GH_ACTION_REPOSITORY: ${{ github.action_repository }} + GH_ACTION_REF: ${{ github.action_ref }} + + # TODO download pre-compiled jar instead (when available) + - name: Set up Maven + if: steps.cache.outputs.cache-hit != 'true' + uses: stCarolas/setup-maven@v4.5 + with: + maven-version: 3.6.3 + - name: Compile CryptoAnalysis + if: steps.cache.outputs.cache-hit != 'true' shell: bash run: mvn package -DskipTests=true - working-directory: 'CryptoAnalysisBuild_98e04be6' + working-directory: CryptoAnalysisBuild_98e04be6 - name: Copy JAR to convenient path + if: steps.cache.outputs.cache-hit != 'true' shell: bash run: cp CryptoAnalysisBuild_98e04be6/CryptoAnalysis/build/CryptoAnalysis-*-jar-with-dependencies.jar CryptoAnalysisBuild_98e04be6/CryptoAnalysis.jar + + - name: Save CryptoAnalysis Cache + uses: actions/cache/save@v3 + if: always() + with: + path: CryptoAnalysisBuild_98e04be6/CryptoAnalysis.jar + key: ${{ steps.cache-key.outputs.cache-key }} + env: + # workaround for https://github.com/actions/runner/issues/2473 + GH_ACTION_REPOSITORY: ${{ github.action_repository }} + GH_ACTION_REF: ${{ github.action_ref }} + - run: | java -cp CryptoAnalysisBuild_98e04be6/CryptoAnalysis.jar crypto.HeadlessCryptoScanner \ --rulesDir ${{ inputs.rulesDir }} \ From 9e5678c4642c3f700c0b0cf4c5ca0f92dcc0319d Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Tue, 19 Dec 2023 21:27:42 +0100 Subject: [PATCH 14/25] test specifying version --- .github/workflows/test.yml | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5825aa708..75041fcb7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,17 +7,8 @@ jobs: runs-on: ubuntu-latest name: Test CryptoAnalysis Action 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: '11' - name: Run CogniCrypt - uses: Timbals/CryptoAnalysis@annotations + uses: Timbals/CryptoAnalysis@8894fae7b78de54fadd3de2d6cd5a77138e0b3df with: rulesDir: "$(pwd)/JCA-CrySL-rules" appPath: "$(pwd)/CryptoAnalysisTargets/CogniCryptDemoExample/Examples.jar" From 6701a9b8038b60aab989cbcf84572dc1d33ce7a1 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Tue, 19 Dec 2023 22:01:58 +0100 Subject: [PATCH 15/25] actually check out the example jar --- .github/workflows/test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 75041fcb7..2fbd9a9e7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,6 +7,8 @@ jobs: runs-on: ubuntu-latest name: Test CryptoAnalysis Action steps: + - name: Checkout source code + uses: actions/checkout@v3 - name: Run CogniCrypt uses: Timbals/CryptoAnalysis@8894fae7b78de54fadd3de2d6cd5a77138e0b3df with: From af4c6886b6181321fd15ff2638e0e3d61663f8a9 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Tue, 19 Dec 2023 22:03:20 +0100 Subject: [PATCH 16/25] only save cache when it wasn't hit before --- .github/workflows/test.yml | 2 +- action.yml | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2fbd9a9e7..45009c09b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,7 +10,7 @@ jobs: - name: Checkout source code uses: actions/checkout@v3 - name: Run CogniCrypt - uses: Timbals/CryptoAnalysis@8894fae7b78de54fadd3de2d6cd5a77138e0b3df + uses: Timbals/CryptoAnalysis@a4032930f0a7b3bdb761130eae8d2229c9534b0a with: rulesDir: "$(pwd)/JCA-CrySL-rules" appPath: "$(pwd)/CryptoAnalysisTargets/CogniCryptDemoExample/Examples.jar" diff --git a/action.yml b/action.yml index f36a858f3..c030e2d15 100644 --- a/action.yml +++ b/action.yml @@ -80,10 +80,9 @@ runs: if: steps.cache.outputs.cache-hit != 'true' shell: bash run: cp CryptoAnalysisBuild_98e04be6/CryptoAnalysis/build/CryptoAnalysis-*-jar-with-dependencies.jar CryptoAnalysisBuild_98e04be6/CryptoAnalysis.jar - - name: Save CryptoAnalysis Cache uses: actions/cache/save@v3 - if: always() + if: steps.cache.outputs.cache-hit != 'true' with: path: CryptoAnalysisBuild_98e04be6/CryptoAnalysis.jar key: ${{ steps.cache-key.outputs.cache-key }} From bc2655b4cf70f12e89d2a0feb6c4ba400a4f9b49 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Thu, 21 Dec 2023 16:44:19 +0100 Subject: [PATCH 17/25] download pre-compiled jar in action when available --- .github/workflows/test.yml | 2 +- action.yml | 26 ++++++++++++++++---------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 45009c09b..a0a0f1af6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,7 +10,7 @@ jobs: - name: Checkout source code uses: actions/checkout@v3 - name: Run CogniCrypt - uses: Timbals/CryptoAnalysis@a4032930f0a7b3bdb761130eae8d2229c9534b0a + uses: Timbals/CryptoAnalysis@be1b88d536592c3ab08af5969808d075b8c59d82 with: rulesDir: "$(pwd)/JCA-CrySL-rules" appPath: "$(pwd)/CryptoAnalysisTargets/CogniCryptDemoExample/Examples.jar" diff --git a/action.yml b/action.yml index c030e2d15..1181895c0 100644 --- a/action.yml +++ b/action.yml @@ -60,22 +60,32 @@ runs: with: path: CryptoAnalysisBuild_98e04be6/CryptoAnalysis.jar key: ${{ steps.cache-key.outputs.cache-key }} - env: - # workaround for https://github.com/actions/runner/issues/2473 - GH_ACTION_REPOSITORY: ${{ github.action_repository }} - GH_ACTION_REF: ${{ github.action_ref }} - # TODO download pre-compiled jar instead (when available) - name: Set up Maven if: steps.cache.outputs.cache-hit != 'true' uses: stCarolas/setup-maven@v4.5 with: maven-version: 3.6.3 - - name: Compile CryptoAnalysis + + # Fetch pre-compiled JAR from the maven repository + - name: Download pre-compiled JAR + id: download if: steps.cache.outputs.cache-hit != 'true' + continue-on-error: true + shell: bash + run: mvn dependency:copy -Dartifact=de.fraunhofer.iem:CryptoAnalysis:${{ env.GH_ACTION_REF }}:jar:jar-with-dependencies -DoutputDirectory=CryptoAnalysis/build + working-directory: CryptoAnalysisBuild_98e04be6 + env: + GH_ACTION_REF: ${{ github.action_ref }} + # Compile from source when no pre-compiled JAR is available. + # This also makes the action work for non-releases, + # meaning any ref that can be resolved by https://github.com/actions/checkout. + - name: Compile CryptoAnalysis + if: steps.cache.outputs.cache-hit != 'true' && steps.download.outcome != 'success' shell: bash run: mvn package -DskipTests=true working-directory: CryptoAnalysisBuild_98e04be6 + - name: Copy JAR to convenient path if: steps.cache.outputs.cache-hit != 'true' shell: bash @@ -86,10 +96,6 @@ runs: with: path: CryptoAnalysisBuild_98e04be6/CryptoAnalysis.jar key: ${{ steps.cache-key.outputs.cache-key }} - env: - # workaround for https://github.com/actions/runner/issues/2473 - GH_ACTION_REPOSITORY: ${{ github.action_repository }} - GH_ACTION_REF: ${{ github.action_ref }} - run: | java -cp CryptoAnalysisBuild_98e04be6/CryptoAnalysis.jar crypto.HeadlessCryptoScanner \ From b3e0312847970848bc21189e036bd2a88995ffd1 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Thu, 21 Dec 2023 17:16:53 +0100 Subject: [PATCH 18/25] add a message when the source file for an error could not be found --- .../main/java/crypto/reporting/GitHubAnnotationReporter.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CryptoAnalysis/src/main/java/crypto/reporting/GitHubAnnotationReporter.java b/CryptoAnalysis/src/main/java/crypto/reporting/GitHubAnnotationReporter.java index 20dfcbf46..661438984 100644 --- a/CryptoAnalysis/src/main/java/crypto/reporting/GitHubAnnotationReporter.java +++ b/CryptoAnalysis/src/main/java/crypto/reporting/GitHubAnnotationReporter.java @@ -59,7 +59,7 @@ public void handleAnalysisResults() { SootClass clazz = cell.getRowKey(); Path path = classToSourcePath(clazz); - boolean sourceExists = Files.exists(path); // TODO log a warning when a source file could not be found (and recommend checking the `basePath` again + boolean sourceExists = Files.exists(path); for (AbstractError error : cell.getValue()) { String title = error.getClass().getSimpleName() + " violating CrySL rule for " + error.getRule().getClassName(); @@ -88,6 +88,8 @@ public void handleAnalysisResults() { message.append(System.lineSeparator()).append("at column ").append(column); } + message.append(System.lineSeparator()).append(System.lineSeparator()).append("Corresponding source file could not be found (at ").append(path).append("). The base path might be set incorrectly."); + ActionsAnnotation.printAnnotation(message.toString(), title, null, null, null, null, null); } } From e93fc5d0ac840f26392e6cbc5bb05a4740c9afee Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Thu, 4 Jan 2024 14:11:25 +0100 Subject: [PATCH 19/25] allow relative paths --- .../src/main/java/crypto/HeadlessCryptoScanner.java | 10 ++++++---- README.md | 8 ++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/CryptoAnalysis/src/main/java/crypto/HeadlessCryptoScanner.java b/CryptoAnalysis/src/main/java/crypto/HeadlessCryptoScanner.java index 3723693d0..695bad2e8 100644 --- a/CryptoAnalysis/src/main/java/crypto/HeadlessCryptoScanner.java +++ b/CryptoAnalysis/src/main/java/crypto/HeadlessCryptoScanner.java @@ -93,16 +93,18 @@ protected List getRules() { switch(settings.getRulesetPathType()) { case DIR: try { - rules.addAll(ruleReader.readFromDirectory(new File(settings.getRulesetPathDir()))); - rulesetRootPath = settings.getRulesetPathDir().substring(0, settings.getRulesetPathDir().lastIndexOf(File.separator)); + File ruleSetDir = new File(settings.getRulesetPathDir()); + rules.addAll(ruleReader.readFromDirectory(ruleSetDir)); + rulesetRootPath = ruleSetDir.getParent(); } catch (CryptoAnalysisException e) { LOGGER.error("Error happened when getting the CrySL rules from the specified directory: " + settings.getRulesetPathDir(), e); } break; case ZIP: try { - rules.addAll(ruleReader.readFromZipFile(new File(settings.getRulesetPathDir()))); - rulesetRootPath = settings.getRulesetPathDir().substring(0, settings.getRulesetPathDir().lastIndexOf(File.separator)); + File ruleSetZip = new File(settings.getRulesetPathDir()); + rules.addAll(ruleReader.readFromZipFile(ruleSetZip)); + rulesetRootPath = ruleSetZip.getParent(); } catch (CryptoAnalysisException e) { LOGGER.error("Error happened when getting the CrySL rules from the specified file: " + settings.getRulesetPathDir(), e); } diff --git a/README.md b/README.md index 2f93734fc..5254d477b 100644 --- a/README.md +++ b/README.md @@ -26,13 +26,13 @@ A packaged `jar` artifact including all dependency is found in `CryptoAnalysis/ ## Usage CogniCryptSAST can be started in headless mode (i.e., detached from Eclipse) via the class `crypto.HeadlessCryptoScanner`. It requires two arguments: -* The absolute path to the directory of the CrySL (source code format) rule files. The source code for the rules which contain specification for the JCA is found [here](https://github.com/CROSSINGTUD/Crypto-API-Rules). -* The absolute path of the application to be analyzed (.jar file or the root compilation output folder which contains the .class files in subdirectories) +* The path to the directory of the CrySL (source code format) rule files. The source code for the rules which contain specification for the JCA is found [here](https://github.com/CROSSINGTUD/Crypto-API-Rules). +* The path of the application to be analyzed (.jar file or the root compilation output folder which contains the .class files in subdirectories) ``` java -cp crypto.HeadlessCryptoScanner - --rulesDir - --appPath + --rulesDir + --appPath ``` For an easy start we prepared a .jar containing classes with crypto misuses. The source code for these misuses is found [here](https://github.com/CROSSINGTUD/CryptoAnalysis/tree/develop/CryptoAnalysisTargets/CogniCryptDemoExample/src/main/java/example). To run CogniCryptSAST on these classes, simply execute the following command (on a linux based system). From 255e848b8214963951282a48a9c89c491efad6a2 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Thu, 4 Jan 2024 14:17:12 +0100 Subject: [PATCH 20/25] automatically check out rules --- .github/workflows/test.yml | 3 +-- action.yml | 8 +++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a0a0f1af6..5284845eb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,8 +10,7 @@ jobs: - name: Checkout source code uses: actions/checkout@v3 - name: Run CogniCrypt - uses: Timbals/CryptoAnalysis@be1b88d536592c3ab08af5969808d075b8c59d82 + uses: ./ with: - rulesDir: "$(pwd)/JCA-CrySL-rules" appPath: "$(pwd)/CryptoAnalysisTargets/CogniCryptDemoExample/Examples.jar" basePath: "CryptoAnalysisTargets/CogniCryptDemoExample/src/main/java" diff --git a/action.yml b/action.yml index 1181895c0..9b615a60e 100644 --- a/action.yml +++ b/action.yml @@ -5,7 +5,7 @@ inputs: description: "Path to relate paths in the analyzed jar and the source tree. Class \"com.example\" is searched for at \"basePath/com/example\"." rulesDir: description: "TODO" - required: true + default: "rules_98e04be6/JavaCryptographicArchitecture/src/" appPath: description: "TODO" required: true @@ -38,6 +38,12 @@ runs: java-package: jdk java-version: '11' + - name: Checkout rules + uses: actions/checkout@v4 + with: + repository: CROSSINGTUD/Crypto-API-Rules + path: rules_98e04be6 + - name: Checkout source code uses: actions/checkout@v4 with: From fc417631512de43c0b952a71e6c34a4b93554f84 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Thu, 4 Jan 2024 14:44:20 +0100 Subject: [PATCH 21/25] skip rules that contain errors --- .../java/crypto/cryslhandler/CrySLModelReader.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/CryptoAnalysis/src/main/java/crypto/cryslhandler/CrySLModelReader.java b/CryptoAnalysis/src/main/java/crypto/cryslhandler/CrySLModelReader.java index 75a43c658..96cc1763f 100644 --- a/CryptoAnalysis/src/main/java/crypto/cryslhandler/CrySLModelReader.java +++ b/CryptoAnalysis/src/main/java/crypto/cryslhandler/CrySLModelReader.java @@ -150,11 +150,15 @@ public List readRulesFromFiles(List files) throws CryptoAnalysi if (!(resource instanceof LazyLinkingResource)) { continue; } - - CrySLRule rule = createRuleFromResource(resource); - - if (!ruleMap.containsKey(rule.getClassName())) { - ruleMap.put(rule.getClassName(), rule); + + try { + CrySLRule rule = createRuleFromResource(resource); + + if (!ruleMap.containsKey(rule.getClassName())) { + ruleMap.put(rule.getClassName(), rule); + } + } catch (CryptoAnalysisException e) { + LOGGER.error(e.toString()); } } From c8d502228f6af766b7eb8b9393a871fee8558b2c Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Thu, 4 Jan 2024 15:06:41 +0100 Subject: [PATCH 22/25] use a jar with no rule violations for testing --- .github/workflows/test.yml | 4 ++-- CryptoAnalysisTargets/HelloWorld/HelloWorld.jar | Bin 0 -> 755 bytes CryptoAnalysisTargets/HelloWorld/HelloWorld.java | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 CryptoAnalysisTargets/HelloWorld/HelloWorld.jar create mode 100644 CryptoAnalysisTargets/HelloWorld/HelloWorld.java diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5284845eb..a345dfd22 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,5 +12,5 @@ jobs: - name: Run CogniCrypt uses: ./ with: - appPath: "$(pwd)/CryptoAnalysisTargets/CogniCryptDemoExample/Examples.jar" - basePath: "CryptoAnalysisTargets/CogniCryptDemoExample/src/main/java" + appPath: "CryptoAnalysisTargets/HelloWorld/HelloWorld.jar" + basePath: "CryptoAnalysisTargets/HelloWorld" diff --git a/CryptoAnalysisTargets/HelloWorld/HelloWorld.jar b/CryptoAnalysisTargets/HelloWorld/HelloWorld.jar new file mode 100644 index 0000000000000000000000000000000000000000..d031ea8b651ef233ae6d04800ffc19b9f1bf607a GIT binary patch literal 755 zcmWIWW@Zs#;Nak3C@xotU_b%_K(1NZzMNE?~M6mcD^kDs1QzEN(#6)?9y3w?onhR|t{BlAPqYGlz z9Q>R9eZIW0;709}l`G!9l%Ls?7bKZpH?2!SDtx2gide3tlNLAcRteoaubItE$0T;k z2G5MO63P+$uFI32@$IqKHlFtU@xvod?GG~6EfebboTA22>VNE!R=?B4du$7nj&?Ae zoOA9$ooI(<-K6Egg^Jw8wVgJ4cU~+!`&}!JGbEQRxg$G^e(+U2Y}#=4;uWQX z$N0+X*Vfu{-B`b2)>iW?)63j9pRP%2o@%={^*?)nH#^6f&2QtB7#SE=191R6d^syn z!*kx7mzkDYT%sG6T2!2wpJ%0DsAs^-<(rt9r|S%gK`RABobqx7cr!AI z01bo1KeEd~@sA1+Iw7e6SvSmgFsFljhX4t{kbpZE literal 0 HcmV?d00001 diff --git a/CryptoAnalysisTargets/HelloWorld/HelloWorld.java b/CryptoAnalysisTargets/HelloWorld/HelloWorld.java new file mode 100644 index 000000000..db1db6486 --- /dev/null +++ b/CryptoAnalysisTargets/HelloWorld/HelloWorld.java @@ -0,0 +1,5 @@ +public class HelloWorld { + public static void main(String[] args) { + System.out.println("Hello, World!"); + } +} From 79bdf57b9032c6ee1d35f4dc6276b5f45b804005 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Mon, 8 Jan 2024 12:18:36 +0100 Subject: [PATCH 23/25] add documentation --- README.md | 19 +++++++++++++++++++ action.yml | 4 ++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5254d477b..868420b1e 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,25 @@ Other additional arguments that can be used are as follows: 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). +### Use as a GitHub Action + +CogniCryptSAST can be used as a GitHub action. + +```yaml +- name: Run CogniCrypt + uses: CROSSINGTUD/CryptoAnalysis@version + with: + appPath: "CryptoAnalysisTargets/HelloWorld/HelloWorld.jar" + basePath: "CryptoAnalysisTargets/HelloWorld" +``` + +The `appPath` needs to be configured to point to a compiled version of your application. + +The `basePath` is used to relate paths in the analyzed jar and the source tree. +Class `com.example` is searched for at `basePath/com/example`. + +See [`action.yml`](action.yml) for all input options. + ## Report and Error 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: diff --git a/action.yml b/action.yml index 9b615a60e..5b7ce30e1 100644 --- a/action.yml +++ b/action.yml @@ -4,10 +4,10 @@ inputs: basePath: description: "Path to relate paths in the analyzed jar and the source tree. Class \"com.example\" is searched for at \"basePath/com/example\"." rulesDir: - description: "TODO" + description: "Path to rules (in CrySL source code format)" default: "rules_98e04be6/JavaCryptographicArchitecture/src/" appPath: - description: "TODO" + description: "Path to the JAR of the application to be analyzed" required: true cg: description: "Selection of call graph for analysis (possible values are CHA, SPARK, SPARKLIB)" From a6976c7a034367535dccbf2d63831a08ebd2b1b3 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Fri, 12 Jan 2024 14:18:59 +0100 Subject: [PATCH 24/25] add missing CLI options to the action --- action.yml | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/action.yml b/action.yml index 5b7ce30e1..73885420a 100644 --- a/action.yml +++ b/action.yml @@ -2,31 +2,44 @@ name: CogniCrypt - Static Spplication Security Testing description: CogniCrypt takes rules written in the specification language CrySL as input, and performs a static analysis based on the specification of the rules. inputs: basePath: - description: "Path to relate paths in the analyzed jar and the source tree. Class \"com.example\" is searched for at \"basePath/com/example\"." + description: Path to relate paths in the analyzed jar and the source tree. + Class \"com.example\" is searched for at \"basePath/com/example\". rulesDir: - description: "Path to rules (in CrySL source code format)" + description: Path to rules (in CrySL source code format) default: "rules_98e04be6/JavaCryptographicArchitecture/src/" appPath: - description: "Path to the JAR of the application to be analyzed" + description: Path to the JAR of the application to be analyzed required: true cg: - description: "Selection of call graph for analysis (possible values are CHA, SPARK, SPARKLIB)" + description: Selection of call graph for analysis (possible values are CHA, SPARK, SPARKLIB) sootPath: - description: "Absolute path of the whole project" + description: Absolute path of the whole project identifier: - description: "Identifier for labeling output files" + description: Identifier for labeling output files reportPath: - description: "Directory Location for cryptoanalysis reports" + description: Directory Location for cryptoanalysis reports reportFormat: - description: "format of crytoanalysis reports (possible values are CMD, TXT, SARIF, CSV, CSV_SUMMARY)" + description: format of crytoanalysis reports (possible values are CMD, TXT, SARIF, CSV, CSV_SUMMARY) preanalysis: - description: "Enables pre-analysis" + description: Enables pre-analysis visualization : - description: "Enables the visualization, but also requires reportPath option to be set" + description: Enables the visualization, but also requires reportPath option to be set providerDetection : - description: "Enables provider detection analysis" + description: Enables provider detection analysis dstats: - description: "Disables the output of the analysis statistics in the reports" + description: Disables the output of the analysis statistics in the reports + forbiddenPredicates: + description: Facilitates the specification of forbidden predicates. + Any occurrence will be flagged by the analysis. + This input expects a path to a file containing one predicate per line + ignoreSections: + description: Names of packages, classes and methods to be ignored during the analysis. + This input expects path to a file containing one name per line. + For example, 'de.example.testClass' ignores the class 'testClass', + 'de.example.exampleClass.exampleMethod ignores the method 'exampleMethod' in 'exampleClass', + and 'de.example.*' ignores all classes and methods in the package 'example'. + Using this option may increase the analysis performance. + Note that constructors are methods that can be specified with ''. runs: using: "composite" @@ -115,7 +128,9 @@ runs: ${{ inputs.preanalysis == 'true' && '--preanalysis' || '' }} \ ${{ inputs.visualization == 'true' && '--visualization ' || '' }} \ ${{ inputs.providerDetection == 'true' && '--providerDetection ' || '' }} \ - ${{ inputs.dstats == 'true' && '--dstats' || '' }} + ${{ inputs.dstats == 'true' && '--dstats' || '' }} \ + ${{ inputs.forbiddenPredicates && format('{0} {1}', '--forbiddenPredicates', inputs.forbiddenPredicates) || '' }} \ + ${{ inputs.ignoreSections && format('{0} {1}', '--ignoreSections', inputs.ignoreSections) || '' }} shell: bash # workaround for https://github.com/actions/runner/issues/665 env: From 7ace2b1f372b06a5f5eddc7501e79e1d2e2a7ffb Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Fri, 12 Jan 2024 14:24:10 +0100 Subject: [PATCH 25/25] use comma for separating report formats in action --- action.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/action.yml b/action.yml index 73885420a..9b200d4a0 100644 --- a/action.yml +++ b/action.yml @@ -19,7 +19,9 @@ inputs: reportPath: description: Directory Location for cryptoanalysis reports reportFormat: - description: format of crytoanalysis reports (possible values are CMD, TXT, SARIF, CSV, CSV_SUMMARY) + description: The format of the report. Possible values are CMD, TXT, SARIF, CSV and CSV_SUMMARY. + Multiple formats should be split with a comma (e.g. CMD,TXT,CSV). + Running this action will also implicitly enable the output as GitHub Annotations. preanalysis: description: Enables pre-analysis visualization : @@ -120,7 +122,7 @@ runs: java -cp CryptoAnalysisBuild_98e04be6/CryptoAnalysis.jar crypto.HeadlessCryptoScanner \ --rulesDir ${{ inputs.rulesDir }} \ --appPath ${{ inputs.appPath }} \ - --reportFormat github_annotation ${{ inputs.reportFormat }} \ + --reportFormat github_annotation${{ inputs.reportFormat && format(',{0}', inputs.reportFormat) || '' }} \ ${{ inputs.cg && format('{0} {1}', '--cg', inputs.cg) || '' }} \ ${{ inputs.sootPath && format('{0} {1}', '--sootPath', inputs.sootPath) || '' }} \ ${{ inputs.identifier && format('{0} {1}', '--identifier', inputs.identifier) || '' }} \