diff --git a/pkg/report/graphviz.go b/pkg/report/graphviz.go index 8180734e..fffe9c67 100644 --- a/pkg/report/graphviz.go +++ b/pkg/report/graphviz.go @@ -543,7 +543,7 @@ func WriteDataAssetDiagramGraphvizDOT(parsedModel *types.Model, diagramFilenameD dataAssets = append(dataAssets, dataAsset) } - types.SortByDataAssetDataBreachProbabilityAndTitle(parsedModel, dataAssets) + sortByDataAssetDataBreachProbabilityAndTitle(parsedModel, dataAssets) for _, dataAsset := range dataAssets { dotContent.WriteString(makeDataAssetNode(parsedModel, dataAsset)) dotContent.WriteString("\n") @@ -584,9 +584,20 @@ func WriteDataAssetDiagramGraphvizDOT(parsedModel *types.Model, diagramFilenameD return file, nil } +func sortByDataAssetDataBreachProbabilityAndTitle(parsedModel *types.Model, assets []*types.DataAsset) { + sort.Slice(assets, func(i, j int) bool { + highestDataBreachProbabilityLeft := assets[i].IdentifiedDataBreachProbability(parsedModel) + highestDataBreachProbabilityRight := assets[j].IdentifiedDataBreachProbability(parsedModel) + if highestDataBreachProbabilityLeft == highestDataBreachProbabilityRight { + return assets[i].Title < assets[j].Title + } + return highestDataBreachProbabilityLeft > highestDataBreachProbabilityRight + }) +} + func makeDataAssetNode(parsedModel *types.Model, dataAsset *types.DataAsset) string { var color string - switch dataAsset.IdentifiedDataBreachProbabilityStillAtRisk(parsedModel) { + switch identifiedDataBreachProbabilityStillAtRisk(parsedModel, dataAsset) { case types.Probable: color = rgbHexColorHighRisk() case types.Possible: @@ -596,7 +607,7 @@ func makeDataAssetNode(parsedModel *types.Model, dataAsset *types.DataAsset) str default: color = "#444444" // since black is too dark here as fill color } - if !dataAsset.IsDataBreachPotentialStillAtRisk(parsedModel) { + if !isDataBreachPotentialStillAtRisk(parsedModel, dataAsset) { color = "#444444" // since black is too dark here as fill color } return " " + hash(dataAsset.Id) + ` [ label=<` + encode(dataAsset.Title) + `> penwidth="3.0" style="filled" fillcolor="` + color + `" color="` + color + "\"\n ]; " @@ -621,7 +632,7 @@ func makeTechAssetNode(parsedModel *types.Model, technicalAsset *types.Technical default: color = "#444444" // since black is too dark here as fill color } - if len(types.ReduceToOnlyStillAtRisk(parsedModel, generatedRisks)) == 0 { + if len(types.ReduceToOnlyStillAtRisk(generatedRisks)) == 0 { color = "#444444" // since black is too dark here as fill color } } diff --git a/pkg/report/json.go b/pkg/report/json.go index 1bd73ce4..94732a64 100644 --- a/pkg/report/json.go +++ b/pkg/report/json.go @@ -9,15 +9,6 @@ import ( ) func WriteRisksJSON(parsedModel *types.Model, filename string) error { - /* - remainingRisks := make([]model.Risk, 0) - for _, category := range model.SortedRiskCategories() { - risks := model.SortedRisksOfCategory(category) - for _, risk := range model.ReduceToOnlyStillAtRisk(risks) { - remainingRisks = append(remainingRisks, risk) - } - } - */ jsonBytes, err := json.Marshal(types.AllRisks(parsedModel)) if err != nil { return fmt.Errorf("failed to marshal risks to JSON: %w", err) @@ -44,7 +35,7 @@ func WriteTechnicalAssetsJSON(parsedModel *types.Model, filename string) error { } func WriteStatsJSON(parsedModel *types.Model, filename string) error { - jsonBytes, err := json.Marshal(types.OverallRiskStatistics(parsedModel)) + jsonBytes, err := json.Marshal(overallRiskStatistics(parsedModel)) if err != nil { return fmt.Errorf("failed to marshal stats to JSON: %w", err) } @@ -54,3 +45,49 @@ func WriteStatsJSON(parsedModel *types.Model, filename string) error { } return nil } + +func overallRiskStatistics(parsedModel *types.Model) types.RiskStatistics { + result := types.RiskStatistics{} + result.Risks = make(map[string]map[string]int) + result.Risks[types.CriticalSeverity.String()] = make(map[string]int) + result.Risks[types.CriticalSeverity.String()][types.Unchecked.String()] = 0 + result.Risks[types.CriticalSeverity.String()][types.InDiscussion.String()] = 0 + result.Risks[types.CriticalSeverity.String()][types.Accepted.String()] = 0 + result.Risks[types.CriticalSeverity.String()][types.InProgress.String()] = 0 + result.Risks[types.CriticalSeverity.String()][types.Mitigated.String()] = 0 + result.Risks[types.CriticalSeverity.String()][types.FalsePositive.String()] = 0 + result.Risks[types.HighSeverity.String()] = make(map[string]int) + result.Risks[types.HighSeverity.String()][types.Unchecked.String()] = 0 + result.Risks[types.HighSeverity.String()][types.InDiscussion.String()] = 0 + result.Risks[types.HighSeverity.String()][types.Accepted.String()] = 0 + result.Risks[types.HighSeverity.String()][types.InProgress.String()] = 0 + result.Risks[types.HighSeverity.String()][types.Mitigated.String()] = 0 + result.Risks[types.HighSeverity.String()][types.FalsePositive.String()] = 0 + result.Risks[types.ElevatedSeverity.String()] = make(map[string]int) + result.Risks[types.ElevatedSeverity.String()][types.Unchecked.String()] = 0 + result.Risks[types.ElevatedSeverity.String()][types.InDiscussion.String()] = 0 + result.Risks[types.ElevatedSeverity.String()][types.Accepted.String()] = 0 + result.Risks[types.ElevatedSeverity.String()][types.InProgress.String()] = 0 + result.Risks[types.ElevatedSeverity.String()][types.Mitigated.String()] = 0 + result.Risks[types.ElevatedSeverity.String()][types.FalsePositive.String()] = 0 + result.Risks[types.MediumSeverity.String()] = make(map[string]int) + result.Risks[types.MediumSeverity.String()][types.Unchecked.String()] = 0 + result.Risks[types.MediumSeverity.String()][types.InDiscussion.String()] = 0 + result.Risks[types.MediumSeverity.String()][types.Accepted.String()] = 0 + result.Risks[types.MediumSeverity.String()][types.InProgress.String()] = 0 + result.Risks[types.MediumSeverity.String()][types.Mitigated.String()] = 0 + result.Risks[types.MediumSeverity.String()][types.FalsePositive.String()] = 0 + result.Risks[types.LowSeverity.String()] = make(map[string]int) + result.Risks[types.LowSeverity.String()][types.Unchecked.String()] = 0 + result.Risks[types.LowSeverity.String()][types.InDiscussion.String()] = 0 + result.Risks[types.LowSeverity.String()][types.Accepted.String()] = 0 + result.Risks[types.LowSeverity.String()][types.InProgress.String()] = 0 + result.Risks[types.LowSeverity.String()][types.Mitigated.String()] = 0 + result.Risks[types.LowSeverity.String()][types.FalsePositive.String()] = 0 + for _, risks := range parsedModel.GeneratedRisksByCategory { + for _, risk := range risks { + result.Risks[risk.Severity.String()][risk.RiskStatus.String()]++ + } + } + return result +} diff --git a/pkg/report/report.go b/pkg/report/report.go index 521ee4a4..1782d964 100644 --- a/pkg/report/report.go +++ b/pkg/report/report.go @@ -225,7 +225,7 @@ func (r *pdfReporter) createTableOfContents(parsedModel *types.Model) { risksStr := "Risks" catStr := "Categories" - count, catCount := types.TotalRiskCount(parsedModel), len(parsedModel.GeneratedRisksByCategory) + count, catCount := totalRiskCount(parsedModel), len(parsedModel.GeneratedRisksByCategory) if count == 1 { risksStr = "Risk" } @@ -247,7 +247,7 @@ func (r *pdfReporter) createTableOfContents(parsedModel *types.Model) { y += 6 risksStr = "Risks" catStr = "Categories" - count, catCount = len(types.FilteredByStillAtRisk(parsedModel)), len(types.CategoriesOfOnlyRisksStillAtRisk(parsedModel, parsedModel.GeneratedRisksByCategory)) + count, catCount = len(filteredByStillAtRisk(parsedModel)), len(reduceToOnlyStillAtRisk(parsedModel.GeneratedRisksByCategory)) if count == 1 { risksStr = "Risk" } @@ -338,13 +338,13 @@ func (r *pdfReporter) createTableOfContents(parsedModel *types.Model) { r.pdf.Link(10, y-5, 172.5, 6.5, r.pdf.AddLink()) y += 6 - modelFailures := types.FlattenRiskSlice(types.FilterByModelFailures(parsedModel, parsedModel.GeneratedRisksByCategory)) + modelFailures := flattenRiskSlice(filterByModelFailures(parsedModel, parsedModel.GeneratedRisksByCategory)) risksStr = "Risks" count = len(modelFailures) if count == 1 { risksStr = "Risk" } - countStillAtRisk := len(types.ReduceToOnlyStillAtRisk(parsedModel, modelFailures)) + countStillAtRisk := len(types.ReduceToOnlyStillAtRisk(modelFailures)) if countStillAtRisk > 0 { colorModelFailure(r.pdf) } @@ -403,7 +403,7 @@ func (r *pdfReporter) createTableOfContents(parsedModel *types.Model) { default: r.pdfColorBlack() } - if len(types.ReduceToOnlyStillAtRisk(parsedModel, newRisksStr)) == 0 { + if len(types.ReduceToOnlyStillAtRisk(newRisksStr)) == 0 { r.pdfColorBlack() } y += 6 @@ -411,7 +411,7 @@ func (r *pdfReporter) createTableOfContents(parsedModel *types.Model) { r.pageBreakInLists() y = 40 } - countStillAtRisk := len(types.ReduceToOnlyStillAtRisk(parsedModel, newRisksStr)) + countStillAtRisk := len(types.ReduceToOnlyStillAtRisk(newRisksStr)) suffix := strconv.Itoa(countStillAtRisk) + " / " + strconv.Itoa(len(newRisksStr)) + " Risk" if len(newRisksStr) != 1 { suffix += "s" @@ -449,7 +449,7 @@ func (r *pdfReporter) createTableOfContents(parsedModel *types.Model) { r.pageBreakInLists() y = 40 } - countStillAtRisk := len(types.ReduceToOnlyStillAtRisk(parsedModel, newRisksStr)) + countStillAtRisk := len(types.ReduceToOnlyStillAtRisk(newRisksStr)) suffix := strconv.Itoa(countStillAtRisk) + " / " + strconv.Itoa(len(newRisksStr)) + " Risk" if len(newRisksStr) != 1 { suffix += "s" @@ -472,7 +472,7 @@ func (r *pdfReporter) createTableOfContents(parsedModel *types.Model) { default: r.pdfColorBlack() } - if len(types.ReduceToOnlyStillAtRisk(parsedModel, newRisksStr)) == 0 { + if len(types.ReduceToOnlyStillAtRisk(newRisksStr)) == 0 { r.pdfColorBlack() } } @@ -509,12 +509,12 @@ func (r *pdfReporter) createTableOfContents(parsedModel *types.Model) { y = 40 } newRisksStr := dataAsset.IdentifiedDataBreachProbabilityRisks(parsedModel) - countStillAtRisk := len(types.ReduceToOnlyStillAtRisk(parsedModel, newRisksStr)) + countStillAtRisk := len(types.ReduceToOnlyStillAtRisk(newRisksStr)) suffix := strconv.Itoa(countStillAtRisk) + " / " + strconv.Itoa(len(newRisksStr)) + " Risk" if len(newRisksStr) != 1 { suffix += "s" } - switch dataAsset.IdentifiedDataBreachProbabilityStillAtRisk(parsedModel) { + switch identifiedDataBreachProbabilityStillAtRisk(parsedModel, dataAsset) { case types.Probable: colorHighRisk(r.pdf) case types.Possible: @@ -524,7 +524,7 @@ func (r *pdfReporter) createTableOfContents(parsedModel *types.Model) { default: r.pdfColorBlack() } - if !dataAsset.IsDataBreachPotentialStillAtRisk(parsedModel) { + if !isDataBreachPotentialStillAtRisk(parsedModel, dataAsset) { r.pdfColorBlack() } r.pdf.Text(11, y, " "+uni(dataAsset.Title)+": "+suffix) @@ -642,20 +642,68 @@ func sortedTechnicalAssetsByRiskSeverityAndTitle(parsedModel *types.Model) []*ty for _, asset := range parsedModel.TechnicalAssets { assets = append(assets, asset) } - types.SortByTechnicalAssetRiskSeverityAndTitleStillAtRisk(assets, parsedModel) + sortByTechnicalAssetRiskSeverityAndTitleStillAtRisk(assets, parsedModel) return assets } +func sortByTechnicalAssetRiskSeverityAndTitleStillAtRisk(assets []*types.TechnicalAsset, parsedModel *types.Model) { + sort.Slice(assets, func(i, j int) bool { + risksLeft := types.ReduceToOnlyStillAtRisk(assets[i].GeneratedRisks(parsedModel)) + risksRight := types.ReduceToOnlyStillAtRisk(assets[j].GeneratedRisks(parsedModel)) + highestSeverityLeft := types.HighestSeverityStillAtRisk(parsedModel, risksLeft) + highestSeverityRight := types.HighestSeverityStillAtRisk(parsedModel, risksRight) + var result bool + if highestSeverityLeft == highestSeverityRight { + if len(risksLeft) == 0 && len(risksRight) > 0 { + return false + } else if len(risksLeft) > 0 && len(risksRight) == 0 { + return true + } else { + result = assets[i].Title < assets[j].Title + } + } else { + result = highestSeverityLeft > highestSeverityRight + } + if assets[i].OutOfScope && assets[j].OutOfScope { + result = assets[i].Title < assets[j].Title + } else if assets[i].OutOfScope { + result = false + } else if assets[j].OutOfScope { + result = true + } + return result + }) +} + func sortedDataAssetsByDataBreachProbabilityAndTitle(parsedModel *types.Model) []*types.DataAsset { assets := make([]*types.DataAsset, 0) for _, asset := range parsedModel.DataAssets { assets = append(assets, asset) } - types.SortByDataAssetDataBreachProbabilityAndTitleStillAtRisk(parsedModel, assets) + sortByDataAssetDataBreachProbabilityAndTitleStillAtRisk(parsedModel, assets) return assets } +func sortByDataAssetDataBreachProbabilityAndTitleStillAtRisk(parsedModel *types.Model, assets []*types.DataAsset) { + sort.Slice(assets, func(i, j int) bool { + risksLeft := identifiedDataBreachProbabilityRisksStillAtRisk(parsedModel, assets[i]) + risksRight := identifiedDataBreachProbabilityRisksStillAtRisk(parsedModel, assets[j]) + highestDataBreachProbabilityLeft := identifiedDataBreachProbabilityStillAtRisk(parsedModel, assets[i]) + highestDataBreachProbabilityRight := identifiedDataBreachProbabilityStillAtRisk(parsedModel, assets[j]) + if highestDataBreachProbabilityLeft == highestDataBreachProbabilityRight { + if len(risksLeft) == 0 && len(risksRight) > 0 { + return false + } + if len(risksLeft) > 0 && len(risksRight) == 0 { + return true + } + return assets[i].Title < assets[j].Title + } + return highestDataBreachProbabilityLeft > highestDataBreachProbabilityRight + }) +} + func (r *pdfReporter) defineLinkTarget(alias string) { pageNumbStr := strconv.Itoa(r.pdf.PageNo()) if len(pageNumbStr) == 1 { @@ -728,18 +776,18 @@ func (r *pdfReporter) createManagementSummary(parsedModel *types.Model, tempFold r.addHeadline(title, false) r.defineLinkTarget("{management-summary}") r.currentChapterTitleBreadcrumb = title - countCritical := len(types.FilteredByOnlyCriticalRisks(parsedModel)) - countHigh := len(types.FilteredByOnlyHighRisks(parsedModel)) - countElevated := len(types.FilteredByOnlyElevatedRisks(parsedModel)) - countMedium := len(types.FilteredByOnlyMediumRisks(parsedModel)) - countLow := len(types.FilteredByOnlyLowRisks(parsedModel)) - - countStatusUnchecked := len(types.FilteredByRiskTrackingUnchecked(parsedModel)) - countStatusInDiscussion := len(types.FilteredByRiskTrackingInDiscussion(parsedModel)) - countStatusAccepted := len(types.FilteredByRiskTrackingAccepted(parsedModel)) - countStatusInProgress := len(types.FilteredByRiskTrackingInProgress(parsedModel)) - countStatusMitigated := len(types.FilteredByRiskTrackingMitigated(parsedModel)) - countStatusFalsePositive := len(types.FilteredByRiskTrackingFalsePositive(parsedModel)) + countCritical := len(filteredBySeverity(parsedModel, types.CriticalSeverity)) + countHigh := len(filteredBySeverity(parsedModel, types.HighSeverity)) + countElevated := len(filteredBySeverity(parsedModel, types.ElevatedSeverity)) + countMedium := len(filteredBySeverity(parsedModel, types.MediumSeverity)) + countLow := len(filteredBySeverity(parsedModel, types.LowSeverity)) + + countStatusUnchecked := len(filteredByRiskStatus(parsedModel, types.Unchecked)) + countStatusInDiscussion := len(filteredByRiskStatus(parsedModel, types.InDiscussion)) + countStatusAccepted := len(filteredByRiskStatus(parsedModel, types.Accepted)) + countStatusInProgress := len(filteredByRiskStatus(parsedModel, types.InProgress)) + countStatusMitigated := len(filteredByRiskStatus(parsedModel, types.Mitigated)) + countStatusFalsePositive := len(filteredByRiskStatus(parsedModel, types.FalsePositive)) html := r.pdf.HTMLBasicNew() html.Write(5, "Threagile toolkit was used to model the architecture of \""+uni(parsedModel.Title)+"\" "+ @@ -755,7 +803,7 @@ func (r *pdfReporter) createManagementSummary(parsedModel *types.Model, tempFold "the application in a Defense-in-Depth approach. Additionally, for each risk finding a "+ "link towards a matching OWASP Cheat Sheet or similar with technical details about how to implement a mitigation is given."+ "

"+ - "In total "+strconv.Itoa(types.TotalRiskCount(parsedModel))+" initial risks in "+strconv.Itoa(len(parsedModel.GeneratedRisksByCategory))+" categories have "+ + "In total "+strconv.Itoa(totalRiskCount(parsedModel))+" initial risks in "+strconv.Itoa(len(parsedModel.GeneratedRisksByCategory))+" categories have "+ "been identified during the threat modeling process:

") // TODO plural singular stuff risk/s category/ies has/have r.pdf.SetFont("Helvetica", "B", fontSizeBody) @@ -918,7 +966,7 @@ func (r *pdfReporter) createManagementSummary(parsedModel *types.Model, tempFold func (r *pdfReporter) createRiskMitigationStatus(parsedModel *types.Model, tempFolder string) error { r.pdf.SetTextColor(0, 0, 0) - stillAtRisk := types.FilteredByStillAtRisk(parsedModel) + stillAtRisk := filteredByStillAtRisk(parsedModel) count := len(stillAtRisk) title := "Risk Mitigation" r.addHeadline(title, false) @@ -928,18 +976,18 @@ func (r *pdfReporter) createRiskMitigationStatus(parsedModel *types.Model, tempF html := r.pdf.HTMLBasicNew() html.Write(5, "The following chart gives a high-level overview of the risk tracking status (including mitigated risks):") - risksCritical := types.FilteredByOnlyCriticalRisks(parsedModel) - risksHigh := types.FilteredByOnlyHighRisks(parsedModel) - risksElevated := types.FilteredByOnlyElevatedRisks(parsedModel) - risksMedium := types.FilteredByOnlyMediumRisks(parsedModel) - risksLow := types.FilteredByOnlyLowRisks(parsedModel) + risksCritical := filteredBySeverity(parsedModel, types.CriticalSeverity) + risksHigh := filteredBySeverity(parsedModel, types.HighSeverity) + risksElevated := filteredBySeverity(parsedModel, types.ElevatedSeverity) + risksMedium := filteredBySeverity(parsedModel, types.MediumSeverity) + risksLow := filteredBySeverity(parsedModel, types.LowSeverity) - countStatusUnchecked := len(types.FilteredByRiskTrackingUnchecked(parsedModel)) - countStatusInDiscussion := len(types.FilteredByRiskTrackingInDiscussion(parsedModel)) - countStatusAccepted := len(types.FilteredByRiskTrackingAccepted(parsedModel)) - countStatusInProgress := len(types.FilteredByRiskTrackingInProgress(parsedModel)) - countStatusMitigated := len(types.FilteredByRiskTrackingMitigated(parsedModel)) - countStatusFalsePositive := len(types.FilteredByRiskTrackingFalsePositive(parsedModel)) + countStatusUnchecked := len(filteredByRiskStatus(parsedModel, types.Unchecked)) + countStatusInDiscussion := len(filteredByRiskStatus(parsedModel, types.InDiscussion)) + countStatusAccepted := len(filteredByRiskStatus(parsedModel, types.Accepted)) + countStatusInProgress := len(filteredByRiskStatus(parsedModel, types.InProgress)) + countStatusMitigated := len(filteredByRiskStatus(parsedModel, types.Mitigated)) + countStatusFalsePositive := len(filteredByRiskStatus(parsedModel, types.FalsePositive)) stackedBarChartRiskTracking := chart.StackedBarChart{ Width: 4000, @@ -951,17 +999,17 @@ func (r *pdfReporter) createRiskMitigationStatus(parsedModel *types.Model, tempF Name: types.LowSeverity.Title(), Width: 130, Values: []chart.Value{ - {Value: float64(len(types.ReduceToOnlyRiskTrackingUnchecked(parsedModel, risksLow))), Label: types.Unchecked.Title(), + {Value: float64(len(reduceToRiskStatus(risksLow, types.Unchecked))), Label: types.Unchecked.Title(), Style: chart.Style{FillColor: makeColor(RgbHexColorRiskStatusUnchecked()).WithAlpha(98), StrokeColor: drawing.ColorFromHex("999")}}, - {Value: float64(len(types.ReduceToOnlyRiskTrackingInDiscussion(parsedModel, risksLow))), Label: types.InDiscussion.Title(), + {Value: float64(len(reduceToRiskStatus(risksLow, types.InDiscussion))), Label: types.InDiscussion.Title(), Style: chart.Style{FillColor: makeColor(rgbHexColorRiskStatusInDiscussion()).WithAlpha(98), StrokeColor: drawing.ColorFromHex("999")}}, - {Value: float64(len(types.ReduceToOnlyRiskTrackingAccepted(parsedModel, risksLow))), Label: types.Accepted.Title(), + {Value: float64(len(reduceToRiskStatus(risksLow, types.Accepted))), Label: types.Accepted.Title(), Style: chart.Style{FillColor: makeColor(rgbHexColorRiskStatusAccepted()).WithAlpha(98), StrokeColor: drawing.ColorFromHex("999")}}, - {Value: float64(len(types.ReduceToOnlyRiskTrackingInProgress(parsedModel, risksLow))), Label: types.InProgress.Title(), + {Value: float64(len(reduceToRiskStatus(risksLow, types.InProgress))), Label: types.InProgress.Title(), Style: chart.Style{FillColor: makeColor(rgbHexColorRiskStatusInProgress()).WithAlpha(98), StrokeColor: drawing.ColorFromHex("999")}}, - {Value: float64(len(types.ReduceToOnlyRiskTrackingMitigated(parsedModel, risksLow))), Label: types.Mitigated.Title(), + {Value: float64(len(reduceToRiskStatus(risksLow, types.Mitigated))), Label: types.Mitigated.Title(), Style: chart.Style{FillColor: makeColor(rgbHexColorRiskStatusMitigated()).WithAlpha(98), StrokeColor: drawing.ColorFromHex("999")}}, - {Value: float64(len(types.ReduceToOnlyRiskTrackingFalsePositive(parsedModel, risksLow))), Label: types.FalsePositive.Title(), + {Value: float64(len(reduceToRiskStatus(risksLow, types.FalsePositive))), Label: types.FalsePositive.Title(), Style: chart.Style{FillColor: makeColor(rgbHexColorRiskStatusFalsePositive()).WithAlpha(98), StrokeColor: drawing.ColorFromHex("999")}}, }, }, @@ -969,17 +1017,17 @@ func (r *pdfReporter) createRiskMitigationStatus(parsedModel *types.Model, tempF Name: types.MediumSeverity.Title(), Width: 130, Values: []chart.Value{ - {Value: float64(len(types.ReduceToOnlyRiskTrackingUnchecked(parsedModel, risksMedium))), Label: types.Unchecked.Title(), + {Value: float64(len(reduceToRiskStatus(risksMedium, types.Unchecked))), Label: types.Unchecked.Title(), Style: chart.Style{FillColor: makeColor(RgbHexColorRiskStatusUnchecked()).WithAlpha(98), StrokeColor: drawing.ColorFromHex("999")}}, - {Value: float64(len(types.ReduceToOnlyRiskTrackingInDiscussion(parsedModel, risksMedium))), Label: types.InDiscussion.Title(), + {Value: float64(len(reduceToRiskStatus(risksMedium, types.InDiscussion))), Label: types.InDiscussion.Title(), Style: chart.Style{FillColor: makeColor(rgbHexColorRiskStatusInDiscussion()).WithAlpha(98), StrokeColor: drawing.ColorFromHex("999")}}, - {Value: float64(len(types.ReduceToOnlyRiskTrackingAccepted(parsedModel, risksMedium))), Label: types.Accepted.Title(), + {Value: float64(len(reduceToRiskStatus(risksMedium, types.Accepted))), Label: types.Accepted.Title(), Style: chart.Style{FillColor: makeColor(rgbHexColorRiskStatusAccepted()).WithAlpha(98), StrokeColor: drawing.ColorFromHex("999")}}, - {Value: float64(len(types.ReduceToOnlyRiskTrackingInProgress(parsedModel, risksMedium))), Label: types.InProgress.Title(), + {Value: float64(len(reduceToRiskStatus(risksMedium, types.InProgress))), Label: types.InProgress.Title(), Style: chart.Style{FillColor: makeColor(rgbHexColorRiskStatusInProgress()).WithAlpha(98), StrokeColor: drawing.ColorFromHex("999")}}, - {Value: float64(len(types.ReduceToOnlyRiskTrackingMitigated(parsedModel, risksMedium))), Label: types.Mitigated.Title(), + {Value: float64(len(reduceToRiskStatus(risksMedium, types.Mitigated))), Label: types.Mitigated.Title(), Style: chart.Style{FillColor: makeColor(rgbHexColorRiskStatusMitigated()).WithAlpha(98), StrokeColor: drawing.ColorFromHex("999")}}, - {Value: float64(len(types.ReduceToOnlyRiskTrackingFalsePositive(parsedModel, risksMedium))), Label: types.FalsePositive.Title(), + {Value: float64(len(reduceToRiskStatus(risksMedium, types.FalsePositive))), Label: types.FalsePositive.Title(), Style: chart.Style{FillColor: makeColor(rgbHexColorRiskStatusFalsePositive()).WithAlpha(98), StrokeColor: drawing.ColorFromHex("999")}}, }, }, @@ -987,17 +1035,17 @@ func (r *pdfReporter) createRiskMitigationStatus(parsedModel *types.Model, tempF Name: types.ElevatedSeverity.Title(), Width: 130, Values: []chart.Value{ - {Value: float64(len(types.ReduceToOnlyRiskTrackingUnchecked(parsedModel, risksElevated))), Label: types.Unchecked.Title(), + {Value: float64(len(reduceToRiskStatus(risksElevated, types.Unchecked))), Label: types.Unchecked.Title(), Style: chart.Style{FillColor: makeColor(RgbHexColorRiskStatusUnchecked()).WithAlpha(98), StrokeColor: drawing.ColorFromHex("999")}}, - {Value: float64(len(types.ReduceToOnlyRiskTrackingInDiscussion(parsedModel, risksElevated))), Label: types.InDiscussion.Title(), + {Value: float64(len(reduceToRiskStatus(risksElevated, types.InDiscussion))), Label: types.InDiscussion.Title(), Style: chart.Style{FillColor: makeColor(rgbHexColorRiskStatusInDiscussion()).WithAlpha(98), StrokeColor: drawing.ColorFromHex("999")}}, - {Value: float64(len(types.ReduceToOnlyRiskTrackingAccepted(parsedModel, risksElevated))), Label: types.Accepted.Title(), + {Value: float64(len(reduceToRiskStatus(risksElevated, types.Accepted))), Label: types.Accepted.Title(), Style: chart.Style{FillColor: makeColor(rgbHexColorRiskStatusAccepted()).WithAlpha(98), StrokeColor: drawing.ColorFromHex("999")}}, - {Value: float64(len(types.ReduceToOnlyRiskTrackingInProgress(parsedModel, risksElevated))), Label: types.InProgress.Title(), + {Value: float64(len(reduceToRiskStatus(risksElevated, types.InProgress))), Label: types.InProgress.Title(), Style: chart.Style{FillColor: makeColor(rgbHexColorRiskStatusInProgress()).WithAlpha(98), StrokeColor: drawing.ColorFromHex("999")}}, - {Value: float64(len(types.ReduceToOnlyRiskTrackingMitigated(parsedModel, risksElevated))), Label: types.Mitigated.Title(), + {Value: float64(len(reduceToRiskStatus(risksElevated, types.Mitigated))), Label: types.Mitigated.Title(), Style: chart.Style{FillColor: makeColor(rgbHexColorRiskStatusMitigated()).WithAlpha(98), StrokeColor: drawing.ColorFromHex("999")}}, - {Value: float64(len(types.ReduceToOnlyRiskTrackingFalsePositive(parsedModel, risksElevated))), Label: types.FalsePositive.Title(), + {Value: float64(len(reduceToRiskStatus(risksElevated, types.FalsePositive))), Label: types.FalsePositive.Title(), Style: chart.Style{FillColor: makeColor(rgbHexColorRiskStatusFalsePositive()).WithAlpha(98), StrokeColor: drawing.ColorFromHex("999")}}, }, }, @@ -1005,17 +1053,17 @@ func (r *pdfReporter) createRiskMitigationStatus(parsedModel *types.Model, tempF Name: types.HighSeverity.Title(), Width: 130, Values: []chart.Value{ - {Value: float64(len(types.ReduceToOnlyRiskTrackingUnchecked(parsedModel, risksHigh))), Label: types.Unchecked.Title(), + {Value: float64(len(reduceToRiskStatus(risksHigh, types.Unchecked))), Label: types.Unchecked.Title(), Style: chart.Style{FillColor: makeColor(RgbHexColorRiskStatusUnchecked()).WithAlpha(98), StrokeColor: drawing.ColorFromHex("999")}}, - {Value: float64(len(types.ReduceToOnlyRiskTrackingInDiscussion(parsedModel, risksHigh))), Label: types.InDiscussion.Title(), + {Value: float64(len(reduceToRiskStatus(risksHigh, types.InDiscussion))), Label: types.InDiscussion.Title(), Style: chart.Style{FillColor: makeColor(rgbHexColorRiskStatusInDiscussion()).WithAlpha(98), StrokeColor: drawing.ColorFromHex("999")}}, - {Value: float64(len(types.ReduceToOnlyRiskTrackingAccepted(parsedModel, risksHigh))), Label: types.Accepted.Title(), + {Value: float64(len(reduceToRiskStatus(risksHigh, types.Accepted))), Label: types.Accepted.Title(), Style: chart.Style{FillColor: makeColor(rgbHexColorRiskStatusAccepted()).WithAlpha(98), StrokeColor: drawing.ColorFromHex("999")}}, - {Value: float64(len(types.ReduceToOnlyRiskTrackingInProgress(parsedModel, risksHigh))), Label: types.InProgress.Title(), + {Value: float64(len(reduceToRiskStatus(risksHigh, types.InProgress))), Label: types.InProgress.Title(), Style: chart.Style{FillColor: makeColor(rgbHexColorRiskStatusInProgress()).WithAlpha(98), StrokeColor: drawing.ColorFromHex("999")}}, - {Value: float64(len(types.ReduceToOnlyRiskTrackingMitigated(parsedModel, risksHigh))), Label: types.Mitigated.Title(), + {Value: float64(len(reduceToRiskStatus(risksHigh, types.Mitigated))), Label: types.Mitigated.Title(), Style: chart.Style{FillColor: makeColor(rgbHexColorRiskStatusMitigated()).WithAlpha(98), StrokeColor: drawing.ColorFromHex("999")}}, - {Value: float64(len(types.ReduceToOnlyRiskTrackingFalsePositive(parsedModel, risksHigh))), Label: types.FalsePositive.Title(), + {Value: float64(len(reduceToRiskStatus(risksHigh, types.FalsePositive))), Label: types.FalsePositive.Title(), Style: chart.Style{FillColor: makeColor(rgbHexColorRiskStatusFalsePositive()).WithAlpha(98), StrokeColor: drawing.ColorFromHex("999")}}, }, }, @@ -1023,17 +1071,17 @@ func (r *pdfReporter) createRiskMitigationStatus(parsedModel *types.Model, tempF Name: types.CriticalSeverity.Title(), Width: 130, Values: []chart.Value{ - {Value: float64(len(types.ReduceToOnlyRiskTrackingUnchecked(parsedModel, risksCritical))), Label: types.Unchecked.Title(), + {Value: float64(len(reduceToRiskStatus(risksCritical, types.Unchecked))), Label: types.Unchecked.Title(), Style: chart.Style{FillColor: makeColor(RgbHexColorRiskStatusUnchecked()).WithAlpha(98), StrokeColor: drawing.ColorFromHex("999")}}, - {Value: float64(len(types.ReduceToOnlyRiskTrackingInDiscussion(parsedModel, risksCritical))), Label: types.InDiscussion.Title(), + {Value: float64(len(reduceToRiskStatus(risksCritical, types.InDiscussion))), Label: types.InDiscussion.Title(), Style: chart.Style{FillColor: makeColor(rgbHexColorRiskStatusInDiscussion()).WithAlpha(98), StrokeColor: drawing.ColorFromHex("999")}}, - {Value: float64(len(types.ReduceToOnlyRiskTrackingAccepted(parsedModel, risksCritical))), Label: types.Accepted.Title(), + {Value: float64(len(reduceToRiskStatus(risksCritical, types.Accepted))), Label: types.Accepted.Title(), Style: chart.Style{FillColor: makeColor(rgbHexColorRiskStatusAccepted()).WithAlpha(98), StrokeColor: drawing.ColorFromHex("999")}}, - {Value: float64(len(types.ReduceToOnlyRiskTrackingInProgress(parsedModel, risksCritical))), Label: types.InProgress.Title(), + {Value: float64(len(reduceToRiskStatus(risksCritical, types.InProgress))), Label: types.InProgress.Title(), Style: chart.Style{FillColor: makeColor(rgbHexColorRiskStatusInProgress()).WithAlpha(98), StrokeColor: drawing.ColorFromHex("999")}}, - {Value: float64(len(types.ReduceToOnlyRiskTrackingMitigated(parsedModel, risksCritical))), Label: types.Mitigated.Title(), + {Value: float64(len(reduceToRiskStatus(risksCritical, types.Mitigated))), Label: types.Mitigated.Title(), Style: chart.Style{FillColor: makeColor(rgbHexColorRiskStatusMitigated()).WithAlpha(98), StrokeColor: drawing.ColorFromHex("999")}}, - {Value: float64(len(types.ReduceToOnlyRiskTrackingFalsePositive(parsedModel, risksCritical))), Label: types.FalsePositive.Title(), + {Value: float64(len(reduceToRiskStatus(risksCritical, types.FalsePositive))), Label: types.FalsePositive.Title(), Style: chart.Style{FillColor: makeColor(rgbHexColorRiskStatusFalsePositive()).WithAlpha(98), StrokeColor: drawing.ColorFromHex("999")}}, }, }, @@ -1105,16 +1153,16 @@ func (r *pdfReporter) createRiskMitigationStatus(parsedModel *types.Model, tempF "After removal of risks with status mitigated and false positive "+ "the following "+strconv.Itoa(count)+" remain unmitigated:") - countCritical := len(types.ReduceToOnlyStillAtRisk(parsedModel, types.FilteredByOnlyCriticalRisks(parsedModel))) - countHigh := len(types.ReduceToOnlyStillAtRisk(parsedModel, types.FilteredByOnlyHighRisks(parsedModel))) - countElevated := len(types.ReduceToOnlyStillAtRisk(parsedModel, types.FilteredByOnlyElevatedRisks(parsedModel))) - countMedium := len(types.ReduceToOnlyStillAtRisk(parsedModel, types.FilteredByOnlyMediumRisks(parsedModel))) - countLow := len(types.ReduceToOnlyStillAtRisk(parsedModel, types.FilteredByOnlyLowRisks(parsedModel))) + countCritical := len(types.ReduceToOnlyStillAtRisk(filteredBySeverity(parsedModel, types.CriticalSeverity))) + countHigh := len(types.ReduceToOnlyStillAtRisk(filteredBySeverity(parsedModel, types.HighSeverity))) + countElevated := len(types.ReduceToOnlyStillAtRisk(filteredBySeverity(parsedModel, types.ElevatedSeverity))) + countMedium := len(types.ReduceToOnlyStillAtRisk(filteredBySeverity(parsedModel, types.MediumSeverity))) + countLow := len(types.ReduceToOnlyStillAtRisk(filteredBySeverity(parsedModel, types.LowSeverity))) - countBusinessSide := len(types.ReduceToOnlyStillAtRisk(parsedModel, types.FilteredByOnlyBusinessSide(parsedModel))) - countArchitecture := len(types.ReduceToOnlyStillAtRisk(parsedModel, types.FilteredByOnlyArchitecture(parsedModel))) - countDevelopment := len(types.ReduceToOnlyStillAtRisk(parsedModel, types.FilteredByOnlyDevelopment(parsedModel))) - countOperation := len(types.ReduceToOnlyStillAtRisk(parsedModel, types.FilteredByOnlyOperation(parsedModel))) + countBusinessSide := len(types.ReduceToOnlyStillAtRisk(filteredByRiskFunction(parsedModel, types.BusinessSide))) + countArchitecture := len(types.ReduceToOnlyStillAtRisk(filteredByRiskFunction(parsedModel, types.Architecture))) + countDevelopment := len(types.ReduceToOnlyStillAtRisk(filteredByRiskFunction(parsedModel, types.Development))) + countOperation := len(types.ReduceToOnlyStillAtRisk(filteredByRiskFunction(parsedModel, types.Operations))) pieChartRemainingRiskSeverity := chart.PieChart{ Width: 1500, @@ -1226,6 +1274,41 @@ func (r *pdfReporter) createRiskMitigationStatus(parsedModel *types.Model, tempF return nil } +func filteredByRiskStatus(parsedModel *types.Model, status types.RiskStatus) []*types.Risk { + filteredRisks := make([]*types.Risk, 0) + for _, risks := range parsedModel.GeneratedRisksByCategory { + for _, risk := range risks { + if risk.RiskStatus == status { + filteredRisks = append(filteredRisks, risk) + } + } + } + return filteredRisks +} + +func filteredByRiskFunction(parsedModel *types.Model, function types.RiskFunction) []*types.Risk { + filteredRisks := make([]*types.Risk, 0) + for categoryId, risks := range parsedModel.GeneratedRisksByCategory { + for _, risk := range risks { + category := types.GetRiskCategory(parsedModel, categoryId) + if category.Function == function { + filteredRisks = append(filteredRisks, risk) + } + } + } + return filteredRisks +} + +func reduceToRiskStatus(risks []*types.Risk, status types.RiskStatus) []*types.Risk { + filteredRisks := make([]*types.Risk, 0) + for _, risk := range risks { + if risk.RiskStatus == status { + filteredRisks = append(filteredRisks, risk) + } + } + return filteredRisks +} + // CAUTION: Long labels might cause endless loop, then remove labels and render them manually later inside the PDF func (r *pdfReporter) embedStackedBarChart(sbcChart chart.StackedBarChart, x float64, y float64, tempFolder string) error { tmpFilePNG, err := os.CreateTemp(tempFolder, "chart-*-.png") @@ -1283,9 +1366,9 @@ func (r *pdfReporter) createImpactRemainingRisks(parsedModel *types.Model) { func (r *pdfReporter) renderImpactAnalysis(parsedModel *types.Model, initialRisks bool) { r.pdf.SetTextColor(0, 0, 0) - count, catCount := types.TotalRiskCount(parsedModel), len(parsedModel.GeneratedRisksByCategory) + count, catCount := totalRiskCount(parsedModel), len(parsedModel.GeneratedRisksByCategory) if !initialRisks { - count, catCount = len(types.FilteredByStillAtRisk(parsedModel)), len(types.CategoriesOfOnlyRisksStillAtRisk(parsedModel, parsedModel.GeneratedRisksByCategory)) + count, catCount = len(filteredByStillAtRisk(parsedModel)), len(reduceToOnlyStillAtRisk(parsedModel.GeneratedRisksByCategory)) } riskStr, catStr := "Risks", "Categories" if count == 1 { @@ -1326,15 +1409,15 @@ func (r *pdfReporter) renderImpactAnalysis(parsedModel *types.Model, initialRisk html.Write(5, "Risk finding paragraphs are clickable and link to the corresponding chapter.") r.pdf.SetFont("Helvetica", "", fontSizeBody) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyCriticalRisks(parsedModel, parsedModel.GeneratedRisksByCategory, initialRisks)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(parsedModel.GeneratedRisksByCategory, initialRisks, types.CriticalSeverity)), types.CriticalSeverity, false, initialRisks, true, false) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyHighRisks(parsedModel, parsedModel.GeneratedRisksByCategory, initialRisks)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(parsedModel.GeneratedRisksByCategory, initialRisks, types.HighSeverity)), types.HighSeverity, false, initialRisks, true, false) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyElevatedRisks(parsedModel, parsedModel.GeneratedRisksByCategory, initialRisks)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(parsedModel.GeneratedRisksByCategory, initialRisks, types.ElevatedSeverity)), types.ElevatedSeverity, false, initialRisks, true, false) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyMediumRisks(parsedModel, parsedModel.GeneratedRisksByCategory, initialRisks)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(parsedModel.GeneratedRisksByCategory, initialRisks, types.MediumSeverity)), types.MediumSeverity, false, initialRisks, true, false) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyLowRisks(parsedModel, parsedModel.GeneratedRisksByCategory, initialRisks)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(parsedModel.GeneratedRisksByCategory, initialRisks, types.LowSeverity)), types.LowSeverity, false, initialRisks, true, false) r.pdf.SetDrawColor(0, 0, 0) @@ -1415,13 +1498,13 @@ func sortedTechnicalAssetsByRAAAndTitle(parsedModel *types.Model) []*types.Techn func (r *pdfReporter) createModelFailures(parsedModel *types.Model) { r.pdf.SetTextColor(0, 0, 0) - modelFailures := types.FlattenRiskSlice(types.FilterByModelFailures(parsedModel, parsedModel.GeneratedRisksByCategory)) + modelFailures := flattenRiskSlice(filterByModelFailures(parsedModel, parsedModel.GeneratedRisksByCategory)) risksStr := "Risks" count := len(modelFailures) if count == 1 { risksStr = "Risk" } - countStillAtRisk := len(types.ReduceToOnlyStillAtRisk(parsedModel, modelFailures)) + countStillAtRisk := len(types.ReduceToOnlyStillAtRisk(modelFailures)) if countStillAtRisk > 0 { colorModelFailure(r.pdf) } @@ -1443,20 +1526,20 @@ func (r *pdfReporter) createModelFailures(parsedModel *types.Model) { html.Write(5, "Risk finding paragraphs are clickable and link to the corresponding chapter.") r.pdf.SetFont("Helvetica", "", fontSizeBody) - modelFailuresByCategory := types.FilterByModelFailures(parsedModel, parsedModel.GeneratedRisksByCategory) + modelFailuresByCategory := filterByModelFailures(parsedModel, parsedModel.GeneratedRisksByCategory) if len(modelFailuresByCategory) == 0 { r.pdfColorGray() html.Write(5, "

No potential model failures have been identified.") } else { - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyCriticalRisks(parsedModel, modelFailuresByCategory, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(modelFailuresByCategory, true, types.CriticalSeverity)), types.CriticalSeverity, true, true, false, true) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyHighRisks(parsedModel, modelFailuresByCategory, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(modelFailuresByCategory, true, types.HighSeverity)), types.HighSeverity, true, true, false, true) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyElevatedRisks(parsedModel, modelFailuresByCategory, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(modelFailuresByCategory, true, types.ElevatedSeverity)), types.ElevatedSeverity, true, true, false, true) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyMediumRisks(parsedModel, modelFailuresByCategory, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(modelFailuresByCategory, true, types.MediumSeverity)), types.MediumSeverity, true, true, false, true) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyLowRisks(parsedModel, modelFailuresByCategory, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(modelFailuresByCategory, true, types.LowSeverity)), types.LowSeverity, true, true, false, true) } @@ -1464,6 +1547,26 @@ func (r *pdfReporter) createModelFailures(parsedModel *types.Model) { r.pdf.SetDashPattern([]float64{}, 0) } +func filterByModelFailures(parsedModel *types.Model, risksByCat map[string][]*types.Risk) map[string][]*types.Risk { + result := make(map[string][]*types.Risk) + for categoryId, risks := range risksByCat { + category := types.GetRiskCategory(parsedModel, categoryId) + if category.ModelFailurePossibleReason { + result[categoryId] = risks + } + } + + return result +} + +func flattenRiskSlice(risksByCat map[string][]*types.Risk) []*types.Risk { + result := make([]*types.Risk, 0) + for _, risks := range risksByCat { + result = append(result, risks...) + } + return result +} + func (r *pdfReporter) createRAA(parsedModel *types.Model, introTextRAA string) { uni := r.pdf.UnicodeTranslatorFromDescriptor("") r.pdf.SetTextColor(0, 0, 0) @@ -1504,7 +1607,7 @@ func (r *pdfReporter) createRAA(parsedModel *types.Model, introTextRAA string) { default: r.pdfColorBlack() } - if len(types.ReduceToOnlyStillAtRisk(parsedModel, newRisksStr)) == 0 { + if len(types.ReduceToOnlyStillAtRisk(newRisksStr)) == 0 { r.pdfColorBlack() } @@ -1617,7 +1720,7 @@ func (r *pdfReporter) addCategories(parsedModel *types.Model, riskCategories []* for _, riskCategory := range riskCategories { risksStr := parsedModel.GeneratedRisksByCategory[riskCategory.ID] if !initialRisks { - risksStr = types.ReduceToOnlyStillAtRisk(parsedModel, risksStr) + risksStr = types.ReduceToOnlyStillAtRisk(risksStr) } if len(risksStr) == 0 { continue @@ -1661,7 +1764,7 @@ func (r *pdfReporter) addCategories(parsedModel *types.Model, riskCategories []* case types.LowSeverity: colorLowRisk(r.pdf) } - if len(types.ReduceToOnlyStillAtRisk(parsedModel, risksStr)) == 0 { + if len(types.ReduceToOnlyStillAtRisk(risksStr)) == 0 { r.pdfColorBlack() } html.Write(5, strBuilder.String()) @@ -1676,7 +1779,7 @@ func (r *pdfReporter) addCategories(parsedModel *types.Model, riskCategories []* if !initialRisks { initialStr = "Remaining" } - remainingRisks := types.ReduceToOnlyStillAtRisk(parsedModel, risksStr) + remainingRisks := types.ReduceToOnlyStillAtRisk(risksStr) suffix := strconv.Itoa(count) + " " + initialStr + " Risk" if bothInitialAndRemainingRisks { suffix = strconv.Itoa(len(remainingRisks)) + " / " + strconv.Itoa(count) + " Risk" @@ -1686,9 +1789,9 @@ func (r *pdfReporter) addCategories(parsedModel *types.Model, riskCategories []* } suffix += " - Exploitation likelihood is " if initialRisks { - suffix += types.HighestExploitationLikelihood(risksStr).Title() + " with " + types.HighestExploitationImpact(risksStr).Title() + " impact." + suffix += highestExploitationLikelihood(risksStr).Title() + " with " + highestExploitationImpact(risksStr).Title() + " impact." } else { - suffix += types.HighestExploitationLikelihood(remainingRisks).Title() + " with " + types.HighestExploitationImpact(remainingRisks).Title() + " impact." + suffix += highestExploitationLikelihood(remainingRisks).Title() + " with " + highestExploitationImpact(remainingRisks).Title() + " impact." } strBuilder.WriteString(suffix + "
") html.Write(5, strBuilder.String()) @@ -1707,6 +1810,26 @@ func (r *pdfReporter) addCategories(parsedModel *types.Model, riskCategories []* } } +func highestExploitationLikelihood(risks []*types.Risk) types.RiskExploitationLikelihood { + result := types.Unlikely + for _, risk := range risks { + if risk.ExploitationLikelihood > result { + result = risk.ExploitationLikelihood + } + } + return result +} + +func highestExploitationImpact(risks []*types.Risk) types.RiskExploitationImpact { + result := types.LowImpact + for _, risk := range risks { + if risk.ExploitationImpact > result { + result = risk.ExploitationImpact + } + } + return result +} + func firstParagraph(text string) string { firstParagraphRegEx := regexp.MustCompile(`(.*?)((
)|(

))`) match := firstParagraphRegEx.FindStringSubmatch(text) @@ -1723,19 +1846,19 @@ func (r *pdfReporter) createAssignmentByFunction(parsedModel *types.Model) { r.defineLinkTarget("{function-assignment}") r.currentChapterTitleBreadcrumb = title - risksBusinessSideFunction := types.RisksOfOnlyBusinessSide(parsedModel, parsedModel.GeneratedRisksByCategory) - risksArchitectureFunction := types.RisksOfOnlyArchitecture(parsedModel, parsedModel.GeneratedRisksByCategory) - risksDevelopmentFunction := types.RisksOfOnlyDevelopment(parsedModel, parsedModel.GeneratedRisksByCategory) - risksOperationFunction := types.RisksOfOnlyOperation(parsedModel, parsedModel.GeneratedRisksByCategory) + risksBusinessSideFunction := reduceToFunctionRisk(parsedModel, parsedModel.GeneratedRisksByCategory, types.BusinessSide) + risksArchitectureFunction := reduceToFunctionRisk(parsedModel, parsedModel.GeneratedRisksByCategory, types.Architecture) + risksDevelopmentFunction := reduceToFunctionRisk(parsedModel, parsedModel.GeneratedRisksByCategory, types.Development) + risksOperationFunction := reduceToFunctionRisk(parsedModel, parsedModel.GeneratedRisksByCategory, types.Operations) - countBusinessSideFunction := types.CountRisks(risksBusinessSideFunction) - countArchitectureFunction := types.CountRisks(risksArchitectureFunction) - countDevelopmentFunction := types.CountRisks(risksDevelopmentFunction) - countOperationFunction := types.CountRisks(risksOperationFunction) + countBusinessSideFunction := countRisks(risksBusinessSideFunction) + countArchitectureFunction := countRisks(risksArchitectureFunction) + countDevelopmentFunction := countRisks(risksDevelopmentFunction) + countOperationFunction := countRisks(risksOperationFunction) var intro strings.Builder intro.WriteString("This chapter clusters and assigns the risks by functions which are most likely able to " + "check and mitigate them: " + - "In total " + strconv.Itoa(types.TotalRiskCount(parsedModel)) + " potential risks have been identified during the threat modeling process " + + "In total " + strconv.Itoa(totalRiskCount(parsedModel)) + " potential risks have been identified during the threat modeling process " + "of which " + strconv.Itoa(countBusinessSideFunction) + " should be checked by " + types.BusinessSide.Title() + ", " + "" + strconv.Itoa(countArchitectureFunction) + " should be checked by " + types.Architecture.Title() + ", " + "" + strconv.Itoa(countDevelopmentFunction) + " should be checked by " + types.Development.Title() + ", " + @@ -1764,15 +1887,15 @@ func (r *pdfReporter) createAssignmentByFunction(parsedModel *types.Model) { r.pdf.SetTextColor(150, 150, 150) html.Write(5, "

n/a") } else { - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyCriticalRisks(parsedModel, risksBusinessSideFunction, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksBusinessSideFunction, true, types.CriticalSeverity)), types.CriticalSeverity, true, true, false, false) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyHighRisks(parsedModel, risksBusinessSideFunction, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksBusinessSideFunction, true, types.HighSeverity)), types.HighSeverity, true, true, false, false) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyElevatedRisks(parsedModel, risksBusinessSideFunction, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksBusinessSideFunction, true, types.ElevatedSeverity)), types.ElevatedSeverity, true, true, false, false) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyMediumRisks(parsedModel, risksBusinessSideFunction, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksBusinessSideFunction, true, types.MediumSeverity)), types.MediumSeverity, true, true, false, false) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyLowRisks(parsedModel, risksBusinessSideFunction, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksBusinessSideFunction, true, types.LowSeverity)), types.LowSeverity, true, true, false, false) } r.pdf.SetLeftMargin(oldLeft) @@ -1791,15 +1914,15 @@ func (r *pdfReporter) createAssignmentByFunction(parsedModel *types.Model) { r.pdf.SetTextColor(150, 150, 150) html.Write(5, "

n/a") } else { - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyCriticalRisks(parsedModel, risksArchitectureFunction, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksArchitectureFunction, true, types.CriticalSeverity)), types.CriticalSeverity, true, true, false, false) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyHighRisks(parsedModel, risksArchitectureFunction, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksArchitectureFunction, true, types.HighSeverity)), types.HighSeverity, true, true, false, false) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyElevatedRisks(parsedModel, risksArchitectureFunction, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksArchitectureFunction, true, types.ElevatedSeverity)), types.ElevatedSeverity, true, true, false, false) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyMediumRisks(parsedModel, risksArchitectureFunction, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksArchitectureFunction, true, types.MediumSeverity)), types.MediumSeverity, true, true, false, false) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyLowRisks(parsedModel, risksArchitectureFunction, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksArchitectureFunction, true, types.LowSeverity)), types.LowSeverity, true, true, false, false) } r.pdf.SetLeftMargin(oldLeft) @@ -1818,15 +1941,15 @@ func (r *pdfReporter) createAssignmentByFunction(parsedModel *types.Model) { r.pdf.SetTextColor(150, 150, 150) html.Write(5, "

n/a") } else { - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyCriticalRisks(parsedModel, risksDevelopmentFunction, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksDevelopmentFunction, true, types.CriticalSeverity)), types.CriticalSeverity, true, true, false, false) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyHighRisks(parsedModel, risksDevelopmentFunction, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksDevelopmentFunction, true, types.HighSeverity)), types.HighSeverity, true, true, false, false) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyElevatedRisks(parsedModel, risksDevelopmentFunction, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksDevelopmentFunction, true, types.ElevatedSeverity)), types.ElevatedSeverity, true, true, false, false) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyMediumRisks(parsedModel, risksDevelopmentFunction, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksDevelopmentFunction, true, types.MediumSeverity)), types.MediumSeverity, true, true, false, false) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyLowRisks(parsedModel, risksDevelopmentFunction, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksDevelopmentFunction, true, types.LowSeverity)), types.LowSeverity, true, true, false, false) } r.pdf.SetLeftMargin(oldLeft) @@ -1845,15 +1968,15 @@ func (r *pdfReporter) createAssignmentByFunction(parsedModel *types.Model) { r.pdf.SetTextColor(150, 150, 150) html.Write(5, "

n/a") } else { - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyCriticalRisks(parsedModel, risksOperationFunction, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksOperationFunction, true, types.CriticalSeverity)), types.CriticalSeverity, true, true, false, false) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyHighRisks(parsedModel, risksOperationFunction, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksOperationFunction, true, types.HighSeverity)), types.HighSeverity, true, true, false, false) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyElevatedRisks(parsedModel, risksOperationFunction, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksOperationFunction, true, types.ElevatedSeverity)), types.ElevatedSeverity, true, true, false, false) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyMediumRisks(parsedModel, risksOperationFunction, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksOperationFunction, true, types.MediumSeverity)), types.MediumSeverity, true, true, false, false) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyLowRisks(parsedModel, risksOperationFunction, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksOperationFunction, true, types.LowSeverity)), types.LowSeverity, true, true, false, false) } r.pdf.SetLeftMargin(oldLeft) @@ -1862,6 +1985,19 @@ func (r *pdfReporter) createAssignmentByFunction(parsedModel *types.Model) { r.pdf.SetDashPattern([]float64{}, 0) } +func reduceToFunctionRisk(parsedModel *types.Model, risksByCategory map[string][]*types.Risk, function types.RiskFunction) map[string][]*types.Risk { + result := make(map[string][]*types.Risk) + for categoryId, risks := range risksByCategory { + for _, risk := range risks { + category := types.GetRiskCategory(parsedModel, categoryId) + if category.Function == function { + result[categoryId] = append(result[categoryId], risk) + } + } + } + return result +} + func (r *pdfReporter) createSTRIDE(parsedModel *types.Model) { r.pdf.SetTextColor(0, 0, 0) title := "STRIDE Classification of Identified Risks" @@ -1869,22 +2005,22 @@ func (r *pdfReporter) createSTRIDE(parsedModel *types.Model) { r.defineLinkTarget("{stride}") r.currentChapterTitleBreadcrumb = title - risksSTRIDESpoofing := types.RisksOfOnlySTRIDESpoofing(parsedModel, parsedModel.GeneratedRisksByCategory) - risksSTRIDETampering := types.RisksOfOnlySTRIDETampering(parsedModel, parsedModel.GeneratedRisksByCategory) - risksSTRIDERepudiation := types.RisksOfOnlySTRIDERepudiation(parsedModel, parsedModel.GeneratedRisksByCategory) - risksSTRIDEInformationDisclosure := types.RisksOfOnlySTRIDEInformationDisclosure(parsedModel, parsedModel.GeneratedRisksByCategory) - risksSTRIDEDenialOfService := types.RisksOfOnlySTRIDEDenialOfService(parsedModel, parsedModel.GeneratedRisksByCategory) - risksSTRIDEElevationOfPrivilege := types.RisksOfOnlySTRIDEElevationOfPrivilege(parsedModel, parsedModel.GeneratedRisksByCategory) - - countSTRIDESpoofing := types.CountRisks(risksSTRIDESpoofing) - countSTRIDETampering := types.CountRisks(risksSTRIDETampering) - countSTRIDERepudiation := types.CountRisks(risksSTRIDERepudiation) - countSTRIDEInformationDisclosure := types.CountRisks(risksSTRIDEInformationDisclosure) - countSTRIDEDenialOfService := types.CountRisks(risksSTRIDEDenialOfService) - countSTRIDEElevationOfPrivilege := types.CountRisks(risksSTRIDEElevationOfPrivilege) + risksSTRIDESpoofing := reduceToSTRIDERisk(parsedModel, parsedModel.GeneratedRisksByCategory, types.Spoofing) + risksSTRIDETampering := reduceToSTRIDERisk(parsedModel, parsedModel.GeneratedRisksByCategory, types.Tampering) + risksSTRIDERepudiation := reduceToSTRIDERisk(parsedModel, parsedModel.GeneratedRisksByCategory, types.Repudiation) + risksSTRIDEInformationDisclosure := reduceToSTRIDERisk(parsedModel, parsedModel.GeneratedRisksByCategory, types.InformationDisclosure) + risksSTRIDEDenialOfService := reduceToSTRIDERisk(parsedModel, parsedModel.GeneratedRisksByCategory, types.DenialOfService) + risksSTRIDEElevationOfPrivilege := reduceToSTRIDERisk(parsedModel, parsedModel.GeneratedRisksByCategory, types.ElevationOfPrivilege) + + countSTRIDESpoofing := countRisks(risksSTRIDESpoofing) + countSTRIDETampering := countRisks(risksSTRIDETampering) + countSTRIDERepudiation := countRisks(risksSTRIDERepudiation) + countSTRIDEInformationDisclosure := countRisks(risksSTRIDEInformationDisclosure) + countSTRIDEDenialOfService := countRisks(risksSTRIDEDenialOfService) + countSTRIDEElevationOfPrivilege := countRisks(risksSTRIDEElevationOfPrivilege) var intro strings.Builder intro.WriteString("This chapter clusters and classifies the risks by STRIDE categories: " + - "In total " + strconv.Itoa(types.TotalRiskCount(parsedModel)) + " potential risks have been identified during the threat modeling process " + + "In total " + strconv.Itoa(totalRiskCount(parsedModel)) + " potential risks have been identified during the threat modeling process " + "of which " + strconv.Itoa(countSTRIDESpoofing) + " in the " + types.Spoofing.Title() + " category, " + "" + strconv.Itoa(countSTRIDETampering) + " in the " + types.Tampering.Title() + " category, " + "" + strconv.Itoa(countSTRIDERepudiation) + " in the " + types.Repudiation.Title() + " category, " + @@ -1915,15 +2051,15 @@ func (r *pdfReporter) createSTRIDE(parsedModel *types.Model) { r.pdf.SetTextColor(150, 150, 150) html.Write(5, "

n/a") } else { - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyCriticalRisks(parsedModel, risksSTRIDESpoofing, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksSTRIDESpoofing, true, types.CriticalSeverity)), types.CriticalSeverity, true, true, false, true) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyHighRisks(parsedModel, risksSTRIDESpoofing, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksSTRIDESpoofing, true, types.HighSeverity)), types.HighSeverity, true, true, false, true) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyElevatedRisks(parsedModel, risksSTRIDESpoofing, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksSTRIDESpoofing, true, types.ElevatedSeverity)), types.ElevatedSeverity, true, true, false, true) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyMediumRisks(parsedModel, risksSTRIDESpoofing, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksSTRIDESpoofing, true, types.MediumSeverity)), types.MediumSeverity, true, true, false, true) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyLowRisks(parsedModel, risksSTRIDESpoofing, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksSTRIDESpoofing, true, types.LowSeverity)), types.LowSeverity, true, true, false, true) } r.pdf.SetLeftMargin(oldLeft) @@ -1942,15 +2078,15 @@ func (r *pdfReporter) createSTRIDE(parsedModel *types.Model) { r.pdf.SetTextColor(150, 150, 150) html.Write(5, "

n/a") } else { - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyCriticalRisks(parsedModel, risksSTRIDETampering, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksSTRIDETampering, true, types.CriticalSeverity)), types.CriticalSeverity, true, true, false, true) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyHighRisks(parsedModel, risksSTRIDETampering, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksSTRIDETampering, true, types.HighSeverity)), types.HighSeverity, true, true, false, true) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyElevatedRisks(parsedModel, risksSTRIDETampering, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksSTRIDETampering, true, types.ElevatedSeverity)), types.ElevatedSeverity, true, true, false, true) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyMediumRisks(parsedModel, risksSTRIDETampering, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksSTRIDETampering, true, types.MediumSeverity)), types.MediumSeverity, true, true, false, true) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyLowRisks(parsedModel, risksSTRIDETampering, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksSTRIDETampering, true, types.LowSeverity)), types.LowSeverity, true, true, false, true) } r.pdf.SetLeftMargin(oldLeft) @@ -1969,15 +2105,15 @@ func (r *pdfReporter) createSTRIDE(parsedModel *types.Model) { r.pdf.SetTextColor(150, 150, 150) html.Write(5, "

n/a") } else { - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyCriticalRisks(parsedModel, risksSTRIDERepudiation, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksSTRIDERepudiation, true, types.CriticalSeverity)), types.CriticalSeverity, true, true, false, true) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyHighRisks(parsedModel, risksSTRIDERepudiation, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksSTRIDERepudiation, true, types.HighSeverity)), types.HighSeverity, true, true, false, true) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyElevatedRisks(parsedModel, risksSTRIDERepudiation, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksSTRIDERepudiation, true, types.ElevatedSeverity)), types.ElevatedSeverity, true, true, false, true) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyMediumRisks(parsedModel, risksSTRIDERepudiation, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksSTRIDERepudiation, true, types.MediumSeverity)), types.MediumSeverity, true, true, false, true) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyLowRisks(parsedModel, risksSTRIDERepudiation, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksSTRIDERepudiation, true, types.LowSeverity)), types.LowSeverity, true, true, false, true) } r.pdf.SetLeftMargin(oldLeft) @@ -1996,15 +2132,15 @@ func (r *pdfReporter) createSTRIDE(parsedModel *types.Model) { r.pdf.SetTextColor(150, 150, 150) html.Write(5, "

n/a") } else { - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyCriticalRisks(parsedModel, risksSTRIDEInformationDisclosure, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksSTRIDEInformationDisclosure, true, types.CriticalSeverity)), types.CriticalSeverity, true, true, false, true) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyHighRisks(parsedModel, risksSTRIDEInformationDisclosure, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksSTRIDEInformationDisclosure, true, types.HighSeverity)), types.HighSeverity, true, true, false, true) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyElevatedRisks(parsedModel, risksSTRIDEInformationDisclosure, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksSTRIDEInformationDisclosure, true, types.ElevatedSeverity)), types.ElevatedSeverity, true, true, false, true) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyMediumRisks(parsedModel, risksSTRIDEInformationDisclosure, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksSTRIDEInformationDisclosure, true, types.MediumSeverity)), types.MediumSeverity, true, true, false, true) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyLowRisks(parsedModel, risksSTRIDEInformationDisclosure, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksSTRIDEInformationDisclosure, true, types.LowSeverity)), types.LowSeverity, true, true, false, true) } r.pdf.SetLeftMargin(oldLeft) @@ -2023,15 +2159,15 @@ func (r *pdfReporter) createSTRIDE(parsedModel *types.Model) { r.pdf.SetTextColor(150, 150, 150) html.Write(5, "

n/a") } else { - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyCriticalRisks(parsedModel, risksSTRIDEDenialOfService, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksSTRIDEDenialOfService, true, types.CriticalSeverity)), types.CriticalSeverity, true, true, false, true) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyHighRisks(parsedModel, risksSTRIDEDenialOfService, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksSTRIDEDenialOfService, true, types.HighSeverity)), types.HighSeverity, true, true, false, true) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyElevatedRisks(parsedModel, risksSTRIDEDenialOfService, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksSTRIDEDenialOfService, true, types.ElevatedSeverity)), types.ElevatedSeverity, true, true, false, true) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyMediumRisks(parsedModel, risksSTRIDEDenialOfService, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksSTRIDEDenialOfService, true, types.MediumSeverity)), types.MediumSeverity, true, true, false, true) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyLowRisks(parsedModel, risksSTRIDEDenialOfService, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksSTRIDEDenialOfService, true, types.LowSeverity)), types.LowSeverity, true, true, false, true) } r.pdf.SetLeftMargin(oldLeft) @@ -2050,15 +2186,15 @@ func (r *pdfReporter) createSTRIDE(parsedModel *types.Model) { r.pdf.SetTextColor(150, 150, 150) html.Write(5, "

n/a") } else { - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyCriticalRisks(parsedModel, risksSTRIDEElevationOfPrivilege, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksSTRIDEElevationOfPrivilege, true, types.CriticalSeverity)), types.CriticalSeverity, true, true, false, true) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyHighRisks(parsedModel, risksSTRIDEElevationOfPrivilege, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksSTRIDEElevationOfPrivilege, true, types.HighSeverity)), types.HighSeverity, true, true, false, true) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyElevatedRisks(parsedModel, risksSTRIDEElevationOfPrivilege, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksSTRIDEElevationOfPrivilege, true, types.ElevatedSeverity)), types.ElevatedSeverity, true, true, false, true) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyMediumRisks(parsedModel, risksSTRIDEElevationOfPrivilege, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksSTRIDEElevationOfPrivilege, true, types.MediumSeverity)), types.MediumSeverity, true, true, false, true) - r.addCategories(parsedModel, types.GetRiskCategories(parsedModel, types.CategoriesOfOnlyLowRisks(parsedModel, risksSTRIDEElevationOfPrivilege, true)), + r.addCategories(parsedModel, getRiskCategories(parsedModel, reduceToSeverityRisk(risksSTRIDEElevationOfPrivilege, true, types.LowSeverity)), types.LowSeverity, true, true, false, true) } r.pdf.SetLeftMargin(oldLeft) @@ -2067,6 +2203,82 @@ func (r *pdfReporter) createSTRIDE(parsedModel *types.Model) { r.pdf.SetDashPattern([]float64{}, 0) } +func countRisks(risksByCategory map[string][]*types.Risk) int { + result := 0 + for _, risks := range risksByCategory { + result += len(risks) + } + return result +} + +func getRiskCategories(parsedModel *types.Model, categoryIDs []string) []*types.RiskCategory { + categoryMap := make(map[string]*types.RiskCategory) + for _, categoryId := range categoryIDs { + category := types.GetRiskCategory(parsedModel, categoryId) + if category != nil { + categoryMap[categoryId] = category + } + } + + categories := make([]*types.RiskCategory, 0) + for categoryId := range categoryMap { + categories = append(categories, categoryMap[categoryId]) + } + + return categories +} + +func reduceToSeverityRisk(risksByCategory map[string][]*types.Risk, initialRisks bool, severity types.RiskSeverity) []string { + categories := make(map[string]struct{}) // Go's trick of unique elements is a map + for categoryId, risks := range risksByCategory { + for _, risk := range risks { + if !initialRisks && !risk.RiskStatus.IsStillAtRisk() { + continue + } + if risk.Severity == severity { + categories[categoryId] = struct{}{} + } + } + } + // return as slice (of now unique values) + return keysAsSlice(categories) +} + +func reduceToOnlyStillAtRisk(risksByCategory map[string][]*types.Risk) []string { + categories := make(map[string]struct{}) // Go's trick of unique elements is a map + for categoryId, risks := range risksByCategory { + for _, risk := range risks { + if !risk.RiskStatus.IsStillAtRisk() { + continue + } + categories[categoryId] = struct{}{} + } + } + // return as slice (of now unique values) + return keysAsSlice(categories) +} + +func keysAsSlice(categories map[string]struct{}) []string { + result := make([]string, 0, len(categories)) + for k := range categories { + result = append(result, k) + } + return result +} + +func reduceToSTRIDERisk(parsedModel *types.Model, risksByCategory map[string][]*types.Risk, stride types.STRIDE) map[string][]*types.Risk { + result := make(map[string][]*types.Risk) + for categoryId, risks := range risksByCategory { + for _, risk := range risks { + category := types.GetRiskCategory(parsedModel, categoryId) + if category != nil && category.STRIDE == stride { + result[categoryId] = append(result[categoryId], risk) + } + } + } + return result +} + func (r *pdfReporter) createSecurityRequirements(parsedModel *types.Model) { uni := r.pdf.UnicodeTranslatorFromDescriptor("") r.pdf.SetTextColor(0, 0, 0) @@ -2300,13 +2512,13 @@ func (r *pdfReporter) createRiskCategories(parsedModel *types.Model) { r.defineLinkTarget("{intro-risks-by-vulnerability-category}") html := r.pdf.HTMLBasicNew() var text strings.Builder - text.WriteString("In total " + strconv.Itoa(types.TotalRiskCount(parsedModel)) + " potential risks have been identified during the threat modeling process " + + text.WriteString("In total " + strconv.Itoa(totalRiskCount(parsedModel)) + " potential risks have been identified during the threat modeling process " + "of which " + - "" + strconv.Itoa(len(types.FilteredByOnlyCriticalRisks(parsedModel))) + " are rated as critical, " + - "" + strconv.Itoa(len(types.FilteredByOnlyHighRisks(parsedModel))) + " as high, " + - "" + strconv.Itoa(len(types.FilteredByOnlyElevatedRisks(parsedModel))) + " as elevated, " + - "" + strconv.Itoa(len(types.FilteredByOnlyMediumRisks(parsedModel))) + " as medium, " + - "and " + strconv.Itoa(len(types.FilteredByOnlyLowRisks(parsedModel))) + " as low. " + + "" + strconv.Itoa(len(filteredBySeverity(parsedModel, types.CriticalSeverity))) + " are rated as critical, " + + "" + strconv.Itoa(len(filteredBySeverity(parsedModel, types.HighSeverity))) + " as high, " + + "" + strconv.Itoa(len(filteredBySeverity(parsedModel, types.ElevatedSeverity))) + " as elevated, " + + "" + strconv.Itoa(len(filteredBySeverity(parsedModel, types.MediumSeverity))) + " as medium, " + + "and " + strconv.Itoa(len(filteredBySeverity(parsedModel, types.LowSeverity))) + " as low. " + "

These risks are distributed across " + strconv.Itoa(len(parsedModel.GeneratedRisksByCategory)) + " vulnerability categories. ") text.WriteString("The following sub-chapters of this section describe each identified risk category.") // TODO more explanation text html.Write(5, text.String()) @@ -2330,12 +2542,12 @@ func (r *pdfReporter) createRiskCategories(parsedModel *types.Model) { default: r.pdfColorBlack() } - if len(types.ReduceToOnlyStillAtRisk(parsedModel, risksStr)) == 0 { + if len(types.ReduceToOnlyStillAtRisk(risksStr)) == 0 { r.pdfColorBlack() } // category title - countStillAtRisk := len(types.ReduceToOnlyStillAtRisk(parsedModel, risksStr)) + countStillAtRisk := len(types.ReduceToOnlyStillAtRisk(risksStr)) suffix := strconv.Itoa(countStillAtRisk) + " / " + strconv.Itoa(len(risksStr)) + " Risk" if len(risksStr) != 1 { suffix += "s" @@ -2567,13 +2779,13 @@ func (r *pdfReporter) createTechnicalAssets(parsedModel *types.Model) { r.defineLinkTarget("{intro-risks-by-technical-asset}") html := r.pdf.HTMLBasicNew() var text strings.Builder - text.WriteString("In total " + strconv.Itoa(types.TotalRiskCount(parsedModel)) + " potential risks have been identified during the threat modeling process " + + text.WriteString("In total " + strconv.Itoa(totalRiskCount(parsedModel)) + " potential risks have been identified during the threat modeling process " + "of which " + - "" + strconv.Itoa(len(types.FilteredByOnlyCriticalRisks(parsedModel))) + " are rated as critical, " + - "" + strconv.Itoa(len(types.FilteredByOnlyHighRisks(parsedModel))) + " as high, " + - "" + strconv.Itoa(len(types.FilteredByOnlyElevatedRisks(parsedModel))) + " as elevated, " + - "" + strconv.Itoa(len(types.FilteredByOnlyMediumRisks(parsedModel))) + " as medium, " + - "and " + strconv.Itoa(len(types.FilteredByOnlyLowRisks(parsedModel))) + " as low. " + + "" + strconv.Itoa(len(filteredBySeverity(parsedModel, types.CriticalSeverity))) + " are rated as critical, " + + "" + strconv.Itoa(len(filteredBySeverity(parsedModel, types.HighSeverity))) + " as high, " + + "" + strconv.Itoa(len(filteredBySeverity(parsedModel, types.ElevatedSeverity))) + " as elevated, " + + "" + strconv.Itoa(len(filteredBySeverity(parsedModel, types.MediumSeverity))) + " as medium, " + + "and " + strconv.Itoa(len(filteredBySeverity(parsedModel, types.LowSeverity))) + " as low. " + "

These risks are distributed across " + strconv.Itoa(len(parsedModel.InScopeTechnicalAssets())) + " in-scope technical assets. ") text.WriteString("The following sub-chapters of this section describe each identified risk grouped by technical asset. ") // TODO more explanation text text.WriteString("The RAA value of a technical asset is the calculated \"Relative Attacker Attractiveness\" value in percent.") @@ -2582,7 +2794,7 @@ func (r *pdfReporter) createTechnicalAssets(parsedModel *types.Model) { r.currentChapterTitleBreadcrumb = title for _, technicalAsset := range sortedTechnicalAssetsByRiskSeverityAndTitle(parsedModel) { risksStr := technicalAsset.GeneratedRisks(parsedModel) - countStillAtRisk := len(types.ReduceToOnlyStillAtRisk(parsedModel, risksStr)) + countStillAtRisk := len(types.ReduceToOnlyStillAtRisk(risksStr)) suffix := strconv.Itoa(countStillAtRisk) + " / " + strconv.Itoa(len(risksStr)) + " Risk" if len(risksStr) != 1 { suffix += "s" @@ -2605,7 +2817,7 @@ func (r *pdfReporter) createTechnicalAssets(parsedModel *types.Model) { default: r.pdfColorBlack() } - if len(types.ReduceToOnlyStillAtRisk(parsedModel, risksStr)) == 0 { + if len(types.ReduceToOnlyStillAtRisk(risksStr)) == 0 { r.pdfColorBlack() } } @@ -3370,6 +3582,18 @@ func (r *pdfReporter) createTechnicalAssets(parsedModel *types.Model) { } } +func filteredBySeverity(parsedModel *types.Model, severity types.RiskSeverity) []*types.Risk { + filteredRisks := make([]*types.Risk, 0) + for _, risks := range parsedModel.GeneratedRisksByCategory { + for _, risk := range risks { + if risk.Severity == severity { + filteredRisks = append(filteredRisks, risk) + } + } + } + return filteredRisks +} + func (r *pdfReporter) createDataAssets(parsedModel *types.Model) { uni := r.pdf.UnicodeTranslatorFromDescriptor("") title := "Identified Data Breach Probabilities by Data Asset" @@ -3377,13 +3601,13 @@ func (r *pdfReporter) createDataAssets(parsedModel *types.Model) { r.addHeadline(title, false) r.defineLinkTarget("{intro-risks-by-data-asset}") html := r.pdf.HTMLBasicNew() - html.Write(5, "In total "+strconv.Itoa(types.TotalRiskCount(parsedModel))+" potential risks have been identified during the threat modeling process "+ + html.Write(5, "In total "+strconv.Itoa(totalRiskCount(parsedModel))+" potential risks have been identified during the threat modeling process "+ "of which "+ - ""+strconv.Itoa(len(types.FilteredByOnlyCriticalRisks(parsedModel)))+" are rated as critical, "+ - ""+strconv.Itoa(len(types.FilteredByOnlyHighRisks(parsedModel)))+" as high, "+ - ""+strconv.Itoa(len(types.FilteredByOnlyElevatedRisks(parsedModel)))+" as elevated, "+ - ""+strconv.Itoa(len(types.FilteredByOnlyMediumRisks(parsedModel)))+" as medium, "+ - "and "+strconv.Itoa(len(types.FilteredByOnlyLowRisks(parsedModel)))+" as low. "+ + ""+strconv.Itoa(len(filteredBySeverity(parsedModel, types.CriticalSeverity)))+" are rated as critical, "+ + ""+strconv.Itoa(len(filteredBySeverity(parsedModel, types.HighSeverity)))+" as high, "+ + ""+strconv.Itoa(len(filteredBySeverity(parsedModel, types.ElevatedSeverity)))+" as elevated, "+ + ""+strconv.Itoa(len(filteredBySeverity(parsedModel, types.MediumSeverity)))+" as medium, "+ + "and "+strconv.Itoa(len(filteredBySeverity(parsedModel, types.LowSeverity)))+" as low. "+ "

These risks are distributed across "+strconv.Itoa(len(parsedModel.DataAssets))+" data assets. ") html.Write(5, "The following sub-chapters of this section describe the derived data breach probabilities grouped by data asset.
") // TODO more explanation text r.pdf.SetFont("Helvetica", "", fontSizeSmall) @@ -3399,7 +3623,7 @@ func (r *pdfReporter) createDataAssets(parsedModel *types.Model) { html.Write(5, "


") } r.pdfColorBlack() - switch dataAsset.IdentifiedDataBreachProbabilityStillAtRisk(parsedModel) { + switch identifiedDataBreachProbabilityStillAtRisk(parsedModel, dataAsset) { case types.Probable: colorHighRisk(r.pdf) case types.Possible: @@ -3409,11 +3633,11 @@ func (r *pdfReporter) createDataAssets(parsedModel *types.Model) { default: r.pdfColorBlack() } - if !dataAsset.IsDataBreachPotentialStillAtRisk(parsedModel) { + if !isDataBreachPotentialStillAtRisk(parsedModel, dataAsset) { r.pdfColorBlack() } risksStr := dataAsset.IdentifiedDataBreachProbabilityRisks(parsedModel) - countStillAtRisk := len(types.ReduceToOnlyStillAtRisk(parsedModel, risksStr)) + countStillAtRisk := len(types.ReduceToOnlyStillAtRisk(risksStr)) suffix := strconv.Itoa(countStillAtRisk) + " / " + strconv.Itoa(len(risksStr)) + " Risk" if len(risksStr) != 1 { suffix += "s" @@ -3426,35 +3650,6 @@ func (r *pdfReporter) createDataAssets(parsedModel *types.Model) { html.Write(5, "

") r.pdf.SetFont("Helvetica", "", fontSizeBody) - /* - r.pdfColorGray() - r.pdf.CellFormat(5, 6, "", "0", 0, "", false, 0, "") - r.pdf.CellFormat(40, 6, "Indirect Breach:", "0", 0, "", false, 0, "") - r.pdfColorBlack() - r.pdf.SetFont("Helvetica", "B", fontSizeBody) - probability := dataAsset.IdentifiedDataBreachProbability() - dataBreachText := probability.String() - switch probability { - case model.Probable: - colorHighRisk(r.pdf) - case model.Possible: - colorMediumRisk(r.pdf) - case model.Improbable: - colorLowRisk(r.pdf) - default: - r.pdfColorBlack() - } - if !dataAsset.IsDataBreachPotentialStillAtRisk() { - r.pdfColorBlack() - dataBreachText = "none" - } - r.pdf.MultiCell(145, 6, dataBreachText, "0", "0", false) - r.pdf.SetFont("Helvetica", "", fontSizeBody) - if r.pdf.GetY() > 265 { - r.pageBreak() - r.pdf.SetY(36) - } - */ r.pdfColorGray() r.pdf.CellFormat(5, 6, "", "0", 0, "", false, 0, "") r.pdf.CellFormat(40, 6, "ID:", "0", 0, "", false, 0, "") @@ -3651,73 +3846,12 @@ func (r *pdfReporter) createDataAssets(parsedModel *types.Model) { } r.pdf.MultiCell(145, 6, uni(receivedViaText), "0", "0", false) - /* - // where is this data asset at risk (i.e. why) - risksByTechAssetId := dataAsset.IdentifiedRisksByResponsibleTechnicalAssetId() - techAssetsResponsible := make([]model.TechnicalAsset, 0) - for techAssetId, _ := range risksByTechAssetId { - techAssetsResponsible = append(techAssetsResponsible, parsedModel.TechnicalAssets[techAssetId]) - } - sort.Sort(model.ByTechnicalAssetRiskSeverityAndTitleSortStillAtRisk(techAssetsResponsible)) - assetStr := "assets" - if len(techAssetsResponsible) == 1 { - assetStr = "asset" - } - if r.pdf.GetY() > 265 { - r.pageBreak() - r.pdf.SetY(36) - } - r.pdfColorGray() - r.pdf.CellFormat(5, 6, "", "0", 0, "", false, 0, "") - r.pdf.CellFormat(40, 6, "Risk via:", "0", 0, "", false, 0, "") - if len(techAssetsResponsible) == 0 { - r.pdfColorGray() - r.pdf.MultiCell(145, 6, "This data asset is not directly at risk via any technical asset.", "0", "0", false) - } else { - r.pdfColorBlack() - r.pdf.MultiCell(145, 6, "This data asset is at direct risk via "+strconv.Itoa(len(techAssetsResponsible))+" technical "+assetStr+":", "0", "0", false) - for _, techAssetResponsible := range techAssetsResponsible { - if r.pdf.GetY() > 265 { - r.pageBreak() - r.pdf.SetY(36) - } - switch model.HighestSeverityStillAtRisk(techAssetResponsible.GeneratedRisks()) { - case model.High: - colorHighRisk(r.pdf) - case model.Medium: - colorMediumRisk(r.pdf) - case model.Low: - colorLowRisk(r.pdf) - default: - r.pdfColorBlack() - } - risksStr := techAssetResponsible.GeneratedRisks() - if len(model.ReduceToOnlyStillAtRisk(risksStr)) == 0 { - r.pdfColorBlack() - } - riskStr := "risksStr" - if len(risksStr) == 1 { - riskStr = "risk" - } - r.pdf.CellFormat(10, 6, "", "0", 0, "", false, 0, "") - posY := r.pdf.GetY() - risksResponsible := techAssetResponsible.GeneratedRisks() - risksResponsibleStillAtRisk := model.ReduceToOnlyStillAtRisk(risksResponsible) - r.pdf.SetFont("Helvetica", "", fontSizeSmall) - r.pdf.MultiCell(185, 6, uni(techAssetResponsible.Title)+": "+strconv.Itoa(len(risksResponsibleStillAtRisk))+" / "+strconv.Itoa(len(risksResponsible))+" "+riskStr, "0", "0", false) - r.pdf.SetFont("Helvetica", "", fontSizeBody) - r.pdf.Link(20, posY, 180, r.pdf.GetY()-posY, tocLinkIdByAssetId[techAssetResponsible.ID]) - } - r.pdfColorBlack() - } - */ - r.pdfColorGray() r.pdf.CellFormat(5, 6, "", "0", 0, "", false, 0, "") r.pdf.CellFormat(40, 6, "Data Breach:", "0", 0, "", false, 0, "") r.pdfColorBlack() r.pdf.SetFont("Helvetica", "B", fontSizeBody) - dataBreachProbability := dataAsset.IdentifiedDataBreachProbabilityStillAtRisk(parsedModel) + dataBreachProbability := identifiedDataBreachProbabilityStillAtRisk(parsedModel, dataAsset) riskText := dataBreachProbability.String() switch dataBreachProbability { case types.Probable: @@ -3729,7 +3863,7 @@ func (r *pdfReporter) createDataAssets(parsedModel *types.Model) { default: r.pdfColorBlack() } - if !dataAsset.IsDataBreachPotentialStillAtRisk(parsedModel) { + if !isDataBreachPotentialStillAtRisk(parsedModel, dataAsset) { r.pdfColorBlack() riskText = "none" } @@ -3741,8 +3875,8 @@ func (r *pdfReporter) createDataAssets(parsedModel *types.Model) { } // how can is this data asset be indirectly lost (i.e. why) - dataBreachRisksStillAtRisk := dataAsset.IdentifiedDataBreachProbabilityRisksStillAtRisk(parsedModel) - types.SortByDataBreachProbability(dataBreachRisksStillAtRisk, parsedModel) + dataBreachRisksStillAtRisk := identifiedDataBreachProbabilityRisksStillAtRisk(parsedModel, dataAsset) + sortByDataBreachProbability(dataBreachRisksStillAtRisk, parsedModel) if r.pdf.GetY() > 265 { r.pageBreak() r.pdf.SetY(36) @@ -3791,6 +3925,81 @@ func (r *pdfReporter) createDataAssets(parsedModel *types.Model) { } } +func sortByDataBreachProbability(risks []*types.Risk, parsedModel *types.Model) { + sort.Slice(risks, func(i, j int) bool { + + if risks[i].DataBreachProbability == risks[j].DataBreachProbability { + trackingStatusLeft := risks[i].RiskStatus + trackingStatusRight := risks[j].RiskStatus + if trackingStatusLeft == trackingStatusRight { + return risks[i].Title < risks[j].Title + } else { + return trackingStatusLeft < trackingStatusRight + } + } + return risks[i].DataBreachProbability > risks[j].DataBreachProbability + }) +} + +func identifiedDataBreachProbabilityRisksStillAtRisk(parsedModel *types.Model, dataAsset *types.DataAsset) []*types.Risk { + result := make([]*types.Risk, 0) + for _, risk := range filteredByStillAtRisk(parsedModel) { + for _, techAsset := range risk.DataBreachTechnicalAssetIDs { + if contains(parsedModel.TechnicalAssets[techAsset].DataAssetsProcessed, dataAsset.Id) { + result = append(result, risk) + break + } + } + } + return result +} + +func identifiedDataBreachProbabilityStillAtRisk(parsedModel *types.Model, dataAsset *types.DataAsset) types.DataBreachProbability { + highestProbability := types.Improbable + for _, risk := range filteredByStillAtRisk(parsedModel) { + for _, techAsset := range risk.DataBreachTechnicalAssetIDs { + if contains(parsedModel.TechnicalAssets[techAsset].DataAssetsProcessed, dataAsset.Id) { + if risk.DataBreachProbability > highestProbability { + highestProbability = risk.DataBreachProbability + break + } + } + } + } + return highestProbability +} + +func isDataBreachPotentialStillAtRisk(parsedModel *types.Model, dataAsset *types.DataAsset) bool { + for _, risk := range filteredByStillAtRisk(parsedModel) { + for _, techAsset := range risk.DataBreachTechnicalAssetIDs { + if contains(parsedModel.TechnicalAssets[techAsset].DataAssetsProcessed, dataAsset.Id) { + return true + } + } + } + return false +} + +func filteredByStillAtRisk(parsedModel *types.Model) []*types.Risk { + filteredRisks := make([]*types.Risk, 0) + for _, risks := range parsedModel.GeneratedRisksByCategory { + for _, risk := range risks { + if risk.RiskStatus.IsStillAtRisk() { + filteredRisks = append(filteredRisks, risk) + } + } + } + return filteredRisks +} + +func totalRiskCount(parsedModel *types.Model) int { + count := 0 + for _, risks := range parsedModel.GeneratedRisksByCategory { + count += len(risks) + } + return count +} + func (r *pdfReporter) createTrustBoundaries(parsedModel *types.Model) { uni := r.pdf.UnicodeTranslatorFromDescriptor("") title := "Trust Boundaries" diff --git a/pkg/security/types/data_asset.go b/pkg/security/types/data_asset.go index 399276bc..e05fa32a 100644 --- a/pkg/security/types/data_asset.go +++ b/pkg/security/types/data_asset.go @@ -31,37 +31,6 @@ func (what DataAsset) IsTaggedWithBaseTag(baseTag string) bool { return IsTaggedWithBaseTag(what.Tags, baseTag) } -func (what DataAsset) IdentifiedRisksByResponsibleTechnicalAssetId(model *Model) map[string][]*Risk { - uniqueTechAssetIDsResponsibleForThisDataAsset := make(map[string]interface{}) - for _, techAsset := range what.ProcessedByTechnicalAssetsSorted(model) { - if len(techAsset.GeneratedRisks(model)) > 0 { - uniqueTechAssetIDsResponsibleForThisDataAsset[techAsset.Id] = true - } - } - for _, techAsset := range what.StoredByTechnicalAssetsSorted(model) { - if len(techAsset.GeneratedRisks(model)) > 0 { - uniqueTechAssetIDsResponsibleForThisDataAsset[techAsset.Id] = true - } - } - - result := make(map[string][]*Risk) - for techAssetId := range uniqueTechAssetIDsResponsibleForThisDataAsset { - result[techAssetId] = append(result[techAssetId], model.TechnicalAssets[techAssetId].GeneratedRisks(model)...) - } - return result -} - -func (what DataAsset) IsDataBreachPotentialStillAtRisk(parsedModel *Model) bool { - for _, risk := range FilteredByStillAtRisk(parsedModel) { - for _, techAsset := range risk.DataBreachTechnicalAssetIDs { - if contains(parsedModel.TechnicalAssets[techAsset].DataAssetsProcessed, what.Id) { - return true - } - } - } - return false -} - func (what DataAsset) IdentifiedDataBreachProbability(parsedModel *Model) DataBreachProbability { highestProbability := Improbable for _, risk := range AllRisks(parsedModel) { @@ -77,34 +46,6 @@ func (what DataAsset) IdentifiedDataBreachProbability(parsedModel *Model) DataBr return highestProbability } -func (what DataAsset) IdentifiedDataBreachProbabilityStillAtRisk(parsedModel *Model) DataBreachProbability { - highestProbability := Improbable - for _, risk := range FilteredByStillAtRisk(parsedModel) { - for _, techAsset := range risk.DataBreachTechnicalAssetIDs { - if contains(parsedModel.TechnicalAssets[techAsset].DataAssetsProcessed, what.Id) { - if risk.DataBreachProbability > highestProbability { - highestProbability = risk.DataBreachProbability - break - } - } - } - } - return highestProbability -} - -func (what DataAsset) IdentifiedDataBreachProbabilityRisksStillAtRisk(parsedModel *Model) []*Risk { - result := make([]*Risk, 0) - for _, risk := range FilteredByStillAtRisk(parsedModel) { - for _, techAsset := range risk.DataBreachTechnicalAssetIDs { - if contains(parsedModel.TechnicalAssets[techAsset].DataAssetsProcessed, what.Id) { - result = append(result, risk) - break - } - } - } - return result -} - func (what DataAsset) IdentifiedDataBreachProbabilityRisks(parsedModel *Model) []*Risk { result := make([]*Risk, 0) for _, risk := range AllRisks(parsedModel) { @@ -174,36 +115,6 @@ func (what DataAsset) ReceivedViaCommLinksSorted(parsedModel *Model) []*Communic return result } -func SortByDataAssetDataBreachProbabilityAndTitle(parsedModel *Model, assets []*DataAsset) { - sort.Slice(assets, func(i, j int) bool { - highestDataBreachProbabilityLeft := assets[i].IdentifiedDataBreachProbability(parsedModel) - highestDataBreachProbabilityRight := assets[j].IdentifiedDataBreachProbability(parsedModel) - if highestDataBreachProbabilityLeft == highestDataBreachProbabilityRight { - return assets[i].Title < assets[j].Title - } - return highestDataBreachProbabilityLeft > highestDataBreachProbabilityRight - }) -} - -func SortByDataAssetDataBreachProbabilityAndTitleStillAtRisk(parsedModel *Model, assets []*DataAsset) { - sort.Slice(assets, func(i, j int) bool { - risksLeft := assets[i].IdentifiedDataBreachProbabilityRisksStillAtRisk(parsedModel) - risksRight := assets[j].IdentifiedDataBreachProbabilityRisksStillAtRisk(parsedModel) - highestDataBreachProbabilityLeft := assets[i].IdentifiedDataBreachProbabilityStillAtRisk(parsedModel) - highestDataBreachProbabilityRight := assets[j].IdentifiedDataBreachProbabilityStillAtRisk(parsedModel) - if highestDataBreachProbabilityLeft == highestDataBreachProbabilityRight { - if len(risksLeft) == 0 && len(risksRight) > 0 { - return false - } - if len(risksLeft) > 0 && len(risksRight) == 0 { - return true - } - return assets[i].Title < assets[j].Title - } - return highestDataBreachProbabilityLeft > highestDataBreachProbabilityRight - }) -} - type ByDataAssetTitleSort []*DataAsset func (what ByDataAssetTitleSort) Len() int { return len(what) } diff --git a/pkg/security/types/risks.go b/pkg/security/types/risks.go index 39d1b5ca..7349d763 100644 --- a/pkg/security/types/risks.go +++ b/pkg/security/types/risks.go @@ -29,23 +29,6 @@ func GetRiskCategory(parsedModel *Model, categoryID string) *RiskCategory { return nil } -func GetRiskCategories(parsedModel *Model, categoryIDs []string) []*RiskCategory { - categoryMap := make(map[string]*RiskCategory) - for _, categoryId := range categoryIDs { - category := GetRiskCategory(parsedModel, categoryId) - if category != nil { - categoryMap[categoryId] = category - } - } - - categories := make([]*RiskCategory, 0) - for categoryId := range categoryMap { - categories = append(categories, categoryMap[categoryId]) - } - - return categories -} - func AllRisks(parsedModel *Model) []*Risk { result := make([]*Risk, 0) for _, risks := range parsedModel.GeneratedRisksByCategory { @@ -54,7 +37,7 @@ func AllRisks(parsedModel *Model) []*Risk { return result } -func ReduceToOnlyStillAtRisk(parsedModel *Model, risks []*Risk) []*Risk { +func ReduceToOnlyStillAtRisk(risks []*Risk) []*Risk { filteredRisks := make([]*Risk, 0) for _, risk := range risks { if risk.RiskStatus.IsStillAtRisk() { @@ -64,26 +47,6 @@ func ReduceToOnlyStillAtRisk(parsedModel *Model, risks []*Risk) []*Risk { return filteredRisks } -func HighestExploitationLikelihood(risks []*Risk) RiskExploitationLikelihood { - result := Unlikely - for _, risk := range risks { - if risk.ExploitationLikelihood > result { - result = risk.ExploitationLikelihood - } - } - return result -} - -func HighestExploitationImpact(risks []*Risk) RiskExploitationImpact { - result := LowImpact - for _, risk := range risks { - if risk.ExploitationImpact > result { - result = risk.ExploitationImpact - } - } - return result -} - func HighestSeverityStillAtRisk(model *Model, risks []*Risk) RiskSeverity { result := LowSeverity for _, risk := range risks { @@ -106,8 +69,8 @@ func (what ByRiskCategoryTitleSort) Less(i, j int) bool { func SortByRiskCategoryHighestContainingRiskSeveritySortStillAtRisk(parsedModel *Model, riskCategories []*RiskCategory) { sort.Slice(riskCategories, func(i, j int) bool { - risksLeft := ReduceToOnlyStillAtRisk(parsedModel, parsedModel.GeneratedRisksByCategory[riskCategories[i].ID]) - risksRight := ReduceToOnlyStillAtRisk(parsedModel, parsedModel.GeneratedRisksByCategory[riskCategories[j].ID]) + risksLeft := ReduceToOnlyStillAtRisk(parsedModel.GeneratedRisksByCategory[riskCategories[i].ID]) + risksRight := ReduceToOnlyStillAtRisk(parsedModel.GeneratedRisksByCategory[riskCategories[j].ID]) highestLeft := HighestSeverityStillAtRisk(parsedModel, risksLeft) highestRight := HighestSeverityStillAtRisk(parsedModel, risksRight) if highestLeft == highestRight { @@ -156,22 +119,6 @@ func SortByRiskSeverity(risks []*Risk, parsedModel *Model) { }) } -func SortByDataBreachProbability(risks []*Risk, parsedModel *Model) { - sort.Slice(risks, func(i, j int) bool { - - if risks[i].DataBreachProbability == risks[j].DataBreachProbability { - trackingStatusLeft := risks[i].RiskStatus - trackingStatusRight := risks[j].RiskStatus - if trackingStatusLeft == trackingStatusRight { - return risks[i].Title < risks[j].Title - } else { - return trackingStatusLeft < trackingStatusRight - } - } - return risks[i].DataBreachProbability > risks[j].DataBreachProbability - }) -} - // as in Go ranging over map is random order, range over them in sorted (hence reproducible) way: func SortedRiskCategories(parsedModel *Model) []*RiskCategory { @@ -197,633 +144,3 @@ func SortedRisksOfCategory(parsedModel *Model, category *RiskCategory) []*Risk { SortByRiskSeverity(risks, parsedModel) return risks } - -func CountRisks(risksByCategory map[string][]*Risk) int { - result := 0 - for _, risks := range risksByCategory { - result += len(risks) - } - return result -} - -func RisksOfOnlySTRIDESpoofing(parsedModel *Model, risksByCategory map[string][]*Risk) map[string][]*Risk { - result := make(map[string][]*Risk) - for categoryId, risks := range risksByCategory { - for _, risk := range risks { - category := GetRiskCategory(parsedModel, categoryId) - if category != nil { - if category.STRIDE == Spoofing { - result[categoryId] = append(result[categoryId], risk) - } - } - } - } - return result -} - -func RisksOfOnlySTRIDETampering(parsedModel *Model, risksByCategory map[string][]*Risk) map[string][]*Risk { - result := make(map[string][]*Risk) - for categoryId, risks := range risksByCategory { - for _, risk := range risks { - category := GetRiskCategory(parsedModel, categoryId) - if category != nil { - if category.STRIDE == Tampering { - result[categoryId] = append(result[categoryId], risk) - } - } - } - } - return result -} - -func RisksOfOnlySTRIDERepudiation(parsedModel *Model, risksByCategory map[string][]*Risk) map[string][]*Risk { - result := make(map[string][]*Risk) - for categoryId, risks := range risksByCategory { - for _, risk := range risks { - category := GetRiskCategory(parsedModel, categoryId) - if category.STRIDE == Repudiation { - result[categoryId] = append(result[categoryId], risk) - } - } - } - return result -} - -func RisksOfOnlySTRIDEInformationDisclosure(parsedModel *Model, risksByCategory map[string][]*Risk) map[string][]*Risk { - result := make(map[string][]*Risk) - for categoryId, risks := range risksByCategory { - for _, risk := range risks { - category := GetRiskCategory(parsedModel, categoryId) - if category.STRIDE == InformationDisclosure { - result[categoryId] = append(result[categoryId], risk) - } - } - } - return result -} - -func RisksOfOnlySTRIDEDenialOfService(parsedModel *Model, risksByCategory map[string][]*Risk) map[string][]*Risk { - result := make(map[string][]*Risk) - for categoryId, risks := range risksByCategory { - for _, risk := range risks { - category := GetRiskCategory(parsedModel, categoryId) - if category.STRIDE == DenialOfService { - result[categoryId] = append(result[categoryId], risk) - } - } - } - return result -} - -func RisksOfOnlySTRIDEElevationOfPrivilege(parsedModel *Model, risksByCategory map[string][]*Risk) map[string][]*Risk { - result := make(map[string][]*Risk) - for categoryId, risks := range risksByCategory { - for _, risk := range risks { - category := GetRiskCategory(parsedModel, categoryId) - if category.STRIDE == ElevationOfPrivilege { - result[categoryId] = append(result[categoryId], risk) - } - } - } - return result -} - -func RisksOfOnlyBusinessSide(parsedModel *Model, risksByCategory map[string][]*Risk) map[string][]*Risk { - result := make(map[string][]*Risk) - for categoryId, risks := range risksByCategory { - for _, risk := range risks { - category := GetRiskCategory(parsedModel, categoryId) - if category.Function == BusinessSide { - result[categoryId] = append(result[categoryId], risk) - } - } - } - return result -} - -func RisksOfOnlyArchitecture(parsedModel *Model, risksByCategory map[string][]*Risk) map[string][]*Risk { - result := make(map[string][]*Risk) - for categoryId, risks := range risksByCategory { - for _, risk := range risks { - category := GetRiskCategory(parsedModel, categoryId) - if category.Function == Architecture { - result[categoryId] = append(result[categoryId], risk) - } - } - } - return result -} - -func RisksOfOnlyDevelopment(parsedModel *Model, risksByCategory map[string][]*Risk) map[string][]*Risk { - result := make(map[string][]*Risk) - for categoryId, risks := range risksByCategory { - for _, risk := range risks { - category := GetRiskCategory(parsedModel, categoryId) - if category.Function == Development { - result[categoryId] = append(result[categoryId], risk) - } - } - } - return result -} - -func RisksOfOnlyOperation(parsedModel *Model, risksByCategory map[string][]*Risk) map[string][]*Risk { - result := make(map[string][]*Risk) - for categoryId, risks := range risksByCategory { - for _, risk := range risks { - category := GetRiskCategory(parsedModel, categoryId) - if category.Function == Operations { - result[categoryId] = append(result[categoryId], risk) - } - } - } - return result -} - -func CategoriesOfOnlyRisksStillAtRisk(parsedModel *Model, risksByCategory map[string][]*Risk) []string { - categories := make(map[string]struct{}) // Go's trick of unique elements is a map - for categoryId, risks := range risksByCategory { - for _, risk := range risks { - if !risk.RiskStatus.IsStillAtRisk() { - continue - } - categories[categoryId] = struct{}{} - } - } - // return as slice (of now unique values) - return keysAsSlice(categories) -} - -func CategoriesOfOnlyCriticalRisks(parsedModel *Model, risksByCategory map[string][]*Risk, initialRisks bool) []string { - categories := make(map[string]struct{}) // Go's trick of unique elements is a map - for categoryId, risks := range risksByCategory { - for _, risk := range risks { - if !initialRisks && !risk.RiskStatus.IsStillAtRisk() { - continue - } - if risk.Severity == CriticalSeverity { - categories[categoryId] = struct{}{} - } - } - } - // return as slice (of now unique values) - return keysAsSlice(categories) -} - -func CategoriesOfOnlyHighRisks(parsedModel *Model, risksByCategory map[string][]*Risk, initialRisks bool) []string { - categories := make(map[string]struct{}) // Go's trick of unique elements is a map - for categoryId, risks := range risksByCategory { - for _, risk := range risks { - if !initialRisks && !risk.RiskStatus.IsStillAtRisk() { - continue - } - highest := HighestSeverity(parsedModel.GeneratedRisksByCategory[categoryId]) - if !initialRisks { - highest = HighestSeverityStillAtRisk(parsedModel, parsedModel.GeneratedRisksByCategory[categoryId]) - } - if risk.Severity == HighSeverity && highest < CriticalSeverity { - categories[categoryId] = struct{}{} - } - } - } - // return as slice (of now unique values) - return keysAsSlice(categories) -} - -func CategoriesOfOnlyElevatedRisks(parsedModel *Model, risksByCategory map[string][]*Risk, initialRisks bool) []string { - categories := make(map[string]struct{}) // Go's trick of unique elements is a map - for categoryId, risks := range risksByCategory { - for _, risk := range risks { - if !initialRisks && !risk.RiskStatus.IsStillAtRisk() { - continue - } - highest := HighestSeverity(parsedModel.GeneratedRisksByCategory[categoryId]) - if !initialRisks { - highest = HighestSeverityStillAtRisk(parsedModel, parsedModel.GeneratedRisksByCategory[categoryId]) - } - if risk.Severity == ElevatedSeverity && highest < HighSeverity { - categories[categoryId] = struct{}{} - } - } - } - // return as slice (of now unique values) - return keysAsSlice(categories) -} - -func CategoriesOfOnlyMediumRisks(parsedModel *Model, risksByCategory map[string][]*Risk, initialRisks bool) []string { - categories := make(map[string]struct{}) // Go's trick of unique elements is a map - for categoryId, risks := range risksByCategory { - for _, risk := range risks { - if !initialRisks && !risk.RiskStatus.IsStillAtRisk() { - continue - } - highest := HighestSeverity(parsedModel.GeneratedRisksByCategory[categoryId]) - if !initialRisks { - highest = HighestSeverityStillAtRisk(parsedModel, parsedModel.GeneratedRisksByCategory[categoryId]) - } - if risk.Severity == MediumSeverity && highest < ElevatedSeverity { - categories[categoryId] = struct{}{} - } - } - } - // return as slice (of now unique values) - return keysAsSlice(categories) -} - -func CategoriesOfOnlyLowRisks(parsedModel *Model, risksByCategory map[string][]*Risk, initialRisks bool) []string { - categories := make(map[string]struct{}) // Go's trick of unique elements is a map - for categoryId, risks := range risksByCategory { - for _, risk := range risks { - if !initialRisks && !risk.RiskStatus.IsStillAtRisk() { - continue - } - highest := HighestSeverity(parsedModel.GeneratedRisksByCategory[categoryId]) - if !initialRisks { - highest = HighestSeverityStillAtRisk(parsedModel, parsedModel.GeneratedRisksByCategory[categoryId]) - } - if risk.Severity == LowSeverity && highest < MediumSeverity { - categories[categoryId] = struct{}{} - } - } - } - // return as slice (of now unique values) - return keysAsSlice(categories) -} - -func HighestSeverity(risks []*Risk) RiskSeverity { - result := LowSeverity - for _, risk := range risks { - if risk.Severity > result { - result = risk.Severity - } - } - return result -} - -func keysAsSlice(categories map[string]struct{}) []string { - result := make([]string, 0, len(categories)) - for k := range categories { - result = append(result, k) - } - return result -} - -func FilteredByOnlyBusinessSide(parsedModel *Model) []*Risk { - filteredRisks := make([]*Risk, 0) - for categoryId, risks := range parsedModel.GeneratedRisksByCategory { - for _, risk := range risks { - category := GetRiskCategory(parsedModel, categoryId) - if category.Function == BusinessSide { - filteredRisks = append(filteredRisks, risk) - } - } - } - return filteredRisks -} - -func FilteredByOnlyArchitecture(parsedModel *Model) []*Risk { - filteredRisks := make([]*Risk, 0) - for categoryId, risks := range parsedModel.GeneratedRisksByCategory { - for _, risk := range risks { - category := GetRiskCategory(parsedModel, categoryId) - if category.Function == Architecture { - filteredRisks = append(filteredRisks, risk) - } - } - } - return filteredRisks -} - -func FilteredByOnlyDevelopment(parsedModel *Model) []*Risk { - filteredRisks := make([]*Risk, 0) - for categoryId, risks := range parsedModel.GeneratedRisksByCategory { - for _, risk := range risks { - category := GetRiskCategory(parsedModel, categoryId) - if category.Function == Development { - filteredRisks = append(filteredRisks, risk) - } - } - } - return filteredRisks -} - -func FilteredByOnlyOperation(parsedModel *Model) []*Risk { - filteredRisks := make([]*Risk, 0) - for categoryId, risks := range parsedModel.GeneratedRisksByCategory { - for _, risk := range risks { - category := GetRiskCategory(parsedModel, categoryId) - if category.Function == Operations { - filteredRisks = append(filteredRisks, risk) - } - } - } - return filteredRisks -} - -func FilteredByOnlyCriticalRisks(parsedModel *Model) []*Risk { - filteredRisks := make([]*Risk, 0) - for _, risks := range parsedModel.GeneratedRisksByCategory { - for _, risk := range risks { - if risk.Severity == CriticalSeverity { - filteredRisks = append(filteredRisks, risk) - } - } - } - return filteredRisks -} - -func FilteredByOnlyHighRisks(parsedModel *Model) []*Risk { - filteredRisks := make([]*Risk, 0) - for _, risks := range parsedModel.GeneratedRisksByCategory { - for _, risk := range risks { - if risk.Severity == HighSeverity { - filteredRisks = append(filteredRisks, risk) - } - } - } - return filteredRisks -} - -func FilteredByOnlyElevatedRisks(parsedModel *Model) []*Risk { - filteredRisks := make([]*Risk, 0) - for _, risks := range parsedModel.GeneratedRisksByCategory { - for _, risk := range risks { - if risk.Severity == ElevatedSeverity { - filteredRisks = append(filteredRisks, risk) - } - } - } - return filteredRisks -} - -func FilteredByOnlyMediumRisks(parsedModel *Model) []*Risk { - filteredRisks := make([]*Risk, 0) - for _, risks := range parsedModel.GeneratedRisksByCategory { - for _, risk := range risks { - if risk.Severity == MediumSeverity { - filteredRisks = append(filteredRisks, risk) - } - } - } - return filteredRisks -} - -func FilteredByOnlyLowRisks(parsedModel *Model) []*Risk { - filteredRisks := make([]*Risk, 0) - for _, risks := range parsedModel.GeneratedRisksByCategory { - for _, risk := range risks { - if risk.Severity == LowSeverity { - filteredRisks = append(filteredRisks, risk) - } - } - } - return filteredRisks -} - -func FilterByModelFailures(parsedModel *Model, risksByCat map[string][]*Risk) map[string][]*Risk { - result := make(map[string][]*Risk) - for categoryId, risks := range risksByCat { - category := GetRiskCategory(parsedModel, categoryId) - if category.ModelFailurePossibleReason { - result[categoryId] = risks - } - } - - return result -} - -func FlattenRiskSlice(risksByCat map[string][]*Risk) []*Risk { - result := make([]*Risk, 0) - for _, risks := range risksByCat { - result = append(result, risks...) - } - return result -} - -func TotalRiskCount(parsedModel *Model) int { - count := 0 - for _, risks := range parsedModel.GeneratedRisksByCategory { - count += len(risks) - } - return count -} - -func FilteredByRiskTrackingUnchecked(parsedModel *Model) []*Risk { - filteredRisks := make([]*Risk, 0) - for _, risks := range parsedModel.GeneratedRisksByCategory { - for _, risk := range risks { - if risk.RiskStatus == Unchecked { - filteredRisks = append(filteredRisks, risk) - } - } - } - return filteredRisks -} - -func FilteredByRiskTrackingInDiscussion(parsedModel *Model) []*Risk { - filteredRisks := make([]*Risk, 0) - for _, risks := range parsedModel.GeneratedRisksByCategory { - for _, risk := range risks { - if risk.RiskStatus == InDiscussion { - filteredRisks = append(filteredRisks, risk) - } - } - } - return filteredRisks -} - -func FilteredByRiskTrackingAccepted(parsedModel *Model) []*Risk { - filteredRisks := make([]*Risk, 0) - for _, risks := range parsedModel.GeneratedRisksByCategory { - for _, risk := range risks { - if risk.RiskStatus == Accepted { - filteredRisks = append(filteredRisks, risk) - } - } - } - return filteredRisks -} - -func FilteredByRiskTrackingInProgress(parsedModel *Model) []*Risk { - filteredRisks := make([]*Risk, 0) - for _, risks := range parsedModel.GeneratedRisksByCategory { - for _, risk := range risks { - if risk.RiskStatus == InProgress { - filteredRisks = append(filteredRisks, risk) - } - } - } - return filteredRisks -} - -func FilteredByRiskTrackingMitigated(parsedModel *Model) []*Risk { - filteredRisks := make([]*Risk, 0) - for _, risks := range parsedModel.GeneratedRisksByCategory { - for _, risk := range risks { - if risk.RiskStatus == Mitigated { - filteredRisks = append(filteredRisks, risk) - } - } - } - return filteredRisks -} - -func FilteredByRiskTrackingFalsePositive(parsedModel *Model) []*Risk { - filteredRisks := make([]*Risk, 0) - for _, risks := range parsedModel.GeneratedRisksByCategory { - for _, risk := range risks { - if risk.RiskStatus == FalsePositive { - filteredRisks = append(filteredRisks, risk) - } - } - } - return filteredRisks -} - -func ReduceToOnlyHighRisk(risks []*Risk) []*Risk { - filteredRisks := make([]*Risk, 0) - for _, risk := range risks { - if risk.Severity == HighSeverity { - filteredRisks = append(filteredRisks, risk) - } - } - return filteredRisks -} - -func ReduceToOnlyMediumRisk(risks []*Risk) []*Risk { - filteredRisks := make([]*Risk, 0) - for _, risk := range risks { - if risk.Severity == MediumSeverity { - filteredRisks = append(filteredRisks, risk) - } - } - return filteredRisks -} - -func ReduceToOnlyLowRisk(risks []*Risk) []*Risk { - filteredRisks := make([]*Risk, 0) - for _, risk := range risks { - if risk.Severity == LowSeverity { - filteredRisks = append(filteredRisks, risk) - } - } - return filteredRisks -} - -func ReduceToOnlyRiskTrackingUnchecked(parsedModel *Model, risks []*Risk) []*Risk { - filteredRisks := make([]*Risk, 0) - for _, risk := range risks { - if risk.RiskStatus == Unchecked { - filteredRisks = append(filteredRisks, risk) - } - } - return filteredRisks -} - -func ReduceToOnlyRiskTrackingInDiscussion(parsedModel *Model, risks []*Risk) []*Risk { - filteredRisks := make([]*Risk, 0) - for _, risk := range risks { - if risk.RiskStatus == InDiscussion { - filteredRisks = append(filteredRisks, risk) - } - } - return filteredRisks -} - -func ReduceToOnlyRiskTrackingAccepted(parsedModel *Model, risks []*Risk) []*Risk { - filteredRisks := make([]*Risk, 0) - for _, risk := range risks { - if risk.RiskStatus == Accepted { - filteredRisks = append(filteredRisks, risk) - } - } - return filteredRisks -} - -func ReduceToOnlyRiskTrackingInProgress(parsedModel *Model, risks []*Risk) []*Risk { - filteredRisks := make([]*Risk, 0) - for _, risk := range risks { - if risk.RiskStatus == InProgress { - filteredRisks = append(filteredRisks, risk) - } - } - return filteredRisks -} - -func ReduceToOnlyRiskTrackingMitigated(parsedModel *Model, risks []*Risk) []*Risk { - filteredRisks := make([]*Risk, 0) - for _, risk := range risks { - if risk.RiskStatus == Mitigated { - filteredRisks = append(filteredRisks, risk) - } - } - return filteredRisks -} - -func ReduceToOnlyRiskTrackingFalsePositive(parsedModel *Model, risks []*Risk) []*Risk { - filteredRisks := make([]*Risk, 0) - for _, risk := range risks { - if risk.RiskStatus == FalsePositive { - filteredRisks = append(filteredRisks, risk) - } - } - return filteredRisks -} - -func FilteredByStillAtRisk(parsedModel *Model) []*Risk { - filteredRisks := make([]*Risk, 0) - for _, risks := range parsedModel.GeneratedRisksByCategory { - for _, risk := range risks { - if risk.RiskStatus.IsStillAtRisk() { - filteredRisks = append(filteredRisks, risk) - } - } - } - return filteredRisks -} - -func OverallRiskStatistics(parsedModel *Model) RiskStatistics { - result := RiskStatistics{} - result.Risks = make(map[string]map[string]int) - result.Risks[CriticalSeverity.String()] = make(map[string]int) - result.Risks[CriticalSeverity.String()][Unchecked.String()] = 0 - result.Risks[CriticalSeverity.String()][InDiscussion.String()] = 0 - result.Risks[CriticalSeverity.String()][Accepted.String()] = 0 - result.Risks[CriticalSeverity.String()][InProgress.String()] = 0 - result.Risks[CriticalSeverity.String()][Mitigated.String()] = 0 - result.Risks[CriticalSeverity.String()][FalsePositive.String()] = 0 - result.Risks[HighSeverity.String()] = make(map[string]int) - result.Risks[HighSeverity.String()][Unchecked.String()] = 0 - result.Risks[HighSeverity.String()][InDiscussion.String()] = 0 - result.Risks[HighSeverity.String()][Accepted.String()] = 0 - result.Risks[HighSeverity.String()][InProgress.String()] = 0 - result.Risks[HighSeverity.String()][Mitigated.String()] = 0 - result.Risks[HighSeverity.String()][FalsePositive.String()] = 0 - result.Risks[ElevatedSeverity.String()] = make(map[string]int) - result.Risks[ElevatedSeverity.String()][Unchecked.String()] = 0 - result.Risks[ElevatedSeverity.String()][InDiscussion.String()] = 0 - result.Risks[ElevatedSeverity.String()][Accepted.String()] = 0 - result.Risks[ElevatedSeverity.String()][InProgress.String()] = 0 - result.Risks[ElevatedSeverity.String()][Mitigated.String()] = 0 - result.Risks[ElevatedSeverity.String()][FalsePositive.String()] = 0 - result.Risks[MediumSeverity.String()] = make(map[string]int) - result.Risks[MediumSeverity.String()][Unchecked.String()] = 0 - result.Risks[MediumSeverity.String()][InDiscussion.String()] = 0 - result.Risks[MediumSeverity.String()][Accepted.String()] = 0 - result.Risks[MediumSeverity.String()][InProgress.String()] = 0 - result.Risks[MediumSeverity.String()][Mitigated.String()] = 0 - result.Risks[MediumSeverity.String()][FalsePositive.String()] = 0 - result.Risks[LowSeverity.String()] = make(map[string]int) - result.Risks[LowSeverity.String()][Unchecked.String()] = 0 - result.Risks[LowSeverity.String()][InDiscussion.String()] = 0 - result.Risks[LowSeverity.String()][Accepted.String()] = 0 - result.Risks[LowSeverity.String()][InProgress.String()] = 0 - result.Risks[LowSeverity.String()][Mitigated.String()] = 0 - result.Risks[LowSeverity.String()][FalsePositive.String()] = 0 - for _, risks := range parsedModel.GeneratedRisksByCategory { - for _, risk := range risks { - result.Risks[risk.Severity.String()][risk.RiskStatus.String()]++ - } - } - return result -} diff --git a/pkg/security/types/technical_asset.go b/pkg/security/types/technical_asset.go index 5a2bdc9b..970a62c1 100644 --- a/pkg/security/types/technical_asset.go +++ b/pkg/security/types/technical_asset.go @@ -365,35 +365,6 @@ func (what TechnicalAsset) GetTrustBoundaryId(model *Model) string { return "" } -func SortByTechnicalAssetRiskSeverityAndTitleStillAtRisk(assets []*TechnicalAsset, parsedModel *Model) { - sort.Slice(assets, func(i, j int) bool { - risksLeft := ReduceToOnlyStillAtRisk(parsedModel, assets[i].GeneratedRisks(parsedModel)) - risksRight := ReduceToOnlyStillAtRisk(parsedModel, assets[j].GeneratedRisks(parsedModel)) - highestSeverityLeft := HighestSeverityStillAtRisk(parsedModel, risksLeft) - highestSeverityRight := HighestSeverityStillAtRisk(parsedModel, risksRight) - var result bool - if highestSeverityLeft == highestSeverityRight { - if len(risksLeft) == 0 && len(risksRight) > 0 { - return false - } else if len(risksLeft) > 0 && len(risksRight) == 0 { - return true - } else { - result = assets[i].Title < assets[j].Title - } - } else { - result = highestSeverityLeft > highestSeverityRight - } - if assets[i].OutOfScope && assets[j].OutOfScope { - result = assets[i].Title < assets[j].Title - } else if assets[i].OutOfScope { - result = false - } else if assets[j].OutOfScope { - result = true - } - return result - }) -} - type ByTechnicalAssetRAAAndTitleSort []*TechnicalAsset func (what ByTechnicalAssetRAAAndTitleSort) Len() int { return len(what) }