Skip to content

Commit

Permalink
Merge pull request #8 from rusakovichma/multiple-threat-modeling-file…
Browse files Browse the repository at this point in the history
…s-support

Multiple threat modeling files support
  • Loading branch information
rusakovichma committed Jul 3, 2022
2 parents c86146c + 9957dc1 commit 40a1757
Show file tree
Hide file tree
Showing 13 changed files with 329 additions and 69 deletions.
20 changes: 14 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,26 @@ All the tool is needed is a <strong>data flow code file</strong> described in th
More detailed instructions can be found on the
[github wiki](https://github.com/rusakovichma/TicTaaC/wiki).
The latest CLI can be downloaded from github in the [releases section](https://github.com/rusakovichma/TicTaaC/releases). <br>
On *nix
<strong>On *nix:</strong>
```
$ ./bin/tic-taac.sh -h
$ ./bin/tic-taac.sh --out . --threatModel [path to threat model file]
$ ./bin/tic-taac.sh --out . --threatModel [path to threat model file(s) or folder to scan]
```
On Windows
<strong>On Windows:</strong>
```
> .\bin\tic-taac.bat -h
> .\bin\tic-taac.bat --out . --threatModel [path to threat model file]
> .\bin\tic-taac.bat --out . --threatModel [path to threat model file(s) or folder to scan]
```

### Docker
See [TicTaaC Docker Hub repository](https://hub.docker.com/r/rusakovichma/tic-taac).

<strong>Quickstart on Windows:</strong>
```
> docker run --volume /D/threat-model:/threat-model --volume /D/report:/report rusakovichma/tic-taac:latest --threatModel /threat-model/ --out /report
```

<strong>*nix script:</strong>
```console
#!/bin/sh

Expand All @@ -50,12 +57,13 @@ docker run --rm \
--volume $THREAT_MODEL_DIR:/threat-model:z \
--volume $(pwd)/report:/report:z \
rusakovichma/tic-taac:$TT_VERSION \
--threatModel /threat-model/simpest-threat-model.yml \
--threatModel /threat-model \
--outFormat html \
--out /report
# Set mitigation strategy for the corresponding threats
# see https://github.com/rusakovichma/TicTaaC/blob/master/expl/mitigations.yml
# --mitigations /threat-model/mitigations.yml
# --mitigations /threat-model/mitigations.yml
# or set the folder where scan the mitigations files: --mitigations /mitigations
```
### Jenkins pipeline
For TicTaaC usage at Jenkins pipeline, see [Jenkinsfile example](https://github.com/rusakovichma/TicTaaC/blob/master/cicd/Jenkinsfile).
Expand Down
9 changes: 8 additions & 1 deletion RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
# Release Notes
# Release Notes

## [Version 1.2.3](https://github.com/jeremylong/DependencyCheck/releases/tag/v7.1.1) (2022-07-03)

**Changes**

- Bug fixes.
- Multiple Threat Modeling files support and files scan ([see #1](https://github.com/rusakovichma/TicTaaC/issues/1)).
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.github.rusakovichma</groupId>
<artifactId>tictaac</artifactId>
<version>1.1.4</version>
<version>1.2.3</version>

<name>TicTaaC</name>
<url>https://github.com/rusakovichma/TicTaaC.git</url>
Expand All @@ -30,7 +30,7 @@
<connection>scm:git:https://github.com/rusakovichma/TicTaaC.git</connection>
<url>https://github.com/rusakovichma/TicTaaC</url>
<developerConnection>scm:git:https://github.com/rusakovichma/TicTaaC.git</developerConnection>
<tag>v1.0.3</tag>
<tag>v1.2.3</tag>
</scm>
<issueManagement>
<system>github</system>
Expand Down
135 changes: 100 additions & 35 deletions src/main/java/com/github/rusakovichma/tictaac/Launcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,23 @@
import com.github.rusakovichma.tictaac.provider.mitigation.*;
import com.github.rusakovichma.tictaac.provider.model.StandardThreatModelProvider;
import com.github.rusakovichma.tictaac.provider.model.ThreatModelProvider;
import com.github.rusakovichma.tictaac.provider.reader.ThreatModelFilter;
import com.github.rusakovichma.tictaac.provider.rules.StandardThreatRulesProvider;
import com.github.rusakovichma.tictaac.provider.rules.ThreatRulesProvider;
import com.github.rusakovichma.tictaac.reporter.*;
import com.github.rusakovichma.tictaac.util.ConsoleUtil;
import com.github.rusakovichma.tictaac.util.FileUtil;
import com.github.rusakovichma.tictaac.util.ResourceUtil;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;

public class Launcher {

private static final String DEFAULT_REPORT_NAME = "threat-model-report";
private static final String OUT_PATH_DEFAULT = ".";
private static final ReportFormat DEFAULT_OUT_FORMAT = ReportFormat.html;
private static final String DEFAULT_THREAT_LIBRARY = "classpath:/threats-library/default-threats-library.yml";
Expand All @@ -64,6 +66,36 @@ private static ThreatModelProvider getThreatModel(Map<String, String> params) {

private static Mitigator getMitigator(Map<String, String> params) {
String mitigationsPath = params.get("mitigations");

String threatModelPath = params.get("threatModel");
if (threatModelPath != null || !threatModelPath.isEmpty()) {
String threatModelDir = FileUtil.getParentFolderFromFilePath(threatModelPath);
String threatModelName = FileUtil.getFilenameWithoutExtensionFromPath(threatModelPath);

File threatModelMitigationsFile = new File(
threatModelDir + File.separator + threatModelName + "-mitigations.yml");
if (threatModelMitigationsFile.exists()) {
mitigationsPath = threatModelMitigationsFile.getAbsolutePath();
}
}

if (mitigationsPath != null && !mitigationsPath.isEmpty()) {
File mitigationsFile = new File(mitigationsPath);
if (mitigationsFile.exists() && mitigationsFile.isDirectory()) {
if (threatModelPath != null || !threatModelPath.isEmpty()) {
String threatModelName = FileUtil.getFilenameWithoutExtensionFromPath(threatModelPath);

File threatModelMitigationsFile = new File(
mitigationsFile.getAbsolutePath() + File.separator + threatModelName + "-mitigations.yml");
if (threatModelMitigationsFile.exists()) {
mitigationsPath = threatModelMitigationsFile.getAbsolutePath();
} else {
mitigationsPath = null;
}
}
}
}

if (mitigationsPath == null) {
return new DullMitigator();
}
Expand All @@ -87,64 +119,97 @@ private static ThreatsReporter getThreatsReporter(Map<String, String> params) {
if (outFormat == null) {
outFormat = DEFAULT_OUT_FORMAT;
}

String reportName = DEFAULT_REPORT_NAME;
String threatModelPath = params.get("threatModel");
if (threatModelPath != null || !threatModelPath.isEmpty()) {
reportName = FileUtil.getFilenameWithoutExtensionFromPath(threatModelPath);
}

try {
return new FileStreamThreatsReporter(outPath, outFormat);
return new FileStreamThreatsReporter(outPath, reportName, outFormat);
} catch (FileNotFoundException ex) {
throw new IllegalStateException(ex);
}
}

private static void checkQualityGate(Map<String, String> params, Collection<Threat> threats) {
String failOnThreatRiskParam = params.get("failOnThreatRisk");
if (failOnThreatRiskParam != null) {
ThreatRisk qualityGate = ThreatRisk.fromString(failOnThreatRiskParam.trim());
private static void checkQualityGate(Map<String, List<String>> params, Map<String, Collection<Threat>> threats) {
List<String> failOnThreatRiskParam = params.get("failOnThreatRisk");
if (failOnThreatRiskParam != null && failOnThreatRiskParam.size() != 0) {
ThreatRisk qualityGate = ThreatRisk.fromString(failOnThreatRiskParam.get(0).trim());
if (qualityGate != ThreatRisk.Undefined) {
String notCompliantThreats = threats.stream()
.filter(threat -> threat.getRisk().getOrder() >= qualityGate.getOrder())
.filter(threat -> threat.getMitigationStatus() == MitigationStatus.NotMitigated)
.map(threat -> threat.getId())
.collect(Collectors.joining(", "));

if (notCompliantThreats != null && !notCompliantThreats.isEmpty()) {
throw new QualityGateFailed(String.format("Non-compliant threats found [%s]", notCompliantThreats));
StringBuilder nonComplianceAccumulator = new StringBuilder();

for (Map.Entry<String, Collection<Threat>> entry : threats.entrySet()) {
String notCompliantThreats = entry.getValue().stream()
.filter(threat -> threat.getRisk().getOrder() >= qualityGate.getOrder())
.filter(threat -> threat.getMitigationStatus() == MitigationStatus.NotMitigated)
.map(threat -> threat.getId())
.collect(Collectors.joining(", "));

if (notCompliantThreats != null && !notCompliantThreats.isEmpty()) {
nonComplianceAccumulator.append(String.format("%s: [%s]", entry.getKey(), notCompliantThreats))
.append(System.lineSeparator());
}
}

if (nonComplianceAccumulator.length() != 0) {
throw new QualityGateFailed(String.format("Non-compliant threats found: ",
nonComplianceAccumulator.toString()));
}
}
}
}

public static void main(String[] args) {
final Map<String, String> params = ConsoleUtil.getParamsMap(args);
if (params.isEmpty() || !ConsoleUtil.hasOnlyAllowed(params)
|| params.containsKey("help") || params.containsKey("-h")) {
final Map<String, List<String>> multipleValueParams = ConsoleUtil.getParamsMap(args);
if (multipleValueParams.isEmpty() || !ConsoleUtil.hasOnlyAllowed(multipleValueParams)
|| multipleValueParams.containsKey("help") || multipleValueParams.containsKey("-h")) {
System.out.println(ResourceUtil.readResource("/help-info"));
System.exit(0);
}

if (params.containsKey("version") || params.containsKey("-v")) {
if (multipleValueParams.containsKey("version") || multipleValueParams.containsKey("-v")) {
System.out.println(ResourceUtil.readResource("/version"));
System.exit(0);
}

ThreatRulesProvider rulesProvider = getRulesProvider(params);
Mitigator mitigator = getMitigator(params);
List<String> threatModelsParams = multipleValueParams.get("threatModel");

ThreatEngine threatEngine = getThreatEngine(rulesProvider, mitigator);
if (threatModelsParams == null || threatModelsParams.isEmpty()) {
throw new IllegalStateException("Threat modeling files: '--threatModel %path_to_files_or_folder%' parameters should be provided");
}

ThreatModelProvider threatModelProvider = getThreatModel(params);
ThreatsCollection threats = threatEngine.generateThreats(threatModelProvider.getModel());
List<String> threatModelsFilesOnlyParams = FileUtil.extractFiles(threatModelsParams, new ThreatModelFilter());
Map<String, Collection<Threat>> modelThreats = new LinkedHashMap<>();

try {
getThreatsReporter(params).publish(
new ReportHeader(
threats.getName(), threats.getVersion(), new Date()
),
threats.getThreats()
);
} catch (IOException ex) {
throw new IllegalStateException("Cannot write threat model report to the file [" + params.get("out") + "]", ex);
for (String threatModelsParam : threatModelsFilesOnlyParams) {
Map<String, String> singleValueParams = ConsoleUtil.copySingleValueParamsWithDefinedArg(multipleValueParams,
"threatModel", threatModelsParam);

ThreatRulesProvider rulesProvider = getRulesProvider(singleValueParams);
Mitigator mitigator = getMitigator(singleValueParams);

ThreatEngine threatEngine = getThreatEngine(rulesProvider, mitigator);

ThreatModelProvider threatModelProvider = getThreatModel(singleValueParams);
ThreatsCollection threats = threatEngine.generateThreats(threatModelProvider.getModel());

modelThreats.put(threatModelsParam, threats.getThreats());

try {
getThreatsReporter(singleValueParams).publish(
new ReportHeader(
threats.getName(), threats.getVersion(), new Date()
),
threats.getThreats()
);
} catch (IOException ex) {
throw new IllegalStateException("Cannot write threat model report to the path [" + singleValueParams.get("out") + "]", ex);
}
}

checkQualityGate(params, threats.getThreats());
checkQualityGate(multipleValueParams, modelThreats);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.github.rusakovichma.tictaac.provider.reader;

import com.github.rusakovichma.tictaac.util.FileUtil;

import java.io.File;
import java.io.FileFilter;

public class MitigationsFilter implements FileFilter {

@Override
public boolean accept(File mitigationsFile) {
if (!mitigationsFile.getName().toLowerCase().endsWith(".yml")) {
return false;
}
return FileUtil.findString(mitigationsFile, "mitigated:");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.github.rusakovichma.tictaac.provider.reader;

import com.github.rusakovichma.tictaac.util.FileUtil;

import java.io.File;
import java.io.FileFilter;

public class ThreatModelFilter implements FileFilter {

@Override
public boolean accept(File modelFile) {
if (!modelFile.getName().toLowerCase().endsWith(".yml")) {
return false;
}
return FileUtil.findString(modelFile, "elements:")
&& FileUtil.findString(modelFile, "data-flows:");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,12 @@

public class FileStreamThreatsReporter extends StreamThreatsReporter {

private static final String REPORT_NAME = "threat-model-report";

public FileStreamThreatsReporter(String reportOut, ReportFormat reportFormat)
public FileStreamThreatsReporter(String reportOut, String reportName, ReportFormat reportFormat)
throws FileNotFoundException {
super(new FileOutputStream(new File(
new StringBuilder(reportOut)
.append(File.separator)
.append(REPORT_NAME)
.append(reportName)
.append(".")
.append(reportFormat.name())
.toString())),
Expand Down
Loading

0 comments on commit 40a1757

Please sign in to comment.