diff --git a/cmd/raa/main.go b/cmd/raa/main.go
index 769992ee..44833820 100644
--- a/cmd/raa/main.go
+++ b/cmd/raa/main.go
@@ -36,7 +36,7 @@ func main() {
// _ = os.WriteFile("raa_in.json", data, 0644)
- var input types.ParsedModel
+ var input types.Model
parseError := json.Unmarshal(data, &input)
if parseError != nil {
_, _ = fmt.Fprintf(os.Stderr, "failed to parse model: %v\n", parseError)
@@ -75,7 +75,7 @@ func closeFile(file io.Closer) {
_ = file.Close()
}
-func CalculateRAA(input *types.ParsedModel) string {
+func CalculateRAA(input *types.Model) string {
for techAssetID, techAsset := range input.TechnicalAssets {
aa := calculateAttackerAttractiveness(input, techAsset)
aa += calculatePivotingNeighbourEffectAdjustment(input, techAsset)
@@ -95,7 +95,7 @@ func CalculateRAA(input *types.ParsedModel) string {
var attackerAttractivenessMinimum, attackerAttractivenessMaximum, spread float64 = 0, 0, 0
// set the concrete value in relation to the minimum and maximum of all
-func calculateRelativeAttackerAttractiveness(input *types.ParsedModel, attractiveness float64) float64 {
+func calculateRelativeAttackerAttractiveness(input *types.Model, attractiveness float64) float64 {
if attackerAttractivenessMinimum == 0 || attackerAttractivenessMaximum == 0 {
attackerAttractivenessMinimum, attackerAttractivenessMaximum = 9223372036854775807, -9223372036854775808
// determine (only one time required) the min/max of all
@@ -130,7 +130,7 @@ func calculateRelativeAttackerAttractiveness(input *types.ParsedModel, attractiv
}
// increase the RAA (relative attacker attractiveness) by one third (1/3) of the delta to the highest outgoing neighbour (if positive delta)
-func calculatePivotingNeighbourEffectAdjustment(input *types.ParsedModel, techAsset *types.TechnicalAsset) float64 {
+func calculatePivotingNeighbourEffectAdjustment(input *types.Model, techAsset *types.TechnicalAsset) float64 {
if techAsset.OutOfScope {
return 0
}
@@ -141,7 +141,7 @@ func calculatePivotingNeighbourEffectAdjustment(input *types.ParsedModel, techAs
delta := calculateRelativeAttackerAttractiveness(input, calculateAttackerAttractiveness(input, outgoingNeighbour)) - calculateRelativeAttackerAttractiveness(input, calculateAttackerAttractiveness(input, techAsset))
if delta > 0 {
potentialIncrease := delta / 3
- //fmt.Println("Positive delta from", techAsset.Id, "to", outgoingNeighbour.Id, "is", delta, "yields to pivoting neighbour effect of an increase of", potentialIncrease)
+ //fmt.Println("Positive delta from", techAsset.ID, "to", outgoingNeighbour.ID, "is", delta, "yields to pivoting neighbour effect of an increase of", potentialIncrease)
if potentialIncrease > adjustment {
adjustment = potentialIncrease
}
@@ -153,7 +153,7 @@ func calculatePivotingNeighbourEffectAdjustment(input *types.ParsedModel, techAs
// The sum of all CIAs of the asset itself (fibonacci scale) plus the sum of the comm-links' transferred CIAs
// Multiplied by the quantity values of the data asset for C and I (not A)
-func calculateAttackerAttractiveness(input *types.ParsedModel, techAsset *types.TechnicalAsset) float64 {
+func calculateAttackerAttractiveness(input *types.Model, techAsset *types.TechnicalAsset) float64 {
if techAsset.OutOfScope {
return 0
}
diff --git a/cmd/raa_dummy/main.go b/cmd/raa_dummy/main.go
index 2e974b0e..c43779a4 100644
--- a/cmd/raa_dummy/main.go
+++ b/cmd/raa_dummy/main.go
@@ -23,7 +23,7 @@ func main() {
os.Exit(-2)
}
- var input types.ParsedModel
+ var input types.Model
inError := json.Unmarshal(inData, &input)
if inError != nil {
_, _ = fmt.Fprintf(os.Stderr, "failed to parse model: %v\n", inError)
@@ -44,7 +44,7 @@ func main() {
// used from run caller:
-func CalculateRAA(input *types.ParsedModel) string {
+func CalculateRAA(input *types.Model) string {
for techAssetID, techAsset := range input.TechnicalAssets {
nBig, randError := rand.Int(rand.Reader, big.NewInt(100))
if randError != nil {
diff --git a/cmd/risk_demo/main.go b/cmd/risk_demo/main.go
index c220ff1a..049222a2 100644
--- a/cmd/risk_demo/main.go
+++ b/cmd/risk_demo/main.go
@@ -16,8 +16,6 @@ type customRiskRule string
// exported as symbol (here simply as variable to interface to bundle many functions under one symbol) named "RiskRule"
-var CustomRiskRule customRiskRule
-
func main() {
getInfo := flag.Bool("get-info", false, "get rule info")
generateRisks := flag.Bool("generate-risks", false, "generate risks")
@@ -25,8 +23,7 @@ func main() {
if *getInfo {
rule := new(customRiskRule)
- category := rule.Category()
- riskData, marshalError := json.Marshal(new(model.CustomRisk).Init(category.Id, category, rule.SupportedTags()))
+ riskData, marshalError := json.Marshal(new(model.CustomRiskCategory).Init(rule.Category(), rule.SupportedTags()))
if marshalError != nil {
_, _ = fmt.Fprintf(os.Stderr, "failed to print risk data: %v", marshalError)
@@ -45,7 +42,7 @@ func main() {
os.Exit(-2)
}
- var input types.ParsedModel
+ var input types.Model
inError := json.Unmarshal(inData, &input)
if inError != nil {
_, _ = fmt.Fprintf(os.Stderr, "failed to parse model: %v\n", inError)
@@ -67,9 +64,9 @@ func main() {
os.Exit(-2)
}
-func (r customRiskRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "demo",
+func (r customRiskRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "demo",
Title: "Just a Demo",
Description: "Demo Description",
Impact: "Demo Impact",
@@ -92,7 +89,7 @@ func (r customRiskRule) SupportedTags() []string {
return []string{"demo tag"}
}
-func (r customRiskRule) GenerateRisks(parsedModel *types.ParsedModel) []types.Risk {
+func (r customRiskRule) GenerateRisks(parsedModel *types.Model) []types.Risk {
generatedRisks := make([]types.Risk, 0)
for _, techAsset := range parsedModel.TechnicalAssets {
generatedRisks = append(generatedRisks, createRisk(techAsset))
@@ -101,8 +98,9 @@ func (r customRiskRule) GenerateRisks(parsedModel *types.ParsedModel) []types.Ri
}
func createRisk(technicalAsset *types.TechnicalAsset) types.Risk {
+ category := new(customRiskRule).Category()
risk := types.Risk{
- CategoryId: CustomRiskRule.Category().Id,
+ CategoryId: category.ID,
Severity: types.CalculateSeverity(types.VeryLikely, types.MediumImpact),
ExploitationLikelihood: types.VeryLikely,
ExploitationImpact: types.MediumImpact,
diff --git a/cmd/script/main.go b/cmd/script/main.go
index 5c7a4faf..0de1ee2c 100644
--- a/cmd/script/main.go
+++ b/cmd/script/main.go
@@ -2,22 +2,24 @@ package main
import (
"fmt"
+ "github.com/threagile/threagile/pkg/common"
+ "github.com/threagile/threagile/pkg/input"
+ "github.com/threagile/threagile/pkg/model"
"github.com/threagile/threagile/pkg/script"
- "github.com/threagile/threagile/pkg/script/common"
- "github.com/threagile/threagile/pkg/security/types"
+ "github.com/threagile/threagile/pkg/security/risks"
"gopkg.in/yaml.v3"
"os"
"path/filepath"
)
func main() {
- riskData, riskReadError := os.ReadFile(filepath.Join("test", "risk-category.yaml"))
- if riskReadError != nil {
- fmt.Printf("error reading risk category: %v\n", riskReadError)
+ ruleData, ruleReadError := os.ReadFile(filepath.Join("test", "risk-category.yaml"))
+ if ruleReadError != nil {
+ fmt.Printf("error reading risk category: %v\n", ruleReadError)
return
}
- scripts, parseError := new(script.Script).Parse(riskData)
+ scripts, parseError := new(script.Script).ParseScripts(ruleData)
if parseError != nil {
fmt.Printf("error parsing scripts: %v\n", parseError)
return
@@ -29,52 +31,70 @@ func main() {
return
}
- model := new(types.ParsedModel)
- modelUnmarshalError := yaml.Unmarshal(modelData, model)
+ inputModel := new(input.Model)
+ modelUnmarshalError := yaml.Unmarshal(modelData, inputModel)
if modelUnmarshalError != nil {
fmt.Printf("error parsing model: %v\n", modelUnmarshalError)
return
}
- categoriesModel := new(types.ParsedModel)
- riskUnmarshalError := yaml.Unmarshal(riskData, categoriesModel)
- if riskUnmarshalError != nil {
- fmt.Printf("error parsing risk category: %v\n", riskUnmarshalError)
+ /*
+ categoriesModel := new(input.Model)
+ riskUnmarshalError := yaml.Unmarshal(riskData, categoriesModel)
+ if riskUnmarshalError != nil {
+ fmt.Printf("error parsing risk category: %v\n", riskUnmarshalError)
+ return
+ }
+ */
+
+ parsedModel, modelError := model.ParseModel(&common.Config{}, inputModel, make(risks.RiskRules), make(risks.RiskRules))
+ if modelError != nil {
+ fmt.Printf("error importing model: %v\n", modelError)
return
}
- var risk types.RiskCategory
- if categoriesModel.IndividualRiskCategories != nil {
- for _, item := range categoriesModel.IndividualRiskCategories {
- risk = item
+ _ = parsedModel
+ _ = scripts
+ /*
+ var risk types.RiskCategory
+ if categoriesModel.CustomRiskCategories != nil {
+ for _, item := range categoriesModel.CustomRiskCategories {
+ risk = item
+ }
}
- }
- for name, script := range scripts {
- scope := new(common.Scope)
- addError := scope.Init(model, &risk, script.Utils())
- if addError != nil {
- fmt.Printf("error adding model to scope for %q: %v\n", name, addError)
+ if len(categoriesModel.CustomRiskCategories) == 0 {
+ fmt.Printf("no risk categories\n")
return
}
- risks, errorLiteral, riskError := script.GenerateRisks(scope)
- if riskError != nil {
- fmt.Printf("error generating risks for %q: %v\n", name, riskError)
+ for name, script := range scripts {
+ scope := new(script.Scope)
+ addError := scope.Init(parsedModel, &risk, script.Utils())
+ if addError != nil {
+ fmt.Printf("error adding model to scope for %q: %v\n", name, addError)
+ return
+ }
+
+ risks, errorLiteral, riskError := script.GenerateRisks(scope)
+ if riskError != nil {
+ fmt.Printf("error generating risks for %q: %v\n", name, riskError)
- if len(errorLiteral) > 0 {
- fmt.Printf("in:\n%v\n", script.IndentPrintf(1, errorLiteral))
+ if len(errorLiteral) > 0 {
+ fmt.Printf("in:\n%v\n", script.IndentPrintf(1, errorLiteral))
+ }
+
+ return
}
- return
- }
+ printedRisks, printError := yaml.Marshal(risks)
+ if printError != nil {
+ fmt.Printf("error printing risks for %q: %v\n", name, printError)
+ return
+ }
- printedRisks, printError := yaml.Marshal(risks)
- if printError != nil {
- fmt.Printf("error printing risks for %q: %v\n", name, printError)
- return
+ fmt.Printf("generated risks for %q: \n%v\n", name, string(printedRisks))
}
- fmt.Printf("generated risks for %q: \n%v\n", name, string(printedRisks))
- }
+ */
}
diff --git a/internal/threagile/explain.go b/internal/threagile/explain.go
index 98eea3dc..b0c3023d 100644
--- a/internal/threagile/explain.go
+++ b/internal/threagile/explain.go
@@ -78,7 +78,7 @@ func (what *Threagile) explainRules(cmd *cobra.Command, _ []string) error {
cmd.Println("----------------------")
customRiskRules := model.LoadCustomRiskRules(strings.Split(what.flags.customRiskRulesPluginFlag, ","), common.DefaultProgressReporter{Verbose: what.flags.verboseFlag})
for _, rule := range customRiskRules {
- cmd.Printf("%v: %v\n", rule.Category().Id, rule.Category().Description)
+ cmd.Printf("%v: %v\n", rule.Category().ID, rule.Category().Description)
}
cmd.Println()
cmd.Println("--------------------")
@@ -86,7 +86,7 @@ func (what *Threagile) explainRules(cmd *cobra.Command, _ []string) error {
cmd.Println("--------------------")
cmd.Println()
for _, rule := range risks.GetBuiltInRiskRules() {
- cmd.Printf("%v: %v\n", rule.Category().Id, rule.Category().Description)
+ cmd.Printf("%v: %v\n", rule.Category().ID, rule.Category().Description)
}
cmd.Println()
diff --git a/internal/threagile/list.go b/internal/threagile/list.go
index b49e964a..a76eabaa 100644
--- a/internal/threagile/list.go
+++ b/internal/threagile/list.go
@@ -33,7 +33,7 @@ func (what *Threagile) initList() *Threagile {
cmd.Println("--------------------")
cmd.Println()
for _, rule := range risks.GetBuiltInRiskRules() {
- cmd.Println(rule.Category().Id, "-->", rule.Category().Title, "--> with tags:", rule.SupportedTags())
+ cmd.Println(rule.Category().ID, "-->", rule.Category().Title, "--> with tags:", rule.SupportedTags())
}
return nil
diff --git a/internal/threagile/root.go b/internal/threagile/root.go
index 3005ce40..5d47a8e5 100644
--- a/internal/threagile/root.go
+++ b/internal/threagile/root.go
@@ -72,7 +72,7 @@ func (what *Threagile) initRoot() *Threagile {
what.rootCmd.PersistentFlags().StringVar(&what.flags.customRiskRulesPluginFlag, customRiskRulesPluginFlagName, strings.Join(defaultConfig.RiskRulesPlugins, ","), "comma-separated list of plugins file names with custom risk rules to load")
what.rootCmd.PersistentFlags().IntVar(&what.flags.diagramDpiFlag, diagramDpiFlagName, defaultConfig.DiagramDPI, "DPI used to render: maximum is "+fmt.Sprintf("%d", common.MaxGraphvizDPI)+"")
- what.rootCmd.PersistentFlags().StringVar(&what.flags.skipRiskRulesFlag, skipRiskRulesFlagName, defaultConfig.SkipRiskRules, "comma-separated list of risk rules (by their ID) to skip")
+ what.rootCmd.PersistentFlags().StringVar(&what.flags.skipRiskRulesFlag, skipRiskRulesFlagName, strings.Join(defaultConfig.SkipRiskRules, ","), "comma-separated list of risk rules (by their ID) to skip")
what.rootCmd.PersistentFlags().BoolVar(&what.flags.ignoreOrphanedRiskTrackingFlag, ignoreOrphanedRiskTrackingFlagName, defaultConfig.IgnoreOrphanedRiskTracking, "ignore orphaned risk tracking (just log them) not matching a concrete risk")
what.rootCmd.PersistentFlags().StringVar(&what.flags.templateFileNameFlag, templateFileNameFlagName, defaultConfig.TemplateFilename, "background pdf file")
@@ -264,7 +264,7 @@ func (what *Threagile) readConfig(cmd *cobra.Command, buildTimestamp string) *co
cfg.RiskRulesPlugins = strings.Split(what.flags.customRiskRulesPluginFlag, ",")
}
if isFlagOverridden(flags, skipRiskRulesFlagName) {
- cfg.SkipRiskRules = what.flags.skipRiskRulesFlag
+ cfg.SkipRiskRules = strings.Split(what.flags.skipRiskRulesFlag, ",")
}
if isFlagOverridden(flags, ignoreOrphanedRiskTrackingFlagName) {
cfg.IgnoreOrphanedRiskTracking = what.flags.ignoreOrphanedRiskTrackingFlag
diff --git a/pkg/common/config.go b/pkg/common/config.go
index 3205935f..6235241a 100644
--- a/pkg/common/config.go
+++ b/pkg/common/config.go
@@ -38,7 +38,7 @@ type Config struct {
RAAPlugin string
RiskRulesPlugins []string
- SkipRiskRules string
+ SkipRiskRules []string
ExecuteModelMacro string
RiskExcel RiskExcelConfig
@@ -92,7 +92,7 @@ func (c *Config) Defaults(buildTimestamp string) *Config {
RAAPlugin: RAAPluginName,
RiskRulesPlugins: make([]string, 0),
- SkipRiskRules: "",
+ SkipRiskRules: make([]string, 0),
ExecuteModelMacro: "",
RiskExcel: RiskExcelConfig{
HideColumns: make([]string, 0),
diff --git a/pkg/input/model.go b/pkg/input/model.go
index 9b7d4782..c5feddf0 100644
--- a/pkg/input/model.go
+++ b/pkg/input/model.go
@@ -21,47 +21,47 @@ import (
// === Model Type Stuff ======================================
type Model struct { // TODO: Eventually remove this and directly use ParsedModelRoot? But then the error messages for model errors are not quite as good anymore...
- ThreagileVersion string `yaml:"threagile_version,omitempty" json:"threagile_version,omitempty"`
- Includes []string `yaml:"includes,omitempty" json:"includes,omitempty"`
- Title string `yaml:"title,omitempty" json:"title,omitempty"`
- Author Author `yaml:"author,omitempty" json:"author,omitempty"`
- Contributors []Author `yaml:"contributors,omitempty" json:"contributors,omitempty"`
- Date string `yaml:"date,omitempty" json:"date,omitempty"`
- AppDescription Overview `yaml:"application_description,omitempty" json:"application_description,omitempty"`
- BusinessOverview Overview `yaml:"business_overview,omitempty" json:"business_overview,omitempty"`
- TechnicalOverview Overview `yaml:"technical_overview,omitempty" json:"technical_overview,omitempty"`
- BusinessCriticality string `yaml:"business_criticality,omitempty" json:"business_criticality,omitempty"`
- ManagementSummaryComment string `yaml:"management_summary_comment,omitempty" json:"management_summary_comment,omitempty"`
- SecurityRequirements map[string]string `yaml:"security_requirements,omitempty" json:"security_requirements,omitempty"`
- Questions map[string]string `yaml:"questions,omitempty" json:"questions,omitempty"`
- AbuseCases map[string]string `yaml:"abuse_cases,omitempty" json:"abuse_cases,omitempty"`
- TagsAvailable []string `yaml:"tags_available,omitempty" json:"tags_available,omitempty"`
- DataAssets map[string]DataAsset `yaml:"data_assets,omitempty" json:"data_assets,omitempty"`
- TechnicalAssets map[string]TechnicalAsset `yaml:"technical_assets,omitempty" json:"technical_assets,omitempty"`
- TrustBoundaries map[string]TrustBoundary `yaml:"trust_boundaries,omitempty" json:"trust_boundaries,omitempty"`
- SharedRuntimes map[string]SharedRuntime `yaml:"shared_runtimes,omitempty" json:"shared_runtimes,omitempty"`
- IndividualRiskCategories map[string]IndividualRiskCategory `yaml:"individual_risk_categories,omitempty" json:"individual_risk_categories,omitempty"`
- RiskTracking map[string]RiskTracking `yaml:"risk_tracking,omitempty" json:"risk_tracking,omitempty"`
- DiagramTweakNodesep int `yaml:"diagram_tweak_nodesep,omitempty" json:"diagram_tweak_nodesep,omitempty"`
- DiagramTweakRanksep int `yaml:"diagram_tweak_ranksep,omitempty" json:"diagram_tweak_ranksep,omitempty"`
- DiagramTweakEdgeLayout string `yaml:"diagram_tweak_edge_layout,omitempty" json:"diagram_tweak_edge_layout,omitempty"`
- DiagramTweakSuppressEdgeLabels bool `yaml:"diagram_tweak_suppress_edge_labels,omitempty" json:"diagram_tweak_suppress_edge_labels,omitempty"`
- DiagramTweakLayoutLeftToRight bool `yaml:"diagram_tweak_layout_left_to_right,omitempty" json:"diagram_tweak_layout_left_to_right,omitempty"`
- DiagramTweakInvisibleConnectionsBetweenAssets []string `yaml:"diagram_tweak_invisible_connections_between_assets,omitempty" json:"diagram_tweak_invisible_connections_between_assets,omitempty"`
- DiagramTweakSameRankAssets []string `yaml:"diagram_tweak_same_rank_assets,omitempty" json:"diagram_tweak_same_rank_assets,omitempty"`
+ ThreagileVersion string `yaml:"threagile_version,omitempty" json:"threagile_version,omitempty"`
+ Includes []string `yaml:"includes,omitempty" json:"includes,omitempty"`
+ Title string `yaml:"title,omitempty" json:"title,omitempty"`
+ Author Author `yaml:"author,omitempty" json:"author,omitempty"`
+ Contributors []Author `yaml:"contributors,omitempty" json:"contributors,omitempty"`
+ Date string `yaml:"date,omitempty" json:"date,omitempty"`
+ AppDescription Overview `yaml:"application_description,omitempty" json:"application_description,omitempty"`
+ BusinessOverview Overview `yaml:"business_overview,omitempty" json:"business_overview,omitempty"`
+ TechnicalOverview Overview `yaml:"technical_overview,omitempty" json:"technical_overview,omitempty"`
+ BusinessCriticality string `yaml:"business_criticality,omitempty" json:"business_criticality,omitempty"`
+ ManagementSummaryComment string `yaml:"management_summary_comment,omitempty" json:"management_summary_comment,omitempty"`
+ SecurityRequirements map[string]string `yaml:"security_requirements,omitempty" json:"security_requirements,omitempty"`
+ Questions map[string]string `yaml:"questions,omitempty" json:"questions,omitempty"`
+ AbuseCases map[string]string `yaml:"abuse_cases,omitempty" json:"abuse_cases,omitempty"`
+ TagsAvailable []string `yaml:"tags_available,omitempty" json:"tags_available,omitempty"`
+ DataAssets map[string]DataAsset `yaml:"data_assets,omitempty" json:"data_assets,omitempty"`
+ TechnicalAssets map[string]TechnicalAsset `yaml:"technical_assets,omitempty" json:"technical_assets,omitempty"`
+ TrustBoundaries map[string]TrustBoundary `yaml:"trust_boundaries,omitempty" json:"trust_boundaries,omitempty"`
+ SharedRuntimes map[string]SharedRuntime `yaml:"shared_runtimes,omitempty" json:"shared_runtimes,omitempty"`
+ CustomRiskCategories RiskCategories `yaml:"custom_risk_categories,omitempty" json:"custom_risk_categories,omitempty"`
+ RiskTracking map[string]RiskTracking `yaml:"risk_tracking,omitempty" json:"risk_tracking,omitempty"`
+ DiagramTweakNodesep int `yaml:"diagram_tweak_nodesep,omitempty" json:"diagram_tweak_nodesep,omitempty"`
+ DiagramTweakRanksep int `yaml:"diagram_tweak_ranksep,omitempty" json:"diagram_tweak_ranksep,omitempty"`
+ DiagramTweakEdgeLayout string `yaml:"diagram_tweak_edge_layout,omitempty" json:"diagram_tweak_edge_layout,omitempty"`
+ DiagramTweakSuppressEdgeLabels bool `yaml:"diagram_tweak_suppress_edge_labels,omitempty" json:"diagram_tweak_suppress_edge_labels,omitempty"`
+ DiagramTweakLayoutLeftToRight bool `yaml:"diagram_tweak_layout_left_to_right,omitempty" json:"diagram_tweak_layout_left_to_right,omitempty"`
+ DiagramTweakInvisibleConnectionsBetweenAssets []string `yaml:"diagram_tweak_invisible_connections_between_assets,omitempty" json:"diagram_tweak_invisible_connections_between_assets,omitempty"`
+ DiagramTweakSameRankAssets []string `yaml:"diagram_tweak_same_rank_assets,omitempty" json:"diagram_tweak_same_rank_assets,omitempty"`
}
func (model *Model) Defaults() *Model {
*model = Model{
- Questions: make(map[string]string),
- AbuseCases: make(map[string]string),
- SecurityRequirements: make(map[string]string),
- DataAssets: make(map[string]DataAsset),
- TechnicalAssets: make(map[string]TechnicalAsset),
- TrustBoundaries: make(map[string]TrustBoundary),
- SharedRuntimes: make(map[string]SharedRuntime),
- IndividualRiskCategories: make(map[string]IndividualRiskCategory),
- RiskTracking: make(map[string]RiskTracking),
+ Questions: make(map[string]string),
+ AbuseCases: make(map[string]string),
+ SecurityRequirements: make(map[string]string),
+ DataAssets: make(map[string]DataAsset),
+ TechnicalAssets: make(map[string]TechnicalAsset),
+ TrustBoundaries: make(map[string]TrustBoundary),
+ SharedRuntimes: make(map[string]SharedRuntime),
+ CustomRiskCategories: make(RiskCategories, 0),
+ RiskTracking: make(map[string]RiskTracking),
}
return model
@@ -219,8 +219,8 @@ func (model *Model) Merge(dir string, includeFilename string) error {
return fmt.Errorf("failed to merge shared runtimes: %v", mergeError)
}
- case strings.ToLower("individual_risk_categories"):
- model.IndividualRiskCategories, mergeError = new(IndividualRiskCategory).MergeMap(model.IndividualRiskCategories, includedModel.IndividualRiskCategories)
+ case strings.ToLower("custom_risk_categories"):
+ mergeError = model.CustomRiskCategories.Add(includedModel.CustomRiskCategories...)
if mergeError != nil {
return fmt.Errorf("failed to merge risk categories: %v", mergeError)
}
diff --git a/pkg/input/risk-category.go b/pkg/input/risk-category.go
index 17ecedb2..59336e96 100644
--- a/pkg/input/risk-category.go
+++ b/pkg/input/risk-category.go
@@ -2,10 +2,12 @@ package input
import (
"fmt"
+ "strings"
)
-type IndividualRiskCategory struct {
+type RiskCategory struct {
ID string `yaml:"id,omitempty" json:"id,omitempty"`
+ Title string `json:"title,omitempty" yaml:"title,omitempty"`
Description string `yaml:"description,omitempty" json:"description,omitempty"`
Impact string `yaml:"impact,omitempty" json:"impact,omitempty"`
ASVS string `yaml:"asvs,omitempty" json:"asvs,omitempty"`
@@ -23,7 +25,9 @@ type IndividualRiskCategory struct {
RisksIdentified map[string]RiskIdentified `yaml:"risks_identified,omitempty" json:"risks_identified,omitempty"`
}
-func (what *IndividualRiskCategory) Merge(other IndividualRiskCategory) error {
+type RiskCategories []*RiskCategory
+
+func (what *RiskCategory) Merge(other RiskCategory) error {
var mergeError error
what.ID, mergeError = new(Strings).MergeSingleton(what.ID, other.ID)
if mergeError != nil {
@@ -106,20 +110,16 @@ func (what *IndividualRiskCategory) Merge(other IndividualRiskCategory) error {
return nil
}
-func (what *IndividualRiskCategory) MergeMap(first map[string]IndividualRiskCategory, second map[string]IndividualRiskCategory) (map[string]IndividualRiskCategory, error) {
- for mapKey, mapValue := range second {
- mapItem, ok := first[mapKey]
- if ok {
- mergeError := mapItem.Merge(mapValue)
- if mergeError != nil {
- return first, fmt.Errorf("failed to merge risk category %q: %v", mapKey, mergeError)
+func (what *RiskCategories) Add(items ...*RiskCategory) error {
+ for _, item := range items {
+ for _, value := range *what {
+ if strings.EqualFold(value.ID, item.ID) {
+ return fmt.Errorf("duplicate item %q in risk category list", value.ID)
}
-
- first[mapKey] = mapItem
- } else {
- first[mapKey] = mapValue
}
+
+ *what = append(*what, item)
}
- return first, nil
+ return nil
}
diff --git a/pkg/input/strings.go b/pkg/input/strings.go
index d1dfeea3..17174544 100644
--- a/pkg/input/strings.go
+++ b/pkg/input/strings.go
@@ -2,6 +2,7 @@ package input
import (
"fmt"
+ "gopkg.in/yaml.v3"
"slices"
"strings"
)
@@ -62,3 +63,37 @@ func (what *Strings) MergeUniqueSlice(first []string, second []string) []string
return first
}
+
+func (what *Strings) AddLineNumbers(script any) string {
+ text, isString := script.(string)
+ if !isString {
+ data, _ := yaml.Marshal(script)
+ text = string(data)
+ }
+
+ lines := strings.Split(text, "\n")
+ for n, line := range lines {
+ lines[n] = fmt.Sprintf("%3d:\t%v", n+1, line)
+ }
+
+ return strings.Join(lines, "\n")
+}
+
+func (what *Strings) IndentPrintf(level int, script any) string {
+ text, isString := script.(string)
+ if !isString {
+ data, _ := yaml.Marshal(script)
+ text = string(data)
+ }
+
+ lines := strings.Split(text, "\n")
+ for n, line := range lines {
+ lines[n] = strings.Repeat(" ", level) + line
+ }
+
+ return strings.Join(lines, "\n")
+}
+
+func (what *Strings) IndentLine(level int, format string, params ...any) string {
+ return strings.Repeat(" ", level) + fmt.Sprintf(format, params...)
+}
diff --git a/pkg/macros/add-build-pipeline-macro.go b/pkg/macros/add-build-pipeline-macro.go
index 82b8214c..b4a30c49 100644
--- a/pkg/macros/add-build-pipeline-macro.go
+++ b/pkg/macros/add-build-pipeline-macro.go
@@ -41,7 +41,7 @@ func (m *AddBuildPipeline) GetMacroDetails() MacroDetails {
// TODO add question for type of machine (either physical, virtual, container, etc.)
-func (m *AddBuildPipeline) GetNextQuestion(model *types.ParsedModel) (nextQuestion MacroQuestion, err error) {
+func (m *AddBuildPipeline) GetNextQuestion(model *types.Model) (nextQuestion MacroQuestion, err error) {
counter := len(m.questionsAnswered)
if counter > 3 && !m.codeInspectionUsed {
counter++
@@ -257,19 +257,19 @@ func (m *AddBuildPipeline) GoBack() (message string, validResult bool, err error
return "Undo successful", true, nil
}
-func (m *AddBuildPipeline) GetFinalChangeImpact(modelInput *input.Model, model *types.ParsedModel) (changes []string, message string, validResult bool, err error) {
+func (m *AddBuildPipeline) GetFinalChangeImpact(modelInput *input.Model, model *types.Model) (changes []string, message string, validResult bool, err error) {
changeLogCollector := make([]string, 0)
message, validResult, err = m.applyChange(modelInput, model, &changeLogCollector, true)
return changeLogCollector, message, validResult, err
}
-func (m *AddBuildPipeline) Execute(modelInput *input.Model, model *types.ParsedModel) (message string, validResult bool, err error) {
+func (m *AddBuildPipeline) Execute(modelInput *input.Model, model *types.Model) (message string, validResult bool, err error) {
changeLogCollector := make([]string, 0)
message, validResult, err = m.applyChange(modelInput, model, &changeLogCollector, false)
return message, validResult, err
}
-func (m *AddBuildPipeline) applyChange(modelInput *input.Model, parsedModel *types.ParsedModel, changeLogCollector *[]string, dryRun bool) (message string, validResult bool, err error) {
+func (m *AddBuildPipeline) applyChange(modelInput *input.Model, parsedModel *types.Model, changeLogCollector *[]string, dryRun bool) (message string, validResult bool, err error) {
var serverSideTechAssets = make([]string, 0)
// ################################################
modelInput.AddTagToModelInput(m.macroState["source-repository"][0], dryRun, changeLogCollector)
diff --git a/pkg/macros/add-vault-macro.go b/pkg/macros/add-vault-macro.go
index c07bac0e..1d4e0a63 100644
--- a/pkg/macros/add-vault-macro.go
+++ b/pkg/macros/add-vault-macro.go
@@ -48,7 +48,7 @@ func (m *AddVaultMacro) GetMacroDetails() MacroDetails {
}
}
-func (m *AddVaultMacro) GetNextQuestion(parsedModel *types.ParsedModel) (nextQuestion MacroQuestion, err error) {
+func (m *AddVaultMacro) GetNextQuestion(parsedModel *types.Model) (nextQuestion MacroQuestion, err error) {
counter := len(m.questionsAnswered)
if counter > 5 && !m.withinTrustBoundary {
counter++
@@ -173,19 +173,19 @@ func (m *AddVaultMacro) GoBack() (message string, validResult bool, err error) {
return "Undo successful", true, nil
}
-func (m *AddVaultMacro) GetFinalChangeImpact(modelInput *input.Model, parsedModel *types.ParsedModel) (changes []string, message string, validResult bool, err error) {
+func (m *AddVaultMacro) GetFinalChangeImpact(modelInput *input.Model, parsedModel *types.Model) (changes []string, message string, validResult bool, err error) {
changeLogCollector := make([]string, 0)
message, validResult, err = m.applyChange(modelInput, parsedModel, &changeLogCollector, true)
return changeLogCollector, message, validResult, err
}
-func (m *AddVaultMacro) Execute(modelInput *input.Model, parsedModel *types.ParsedModel) (message string, validResult bool, err error) {
+func (m *AddVaultMacro) Execute(modelInput *input.Model, parsedModel *types.Model) (message string, validResult bool, err error) {
changeLogCollector := make([]string, 0)
message, validResult, err = m.applyChange(modelInput, parsedModel, &changeLogCollector, false)
return message, validResult, err
}
-func (m *AddVaultMacro) applyChange(modelInput *input.Model, parsedModel *types.ParsedModel, changeLogCollector *[]string, dryRun bool) (message string, validResult bool, err error) {
+func (m *AddVaultMacro) applyChange(modelInput *input.Model, parsedModel *types.Model, changeLogCollector *[]string, dryRun bool) (message string, validResult bool, err error) {
modelInput.AddTagToModelInput(m.macroState["vault-name"][0], dryRun, changeLogCollector)
var serverSideTechAssets = make([]string, 0)
diff --git a/pkg/macros/macros.go b/pkg/macros/macros.go
index 58079827..af13aa26 100644
--- a/pkg/macros/macros.go
+++ b/pkg/macros/macros.go
@@ -20,11 +20,11 @@ import (
type Macros interface {
GetMacroDetails() MacroDetails
- GetNextQuestion(model *types.ParsedModel) (nextQuestion MacroQuestion, err error)
+ GetNextQuestion(model *types.Model) (nextQuestion MacroQuestion, err error)
ApplyAnswer(questionID string, answer ...string) (message string, validResult bool, err error)
GoBack() (message string, validResult bool, err error)
- GetFinalChangeImpact(modelInput *input.Model, model *types.ParsedModel) (changes []string, message string, validResult bool, err error)
- Execute(modelInput *input.Model, model *types.ParsedModel) (message string, validResult bool, err error)
+ GetFinalChangeImpact(modelInput *input.Model, model *types.Model) (changes []string, message string, validResult bool, err error)
+ Execute(modelInput *input.Model, model *types.Model) (message string, validResult bool, err error)
}
func ListBuiltInMacros() []Macros {
@@ -55,7 +55,7 @@ func GetMacroByID(id string) (Macros, error) {
return nil, fmt.Errorf("unknown macro id: %v", id)
}
-func ExecuteModelMacro(modelInput *input.Model, inputFile string, parsedModel *types.ParsedModel, macroID string) error {
+func ExecuteModelMacro(modelInput *input.Model, inputFile string, parsedModel *types.Model, macroID string) error {
macros, err := GetMacroByID(macroID)
if err != nil {
return err
diff --git a/pkg/macros/pretty-print-macro.go b/pkg/macros/pretty-print-macro.go
index 262f18eb..5735c09f 100644
--- a/pkg/macros/pretty-print-macro.go
+++ b/pkg/macros/pretty-print-macro.go
@@ -20,7 +20,7 @@ func (*PrettyPrintMacro) GetMacroDetails() MacroDetails {
}
}
-func (*PrettyPrintMacro) GetNextQuestion(_ *types.ParsedModel) (nextQuestion MacroQuestion, err error) {
+func (*PrettyPrintMacro) GetNextQuestion(_ *types.Model) (nextQuestion MacroQuestion, err error) {
return NoMoreQuestions(), nil
}
@@ -32,10 +32,10 @@ func (*PrettyPrintMacro) GoBack() (message string, validResult bool, err error)
return "Cannot go back further", false, nil
}
-func (*PrettyPrintMacro) GetFinalChangeImpact(_ *input.Model, _ *types.ParsedModel) (changes []string, message string, validResult bool, err error) {
+func (*PrettyPrintMacro) GetFinalChangeImpact(_ *input.Model, _ *types.Model) (changes []string, message string, validResult bool, err error) {
return []string{"pretty-printing the model file"}, "Changeset valid", true, err
}
-func (*PrettyPrintMacro) Execute(_ *input.Model, _ *types.ParsedModel) (message string, validResult bool, err error) {
+func (*PrettyPrintMacro) Execute(_ *input.Model, _ *types.Model) (message string, validResult bool, err error) {
return "Model pretty printing successful", true, nil
}
diff --git a/pkg/macros/remove-unused-tags-macro.go b/pkg/macros/remove-unused-tags-macro.go
index 28c6b3eb..f60834a6 100644
--- a/pkg/macros/remove-unused-tags-macro.go
+++ b/pkg/macros/remove-unused-tags-macro.go
@@ -24,7 +24,7 @@ func (*removeUnusedTagsMacro) GetMacroDetails() MacroDetails {
}
}
-func (*removeUnusedTagsMacro) GetNextQuestion(*types.ParsedModel) (nextQuestion MacroQuestion, err error) {
+func (*removeUnusedTagsMacro) GetNextQuestion(*types.Model) (nextQuestion MacroQuestion, err error) {
return NoMoreQuestions(), nil
}
@@ -36,11 +36,11 @@ func (*removeUnusedTagsMacro) GoBack() (message string, validResult bool, err er
return "Cannot go back further", false, nil
}
-func (*removeUnusedTagsMacro) GetFinalChangeImpact(_ *input.Model, _ *types.ParsedModel) (changes []string, message string, validResult bool, err error) {
+func (*removeUnusedTagsMacro) GetFinalChangeImpact(_ *input.Model, _ *types.Model) (changes []string, message string, validResult bool, err error) {
return []string{"remove unused tags from the model file"}, "Changeset valid", true, err
}
-func (*removeUnusedTagsMacro) Execute(modelInput *input.Model, parsedModel *types.ParsedModel) (message string, validResult bool, err error) {
+func (*removeUnusedTagsMacro) Execute(modelInput *input.Model, parsedModel *types.Model) (message string, validResult bool, err error) {
modelInput.TagsAvailable = parsedModel.TagsAvailable
for _, asset := range parsedModel.DataAssets {
modelInput.TagsAvailable = append(modelInput.TagsAvailable, asset.Tags...)
diff --git a/pkg/macros/seed-risk-tracking-macro.go b/pkg/macros/seed-risk-tracking-macro.go
index 45561656..ede9f91e 100644
--- a/pkg/macros/seed-risk-tracking-macro.go
+++ b/pkg/macros/seed-risk-tracking-macro.go
@@ -23,7 +23,7 @@ func (*SeedRiskTrackingMacro) GetMacroDetails() MacroDetails {
}
}
-func (*SeedRiskTrackingMacro) GetNextQuestion(*types.ParsedModel) (nextQuestion MacroQuestion, err error) {
+func (*SeedRiskTrackingMacro) GetNextQuestion(*types.Model) (nextQuestion MacroQuestion, err error) {
return NoMoreQuestions(), nil
}
@@ -35,11 +35,11 @@ func (*SeedRiskTrackingMacro) GoBack() (message string, validResult bool, err er
return "Cannot go back further", false, nil
}
-func (*SeedRiskTrackingMacro) GetFinalChangeImpact(_ *input.Model, _ *types.ParsedModel) (changes []string, message string, validResult bool, err error) {
+func (*SeedRiskTrackingMacro) GetFinalChangeImpact(_ *input.Model, _ *types.Model) (changes []string, message string, validResult bool, err error) {
return []string{"seed the model file with with initial risk tracking entries for all untracked risks"}, "Changeset valid", true, err
}
-func (*SeedRiskTrackingMacro) Execute(modelInput *input.Model, parsedModel *types.ParsedModel) (message string, validResult bool, err error) {
+func (*SeedRiskTrackingMacro) Execute(modelInput *input.Model, parsedModel *types.Model) (message string, validResult bool, err error) {
syntheticRiskIDsToCreateTrackingFor := make([]string, 0)
for id, risk := range parsedModel.GeneratedRisksBySyntheticId {
if !risk.IsRiskTracked(parsedModel) {
diff --git a/pkg/macros/seed-tags-macro.go b/pkg/macros/seed-tags-macro.go
index ab953284..172b403d 100644
--- a/pkg/macros/seed-tags-macro.go
+++ b/pkg/macros/seed-tags-macro.go
@@ -24,7 +24,7 @@ func (*SeedTagsMacro) GetMacroDetails() MacroDetails {
}
}
-func (*SeedTagsMacro) GetNextQuestion(parsedModel *types.ParsedModel) (nextQuestion MacroQuestion, err error) {
+func (*SeedTagsMacro) GetNextQuestion(parsedModel *types.Model) (nextQuestion MacroQuestion, err error) {
return NoMoreQuestions(), nil
}
@@ -36,11 +36,11 @@ func (*SeedTagsMacro) GoBack() (message string, validResult bool, err error) {
return "Cannot go back further", false, nil
}
-func (*SeedTagsMacro) GetFinalChangeImpact(_ *input.Model, _ *types.ParsedModel) (changes []string, message string, validResult bool, err error) {
+func (*SeedTagsMacro) GetFinalChangeImpact(_ *input.Model, _ *types.Model) (changes []string, message string, validResult bool, err error) {
return []string{"seed the model file with supported tags from all risk rules"}, "Changeset valid", true, err
}
-func (*SeedTagsMacro) Execute(modelInput *input.Model, parsedModel *types.ParsedModel) (message string, validResult bool, err error) {
+func (*SeedTagsMacro) Execute(modelInput *input.Model, parsedModel *types.Model) (message string, validResult bool, err error) {
modelInput.TagsAvailable = parsedModel.TagsAvailable
for tag := range parsedModel.AllSupportedTags {
modelInput.TagsAvailable = append(modelInput.TagsAvailable, tag)
diff --git a/pkg/model/custom-risk.go b/pkg/model/custom-risk-category.go
similarity index 53%
rename from pkg/model/custom-risk.go
rename to pkg/model/custom-risk-category.go
index 613273d5..8dba3c54 100644
--- a/pkg/model/custom-risk.go
+++ b/pkg/model/custom-risk-category.go
@@ -2,71 +2,71 @@ package model
import (
"fmt"
+ "github.com/threagile/threagile/pkg/security/risks"
"log"
"strings"
"github.com/threagile/threagile/pkg/security/types"
)
-type CustomRisk struct {
- ID string `json:"id,omitempty" yaml:"id,omitempty"`
- RiskCategory types.RiskCategory `json:"risk_category" yaml:"risk_category,omitempty"`
- Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"`
- runner *runner
+type CustomRiskCategory struct {
+ types.RiskCategory `json:"risk_category" yaml:"risk_category,omitempty"`
+
+ Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"`
+ runner *runner
}
-func (what *CustomRisk) Init(id string, category types.RiskCategory, tags []string) *CustomRisk {
- *what = CustomRisk{
- ID: id,
- RiskCategory: category,
+func (what *CustomRiskCategory) Init(category *types.RiskCategory, tags []string) *CustomRiskCategory {
+ *what = CustomRiskCategory{
+ RiskCategory: *category,
Tags: tags,
}
return what
}
-func (what *CustomRisk) Category() types.RiskCategory {
- return what.RiskCategory
+func (what *CustomRiskCategory) Category() *types.RiskCategory {
+ return &what.RiskCategory
}
-func (what *CustomRisk) SupportedTags() []string {
+func (what *CustomRiskCategory) SupportedTags() []string {
return what.Tags
}
-func (what *CustomRisk) GenerateRisks(parsedModel *types.ParsedModel) []types.Risk {
+func (what *CustomRiskCategory) GenerateRisks(parsedModel *types.Model) []*types.Risk {
if what.runner == nil {
return nil
}
- risks := make([]types.Risk, 0)
- runError := what.runner.Run(parsedModel, &risks, "-generate-risks")
+ generatedRisks := make([]*types.Risk, 0)
+ runError := what.runner.Run(parsedModel, &generatedRisks, "-generate-risks")
if runError != nil {
log.Fatalf("Failed to generate risks for custom risk rule %q: %v\n", what.runner.Filename, runError)
}
- return risks
+ return generatedRisks
}
-func LoadCustomRiskRules(pluginFiles []string, reporter types.ProgressReporter) map[string]*CustomRisk {
+func LoadCustomRiskRules(pluginFiles []string, reporter types.ProgressReporter) risks.RiskRules {
customRiskRuleList := make([]string, 0)
- customRiskRules := make(map[string]*CustomRisk)
+ customRiskRules := make(risks.RiskRules)
if len(pluginFiles) > 0 {
reporter.Info("Loading custom risk rules:", strings.Join(pluginFiles, ", "))
for _, pluginFile := range pluginFiles {
if len(pluginFile) > 0 {
- runner, loadError := new(runner).Load(pluginFile)
+ newRunner, loadError := new(runner).Load(pluginFile)
if loadError != nil {
reporter.Error(fmt.Sprintf("WARNING: Custom risk rule %q not loaded: %v\n", pluginFile, loadError))
}
- risk := new(CustomRisk)
- runError := runner.Run(nil, &risk, "-get-info")
+ risk := new(CustomRiskCategory)
+ runError := newRunner.Run(nil, &risk, "-get-info")
if runError != nil {
reporter.Error(fmt.Sprintf("WARNING: Failed to get info for custom risk rule %q: %v\n", pluginFile, runError))
}
- risk.runner = runner
+ risk.runner = newRunner
customRiskRules[risk.ID] = risk
customRiskRuleList = append(customRiskRuleList, risk.ID)
reporter.Info("Custom risk rule loaded:", risk.ID)
diff --git a/pkg/model/parse.go b/pkg/model/parse.go
index c7f4aaae..ef360da8 100644
--- a/pkg/model/parse.go
+++ b/pkg/model/parse.go
@@ -12,7 +12,7 @@ import (
"time"
)
-func ParseModel(config *common.Config, modelInput *input.Model, builtinRiskRules map[string]risks.RiskRule, customRiskRules map[string]*CustomRisk) (*types.ParsedModel, error) {
+func ParseModel(config *common.Config, modelInput *input.Model, builtinRiskRules risks.RiskRules, customRiskRules risks.RiskRules) (*types.Model, error) {
technologies := make(types.TechnologyMap)
technologiesLoadError := technologies.LoadWithConfig(config, "technologies.yaml")
if technologiesLoadError != nil {
@@ -35,7 +35,7 @@ func ParseModel(config *common.Config, modelInput *input.Model, builtinRiskRules
}
}
- parsedModel := types.ParsedModel{
+ parsedModel := types.Model{
ThreagileVersion: modelInput.ThreagileVersion,
Title: modelInput.Title,
Author: modelInput.Author,
@@ -63,8 +63,8 @@ func ParseModel(config *common.Config, modelInput *input.Model, builtinRiskRules
parsedModel.AllSupportedTags = make(map[string]bool)
parsedModel.IncomingTechnicalCommunicationLinksMappedByTargetId = make(map[string][]*types.CommunicationLink)
parsedModel.DirectContainingTrustBoundaryMappedByTechnicalAssetId = make(map[string]*types.TrustBoundary)
- parsedModel.GeneratedRisksByCategory = make(map[string][]types.Risk)
- parsedModel.GeneratedRisksBySyntheticId = make(map[string]types.Risk)
+ parsedModel.GeneratedRisksByCategory = make(map[string][]*types.Risk)
+ parsedModel.GeneratedRisksBySyntheticId = make(map[string]*types.Risk)
if parsedModel.DiagramTweakNodesep == 0 {
parsedModel.DiagramTweakNodesep = 2
@@ -471,7 +471,7 @@ func ParseModel(config *common.Config, modelInput *input.Model, builtinRiskRules
parsedModel.TrustBoundaries[id] = trustBoundary
for _, technicalAsset := range trustBoundary.TechnicalAssetsInside {
parsedModel.DirectContainingTrustBoundaryMappedByTechnicalAssetId[technicalAsset] = trustBoundary
- //fmt.Println("Asset "+technicalAsset+" is directly in trust boundary "+trustBoundary.Id)
+ //fmt.Println("Asset "+technicalAsset+" is directly in trust boundary "+trustBoundary.ID)
}
}
err = parsedModel.CheckNestedTrustBoundariesExisting()
@@ -518,62 +518,62 @@ func ParseModel(config *common.Config, modelInput *input.Model, builtinRiskRules
parsedModel.SharedRuntimes[id] = sharedRuntime
}
- parsedModel.BuiltInRiskCategories = make(map[string]types.RiskCategory)
for _, rule := range builtinRiskRules {
- category := rule.Category()
- parsedModel.BuiltInRiskCategories[category.Id] = category
+ parsedModel.BuiltInRiskCategories = append(parsedModel.BuiltInRiskCategories, rule.Category())
}
- parsedModel.IndividualRiskCategories = make(map[string]types.RiskCategory)
for _, rule := range customRiskRules {
- parsedModel.IndividualRiskCategories[rule.RiskCategory.Id] = rule.RiskCategory
+ parsedModel.CustomRiskCategories = append(parsedModel.CustomRiskCategories, rule.Category())
}
// Individual Risk Categories (just used as regular risk categories) ===============================================================================
- // parsedModel.IndividualRiskCategories = make(map[string]types.RiskCategory)
- for title, individualCategory := range modelInput.IndividualRiskCategories {
- id := fmt.Sprintf("%v", individualCategory.ID)
-
- function, err := types.ParseRiskFunction(individualCategory.Function)
+ for _, customRiskCategoryCategory := range modelInput.CustomRiskCategories {
+ function, err := types.ParseRiskFunction(customRiskCategoryCategory.Function)
if err != nil {
- return nil, fmt.Errorf("unknown 'function' value of individual risk category %q: %v", title, individualCategory.Function)
+ return nil, fmt.Errorf("unknown 'function' value of individual risk category %q: %v", customRiskCategoryCategory.Title, customRiskCategoryCategory.Function)
}
- stride, err := types.ParseSTRIDE(individualCategory.STRIDE)
+
+ stride, err := types.ParseSTRIDE(customRiskCategoryCategory.STRIDE)
if err != nil {
- return nil, fmt.Errorf("unknown 'stride' value of individual risk category %q: %v", title, individualCategory.STRIDE)
- }
-
- cat := types.RiskCategory{
- Id: id,
- Title: title,
- Description: withDefault(fmt.Sprintf("%v", individualCategory.Description), title),
- Impact: fmt.Sprintf("%v", individualCategory.Impact),
- ASVS: fmt.Sprintf("%v", individualCategory.ASVS),
- CheatSheet: fmt.Sprintf("%v", individualCategory.CheatSheet),
- Action: fmt.Sprintf("%v", individualCategory.Action),
- Mitigation: fmt.Sprintf("%v", individualCategory.Mitigation),
- Check: fmt.Sprintf("%v", individualCategory.Check),
- DetectionLogic: fmt.Sprintf("%v", individualCategory.DetectionLogic),
- RiskAssessment: fmt.Sprintf("%v", individualCategory.RiskAssessment),
- FalsePositives: fmt.Sprintf("%v", individualCategory.FalsePositives),
+ return nil, fmt.Errorf("unknown 'stride' value of individual risk category %q: %v", customRiskCategoryCategory.Title, customRiskCategoryCategory.STRIDE)
+ }
+
+ cat := &types.RiskCategory{
+ ID: customRiskCategoryCategory.ID,
+ Title: customRiskCategoryCategory.Title,
+ Description: customRiskCategoryCategory.Description,
+ Impact: customRiskCategoryCategory.Impact,
+ ASVS: customRiskCategoryCategory.ASVS,
+ CheatSheet: customRiskCategoryCategory.CheatSheet,
+ Action: customRiskCategoryCategory.Action,
+ Mitigation: customRiskCategoryCategory.Mitigation,
+ Check: customRiskCategoryCategory.Check,
+ DetectionLogic: customRiskCategoryCategory.DetectionLogic,
+ RiskAssessment: customRiskCategoryCategory.RiskAssessment,
+ FalsePositives: customRiskCategoryCategory.FalsePositives,
Function: function,
STRIDE: stride,
- ModelFailurePossibleReason: individualCategory.ModelFailurePossibleReason,
- CWE: individualCategory.CWE,
+ ModelFailurePossibleReason: customRiskCategoryCategory.ModelFailurePossibleReason,
+ CWE: customRiskCategoryCategory.CWE,
}
- err = checkIdSyntax(id)
+
+ if cat.Description == "" {
+ cat.Description = customRiskCategoryCategory.Title
+ }
+
+ err = checkIdSyntax(customRiskCategoryCategory.ID)
if err != nil {
return nil, err
}
- if _, exists := parsedModel.IndividualRiskCategories[id]; exists {
- return nil, fmt.Errorf("duplicate id used: %v", id)
+
+ if !parsedModel.CustomRiskCategories.Add(cat) {
+ return nil, fmt.Errorf("duplicate id used: %v", customRiskCategoryCategory.ID)
}
- parsedModel.IndividualRiskCategories[id] = cat
// NOW THE INDIVIDUAL RISK INSTANCES:
//individualRiskInstances := make([]model.Risk, 0)
- if individualCategory.RisksIdentified != nil { // TODO: also add syntax checks of input YAML when linked asset is not found or when synthetic-id is already used...
- for title, individualRiskInstance := range individualCategory.RisksIdentified {
+ if customRiskCategoryCategory.RisksIdentified != nil { // TODO: also add syntax checks of input YAML when linked asset is not found or when synthetic-id is already used...
+ for title, individualRiskInstance := range customRiskCategoryCategory.RisksIdentified {
var mostRelevantDataAssetId, mostRelevantTechnicalAssetId, mostRelevantCommunicationLinkId, mostRelevantTrustBoundaryId, mostRelevantSharedRuntimeId string
var dataBreachProbability types.DataBreachProbability
var dataBreachTechnicalAssetIDs []string
@@ -647,10 +647,10 @@ func ParseModel(config *common.Config, modelInput *input.Model, builtinRiskRules
}
}
- parsedModel.GeneratedRisksByCategory[cat.Id] = append(parsedModel.GeneratedRisksByCategory[cat.Id], types.Risk{
- SyntheticId: createSyntheticId(cat.Id, mostRelevantDataAssetId, mostRelevantTechnicalAssetId, mostRelevantCommunicationLinkId, mostRelevantTrustBoundaryId, mostRelevantSharedRuntimeId),
- Title: fmt.Sprintf("%v", title),
- CategoryId: cat.Id,
+ parsedModel.GeneratedRisksByCategory[cat.ID] = append(parsedModel.GeneratedRisksByCategory[cat.ID], &types.Risk{
+ SyntheticId: createSyntheticId(cat.ID, mostRelevantDataAssetId, mostRelevantTechnicalAssetId, mostRelevantCommunicationLinkId, mostRelevantTrustBoundaryId, mostRelevantSharedRuntimeId),
+ Title: title,
+ CategoryId: cat.ID,
Severity: severity,
ExploitationLikelihood: exploitationLikelihood,
ExploitationImpact: exploitationImpact,
@@ -667,7 +667,7 @@ func ParseModel(config *common.Config, modelInput *input.Model, builtinRiskRules
}
// Risk Tracking ===============================================================================
- parsedModel.RiskTracking = make(map[string]types.RiskTracking)
+ parsedModel.RiskTracking = make(map[string]*types.RiskTracking)
for syntheticRiskId, riskTracking := range modelInput.RiskTracking {
justification := fmt.Sprintf("%v", riskTracking.Justification)
checkedBy := fmt.Sprintf("%v", riskTracking.CheckedBy)
@@ -686,7 +686,7 @@ func ParseModel(config *common.Config, modelInput *input.Model, builtinRiskRules
return nil, fmt.Errorf("unknown 'status' value of risk tracking %q: %v", syntheticRiskId, riskTracking.Status)
}
- tracking := types.RiskTracking{
+ tracking := &types.RiskTracking{
SyntheticRiskId: strings.TrimSpace(syntheticRiskId),
Justification: justification,
CheckedBy: checkedBy,
diff --git a/pkg/model/parse_test.go b/pkg/model/parse_test.go
index 1cd3c92b..7353a844 100644
--- a/pkg/model/parse_test.go
+++ b/pkg/model/parse_test.go
@@ -17,7 +17,7 @@ import (
)
func TestDefaultInputNotFail(t *testing.T) {
- parsedModel, err := ParseModel(&common.Config{}, createInputModel(make(map[string]input.TechnicalAsset), make(map[string]input.DataAsset)), make(map[string]risks.RiskRule), make(map[string]*CustomRisk))
+ parsedModel, err := ParseModel(&common.Config{}, createInputModel(make(map[string]input.TechnicalAsset), make(map[string]input.DataAsset)), make(risks.RiskRules), make(risks.RiskRules))
assert.NoError(t, err)
assert.NotNil(t, parsedModel)
@@ -27,7 +27,7 @@ func TestInferConfidentiality_NotSet_NoOthers_ExpectTODO(t *testing.T) {
ta := make(map[string]input.TechnicalAsset)
da := make(map[string]input.DataAsset)
- _, err := ParseModel(&common.Config{}, createInputModel(ta, da), make(map[string]risks.RiskRule), make(map[string]*CustomRisk))
+ _, err := ParseModel(&common.Config{}, createInputModel(ta, da), make(risks.RiskRules), make(risks.RiskRules))
// TODO: rename test and check if everyone agree that by default it should be public if there are no other assets
assert.NoError(t, err)
@@ -58,7 +58,7 @@ func TestInferConfidentiality_ExpectHighestConfidentiality(t *testing.T) {
taWithPublicConfidentialityDataAsset.DataAssetsProcessed = append(taWithPublicConfidentialityDataAsset.DataAssetsProcessed, daPublicConfidentiality.ID)
ta[taWithPublicConfidentialityDataAsset.ID] = taWithPublicConfidentialityDataAsset
- parsedModel, err := ParseModel(&common.Config{}, createInputModel(ta, da), make(map[string]risks.RiskRule), make(map[string]*CustomRisk))
+ parsedModel, err := ParseModel(&common.Config{}, createInputModel(ta, da), make(risks.RiskRules), make(risks.RiskRules))
assert.NoError(t, err)
assert.Equal(t, types.Confidential, parsedModel.TechnicalAssets[taWithConfidentialConfidentialityDataAsset.ID].Confidentiality)
@@ -70,7 +70,7 @@ func TestInferIntegrity_NotSet_NoOthers_ExpectTODO(t *testing.T) {
ta := make(map[string]input.TechnicalAsset)
da := make(map[string]input.DataAsset)
- _, err := ParseModel(&common.Config{}, createInputModel(ta, da), make(map[string]risks.RiskRule), make(map[string]*CustomRisk))
+ _, err := ParseModel(&common.Config{}, createInputModel(ta, da), make(risks.RiskRules), make(risks.RiskRules))
// TODO: rename test and check if everyone agree that by default it should be public if there are no other assets
assert.NoError(t, err)
@@ -101,7 +101,7 @@ func TestInferIntegrity_ExpectHighestIntegrity(t *testing.T) {
taWithArchiveIntegrityDataAsset.DataAssetsProcessed = append(taWithArchiveIntegrityDataAsset.DataAssetsProcessed, daArchiveIntegrity.ID)
ta[taWithArchiveIntegrityDataAsset.ID] = taWithArchiveIntegrityDataAsset
- parsedModel, err := ParseModel(&common.Config{}, createInputModel(ta, da), make(map[string]risks.RiskRule), make(map[string]*CustomRisk))
+ parsedModel, err := ParseModel(&common.Config{}, createInputModel(ta, da), make(risks.RiskRules), make(risks.RiskRules))
assert.NoError(t, err)
assert.Equal(t, types.Critical, parsedModel.TechnicalAssets[taWithCriticalIntegrityDataAsset.ID].Integrity)
@@ -113,7 +113,7 @@ func TestInferAvailability_NotSet_NoOthers_ExpectTODO(t *testing.T) {
ta := make(map[string]input.TechnicalAsset)
da := make(map[string]input.DataAsset)
- _, err := ParseModel(&common.Config{}, createInputModel(ta, da), make(map[string]risks.RiskRule), make(map[string]*CustomRisk))
+ _, err := ParseModel(&common.Config{}, createInputModel(ta, da), make(risks.RiskRules), make(risks.RiskRules))
assert.NoError(t, err)
}
@@ -143,7 +143,7 @@ func TestInferAvailability_ExpectHighestAvailability(t *testing.T) {
taWithArchiveAvailabilityDataAsset.DataAssetsProcessed = append(taWithArchiveAvailabilityDataAsset.DataAssetsProcessed, daArchiveAvailability.ID)
ta[taWithArchiveAvailabilityDataAsset.ID] = taWithArchiveAvailabilityDataAsset
- parsedModel, err := ParseModel(&common.Config{}, createInputModel(ta, da), make(map[string]risks.RiskRule), make(map[string]*CustomRisk))
+ parsedModel, err := ParseModel(&common.Config{}, createInputModel(ta, da), make(risks.RiskRules), make(risks.RiskRules))
assert.NoError(t, err)
assert.Equal(t, types.Critical, parsedModel.TechnicalAssets[taWithCriticalAvailabilityDataAsset.ID].Availability)
diff --git a/pkg/model/read.go b/pkg/model/read.go
index de2e7178..f6a90cbf 100644
--- a/pkg/model/read.go
+++ b/pkg/model/read.go
@@ -13,10 +13,10 @@ import (
type ReadResult struct {
ModelInput *input.Model
- ParsedModel *types.ParsedModel
+ ParsedModel *types.Model
IntroTextRAA string
- BuiltinRiskRules map[string]risks.RiskRule
- CustomRiskRules map[string]*CustomRisk
+ BuiltinRiskRules risks.RiskRules
+ CustomRiskRules risks.RiskRules
}
func (what ReadResult) ExplainRisk(cfg *common.Config, risk string, reporter common.DefaultProgressReporter) error {
@@ -29,10 +29,7 @@ func ReadAndAnalyzeModel(config *common.Config, progressReporter types.ProgressR
progressReporter.Infof("Writing into output directory: %v", config.OutputFolder)
progressReporter.Infof("Parsing model: %v", config.InputFile)
- builtinRiskRules := make(map[string]risks.RiskRule)
- for _, rule := range risks.GetBuiltInRiskRules() {
- builtinRiskRules[rule.Category().Id] = rule
- }
+ builtinRiskRules := risks.GetBuiltInRiskRules()
customRiskRules := LoadCustomRiskRules(config.RiskRulesPlugins, progressReporter)
modelInput := new(input.Model).Defaults()
@@ -48,7 +45,7 @@ func ReadAndAnalyzeModel(config *common.Config, progressReporter types.ProgressR
introTextRAA := applyRAA(parsedModel, config.PluginFolder, config.RAAPlugin, progressReporter)
- applyRiskGeneration(parsedModel, customRiskRules, builtinRiskRules, config.SkipRiskRules, progressReporter)
+ applyRiskGeneration(parsedModel, builtinRiskRules.Merge(customRiskRules), config.SkipRiskRules, progressReporter)
err := parsedModel.ApplyWildcardRiskTrackingEvaluation(config.IgnoreOrphanedRiskTracking, progressReporter)
if err != nil {
return nil, fmt.Errorf("unable to apply wildcard risk tracking evaluation: %v", err)
@@ -76,59 +73,30 @@ func ReadAndAnalyzeModel(config *common.Config, progressReporter types.ProgressR
}, nil
}
-func applyRisk(parsedModel *types.ParsedModel, rule risks.RiskRule, skippedRules *map[string]bool) {
- id := rule.Category().Id
- _, ok := (*skippedRules)[id]
-
- if ok {
- fmt.Printf("Skipping risk rule %q\n", rule.Category().Id)
- delete(*skippedRules, rule.Category().Id)
- } else {
- parsedModel.AddToListOfSupportedTags(rule.SupportedTags())
- generatedRisks := rule.GenerateRisks(parsedModel)
- if generatedRisks != nil {
- if len(generatedRisks) > 0 {
- parsedModel.GeneratedRisksByCategory[rule.Category().Id] = generatedRisks
- }
- } else {
- fmt.Printf("Failed to generate risks for %q\n", id)
- }
- }
-}
-
-// TODO: refactor skipRiskRules to be a string array instead of a comma-separated string
-func applyRiskGeneration(parsedModel *types.ParsedModel, customRiskRules map[string]*CustomRisk,
- builtinRiskRules map[string]risks.RiskRule,
- skipRiskRules string,
+func applyRiskGeneration(parsedModel *types.Model, rules risks.RiskRules,
+ skipRiskRules []string,
progressReporter types.ProgressReporter) {
progressReporter.Info("Applying risk generation")
skippedRules := make(map[string]bool)
if len(skipRiskRules) > 0 {
- for _, id := range strings.Split(skipRiskRules, ",") {
+ for _, id := range skipRiskRules {
skippedRules[id] = true
}
}
- for _, rule := range builtinRiskRules {
- applyRisk(parsedModel, rule, &skippedRules)
- }
-
- // NOW THE CUSTOM RISK RULES (if any)
- for id, customRule := range customRiskRules {
+ for id, rule := range rules {
_, ok := skippedRules[id]
if ok {
- progressReporter.Infof("Skipping custom risk rule: %v", id)
+ progressReporter.Infof("Skipping risk rule: %v", id)
delete(skippedRules, id)
- } else {
- progressReporter.Infof("Executing custom risk rule: %v", id)
- parsedModel.AddToListOfSupportedTags(customRule.SupportedTags())
- customRisks := customRule.GenerateRisks(parsedModel)
- if len(customRisks) > 0 {
- parsedModel.GeneratedRisksByCategory[customRule.RiskCategory.Id] = customRisks
- }
-
- progressReporter.Infof("Added custom risks: %v", len(customRisks))
+ continue
+ }
+
+ parsedModel.AddToListOfSupportedTags(rule.SupportedTags())
+ newRisks := rule.GenerateRisks(parsedModel)
+ if len(newRisks) > 0 {
+ parsedModel.GeneratedRisksByCategory[id] = newRisks
}
}
@@ -151,7 +119,7 @@ func applyRiskGeneration(parsedModel *types.ParsedModel, customRiskRules map[str
}
}
-func applyRAA(parsedModel *types.ParsedModel, binFolder, raaPlugin string, progressReporter types.ProgressReporter) string {
+func applyRAA(parsedModel *types.Model, binFolder, raaPlugin string, progressReporter types.ProgressReporter) string {
progressReporter.Infof("Applying RAA calculation: %v", raaPlugin)
runner, loadError := new(runner).Load(filepath.Join(binFolder, raaPlugin))
diff --git a/pkg/report/excel.go b/pkg/report/excel.go
index 10e2ee14..eb34e086 100644
--- a/pkg/report/excel.go
+++ b/pkg/report/excel.go
@@ -12,7 +12,7 @@ import (
"unicode/utf8"
)
-func WriteRisksExcelToFile(parsedModel *types.ParsedModel, filename string, config *common.Config) error {
+func WriteRisksExcelToFile(parsedModel *types.Model, filename string, config *common.Config) error {
columns := new(ExcelColumns).GetColumns()
excel := excelize.NewFile()
sheetName := parsedModel.Title
@@ -98,13 +98,11 @@ func WriteRisksExcelToFile(parsedModel *types.ParsedModel, filename string, conf
}
date := ""
- riskTracking := risk.GetRiskTracking(parsedModel)
+ riskTracking := risk.GetRiskTrackingWithDefault(parsedModel)
if !riskTracking.Date.IsZero() {
date = riskTracking.Date.Format("2006-01-02")
}
- riskTrackingStatus := risk.GetRiskTrackingStatusDefaultingUnchecked(parsedModel)
-
riskItems = append(riskItems, RiskItem{
Columns: []string{
risk.Severity.Title(),
@@ -122,13 +120,13 @@ func WriteRisksExcelToFile(parsedModel *types.ParsedModel, filename string, conf
category.Mitigation,
category.Check,
risk.SyntheticId,
- riskTrackingStatus.Title(),
+ riskTracking.Status.Title(),
riskTracking.Justification,
date,
riskTracking.CheckedBy,
riskTracking.Ticket,
},
- Status: riskTrackingStatus,
+ Status: riskTracking.Status,
Severity: risk.Severity,
})
}
@@ -246,7 +244,7 @@ func WriteRisksExcelToFile(parsedModel *types.ParsedModel, filename string, conf
return nil
}
-func WriteTagsExcelToFile(parsedModel *types.ParsedModel, filename string) error { // TODO: eventually when len(sortedTagsAvailable) == 0 is: write a hint in the Excel that no tags are used
+func WriteTagsExcelToFile(parsedModel *types.Model, filename string) error { // TODO: eventually when len(sortedTagsAvailable) == 0 is: write a hint in the Excel that no tags are used
excelRow := 0
excel := excelize.NewFile()
sheetName := parsedModel.Title
@@ -377,7 +375,7 @@ func WriteTagsExcelToFile(parsedModel *types.ParsedModel, filename string) error
return nil
}
-func sortedTrustBoundariesByTitle(parsedModel *types.ParsedModel) []*types.TrustBoundary {
+func sortedTrustBoundariesByTitle(parsedModel *types.Model) []*types.TrustBoundary {
boundaries := make([]*types.TrustBoundary, 0)
for _, boundary := range parsedModel.TrustBoundaries {
boundaries = append(boundaries, boundary)
@@ -386,7 +384,7 @@ func sortedTrustBoundariesByTitle(parsedModel *types.ParsedModel) []*types.Trust
return boundaries
}
-func sortedDataAssetsByTitle(parsedModel *types.ParsedModel) []*types.DataAsset {
+func sortedDataAssetsByTitle(parsedModel *types.Model) []*types.DataAsset {
assets := make([]*types.DataAsset, 0)
for _, asset := range parsedModel.DataAssets {
assets = append(assets, asset)
@@ -395,7 +393,7 @@ func sortedDataAssetsByTitle(parsedModel *types.ParsedModel) []*types.DataAsset
return assets
}
-func writeRow(excel *excelize.File, excelRow *int, sheetName string, axis string, styleBlackLeftBold int, styleBlackCenter int,
+func writeRow(excel *excelize.File, excelRow *int, sheetName string, _ string, styleBlackLeftBold int, styleBlackCenter int,
sortedTags []string, assetTitle string, tagsUsed []string) error {
*excelRow++
diff --git a/pkg/report/graphviz.go b/pkg/report/graphviz.go
index 07644026..8180734e 100644
--- a/pkg/report/graphviz.go
+++ b/pkg/report/graphviz.go
@@ -14,7 +14,7 @@ import (
"github.com/threagile/threagile/pkg/security/types"
)
-func WriteDataFlowDiagramGraphvizDOT(parsedModel *types.ParsedModel,
+func WriteDataFlowDiagramGraphvizDOT(parsedModel *types.Model,
diagramFilenameDOT string, dpi int, addModelTitle bool,
progressReporter progressReporter) (*os.File, error) {
progressReporter.Info("Writing data flow diagram input")
@@ -209,7 +209,7 @@ func WriteDataFlowDiagramGraphvizDOT(parsedModel *types.ParsedModel,
for _, dataFlow := range technicalAsset.CommunicationLinks {
sourceId := technicalAsset.Id
targetId := dataFlow.TargetId
- //log.Println("About to add link from", sourceId, "to", targetId, "with id", dataFlow.Id)
+ //log.Println("About to add link from", sourceId, "to", targetId, "with id", dataFlow.ID)
var arrowStyle, arrowColor, readOrWriteHead, readOrWriteTail string
if dataFlow.Readonly {
readOrWriteHead = "empty"
@@ -272,7 +272,7 @@ func WriteDataFlowDiagramGraphvizDOT(parsedModel *types.ParsedModel,
// Pen Widths:
-func determineArrowPenWidth(cl *types.CommunicationLink, parsedModel *types.ParsedModel) string {
+func determineArrowPenWidth(cl *types.CommunicationLink, parsedModel *types.Model) string {
if determineArrowColor(cl, parsedModel) == Pink {
return fmt.Sprintf("%f", 3.0)
}
@@ -282,7 +282,7 @@ func determineArrowPenWidth(cl *types.CommunicationLink, parsedModel *types.Pars
return fmt.Sprintf("%f", 1.5)
}
-func determineLabelColor(cl *types.CommunicationLink, parsedModel *types.ParsedModel) string {
+func determineLabelColor(cl *types.CommunicationLink, parsedModel *types.Model) string {
// TODO: Just move into main.go and let the generated risk determine the color, don't duplicate the logic here
/*
if dataFlow.Protocol.IsEncrypted() {
@@ -325,7 +325,7 @@ func determineArrowLineStyle(cl *types.CommunicationLink) string {
}
// pink when model forgery attempt (i.e. nothing being sent and received)
-func determineArrowColor(cl *types.CommunicationLink, parsedModel *types.ParsedModel) string {
+func determineArrowColor(cl *types.CommunicationLink, parsedModel *types.Model) string {
// TODO: Just move into main.go and let the generated risk determine the color, don't duplicate the logic here
if len(cl.DataAssetsSent) == 0 && len(cl.DataAssetsReceived) == 0 ||
cl.Protocol == types.UnknownProtocol {
@@ -444,7 +444,7 @@ func GenerateDataFlowDiagramGraphvizImage(dotFile *os.File, targetDir string,
return nil
}
-func makeDiagramSameRankNodeTweaks(parsedModel *types.ParsedModel) (string, error) {
+func makeDiagramSameRankNodeTweaks(parsedModel *types.Model) (string, error) {
// see https://stackoverflow.com/questions/25734244/how-do-i-place-nodes-on-the-same-level-in-dot
tweak := ""
if len(parsedModel.DiagramTweakSameRankAssets) > 0 {
@@ -470,7 +470,7 @@ func makeDiagramSameRankNodeTweaks(parsedModel *types.ParsedModel) (string, erro
return tweak, nil
}
-func makeDiagramInvisibleConnectionsTweaks(parsedModel *types.ParsedModel) (string, error) {
+func makeDiagramInvisibleConnectionsTweaks(parsedModel *types.Model) (string, error) {
// see https://stackoverflow.com/questions/2476575/how-to-control-node-placement-in-graphviz-i-e-avoid-edge-crossings
tweak := ""
if len(parsedModel.DiagramTweakInvisibleConnectionsBetweenAssets) > 0 {
@@ -493,7 +493,7 @@ func makeDiagramInvisibleConnectionsTweaks(parsedModel *types.ParsedModel) (stri
return tweak, nil
}
-func WriteDataAssetDiagramGraphvizDOT(parsedModel *types.ParsedModel, diagramFilenameDOT string, dpi int,
+func WriteDataAssetDiagramGraphvizDOT(parsedModel *types.Model, diagramFilenameDOT string, dpi int,
progressReporter progressReporter) (*os.File, error) {
progressReporter.Info("Writing data asset diagram input")
@@ -584,7 +584,7 @@ func WriteDataAssetDiagramGraphvizDOT(parsedModel *types.ParsedModel, diagramFil
return file, nil
}
-func makeDataAssetNode(parsedModel *types.ParsedModel, dataAsset *types.DataAsset) string {
+func makeDataAssetNode(parsedModel *types.Model, dataAsset *types.DataAsset) string {
var color string
switch dataAsset.IdentifiedDataBreachProbabilityStillAtRisk(parsedModel) {
case types.Probable:
@@ -602,7 +602,7 @@ func makeDataAssetNode(parsedModel *types.ParsedModel, dataAsset *types.DataAsse
return " " + hash(dataAsset.Id) + ` [ label=<` + encode(dataAsset.Title) + `> penwidth="3.0" style="filled" fillcolor="` + color + `" color="` + color + "\"\n ]; "
}
-func makeTechAssetNode(parsedModel *types.ParsedModel, technicalAsset *types.TechnicalAsset, simplified bool) string {
+func makeTechAssetNode(parsedModel *types.Model, technicalAsset *types.TechnicalAsset, simplified bool) string {
if simplified {
color := rgbHexColorOutOfScope()
if !technicalAsset.OutOfScope {
@@ -676,7 +676,7 @@ func determineShapeStyle(ta *types.TechnicalAsset) string {
return "filled"
}
-func determineShapeFillColor(ta *types.TechnicalAsset, parsedModel *types.ParsedModel) string {
+func determineShapeFillColor(ta *types.TechnicalAsset, parsedModel *types.Model) string {
fillColor := VeryLightGray
if (len(ta.DataAssetsProcessed) == 0 && len(ta.DataAssetsStored) == 0) || ta.Technologies.IsUnknown() {
fillColor = LightPink // lightPink, because it's strange when too many technical assets process no data... some ok, but many in a diagram ist a sign of model forgery...
@@ -701,7 +701,7 @@ func determineShapeFillColor(ta *types.TechnicalAsset, parsedModel *types.Parsed
return fillColor
}
-func determineShapeBorderPenWidth(ta *types.TechnicalAsset, parsedModel *types.ParsedModel) string {
+func determineShapeBorderPenWidth(ta *types.TechnicalAsset, parsedModel *types.Model) string {
if determineShapeBorderColor(ta, parsedModel) == Pink {
return fmt.Sprintf("%f", 3.5)
}
@@ -714,7 +714,7 @@ func determineShapeBorderPenWidth(ta *types.TechnicalAsset, parsedModel *types.P
// red when mission-critical integrity, but still unauthenticated (non-readonly) channels access it
// amber when critical integrity, but still unauthenticated (non-readonly) channels access it
// pink when model forgery attempt (i.e. nothing being processed)
-func determineShapeBorderColor(ta *types.TechnicalAsset, parsedModel *types.ParsedModel) string {
+func determineShapeBorderColor(ta *types.TechnicalAsset, parsedModel *types.Model) string {
// Check for red
if ta.Confidentiality == types.StrictlyConfidential {
return Red
@@ -736,7 +736,7 @@ func determineShapeBorderColor(ta *types.TechnicalAsset, parsedModel *types.Pars
return Black
/*
if what.Integrity == MissionCritical {
- for _, dataFlow := range IncomingTechnicalCommunicationLinksMappedByTargetId[what.Id] {
+ for _, dataFlow := range IncomingTechnicalCommunicationLinksMappedByTargetId[what.ID] {
if !dataFlow.Readonly && dataFlow.Authentication == NoneAuthentication {
return Red
}
@@ -744,7 +744,7 @@ func determineShapeBorderColor(ta *types.TechnicalAsset, parsedModel *types.Pars
}
if what.Integrity == Critical {
- for _, dataFlow := range IncomingTechnicalCommunicationLinksMappedByTargetId[what.Id] {
+ for _, dataFlow := range IncomingTechnicalCommunicationLinksMappedByTargetId[what.ID] {
if !dataFlow.Readonly && dataFlow.Authentication == NoneAuthentication {
return Amber
}
@@ -775,7 +775,7 @@ func determineShapeBorderLineStyle(ta *types.TechnicalAsset) string {
}
// red when >= confidential data stored in unencrypted technical asset
-func determineTechnicalAssetLabelColor(ta *types.TechnicalAsset, model *types.ParsedModel) string {
+func determineTechnicalAssetLabelColor(ta *types.TechnicalAsset, model *types.Model) string {
// TODO: Just move into main.go and let the generated risk determine the color, don't duplicate the logic here
// Check for red
if ta.Integrity == types.MissionCritical {
diff --git a/pkg/report/json.go b/pkg/report/json.go
index 8a27d655..1bd73ce4 100644
--- a/pkg/report/json.go
+++ b/pkg/report/json.go
@@ -8,7 +8,7 @@ import (
"github.com/threagile/threagile/pkg/security/types"
)
-func WriteRisksJSON(parsedModel *types.ParsedModel, filename string) error {
+func WriteRisksJSON(parsedModel *types.Model, filename string) error {
/*
remainingRisks := make([]model.Risk, 0)
for _, category := range model.SortedRiskCategories() {
@@ -31,7 +31,7 @@ func WriteRisksJSON(parsedModel *types.ParsedModel, filename string) error {
// TODO: also a "data assets" json?
-func WriteTechnicalAssetsJSON(parsedModel *types.ParsedModel, filename string) error {
+func WriteTechnicalAssetsJSON(parsedModel *types.Model, filename string) error {
jsonBytes, err := json.Marshal(parsedModel.TechnicalAssets)
if err != nil {
return fmt.Errorf("failed to marshal technical assets to JSON: %w", err)
@@ -43,7 +43,7 @@ func WriteTechnicalAssetsJSON(parsedModel *types.ParsedModel, filename string) e
return nil
}
-func WriteStatsJSON(parsedModel *types.ParsedModel, filename string) error {
+func WriteStatsJSON(parsedModel *types.Model, filename string) error {
jsonBytes, err := json.Marshal(types.OverallRiskStatistics(parsedModel))
if err != nil {
return fmt.Errorf("failed to marshal stats to JSON: %w", err)
diff --git a/pkg/report/report.go b/pkg/report/report.go
index 25c699da..017311c7 100644
--- a/pkg/report/report.go
+++ b/pkg/report/report.go
@@ -16,7 +16,6 @@ import (
"github.com/jung-kurt/gofpdf"
"github.com/jung-kurt/gofpdf/contrib/gofpdi"
"github.com/threagile/threagile/pkg/docs"
- "github.com/threagile/threagile/pkg/model"
"github.com/threagile/threagile/pkg/security/risks"
"github.com/threagile/threagile/pkg/security/types"
"github.com/wcharczuk/go-chart"
@@ -54,13 +53,13 @@ func (r *pdfReporter) WriteReportPDF(reportFilename string,
dataFlowDiagramFilenamePNG string,
dataAssetDiagramFilenamePNG string,
modelFilename string,
- skipRiskRules string,
+ skipRiskRules []string,
buildTimestamp string,
modelHash string,
introTextRAA string,
- customRiskRules map[string]*model.CustomRisk,
+ customRiskRules risks.RiskRules,
tempFolder string,
- model *types.ParsedModel) error {
+ model *types.Model) error {
defer func() {
value := recover()
if value != nil {
@@ -113,7 +112,7 @@ func (r *pdfReporter) WriteReportPDF(reportFilename string,
return nil
}
-func (r *pdfReporter) createPdfAndInitMetadata(model *types.ParsedModel) {
+func (r *pdfReporter) createPdfAndInitMetadata(model *types.Model) {
r.pdf = gofpdf.New("P", "mm", "A4", "")
r.pdf.SetCreator(model.Author.Homepage, true)
r.pdf.SetAuthor(model.Author.Name, true)
@@ -148,7 +147,7 @@ func (r *pdfReporter) createPdfAndInitMetadata(model *types.ParsedModel) {
r.linkCounter = 1 // link counting starts at 1 via r.pdf.AddLink
}
-func (r *pdfReporter) addBreadcrumb(parsedModel *types.ParsedModel) {
+func (r *pdfReporter) addBreadcrumb(parsedModel *types.Model) {
if len(r.currentChapterTitleBreadcrumb) > 0 {
uni := r.pdf.UnicodeTranslatorFromDescriptor("")
r.pdf.SetFont("Helvetica", "", 10)
@@ -173,7 +172,7 @@ func (r *pdfReporter) parseBackgroundTemplate(templateFilename string) {
r.diagramLegendTemplateId = gofpdi.ImportPage(r.pdf, templateFilename, 3, "/MediaBox")
}
-func (r *pdfReporter) createCover(parsedModel *types.ParsedModel) {
+func (r *pdfReporter) createCover(parsedModel *types.Model) {
uni := r.pdf.UnicodeTranslatorFromDescriptor("")
r.pdf.AddPage()
gofpdi.UseImportedTemplate(r.pdf, r.coverTemplateId, 0, 0, 0, 300)
@@ -195,7 +194,7 @@ func (r *pdfReporter) createCover(parsedModel *types.ParsedModel) {
r.pdf.SetTextColor(0, 0, 0)
}
-func (r *pdfReporter) createTableOfContents(parsedModel *types.ParsedModel) {
+func (r *pdfReporter) createTableOfContents(parsedModel *types.Model) {
uni := r.pdf.UnicodeTranslatorFromDescriptor("")
r.pdf.AddPage()
r.currentChapterTitleBreadcrumb = "Table of Contents"
@@ -418,10 +417,10 @@ func (r *pdfReporter) createTableOfContents(parsedModel *types.ParsedModel) {
suffix += "s"
}
r.pdf.Text(11, y, " "+uni(category.Title)+": "+suffix)
- r.pdf.Text(175, y, "{"+category.Id+"}")
+ r.pdf.Text(175, y, "{"+category.ID+"}")
r.pdf.Line(15.6, y+1.3, 11+171.5, y+1.3)
- r.tocLinkIdByAssetId[category.Id] = r.pdf.AddLink()
- r.pdf.Link(10, y-5, 172.5, 6.5, r.tocLinkIdByAssetId[category.Id])
+ r.tocLinkIdByAssetId[category.ID] = r.pdf.AddLink()
+ r.pdf.Link(10, y-5, 172.5, 6.5, r.tocLinkIdByAssetId[category.ID])
}
}
@@ -638,7 +637,7 @@ func (r *pdfReporter) createTableOfContents(parsedModel *types.ParsedModel) {
// by the current page number. --> See the "r.pdf.RegisterAlias()" calls during the PDF creation in this file
}
-func sortedTechnicalAssetsByRiskSeverityAndTitle(parsedModel *types.ParsedModel) []*types.TechnicalAsset {
+func sortedTechnicalAssetsByRiskSeverityAndTitle(parsedModel *types.Model) []*types.TechnicalAsset {
assets := make([]*types.TechnicalAsset, 0)
for _, asset := range parsedModel.TechnicalAssets {
assets = append(assets, asset)
@@ -647,7 +646,7 @@ func sortedTechnicalAssetsByRiskSeverityAndTitle(parsedModel *types.ParsedModel)
return assets
}
-func sortedDataAssetsByDataBreachProbabilityAndTitle(parsedModel *types.ParsedModel) []*types.DataAsset {
+func sortedDataAssetsByDataBreachProbabilityAndTitle(parsedModel *types.Model) []*types.DataAsset {
assets := make([]*types.DataAsset, 0)
for _, asset := range parsedModel.DataAssets {
assets = append(assets, asset)
@@ -669,7 +668,7 @@ func (r *pdfReporter) defineLinkTarget(alias string) {
r.linkCounter++
}
-func (r *pdfReporter) createDisclaimer(parsedModel *types.ParsedModel) {
+func (r *pdfReporter) createDisclaimer(parsedModel *types.Model) {
r.pdf.AddPage()
r.currentChapterTitleBreadcrumb = "Disclaimer"
r.defineLinkTarget("{disclaimer}")
@@ -722,7 +721,7 @@ func (r *pdfReporter) createDisclaimer(parsedModel *types.ParsedModel) {
r.pdfColorBlack()
}
-func (r *pdfReporter) createManagementSummary(parsedModel *types.ParsedModel, tempFolder string) error {
+func (r *pdfReporter) createManagementSummary(parsedModel *types.Model, tempFolder string) error {
uni := r.pdf.UnicodeTranslatorFromDescriptor("")
r.pdf.SetTextColor(0, 0, 0)
title := "Management Summary"
@@ -917,7 +916,7 @@ func (r *pdfReporter) createManagementSummary(parsedModel *types.ParsedModel, te
return nil
}
-func (r *pdfReporter) createRiskMitigationStatus(parsedModel *types.ParsedModel, tempFolder string) error {
+func (r *pdfReporter) createRiskMitigationStatus(parsedModel *types.Model, tempFolder string) error {
r.pdf.SetTextColor(0, 0, 0)
stillAtRisk := types.FilteredByStillAtRisk(parsedModel)
count := len(stillAtRisk)
@@ -1274,15 +1273,15 @@ func makeColor(hexColor string) drawing.Color {
return drawing.ColorFromHex(hexColor[i:]) // = remove first char, which is # in rgb hex here
}
-func (r *pdfReporter) createImpactInitialRisks(parsedModel *types.ParsedModel) {
+func (r *pdfReporter) createImpactInitialRisks(parsedModel *types.Model) {
r.renderImpactAnalysis(parsedModel, true)
}
-func (r *pdfReporter) createImpactRemainingRisks(parsedModel *types.ParsedModel) {
+func (r *pdfReporter) createImpactRemainingRisks(parsedModel *types.Model) {
r.renderImpactAnalysis(parsedModel, false)
}
-func (r *pdfReporter) renderImpactAnalysis(parsedModel *types.ParsedModel, initialRisks bool) {
+func (r *pdfReporter) renderImpactAnalysis(parsedModel *types.Model, initialRisks bool) {
r.pdf.SetTextColor(0, 0, 0)
count, catCount := types.TotalRiskCount(parsedModel), len(parsedModel.GeneratedRisksByCategory)
if !initialRisks {
@@ -1342,7 +1341,7 @@ func (r *pdfReporter) renderImpactAnalysis(parsedModel *types.ParsedModel, initi
r.pdf.SetDashPattern([]float64{}, 0)
}
-func (r *pdfReporter) createOutOfScopeAssets(parsedModel *types.ParsedModel) {
+func (r *pdfReporter) createOutOfScopeAssets(parsedModel *types.Model) {
uni := r.pdf.UnicodeTranslatorFromDescriptor("")
r.pdf.SetTextColor(0, 0, 0)
assets := "Assets"
@@ -1405,7 +1404,7 @@ func (r *pdfReporter) createOutOfScopeAssets(parsedModel *types.ParsedModel) {
r.pdf.SetDashPattern([]float64{}, 0)
}
-func sortedTechnicalAssetsByRAAAndTitle(parsedModel *types.ParsedModel) []*types.TechnicalAsset {
+func sortedTechnicalAssetsByRAAAndTitle(parsedModel *types.Model) []*types.TechnicalAsset {
assets := make([]*types.TechnicalAsset, 0)
for _, asset := range parsedModel.TechnicalAssets {
assets = append(assets, asset)
@@ -1414,7 +1413,7 @@ func sortedTechnicalAssetsByRAAAndTitle(parsedModel *types.ParsedModel) []*types
return assets
}
-func (r *pdfReporter) createModelFailures(parsedModel *types.ParsedModel) {
+func (r *pdfReporter) createModelFailures(parsedModel *types.Model) {
r.pdf.SetTextColor(0, 0, 0)
modelFailures := types.FlattenRiskSlice(types.FilterByModelFailures(parsedModel, parsedModel.GeneratedRisksByCategory))
risksStr := "Risks"
@@ -1465,7 +1464,7 @@ func (r *pdfReporter) createModelFailures(parsedModel *types.ParsedModel) {
r.pdf.SetDashPattern([]float64{}, 0)
}
-func (r *pdfReporter) createRAA(parsedModel *types.ParsedModel, introTextRAA string) {
+func (r *pdfReporter) createRAA(parsedModel *types.Model, introTextRAA string) {
uni := r.pdf.UnicodeTranslatorFromDescriptor("")
r.pdf.SetTextColor(0, 0, 0)
chapTitle := "RAA Analysis"
@@ -1603,7 +1602,7 @@ func createDataRiskQuickWins() {
strBuilder.WriteString(uni(technicalAsset.Description))
html.Write(5, strBuilder.String())
strBuilder.Reset()
- r.pdf.Link(9, posY, 190, r.pdf.GetY()-posY+4, tocLinkIdByAssetId[technicalAsset.Id])
+ r.pdf.Link(9, posY, 190, r.pdf.GetY()-posY+4, tocLinkIdByAssetId[technicalAsset.ID])
}
r.pdf.SetDrawColor(0, 0, 0)
@@ -1611,12 +1610,12 @@ func createDataRiskQuickWins() {
}
*/
-func (r *pdfReporter) addCategories(parsedModel *types.ParsedModel, riskCategories []types.RiskCategory, severity types.RiskSeverity, bothInitialAndRemainingRisks bool, initialRisks bool, describeImpact bool, describeDescription bool) {
+func (r *pdfReporter) addCategories(parsedModel *types.Model, riskCategories []*types.RiskCategory, severity types.RiskSeverity, bothInitialAndRemainingRisks bool, initialRisks bool, describeImpact bool, describeDescription bool) {
html := r.pdf.HTMLBasicNew()
var strBuilder strings.Builder
sort.Sort(types.ByRiskCategoryTitleSort(riskCategories))
for _, riskCategory := range riskCategories {
- risksStr := parsedModel.GeneratedRisksByCategory[riskCategory.Id]
+ risksStr := parsedModel.GeneratedRisksByCategory[riskCategory.ID]
if !initialRisks {
risksStr = types.ReduceToOnlyStillAtRisk(parsedModel, risksStr)
}
@@ -1704,7 +1703,7 @@ func (r *pdfReporter) addCategories(parsedModel *types.ParsedModel, riskCategori
}
html.Write(5, strBuilder.String())
strBuilder.Reset()
- r.pdf.Link(9, posY, 190, r.pdf.GetY()-posY+4, r.tocLinkIdByAssetId[riskCategory.Id])
+ r.pdf.Link(9, posY, 190, r.pdf.GetY()-posY+4, r.tocLinkIdByAssetId[riskCategory.ID])
}
}
@@ -1717,7 +1716,7 @@ func firstParagraph(text string) string {
return match[1]
}
-func (r *pdfReporter) createAssignmentByFunction(parsedModel *types.ParsedModel) {
+func (r *pdfReporter) createAssignmentByFunction(parsedModel *types.Model) {
r.pdf.SetTextColor(0, 0, 0)
title := "Assignment by Function"
r.addHeadline(title, false)
@@ -1863,7 +1862,7 @@ func (r *pdfReporter) createAssignmentByFunction(parsedModel *types.ParsedModel)
r.pdf.SetDashPattern([]float64{}, 0)
}
-func (r *pdfReporter) createSTRIDE(parsedModel *types.ParsedModel) {
+func (r *pdfReporter) createSTRIDE(parsedModel *types.Model) {
r.pdf.SetTextColor(0, 0, 0)
title := "STRIDE Classification of Identified Risks"
r.addHeadline(title, false)
@@ -2068,7 +2067,7 @@ func (r *pdfReporter) createSTRIDE(parsedModel *types.ParsedModel) {
r.pdf.SetDashPattern([]float64{}, 0)
}
-func (r *pdfReporter) createSecurityRequirements(parsedModel *types.ParsedModel) {
+func (r *pdfReporter) createSecurityRequirements(parsedModel *types.Model) {
uni := r.pdf.UnicodeTranslatorFromDescriptor("")
r.pdf.SetTextColor(0, 0, 0)
chapTitle := "Security Requirements"
@@ -2100,7 +2099,7 @@ func (r *pdfReporter) createSecurityRequirements(parsedModel *types.ParsedModel)
"taken into account as well. Also custom individual security requirements might exist for the project.")
}
-func sortedKeysOfSecurityRequirements(parsedModel *types.ParsedModel) []string {
+func sortedKeysOfSecurityRequirements(parsedModel *types.Model) []string {
keys := make([]string, 0)
for k := range parsedModel.SecurityRequirements {
keys = append(keys, k)
@@ -2109,7 +2108,7 @@ func sortedKeysOfSecurityRequirements(parsedModel *types.ParsedModel) []string {
return keys
}
-func (r *pdfReporter) createAbuseCases(parsedModel *types.ParsedModel) {
+func (r *pdfReporter) createAbuseCases(parsedModel *types.Model) {
r.pdf.SetTextColor(0, 0, 0)
chapTitle := "Abuse Cases"
r.addHeadline(chapTitle, false)
@@ -2140,7 +2139,7 @@ func (r *pdfReporter) createAbuseCases(parsedModel *types.ParsedModel) {
"taken into account as well. Also custom individual abuse cases might exist for the project.")
}
-func sortedKeysOfAbuseCases(parsedModel *types.ParsedModel) []string {
+func sortedKeysOfAbuseCases(parsedModel *types.Model) []string {
keys := make([]string, 0)
for k := range parsedModel.AbuseCases {
keys = append(keys, k)
@@ -2149,7 +2148,7 @@ func sortedKeysOfAbuseCases(parsedModel *types.ParsedModel) []string {
return keys
}
-func (r *pdfReporter) createQuestions(parsedModel *types.ParsedModel) {
+func (r *pdfReporter) createQuestions(parsedModel *types.Model) {
uni := r.pdf.UnicodeTranslatorFromDescriptor("")
r.pdf.SetTextColor(0, 0, 0)
questions := "Questions"
@@ -2197,7 +2196,7 @@ func (r *pdfReporter) createQuestions(parsedModel *types.ParsedModel) {
}
}
-func sortedKeysOfQuestions(parsedModel *types.ParsedModel) []string {
+func sortedKeysOfQuestions(parsedModel *types.Model) []string {
keys := make([]string, 0)
for k := range parsedModel.Questions {
keys = append(keys, k)
@@ -2206,7 +2205,7 @@ func sortedKeysOfQuestions(parsedModel *types.ParsedModel) []string {
return keys
}
-func (r *pdfReporter) createTagListing(parsedModel *types.ParsedModel) {
+func (r *pdfReporter) createTagListing(parsedModel *types.Model) {
r.pdf.SetTextColor(0, 0, 0)
chapTitle := "Tag Listing"
r.addHeadline(chapTitle, false)
@@ -2274,7 +2273,7 @@ func (r *pdfReporter) createTagListing(parsedModel *types.ParsedModel) {
}
}
-func sortedSharedRuntimesByTitle(parsedModel *types.ParsedModel) []*types.SharedRuntime {
+func sortedSharedRuntimesByTitle(parsedModel *types.Model) []*types.SharedRuntime {
result := make([]*types.SharedRuntime, 0)
for _, runtime := range parsedModel.SharedRuntimes {
result = append(result, runtime)
@@ -2283,7 +2282,7 @@ func sortedSharedRuntimesByTitle(parsedModel *types.ParsedModel) []*types.Shared
return result
}
-func sortedTechnicalAssetsByTitle(parsedModel *types.ParsedModel) []*types.TechnicalAsset {
+func sortedTechnicalAssetsByTitle(parsedModel *types.Model) []*types.TechnicalAsset {
assets := make([]*types.TechnicalAsset, 0)
for _, asset := range parsedModel.TechnicalAssets {
assets = append(assets, asset)
@@ -2292,7 +2291,7 @@ func sortedTechnicalAssetsByTitle(parsedModel *types.ParsedModel) []*types.Techn
return assets
}
-func (r *pdfReporter) createRiskCategories(parsedModel *types.ParsedModel) {
+func (r *pdfReporter) createRiskCategories(parsedModel *types.Model) {
uni := r.pdf.UnicodeTranslatorFromDescriptor("")
// category title
title := "Identified Risks by Vulnerability category"
@@ -2344,7 +2343,7 @@ func (r *pdfReporter) createRiskCategories(parsedModel *types.ParsedModel) {
title := category.Title + ": " + suffix
r.addHeadline(uni(title), true)
r.pdfColorBlack()
- r.defineLinkTarget("{" + category.Id + "}")
+ r.defineLinkTarget("{" + category.ID + "}")
r.currentChapterTitleBreadcrumb = title
// category details
@@ -2482,7 +2481,7 @@ func (r *pdfReporter) createRiskCategories(parsedModel *types.ParsedModel) {
default:
r.pdfColorBlack()
}
- if !risk.GetRiskTrackingStatusDefaultingUnchecked(parsedModel).IsStillAtRisk() {
+ if !risk.RiskStatus.IsStillAtRisk() {
r.pdfColorBlack()
}
posY := r.pdf.GetY()
@@ -2512,9 +2511,9 @@ func (r *pdfReporter) createRiskCategories(parsedModel *types.ParsedModel) {
}
}
-func (r *pdfReporter) writeRiskTrackingStatus(parsedModel *types.ParsedModel, risk types.Risk) {
+func (r *pdfReporter) writeRiskTrackingStatus(parsedModel *types.Model, risk *types.Risk) {
uni := r.pdf.UnicodeTranslatorFromDescriptor("")
- tracking := risk.GetRiskTracking(parsedModel)
+ tracking := risk.GetRiskTrackingWithDefault(parsedModel)
r.pdfColorBlack()
r.pdf.CellFormat(10, 6, "", "0", 0, "", false, 0, "")
switch tracking.Status {
@@ -2559,7 +2558,7 @@ func (r *pdfReporter) writeRiskTrackingStatus(parsedModel *types.ParsedModel, ri
r.pdfColorBlack()
}
-func (r *pdfReporter) createTechnicalAssets(parsedModel *types.ParsedModel) {
+func (r *pdfReporter) createTechnicalAssets(parsedModel *types.Model) {
uni := r.pdf.UnicodeTranslatorFromDescriptor("")
// category title
title := "Identified Risks by Technical Asset"
@@ -2706,7 +2705,7 @@ func (r *pdfReporter) createTechnicalAssets(parsedModel *types.ParsedModel) {
default:
r.pdfColorBlack()
}
- if !risk.GetRiskTrackingStatusDefaultingUnchecked(parsedModel).IsStillAtRisk() {
+ if !risk.RiskStatus.IsStillAtRisk() {
r.pdfColorBlack()
}
posY := r.pdf.GetY()
@@ -3371,7 +3370,7 @@ func (r *pdfReporter) createTechnicalAssets(parsedModel *types.ParsedModel) {
}
}
-func (r *pdfReporter) createDataAssets(parsedModel *types.ParsedModel) {
+func (r *pdfReporter) createDataAssets(parsedModel *types.Model) {
uni := r.pdf.UnicodeTranslatorFromDescriptor("")
title := "Identified Data Breach Probabilities by Data Asset"
r.pdfColorBlack()
@@ -3707,7 +3706,7 @@ func (r *pdfReporter) createDataAssets(parsedModel *types.ParsedModel) {
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.pdf.Link(20, posY, 180, r.pdf.GetY()-posY, tocLinkIdByAssetId[techAssetResponsible.ID])
}
r.pdfColorBlack()
}
@@ -3777,7 +3776,7 @@ func (r *pdfReporter) createDataAssets(parsedModel *types.ParsedModel) {
default:
r.pdfColorBlack()
}
- if !dataBreachRisk.GetRiskTrackingStatusDefaultingUnchecked(parsedModel).IsStillAtRisk() {
+ if !dataBreachRisk.RiskStatus.IsStillAtRisk() {
r.pdfColorBlack()
}
r.pdf.CellFormat(10, 6, "", "0", 0, "", false, 0, "")
@@ -3792,7 +3791,7 @@ func (r *pdfReporter) createDataAssets(parsedModel *types.ParsedModel) {
}
}
-func (r *pdfReporter) createTrustBoundaries(parsedModel *types.ParsedModel) {
+func (r *pdfReporter) createTrustBoundaries(parsedModel *types.Model) {
uni := r.pdf.UnicodeTranslatorFromDescriptor("")
title := "Trust Boundaries"
r.pdfColorBlack()
@@ -3911,7 +3910,7 @@ func (r *pdfReporter) createTrustBoundaries(parsedModel *types.ParsedModel) {
}
}
-func questionsUnanswered(parsedModel *types.ParsedModel) int {
+func questionsUnanswered(parsedModel *types.Model) int {
result := 0
for _, answer := range parsedModel.Questions {
if len(strings.TrimSpace(answer)) == 0 {
@@ -3921,7 +3920,7 @@ func questionsUnanswered(parsedModel *types.ParsedModel) int {
return result
}
-func (r *pdfReporter) createSharedRuntimes(parsedModel *types.ParsedModel) {
+func (r *pdfReporter) createSharedRuntimes(parsedModel *types.Model) {
uni := r.pdf.UnicodeTranslatorFromDescriptor("")
title := "Shared Runtimes"
r.pdfColorBlack()
@@ -4002,7 +4001,7 @@ func (r *pdfReporter) createSharedRuntimes(parsedModel *types.ParsedModel) {
}
}
-func (r *pdfReporter) createRiskRulesChecked(parsedModel *types.ParsedModel, modelFilename string, skipRiskRules string, buildTimestamp string, modelHash string, customRiskRules map[string]*model.CustomRisk) {
+func (r *pdfReporter) createRiskRulesChecked(parsedModel *types.Model, modelFilename string, skipRiskRules []string, buildTimestamp string, modelHash string, customRiskRules risks.RiskRules) {
r.pdf.SetTextColor(0, 0, 0)
title := "Risk Rules Checked by Threagile"
r.addHeadline(title, false)
@@ -4031,14 +4030,13 @@ func (r *pdfReporter) createRiskRulesChecked(parsedModel *types.ParsedModel, mod
strBuilder.Reset()
// TODO use the new run system to discover risk rules instead of hard-coding them here:
- skippedRules := strings.Split(skipRiskRules, ",")
skipped := ""
r.pdf.Ln(-1)
for id, customRule := range customRiskRules {
r.pdf.Ln(-1)
r.pdf.SetFont("Helvetica", "B", fontSizeBody)
- if contains(skippedRules, id) {
+ if contains(skipRiskRules, id) {
skipped = "SKIPPED - "
} else {
skipped = ""
@@ -4074,14 +4072,14 @@ func (r *pdfReporter) createRiskRulesChecked(parsedModel *types.ParsedModel, mod
r.pdf.MultiCell(160, 6, customRule.Category().RiskAssessment, "0", "0", false)
}
- for _, key := range sortedKeysOfIndividualRiskCategories(parsedModel) {
- individualRiskCategory := parsedModel.IndividualRiskCategories[key]
+ sort.Sort(types.ByRiskCategoryTitleSort(parsedModel.CustomRiskCategories))
+ for _, individualRiskCategory := range parsedModel.CustomRiskCategories {
r.pdf.Ln(-1)
r.pdf.SetFont("Helvetica", "B", fontSizeBody)
r.pdf.CellFormat(190, 3, individualRiskCategory.Title, "0", 0, "", false, 0, "")
r.pdf.Ln(-1)
r.pdf.SetFont("Helvetica", "", fontSizeSmall)
- r.pdf.CellFormat(190, 6, individualRiskCategory.Id, "0", 0, "", false, 0, "")
+ r.pdf.CellFormat(190, 6, individualRiskCategory.ID, "0", 0, "", false, 0, "")
r.pdf.Ln(-1)
r.pdf.SetFont("Helvetica", "I", fontSizeBody)
r.pdf.CellFormat(190, 6, "Individual Risk category", "0", 0, "", false, 0, "")
@@ -4112,7 +4110,7 @@ func (r *pdfReporter) createRiskRulesChecked(parsedModel *types.ParsedModel, mod
for _, rule := range risks.GetBuiltInRiskRules() {
r.pdf.Ln(-1)
r.pdf.SetFont("Helvetica", "B", fontSizeBody)
- if contains(skippedRules, rule.Category().Id) {
+ if contains(skipRiskRules, rule.Category().ID) {
skipped = "SKIPPED - "
} else {
skipped = ""
@@ -4120,7 +4118,7 @@ func (r *pdfReporter) createRiskRulesChecked(parsedModel *types.ParsedModel, mod
r.pdf.CellFormat(190, 3, skipped+rule.Category().Title, "0", 0, "", false, 0, "")
r.pdf.Ln(-1)
r.pdf.SetFont("Helvetica", "", fontSizeSmall)
- r.pdf.CellFormat(190, 6, rule.Category().Id, "0", 0, "", false, 0, "")
+ r.pdf.CellFormat(190, 6, rule.Category().ID, "0", 0, "", false, 0, "")
r.pdf.Ln(-1)
r.pdf.SetFont("Helvetica", "", fontSizeBody)
r.pdfColorGray()
@@ -4146,7 +4144,7 @@ func (r *pdfReporter) createRiskRulesChecked(parsedModel *types.ParsedModel, mod
}
}
-func (r *pdfReporter) createTargetDescription(parsedModel *types.ParsedModel, baseFolder string) error {
+func (r *pdfReporter) createTargetDescription(parsedModel *types.Model, baseFolder string) error {
uni := r.pdf.UnicodeTranslatorFromDescriptor("")
r.pdf.SetTextColor(0, 0, 0)
title := "Application Overview"
@@ -4398,15 +4396,6 @@ func (r *pdfReporter) embedDataFlowDiagram(diagramFilenamePNG string, tempFolder
}
}
-func sortedKeysOfIndividualRiskCategories(parsedModel *types.ParsedModel) []string {
- keys := make([]string, 0)
- for k := range parsedModel.IndividualRiskCategories {
- keys = append(keys, k)
- }
- sort.Strings(keys)
- return keys
-}
-
func (r *pdfReporter) embedDataRiskMapping(diagramFilenamePNG string, tempFolder string) {
r.pdf.SetTextColor(0, 0, 0)
title := "Data Mapping"
diff --git a/pkg/script/common/scope.go b/pkg/script/common/scope.go
index 4a3d0e2b..0f67905b 100644
--- a/pkg/script/common/scope.go
+++ b/pkg/script/common/scope.go
@@ -16,7 +16,7 @@ type Scope struct {
returnValue Value
}
-func (what *Scope) Init(model *types.ParsedModel, risk *types.RiskCategory, methods map[string]Statement) error {
+func (what *Scope) Init(model *types.Model, risk *types.RiskCategory, methods map[string]Statement) error {
if model != nil {
data, marshalError := json.Marshal(model)
if marshalError != nil {
diff --git a/pkg/script/script.go b/pkg/script/script.go
index 0e619a2e..e8f9359b 100644
--- a/pkg/script/script.go
+++ b/pkg/script/script.go
@@ -2,6 +2,7 @@ package script
import (
"fmt"
+ "github.com/threagile/threagile/pkg/input"
"github.com/threagile/threagile/pkg/script/common"
"github.com/threagile/threagile/pkg/script/expressions"
"github.com/threagile/threagile/pkg/script/statements"
@@ -16,7 +17,7 @@ type Script struct {
utils map[string]*statements.MethodStatement
}
-func (what *Script) Parse(text []byte) (map[string]Script, error) {
+func (what *Script) ParseScripts(text []byte) (map[string]Script, error) {
items := make(map[string]any)
parseError := yaml.Unmarshal(text, &items)
if parseError != nil {
@@ -26,12 +27,27 @@ func (what *Script) Parse(text []byte) (map[string]Script, error) {
for key, value := range items {
switch strings.ToLower(key) {
case "individual_risk_categories":
- risk, ok := value.(map[string]any)
+ riskScripts, ok := value.(map[string]any)
if !ok {
return nil, fmt.Errorf("unexpected format %T in risk definition", value)
}
- return what.parseRiskScripts(risk)
+ scripts := make(map[string]Script)
+ for scriptID, riskScript := range riskScripts {
+ risk, ok := riskScript.(map[string]any)
+ if !ok {
+ return nil, fmt.Errorf("unexpected format %T in risk definition for %q", riskScript, scriptID)
+ }
+
+ script, scriptError := new(Script).ParseScript(risk)
+ if scriptError != nil {
+ return nil, fmt.Errorf("failed to parse script of risk definition for %q: %v", scriptID, scriptError)
+ }
+
+ scripts[scriptID] = *script
+ }
+
+ return scripts, nil
default:
return nil, fmt.Errorf("unexpected key %q in risk definition", key)
@@ -41,6 +57,39 @@ func (what *Script) Parse(text []byte) (map[string]Script, error) {
return nil, fmt.Errorf("no scripts found")
}
+func (what *Script) ParseScript(script map[string]any) (*Script, error) {
+ for key, value := range script {
+ switch strings.ToLower(key) {
+ case common.Risk:
+ switch castValue := value.(type) {
+ case map[string]any:
+ what.risk = castValue
+
+ default:
+ return what, fmt.Errorf("failed to parse %q: unexpected script type %T\nscript:\n%v", key, value, new(input.Strings).AddLineNumbers(value))
+ }
+
+ case common.Match:
+ item, errorScript, itemError := new(statements.MethodStatement).Parse(value)
+ if itemError != nil {
+ return what, fmt.Errorf("failed to parse %q: %v\nscript:\n%v", key, itemError, new(input.Strings).AddLineNumbers(errorScript))
+ }
+
+ what.match = item
+
+ case common.Utils:
+ item, errorScript, itemError := what.parseUtils(value)
+ if itemError != nil {
+ return what, fmt.Errorf("failed to parse %q: %v\nscript:\n%v", key, itemError, new(input.Strings).AddLineNumbers(errorScript))
+ }
+
+ what.utils = item
+ }
+ }
+
+ return what, nil
+}
+
func (what *Script) GenerateRisks(scope *common.Scope) (map[string]*types.Risk, string, error) {
value, valueOk := what.getItem(scope.Model, "technical_assets")
if !valueOk {
@@ -66,8 +115,6 @@ func (what *Script) GenerateRisks(scope *common.Scope) (map[string]*types.Risk,
}
if isMatch {
- fmt.Printf("risk: %v\n", what.getRiskID(matchScope, techAsset))
-
riskScope, riskCloneError := scope.Clone()
if riskCloneError != nil {
return nil, "", fmt.Errorf("failed to clone scope: %v", riskCloneError)
@@ -86,101 +133,6 @@ func (what *Script) GenerateRisks(scope *common.Scope) (map[string]*types.Risk,
return risks, "", nil
}
-func (what *Script) Utils() map[string]common.Statement {
- utils := make(map[string]common.Statement)
- for name, item := range what.utils {
- utils[name] = item
- }
-
- return utils
-}
-
-func (what *Script) AddLineNumbers(script any) string {
- text, isString := script.(string)
- if !isString {
- data, _ := yaml.Marshal(script)
- text = string(data)
- }
-
- lines := strings.Split(text, "\n")
- for n, line := range lines {
- lines[n] = fmt.Sprintf("%3d:\t%v", n+1, line)
- }
-
- return strings.Join(lines, "\n")
-}
-
-func (what *Script) IndentPrintf(level int, script any) string {
- text, isString := script.(string)
- if !isString {
- data, _ := yaml.Marshal(script)
- text = string(data)
- }
-
- lines := strings.Split(text, "\n")
- for n, line := range lines {
- lines[n] = strings.Repeat(" ", level) + line
- }
-
- return strings.Join(lines, "\n")
-}
-
-func (what *Script) IndentLine(level int, format string, params ...any) string {
- return strings.Repeat(" ", level) + fmt.Sprintf(format, params...)
-}
-
-func (what *Script) parseRiskScripts(item map[string]any) (map[string]Script, error) {
- scripts := make(map[string]Script)
- for key, value := range item {
- risk, ok := value.(map[string]any)
- if !ok {
- return nil, fmt.Errorf("unexpected format %T in risk definition for %q", value, key)
- }
-
- script, parseError := new(Script).parseScript(risk)
- if parseError != nil {
- return nil, fmt.Errorf("failed to parse script of risk definition for %q: %v", key, parseError)
- }
-
- scripts[key] = *script
- }
-
- return scripts, nil
-}
-
-func (what *Script) parseScript(script map[string]any) (*Script, error) {
- for key, value := range script {
- switch strings.ToLower(key) {
- case common.Risk:
- switch castValue := value.(type) {
- case map[string]any:
- what.risk = castValue
-
- default:
- return what, fmt.Errorf("failed to parse %q: unexpected script type %T\nscript:\n%v", key, value, what.AddLineNumbers(value))
- }
-
- case common.Match:
- item, errorScript, itemError := new(statements.MethodStatement).Parse(value)
- if itemError != nil {
- return what, fmt.Errorf("failed to parse %q: %v\nscript:\n%v", key, itemError, what.AddLineNumbers(errorScript))
- }
-
- what.match = item
-
- case common.Utils:
- item, errorScript, itemError := what.parseUtils(value)
- if itemError != nil {
- return what, fmt.Errorf("failed to parse %q: %v\nscript:\n%v", key, itemError, what.AddLineNumbers(errorScript))
- }
-
- what.utils = item
- }
- }
-
- return what, nil
-}
-
func (what *Script) matchRisk(scope *common.Scope) (bool, string, error) {
if what.match == nil {
return false, "", nil
@@ -199,30 +151,6 @@ func (what *Script) matchRisk(scope *common.Scope) (bool, string, error) {
return false, "", nil
}
-func (what *Script) getRiskID(scope *common.Scope, techAsset any) string {
- riskIdValue, riskIdValueOk := what.getItem(scope.Risk, "id")
- if !riskIdValueOk {
- return ""
- }
-
- riskId, riskIdOk := riskIdValue.(string)
- if !riskIdOk {
- return ""
- }
-
- assetIdValue, assetIdValueOk := what.getItem(techAsset, "id")
- if !assetIdValueOk {
- return ""
- }
-
- assetId, assetIdOk := assetIdValue.(string)
- if !assetIdOk {
- return ""
- }
-
- return fmt.Sprintf("%v@%v", riskId, assetId)
-}
-
func (what *Script) generateRisk(scope *common.Scope) (*types.Risk, string, error) {
if what.risk == nil {
return nil, "", fmt.Errorf("no risk template")
diff --git a/pkg/security/risks/builtin/accidental-secret-leak-rule.go b/pkg/security/risks/builtin/accidental-secret-leak-rule.go
index cba9aa55..5c257611 100644
--- a/pkg/security/risks/builtin/accidental-secret-leak-rule.go
+++ b/pkg/security/risks/builtin/accidental-secret-leak-rule.go
@@ -12,9 +12,9 @@ func NewAccidentalSecretLeakRule() *AccidentalSecretLeakRule {
return &AccidentalSecretLeakRule{}
}
-func (*AccidentalSecretLeakRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "accidental-secret-leak",
+func (*AccidentalSecretLeakRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "accidental-secret-leak",
Title: "Accidental Secret Leak",
Description: "Sourcecode repositories (including their histories) as well as artifact registries can accidentally contain secrets like " +
"checked-in or packaged-in passwords, API tokens, certificates, crypto keys, etc.",
@@ -43,12 +43,12 @@ func (*AccidentalSecretLeakRule) SupportedTags() []string {
return []string{"git", "nexus"}
}
-func (r *AccidentalSecretLeakRule) GenerateRisks(parsedModel *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *AccidentalSecretLeakRule) GenerateRisks(parsedModel *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, id := range parsedModel.SortedTechnicalAssetIDs() {
techAsset := parsedModel.TechnicalAssets[id]
if !techAsset.OutOfScope && techAsset.Technologies.GetAttribute(types.MayContainSecrets) {
- var risk types.Risk
+ var risk *types.Risk
if techAsset.IsTaggedWithAny("git") {
risk = r.createRisk(parsedModel, techAsset, "Git", "Git Leak Prevention")
} else {
@@ -60,7 +60,7 @@ func (r *AccidentalSecretLeakRule) GenerateRisks(parsedModel *types.ParsedModel)
return risks
}
-func (r *AccidentalSecretLeakRule) createRisk(parsedModel *types.ParsedModel, technicalAsset *types.TechnicalAsset, prefix, details string) types.Risk {
+func (r *AccidentalSecretLeakRule) createRisk(parsedModel *types.Model, technicalAsset *types.TechnicalAsset, prefix, details string) *types.Risk {
if len(prefix) > 0 {
prefix = " (" + prefix + ")"
}
@@ -80,8 +80,8 @@ func (r *AccidentalSecretLeakRule) createRisk(parsedModel *types.ParsedModel, te
impact = types.HighImpact
}
// create risk
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(types.Unlikely, impact),
ExploitationLikelihood: types.Unlikely,
ExploitationImpact: impact,
@@ -94,8 +94,8 @@ func (r *AccidentalSecretLeakRule) createRisk(parsedModel *types.ParsedModel, te
return risk
}
-func (r *AccidentalSecretLeakRule) MatchRisk(parsedModel *types.ParsedModel, risk string) bool {
- categoryId := r.Category().Id
+func (r *AccidentalSecretLeakRule) MatchRisk(parsedModel *types.Model, risk string) bool {
+ categoryId := r.Category().ID
for _, id := range parsedModel.SortedTechnicalAssetIDs() {
techAsset := parsedModel.TechnicalAssets[id]
if strings.EqualFold(risk, categoryId+"@"+techAsset.Id) || strings.EqualFold(risk, categoryId+"@*") {
@@ -106,8 +106,8 @@ func (r *AccidentalSecretLeakRule) MatchRisk(parsedModel *types.ParsedModel, ris
return false
}
-func (r *AccidentalSecretLeakRule) ExplainRisk(parsedModel *types.ParsedModel, risk string) []string {
- categoryId := r.Category().Id
+func (r *AccidentalSecretLeakRule) ExplainRisk(parsedModel *types.Model, risk string) []string {
+ categoryId := r.Category().ID
explanation := make([]string, 0)
for _, id := range parsedModel.SortedTechnicalAssetIDs() {
techAsset := parsedModel.TechnicalAssets[id]
@@ -138,7 +138,7 @@ func (r *AccidentalSecretLeakRule) ExplainRisk(parsedModel *types.ParsedModel, r
return explanation
}
-func (r *AccidentalSecretLeakRule) explainRisk(parsedModel *types.ParsedModel, technicalAsset *types.TechnicalAsset) []string {
+func (r *AccidentalSecretLeakRule) explainRisk(parsedModel *types.Model, technicalAsset *types.TechnicalAsset) []string {
explanation := make([]string, 0)
impact := types.LowImpact
if technicalAsset.HighestProcessedConfidentiality(parsedModel) == types.StrictlyConfidential ||
diff --git a/pkg/security/risks/builtin/code-backdooring-rule.go b/pkg/security/risks/builtin/code-backdooring-rule.go
index 79c664d2..784f5eb0 100644
--- a/pkg/security/risks/builtin/code-backdooring-rule.go
+++ b/pkg/security/risks/builtin/code-backdooring-rule.go
@@ -10,9 +10,9 @@ func NewCodeBackdooringRule() *CodeBackdooringRule {
return &CodeBackdooringRule{}
}
-func (*CodeBackdooringRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "code-backdooring",
+func (*CodeBackdooringRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "code-backdooring",
Title: "Code Backdooring",
Description: "For each build-pipeline component Code Backdooring risks might arise where attackers compromise the build-pipeline " +
"in order to let backdoored artifacts be shipped into production. Aside from direct code backdooring this includes " +
@@ -46,8 +46,8 @@ func (*CodeBackdooringRule) SupportedTags() []string {
return []string{}
}
-func (r *CodeBackdooringRule) GenerateRisks(parsedModel *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *CodeBackdooringRule) GenerateRisks(parsedModel *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, id := range parsedModel.SortedTechnicalAssetIDs() {
technicalAsset := parsedModel.TechnicalAssets[id]
if !technicalAsset.OutOfScope && technicalAsset.Technologies.GetAttribute(types.IsDevelopmentRelevant) {
@@ -58,12 +58,10 @@ func (r *CodeBackdooringRule) GenerateRisks(parsedModel *types.ParsedModel) []ty
// TODO: ensure that even internet or unmanaged clients coming over a reverse-proxy or load-balancer like component are treated as if it was directly accessed/exposed on the internet or towards unmanaged dev clients
- //riskByLinkAdded := false
for _, callerLink := range parsedModel.IncomingTechnicalCommunicationLinksMappedByTargetId[technicalAsset.Id] {
caller := parsedModel.TechnicalAssets[callerLink.SourceId]
if (!callerLink.VPN && caller.Internet) || caller.OutOfScope {
risks = append(risks, r.createRisk(parsedModel, technicalAsset, true))
- //riskByLinkAdded = true
break
}
}
@@ -72,7 +70,7 @@ func (r *CodeBackdooringRule) GenerateRisks(parsedModel *types.ParsedModel) []ty
return risks
}
-func (r *CodeBackdooringRule) createRisk(input *types.ParsedModel, technicalAsset *types.TechnicalAsset, elevatedRisk bool) types.Risk {
+func (r *CodeBackdooringRule) createRisk(input *types.Model, technicalAsset *types.TechnicalAsset, elevatedRisk bool) *types.Risk {
title := "Code Backdooring risk at " + technicalAsset.Title + ""
impact := types.LowImpact
if !technicalAsset.Technologies.GetAttribute(types.CodeInspectionPlatform) {
@@ -106,8 +104,8 @@ func (r *CodeBackdooringRule) createRisk(input *types.ParsedModel, technicalAsse
dataBreachTechnicalAssetIDs = append(dataBreachTechnicalAssetIDs, key)
}
// create risk
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(types.Unlikely, impact),
ExploitationLikelihood: types.Unlikely,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/builtin/container-baseimage-backdooring-rule.go b/pkg/security/risks/builtin/container-baseimage-backdooring-rule.go
index 54caef5b..4fb48df5 100644
--- a/pkg/security/risks/builtin/container-baseimage-backdooring-rule.go
+++ b/pkg/security/risks/builtin/container-baseimage-backdooring-rule.go
@@ -10,9 +10,9 @@ func NewContainerBaseImageBackdooringRule() *ContainerBaseImageBackdooringRule {
return &ContainerBaseImageBackdooringRule{}
}
-func (*ContainerBaseImageBackdooringRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "container-baseimage-backdooring",
+func (*ContainerBaseImageBackdooringRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "container-baseimage-backdooring",
Title: "Container Base Image Backdooring",
Description: "When a technical asset is built using container technologies, Base Image Backdooring risks might arise where " +
"base images and other layers used contain vulnerable components or backdoors." +
@@ -41,8 +41,8 @@ func (*ContainerBaseImageBackdooringRule) SupportedTags() []string {
return []string{}
}
-func (r *ContainerBaseImageBackdooringRule) GenerateRisks(parsedModel *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *ContainerBaseImageBackdooringRule) GenerateRisks(parsedModel *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, id := range parsedModel.SortedTechnicalAssetIDs() {
technicalAsset := parsedModel.TechnicalAssets[id]
if !technicalAsset.OutOfScope && technicalAsset.Machine == types.Container {
@@ -52,7 +52,7 @@ func (r *ContainerBaseImageBackdooringRule) GenerateRisks(parsedModel *types.Par
return risks
}
-func (r *ContainerBaseImageBackdooringRule) createRisk(parsedModel *types.ParsedModel, technicalAsset *types.TechnicalAsset) types.Risk {
+func (r *ContainerBaseImageBackdooringRule) createRisk(parsedModel *types.Model, technicalAsset *types.TechnicalAsset) *types.Risk {
title := "Container Base Image Backdooring risk at " + technicalAsset.Title + ""
impact := types.MediumImpact
if technicalAsset.HighestProcessedConfidentiality(parsedModel) == types.StrictlyConfidential ||
@@ -60,8 +60,8 @@ func (r *ContainerBaseImageBackdooringRule) createRisk(parsedModel *types.Parsed
technicalAsset.HighestProcessedAvailability(parsedModel) == types.MissionCritical {
impact = types.HighImpact
}
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(types.Unlikely, impact),
ExploitationLikelihood: types.Unlikely,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/builtin/container-platform-escape-rule.go b/pkg/security/risks/builtin/container-platform-escape-rule.go
index 95a85d88..9ac30773 100644
--- a/pkg/security/risks/builtin/container-platform-escape-rule.go
+++ b/pkg/security/risks/builtin/container-platform-escape-rule.go
@@ -10,9 +10,9 @@ func NewContainerPlatformEscapeRule() *ContainerPlatformEscapeRule {
return &ContainerPlatformEscapeRule{}
}
-func (*ContainerPlatformEscapeRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "container-platform-escape",
+func (*ContainerPlatformEscapeRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "container-platform-escape",
Title: "Container Platform Escape",
Description: "Container platforms are especially interesting targets for attackers as they host big parts of a containerized runtime infrastructure. " +
"When not configured and operated with security best practices in mind, attackers might exploit a vulnerability inside an container and escape towards " +
@@ -46,8 +46,8 @@ func (*ContainerPlatformEscapeRule) SupportedTags() []string {
return []string{"docker", "kubernetes", "openshift"}
}
-func (r *ContainerPlatformEscapeRule) GenerateRisks(parsedModel *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *ContainerPlatformEscapeRule) GenerateRisks(parsedModel *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, id := range parsedModel.SortedTechnicalAssetIDs() {
technicalAsset := parsedModel.TechnicalAssets[id]
if !technicalAsset.OutOfScope && technicalAsset.Technologies.GetAttribute(types.ContainerPlatform) {
@@ -57,7 +57,7 @@ func (r *ContainerPlatformEscapeRule) GenerateRisks(parsedModel *types.ParsedMod
return risks
}
-func (r *ContainerPlatformEscapeRule) createRisk(parsedModel *types.ParsedModel, technicalAsset *types.TechnicalAsset) types.Risk {
+func (r *ContainerPlatformEscapeRule) createRisk(parsedModel *types.Model, technicalAsset *types.TechnicalAsset) *types.Risk {
title := "Container Platform Escape risk at " + technicalAsset.Title + ""
impact := types.MediumImpact
if technicalAsset.HighestProcessedConfidentiality(parsedModel) == types.StrictlyConfidential ||
@@ -73,8 +73,8 @@ func (r *ContainerPlatformEscapeRule) createRisk(parsedModel *types.ParsedModel,
}
}
// create risk
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(types.Unlikely, impact),
ExploitationLikelihood: types.Unlikely,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/builtin/cross-site-request-forgery-rule.go b/pkg/security/risks/builtin/cross-site-request-forgery-rule.go
index 0112b8e1..4c60b304 100644
--- a/pkg/security/risks/builtin/cross-site-request-forgery-rule.go
+++ b/pkg/security/risks/builtin/cross-site-request-forgery-rule.go
@@ -10,9 +10,9 @@ func NewCrossSiteRequestForgeryRule() *CrossSiteRequestForgeryRule {
return &CrossSiteRequestForgeryRule{}
}
-func (*CrossSiteRequestForgeryRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "cross-site-request-forgery",
+func (*CrossSiteRequestForgeryRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "cross-site-request-forgery",
Title: "Cross-Site Request Forgery (CSRF)",
Description: "When a web application is accessed via web protocols Cross-Site Request Forgery (CSRF) risks might arise.",
Impact: "If this risk remains unmitigated, attackers might be able to trick logged-in victim users into unwanted actions within the web application " +
@@ -42,8 +42,8 @@ func (*CrossSiteRequestForgeryRule) SupportedTags() []string {
return []string{}
}
-func (r *CrossSiteRequestForgeryRule) GenerateRisks(parsedModel *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *CrossSiteRequestForgeryRule) GenerateRisks(parsedModel *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, id := range parsedModel.SortedTechnicalAssetIDs() {
technicalAsset := parsedModel.TechnicalAssets[id]
if technicalAsset.OutOfScope || !technicalAsset.Technologies.GetAttribute(types.WebApplication) {
@@ -63,15 +63,15 @@ func (r *CrossSiteRequestForgeryRule) GenerateRisks(parsedModel *types.ParsedMod
return risks
}
-func (r *CrossSiteRequestForgeryRule) createRisk(parsedModel *types.ParsedModel, technicalAsset *types.TechnicalAsset, incomingFlow *types.CommunicationLink, likelihood types.RiskExploitationLikelihood) types.Risk {
+func (r *CrossSiteRequestForgeryRule) createRisk(parsedModel *types.Model, technicalAsset *types.TechnicalAsset, incomingFlow *types.CommunicationLink, likelihood types.RiskExploitationLikelihood) *types.Risk {
sourceAsset := parsedModel.TechnicalAssets[incomingFlow.SourceId]
title := "Cross-Site Request Forgery (CSRF) risk at " + technicalAsset.Title + " via " + incomingFlow.Title + " from " + sourceAsset.Title + ""
impact := types.LowImpact
if incomingFlow.HighestIntegrity(parsedModel) == types.MissionCritical {
impact = types.MediumImpact
}
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(likelihood, impact),
ExploitationLikelihood: likelihood,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/builtin/cross-site-scripting-rule.go b/pkg/security/risks/builtin/cross-site-scripting-rule.go
index a1b9a4eb..9ce709e4 100644
--- a/pkg/security/risks/builtin/cross-site-scripting-rule.go
+++ b/pkg/security/risks/builtin/cross-site-scripting-rule.go
@@ -10,9 +10,9 @@ func NewCrossSiteScriptingRule() *CrossSiteScriptingRule {
return &CrossSiteScriptingRule{}
}
-func (*CrossSiteScriptingRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "cross-site-scripting",
+func (*CrossSiteScriptingRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "cross-site-scripting",
Title: "Cross-Site Scripting (XSS)",
Description: "For each web application Cross-Site Scripting (XSS) risks might arise. In terms " +
"of the overall risk level take other applications running on the same domain into account as well.",
@@ -40,8 +40,8 @@ func (*CrossSiteScriptingRule) SupportedTags() []string {
return []string{}
}
-func (r *CrossSiteScriptingRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *CrossSiteScriptingRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, id := range input.SortedTechnicalAssetIDs() {
technicalAsset := input.TechnicalAssets[id]
if technicalAsset.OutOfScope || !technicalAsset.Technologies.GetAttribute(types.WebApplication) { // TODO: also mobile clients or rich-clients as long as they use web-view...
@@ -52,14 +52,14 @@ func (r *CrossSiteScriptingRule) GenerateRisks(input *types.ParsedModel) []types
return risks
}
-func (r *CrossSiteScriptingRule) createRisk(parsedModel *types.ParsedModel, technicalAsset *types.TechnicalAsset) types.Risk {
+func (r *CrossSiteScriptingRule) createRisk(parsedModel *types.Model, technicalAsset *types.TechnicalAsset) *types.Risk {
title := "Cross-Site Scripting (XSS) risk at " + technicalAsset.Title + ""
impact := types.MediumImpact
if technicalAsset.HighestProcessedConfidentiality(parsedModel) == types.StrictlyConfidential || technicalAsset.HighestProcessedIntegrity(parsedModel) == types.MissionCritical {
impact = types.HighImpact
}
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(types.Likely, impact),
ExploitationLikelihood: types.Likely,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/builtin/dos-risky-access-across-trust-boundary-rule.go b/pkg/security/risks/builtin/dos-risky-access-across-trust-boundary-rule.go
index 66445b84..100b2eeb 100644
--- a/pkg/security/risks/builtin/dos-risky-access-across-trust-boundary-rule.go
+++ b/pkg/security/risks/builtin/dos-risky-access-across-trust-boundary-rule.go
@@ -10,9 +10,9 @@ func NewDosRiskyAccessAcrossTrustBoundaryRule() *DosRiskyAccessAcrossTrustBounda
return &DosRiskyAccessAcrossTrustBoundaryRule{}
}
-func (*DosRiskyAccessAcrossTrustBoundaryRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "dos-risky-access-across-trust-boundary",
+func (*DosRiskyAccessAcrossTrustBoundaryRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "dos-risky-access-across-trust-boundary",
Title: "DoS-risky Access Across Trust-Boundary",
Description: "Assets accessed across trust boundaries with critical or mission-critical availability rating " +
"are more prone to Denial-of-Service (DoS) risks.",
@@ -44,8 +44,8 @@ func (*DosRiskyAccessAcrossTrustBoundaryRule) SupportedTags() []string {
return []string{}
}
-func (r *DosRiskyAccessAcrossTrustBoundaryRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *DosRiskyAccessAcrossTrustBoundaryRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, id := range input.SortedTechnicalAssetIDs() {
technicalAsset := input.TechnicalAssets[id]
if !technicalAsset.OutOfScope && !technicalAsset.Technologies.GetAttribute(types.LoadBalancer) &&
@@ -67,7 +67,7 @@ func (r *DosRiskyAccessAcrossTrustBoundaryRule) GenerateRisks(input *types.Parse
return risks
}
-func (r *DosRiskyAccessAcrossTrustBoundaryRule) checkRisk(input *types.ParsedModel, technicalAsset *types.TechnicalAsset, incomingAccess *types.CommunicationLink, linkId string, hopBetween string, risks []types.Risk) []types.Risk {
+func (r *DosRiskyAccessAcrossTrustBoundaryRule) checkRisk(input *types.Model, technicalAsset *types.TechnicalAsset, incomingAccess *types.CommunicationLink, linkId string, hopBetween string, risks []*types.Risk) []*types.Risk {
if incomingAccess.IsAcrossTrustBoundaryNetworkOnly(input) &&
!incomingAccess.Protocol.IsProcessLocal() && incomingAccess.Usage != types.DevOps {
highRisk := technicalAsset.Availability == types.MissionCritical &&
@@ -79,7 +79,7 @@ func (r *DosRiskyAccessAcrossTrustBoundaryRule) checkRisk(input *types.ParsedMod
}
func (r *DosRiskyAccessAcrossTrustBoundaryRule) createRisk(techAsset *types.TechnicalAsset, dataFlow *types.CommunicationLink, linkId string, hopBetween string,
- clientOutsideTrustBoundary *types.TechnicalAsset, moreRisky bool) types.Risk {
+ clientOutsideTrustBoundary *types.TechnicalAsset, moreRisky bool) *types.Risk {
impact := types.LowImpact
if moreRisky {
impact = types.MediumImpact
@@ -87,8 +87,8 @@ func (r *DosRiskyAccessAcrossTrustBoundaryRule) createRisk(techAsset *types.Tech
if len(hopBetween) > 0 {
hopBetween = " forwarded via " + hopBetween + ""
}
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(types.Unlikely, impact),
ExploitationLikelihood: types.Unlikely,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/builtin/incomplete-model-rule.go b/pkg/security/risks/builtin/incomplete-model-rule.go
index 1b1642d5..ddcc1b5e 100644
--- a/pkg/security/risks/builtin/incomplete-model-rule.go
+++ b/pkg/security/risks/builtin/incomplete-model-rule.go
@@ -10,9 +10,9 @@ func NewIncompleteModelRule() *IncompleteModelRule {
return &IncompleteModelRule{}
}
-func (*IncompleteModelRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "incomplete-model",
+func (*IncompleteModelRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "incomplete-model",
Title: "Incomplete Model",
Description: "When the threat model contains unknown technologies or transfers data over unknown protocols, this is " +
"an indicator for an incomplete model.",
@@ -36,8 +36,8 @@ func (*IncompleteModelRule) SupportedTags() []string {
return []string{}
}
-func (r *IncompleteModelRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *IncompleteModelRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, id := range input.SortedTechnicalAssetIDs() {
technicalAsset := input.TechnicalAssets[id]
if !technicalAsset.OutOfScope {
@@ -54,10 +54,10 @@ func (r *IncompleteModelRule) GenerateRisks(input *types.ParsedModel) []types.Ri
return risks
}
-func (r *IncompleteModelRule) createRiskTechAsset(technicalAsset *types.TechnicalAsset) types.Risk {
+func (r *IncompleteModelRule) createRiskTechAsset(technicalAsset *types.TechnicalAsset) *types.Risk {
title := "Unknown Technology specified at technical asset " + technicalAsset.Title + ""
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(types.Unlikely, types.LowImpact),
ExploitationLikelihood: types.Unlikely,
ExploitationImpact: types.LowImpact,
@@ -70,10 +70,10 @@ func (r *IncompleteModelRule) createRiskTechAsset(technicalAsset *types.Technica
return risk
}
-func (r *IncompleteModelRule) createRiskCommLink(technicalAsset *types.TechnicalAsset, commLink *types.CommunicationLink) types.Risk {
+func (r *IncompleteModelRule) createRiskCommLink(technicalAsset *types.TechnicalAsset, commLink *types.CommunicationLink) *types.Risk {
title := "Unknown Protocol specified for communication link " + commLink.Title + " at technical asset " + technicalAsset.Title + ""
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(types.Unlikely, types.LowImpact),
ExploitationLikelihood: types.Unlikely,
ExploitationImpact: types.LowImpact,
diff --git a/pkg/security/risks/builtin/ldap-injection-rule.go b/pkg/security/risks/builtin/ldap-injection-rule.go
index 2e68bd54..744e090f 100644
--- a/pkg/security/risks/builtin/ldap-injection-rule.go
+++ b/pkg/security/risks/builtin/ldap-injection-rule.go
@@ -10,9 +10,9 @@ func NewLdapInjectionRule() *LdapInjectionRule {
return &LdapInjectionRule{}
}
-func (*LdapInjectionRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "ldap-injection",
+func (*LdapInjectionRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "ldap-injection",
Title: "LDAP-Injection",
Description: "When an LDAP server is accessed LDAP-Injection risks might arise. " +
"The risk rating depends on the sensitivity of the LDAP server itself and of the data assets processed.",
@@ -39,8 +39,8 @@ func (*LdapInjectionRule) SupportedTags() []string {
return []string{}
}
-func (r *LdapInjectionRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *LdapInjectionRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, technicalAsset := range input.TechnicalAssets {
incomingFlows := input.IncomingTechnicalCommunicationLinksMappedByTargetId[technicalAsset.Id]
for _, incomingFlow := range incomingFlows {
@@ -59,7 +59,7 @@ func (r *LdapInjectionRule) GenerateRisks(input *types.ParsedModel) []types.Risk
return risks
}
-func (r *LdapInjectionRule) createRisk(input *types.ParsedModel, technicalAsset *types.TechnicalAsset, incomingFlow *types.CommunicationLink, likelihood types.RiskExploitationLikelihood) types.Risk {
+func (r *LdapInjectionRule) createRisk(input *types.Model, technicalAsset *types.TechnicalAsset, incomingFlow *types.CommunicationLink, likelihood types.RiskExploitationLikelihood) *types.Risk {
caller := input.TechnicalAssets[incomingFlow.SourceId]
title := "LDAP-Injection risk at " + caller.Title + " against LDAP server " + technicalAsset.Title + "" +
" via " + incomingFlow.Title + ""
@@ -67,8 +67,8 @@ func (r *LdapInjectionRule) createRisk(input *types.ParsedModel, technicalAsset
if technicalAsset.HighestProcessedConfidentiality(input) == types.StrictlyConfidential || technicalAsset.HighestProcessedIntegrity(input) == types.MissionCritical {
impact = types.HighImpact
}
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(likelihood, impact),
ExploitationLikelihood: likelihood,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/builtin/missing-authentication-rule.go b/pkg/security/risks/builtin/missing-authentication-rule.go
index e13ea97e..5d0917ec 100644
--- a/pkg/security/risks/builtin/missing-authentication-rule.go
+++ b/pkg/security/risks/builtin/missing-authentication-rule.go
@@ -10,9 +10,9 @@ func NewMissingAuthenticationRule() *MissingAuthenticationRule {
return &MissingAuthenticationRule{}
}
-func (*MissingAuthenticationRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "missing-authentication",
+func (*MissingAuthenticationRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "missing-authentication",
Title: "Missing Authentication",
Description: "Technical assets (especially multi-tenant systems) should authenticate incoming requests when the asset processes sensitive data. ",
Impact: "If this risk is unmitigated, attackers might be able to access or modify sensitive data in an unauthenticated way.",
@@ -39,8 +39,8 @@ func (*MissingAuthenticationRule) SupportedTags() []string {
return []string{}
}
-func (r *MissingAuthenticationRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *MissingAuthenticationRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, id := range input.SortedTechnicalAssetIDs() {
technicalAsset := input.TechnicalAssets[id]
if technicalAsset.OutOfScope || technicalAsset.Technologies.GetAttribute(types.NoAuthenticationRequired) {
@@ -77,8 +77,8 @@ func (r *MissingAuthenticationRule) GenerateRisks(input *types.ParsedModel) []ty
return risks
}
-func (r *MissingAuthenticationRule) createRisk(input *types.ParsedModel, technicalAsset *types.TechnicalAsset, incomingAccess, incomingAccessOrigin *types.CommunicationLink, hopBetween string,
- impact types.RiskExploitationImpact, likelihood types.RiskExploitationLikelihood, twoFactor bool, category types.RiskCategory) types.Risk {
+func (r *MissingAuthenticationRule) createRisk(input *types.Model, technicalAsset *types.TechnicalAsset, incomingAccess, incomingAccessOrigin *types.CommunicationLink, hopBetween string,
+ impact types.RiskExploitationImpact, likelihood types.RiskExploitationLikelihood, twoFactor bool, category *types.RiskCategory) *types.Risk {
factorString := ""
if twoFactor {
factorString = "Two-Factor "
@@ -86,8 +86,8 @@ func (r *MissingAuthenticationRule) createRisk(input *types.ParsedModel, technic
if len(hopBetween) > 0 {
hopBetween = "forwarded via " + hopBetween + " "
}
- risk := types.Risk{
- CategoryId: category.Id,
+ risk := &types.Risk{
+ CategoryId: category.ID,
Severity: types.CalculateSeverity(likelihood, impact),
ExploitationLikelihood: likelihood,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/builtin/missing-authentication-second-factor-rule.go b/pkg/security/risks/builtin/missing-authentication-second-factor-rule.go
index 5e1a1198..02c9c6f4 100644
--- a/pkg/security/risks/builtin/missing-authentication-second-factor-rule.go
+++ b/pkg/security/risks/builtin/missing-authentication-second-factor-rule.go
@@ -12,9 +12,9 @@ func NewMissingAuthenticationSecondFactorRule(missingAuthenticationRule *Missing
return &MissingAuthenticationSecondFactorRule{missingAuthenticationRule: missingAuthenticationRule}
}
-func (*MissingAuthenticationSecondFactorRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "missing-authentication-second-factor",
+func (*MissingAuthenticationSecondFactorRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "missing-authentication-second-factor",
Title: "Missing Two-Factor Authentication (2FA)",
Description: "Technical assets (especially multi-tenant systems) should authenticate incoming requests with " +
"two-factor (2FA) authentication when the asset processes or stores highly sensitive data (in terms of confidentiality, integrity, and availability) and is accessed by humans.",
@@ -41,8 +41,8 @@ func (*MissingAuthenticationSecondFactorRule) SupportedTags() []string {
return []string{}
}
-func (r *MissingAuthenticationSecondFactorRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *MissingAuthenticationSecondFactorRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, id := range input.SortedTechnicalAssetIDs() {
technicalAsset := input.TechnicalAssets[id]
if technicalAsset.OutOfScope ||
diff --git a/pkg/security/risks/builtin/missing-build-infrastructure-rule.go b/pkg/security/risks/builtin/missing-build-infrastructure-rule.go
index b116fdc4..b2a1230b 100644
--- a/pkg/security/risks/builtin/missing-build-infrastructure-rule.go
+++ b/pkg/security/risks/builtin/missing-build-infrastructure-rule.go
@@ -10,9 +10,9 @@ func NewMissingBuildInfrastructureRule() *MissingBuildInfrastructureRule {
return &MissingBuildInfrastructureRule{}
}
-func (*MissingBuildInfrastructureRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "missing-build-infrastructure",
+func (*MissingBuildInfrastructureRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "missing-build-infrastructure",
Title: "Missing Build Infrastructure",
Description: "The modeled architecture does not contain a build infrastructure (devops-client, sourcecode-repo, build-pipeline, etc.), " +
"which might be the risk of a model missing critical assets (and thus not seeing their risks). " +
@@ -41,8 +41,8 @@ func (*MissingBuildInfrastructureRule) SupportedTags() []string {
return []string{}
}
-func (r *MissingBuildInfrastructureRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *MissingBuildInfrastructureRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
hasCustomDevelopedParts, hasBuildPipeline, hasSourcecodeRepo, hasDevOpsClient := false, false, false, false
impact := types.LowImpact
var mostRelevantAsset *types.TechnicalAsset
@@ -85,10 +85,10 @@ func (r *MissingBuildInfrastructureRule) GenerateRisks(input *types.ParsedModel)
return risks
}
-func (r *MissingBuildInfrastructureRule) createRisk(technicalAsset *types.TechnicalAsset, impact types.RiskExploitationImpact) types.Risk {
+func (r *MissingBuildInfrastructureRule) createRisk(technicalAsset *types.TechnicalAsset, impact types.RiskExploitationImpact) *types.Risk {
title := "Missing Build Infrastructure in the threat model (referencing asset " + technicalAsset.Title + " as an example)"
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(types.Unlikely, impact),
ExploitationLikelihood: types.Unlikely,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/builtin/missing-cloud-hardening-rule.go b/pkg/security/risks/builtin/missing-cloud-hardening-rule.go
index e3e1f323..59a7a38f 100644
--- a/pkg/security/risks/builtin/missing-cloud-hardening-rule.go
+++ b/pkg/security/risks/builtin/missing-cloud-hardening-rule.go
@@ -13,9 +13,9 @@ func NewMissingCloudHardeningRule() *MissingCloudHardeningRule {
return &MissingCloudHardeningRule{}
}
-func (*MissingCloudHardeningRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "missing-cloud-hardening",
+func (*MissingCloudHardeningRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "missing-cloud-hardening",
Title: "Missing Cloud Hardening",
Description: "Cloud components should be hardened according to the cloud vendor best practices. This affects their " +
"configuration, auditing, and further areas.",
@@ -57,8 +57,8 @@ func (*MissingCloudHardeningRule) SupportedTags() []string {
return res
}
-func (r *MissingCloudHardeningRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *MissingCloudHardeningRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
sharedRuntimesWithUnspecificCloudRisks := make(map[string]bool)
trustBoundariesWithUnspecificCloudRisks := make(map[string]bool)
@@ -348,7 +348,7 @@ func addAccordingToBaseTag(techAsset *types.TechnicalAsset, tags []string,
}
}
-func findMostSensitiveTechnicalAsset(input *types.ParsedModel, techAssets map[string]bool) *types.TechnicalAsset {
+func findMostSensitiveTechnicalAsset(input *types.Model, techAssets map[string]bool) *types.TechnicalAsset {
var mostRelevantAsset *types.TechnicalAsset
keys := make([]string, 0, len(techAssets))
for k := range techAssets {
@@ -364,7 +364,7 @@ func findMostSensitiveTechnicalAsset(input *types.ParsedModel, techAssets map[st
return mostRelevantAsset
}
-func (r *MissingCloudHardeningRule) createRiskForSharedRuntime(input *types.ParsedModel, sharedRuntime *types.SharedRuntime, prefix, details string) types.Risk {
+func (r *MissingCloudHardeningRule) createRiskForSharedRuntime(input *types.Model, sharedRuntime *types.SharedRuntime, prefix, details string) *types.Risk {
id := ""
if len(prefix) > 0 {
id = "@" + strings.ToLower(prefix)
@@ -386,8 +386,8 @@ func (r *MissingCloudHardeningRule) createRiskForSharedRuntime(input *types.Pars
impact = types.VeryHighImpact
}
// create risk
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(types.Unlikely, impact),
ExploitationLikelihood: types.Unlikely,
ExploitationImpact: impact,
@@ -400,7 +400,7 @@ func (r *MissingCloudHardeningRule) createRiskForSharedRuntime(input *types.Pars
return risk
}
-func (r *MissingCloudHardeningRule) createRiskForTrustBoundary(parsedModel *types.ParsedModel, trustBoundary *types.TrustBoundary, prefix, details string) types.Risk {
+func (r *MissingCloudHardeningRule) createRiskForTrustBoundary(parsedModel *types.Model, trustBoundary *types.TrustBoundary, prefix, details string) *types.Risk {
id := ""
if len(prefix) > 0 {
id = "@" + strings.ToLower(prefix)
@@ -422,8 +422,8 @@ func (r *MissingCloudHardeningRule) createRiskForTrustBoundary(parsedModel *type
impact = types.VeryHighImpact
}
// create risk
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(types.Unlikely, impact),
ExploitationLikelihood: types.Unlikely,
ExploitationImpact: impact,
@@ -436,7 +436,7 @@ func (r *MissingCloudHardeningRule) createRiskForTrustBoundary(parsedModel *type
return risk
}
-func (r *MissingCloudHardeningRule) createRiskForTechnicalAsset(parsedModel *types.ParsedModel, technicalAsset *types.TechnicalAsset, prefix, details string) types.Risk {
+func (r *MissingCloudHardeningRule) createRiskForTechnicalAsset(parsedModel *types.Model, technicalAsset *types.TechnicalAsset, prefix, details string) *types.Risk {
id := ""
if len(prefix) > 0 {
id = "@" + strings.ToLower(prefix)
@@ -458,8 +458,8 @@ func (r *MissingCloudHardeningRule) createRiskForTechnicalAsset(parsedModel *typ
impact = types.VeryHighImpact
}
// create risk
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(types.Unlikely, impact),
ExploitationLikelihood: types.Unlikely,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/builtin/missing-file-validation-rule.go b/pkg/security/risks/builtin/missing-file-validation-rule.go
index 955629cf..01344da1 100644
--- a/pkg/security/risks/builtin/missing-file-validation-rule.go
+++ b/pkg/security/risks/builtin/missing-file-validation-rule.go
@@ -10,9 +10,9 @@ func NewMissingFileValidationRule() *MissingFileValidationRule {
return &MissingFileValidationRule{}
}
-func (*MissingFileValidationRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "missing-file-validation",
+func (*MissingFileValidationRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "missing-file-validation",
Title: "Missing File Validation",
Description: "When a technical asset accepts files, these input files should be strictly validated about filename and type.",
Impact: "If this risk is unmitigated, attackers might be able to provide malicious files to the application.",
@@ -40,8 +40,8 @@ func (*MissingFileValidationRule) SupportedTags() []string {
return []string{}
}
-func (r *MissingFileValidationRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *MissingFileValidationRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, id := range input.SortedTechnicalAssetIDs() {
technicalAsset := input.TechnicalAssets[id]
if technicalAsset.OutOfScope || !technicalAsset.CustomDevelopedParts {
@@ -56,7 +56,7 @@ func (r *MissingFileValidationRule) GenerateRisks(input *types.ParsedModel) []ty
return risks
}
-func (r *MissingFileValidationRule) createRisk(input *types.ParsedModel, technicalAsset *types.TechnicalAsset) types.Risk {
+func (r *MissingFileValidationRule) createRisk(input *types.Model, technicalAsset *types.TechnicalAsset) *types.Risk {
title := "Missing File Validation risk at " + technicalAsset.Title + ""
impact := types.LowImpact
if technicalAsset.HighestProcessedConfidentiality(input) == types.StrictlyConfidential ||
@@ -64,8 +64,8 @@ func (r *MissingFileValidationRule) createRisk(input *types.ParsedModel, technic
technicalAsset.HighestProcessedAvailability(input) == types.MissionCritical {
impact = types.MediumImpact
}
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(types.VeryLikely, impact),
ExploitationLikelihood: types.VeryLikely,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/builtin/missing-hardening-rule.go b/pkg/security/risks/builtin/missing-hardening-rule.go
index a6b970c8..06cb0091 100644
--- a/pkg/security/risks/builtin/missing-hardening-rule.go
+++ b/pkg/security/risks/builtin/missing-hardening-rule.go
@@ -15,9 +15,9 @@ func NewMissingHardeningRule() *MissingHardeningRule {
return &MissingHardeningRule{raaLimit: 55, raaLimitReduced: 40}
}
-func (r *MissingHardeningRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "missing-hardening",
+func (r *MissingHardeningRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "missing-hardening",
Title: "Missing Hardening",
Description: "Technical assets with a Relative Attacker Attractiveness (RAA) value of " + strconv.Itoa(r.raaLimit) + " % or higher should be " +
"explicitly hardened taking best practices and vendor hardening guides into account.",
@@ -43,8 +43,8 @@ func (*MissingHardeningRule) SupportedTags() []string {
return []string{"tomcat"}
}
-func (r *MissingHardeningRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *MissingHardeningRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, id := range input.SortedTechnicalAssetIDs() {
technicalAsset := input.TechnicalAssets[id]
if !technicalAsset.OutOfScope {
@@ -57,14 +57,14 @@ func (r *MissingHardeningRule) GenerateRisks(input *types.ParsedModel) []types.R
return risks
}
-func (r *MissingHardeningRule) createRisk(input *types.ParsedModel, technicalAsset *types.TechnicalAsset) types.Risk {
+func (r *MissingHardeningRule) createRisk(input *types.Model, technicalAsset *types.TechnicalAsset) *types.Risk {
title := "Missing Hardening risk at " + technicalAsset.Title + ""
impact := types.LowImpact
if technicalAsset.HighestProcessedConfidentiality(input) == types.StrictlyConfidential || technicalAsset.HighestProcessedIntegrity(input) == types.MissionCritical {
impact = types.MediumImpact
}
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(types.Likely, impact),
ExploitationLikelihood: types.Likely,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/builtin/missing-identity-propagation-rule.go b/pkg/security/risks/builtin/missing-identity-propagation-rule.go
index 54794594..86de9351 100644
--- a/pkg/security/risks/builtin/missing-identity-propagation-rule.go
+++ b/pkg/security/risks/builtin/missing-identity-propagation-rule.go
@@ -10,9 +10,9 @@ func NewMissingIdentityPropagationRule() *MissingIdentityPropagationRule {
return &MissingIdentityPropagationRule{}
}
-func (*MissingIdentityPropagationRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "missing-identity-propagation",
+func (*MissingIdentityPropagationRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "missing-identity-propagation",
Title: "Missing Identity Propagation",
Description: "Technical assets (especially multi-tenant systems), which usually process data for end users should " +
"authorize every request based on the identity of the end user when the data flow is authenticated (i.e. non-public). " +
@@ -45,8 +45,8 @@ func (*MissingIdentityPropagationRule) SupportedTags() []string {
return []string{}
}
-func (r *MissingIdentityPropagationRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *MissingIdentityPropagationRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, id := range input.SortedTechnicalAssetIDs() {
technicalAsset := input.TechnicalAssets[id]
if technicalAsset.OutOfScope {
@@ -83,13 +83,13 @@ func (r *MissingIdentityPropagationRule) GenerateRisks(input *types.ParsedModel)
return risks
}
-func (r *MissingIdentityPropagationRule) createRisk(input *types.ParsedModel, technicalAsset *types.TechnicalAsset, incomingAccess *types.CommunicationLink, moreRisky bool) types.Risk {
+func (r *MissingIdentityPropagationRule) createRisk(input *types.Model, technicalAsset *types.TechnicalAsset, incomingAccess *types.CommunicationLink, moreRisky bool) *types.Risk {
impact := types.LowImpact
if moreRisky {
impact = types.MediumImpact
}
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(types.Unlikely, impact),
ExploitationLikelihood: types.Unlikely,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/builtin/missing-identity-provider-isolation-rule.go b/pkg/security/risks/builtin/missing-identity-provider-isolation-rule.go
index 10398793..df3906f0 100644
--- a/pkg/security/risks/builtin/missing-identity-provider-isolation-rule.go
+++ b/pkg/security/risks/builtin/missing-identity-provider-isolation-rule.go
@@ -10,9 +10,9 @@ func NewMissingIdentityProviderIsolationRule() *MissingIdentityProviderIsolation
return &MissingIdentityProviderIsolationRule{}
}
-func (*MissingIdentityProviderIsolationRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "missing-identity-provider-isolation",
+func (*MissingIdentityProviderIsolationRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "missing-identity-provider-isolation",
Title: "Missing Identity Provider Isolation",
Description: "Highly sensitive identity provider assets and their identity data stores should be isolated from other assets " +
"by their own network segmentation trust-boundary (" + types.ExecutionEnvironment.String() + " boundaries do not count as network isolation).",
@@ -41,8 +41,8 @@ func (*MissingIdentityProviderIsolationRule) SupportedTags() []string {
return []string{}
}
-func (r *MissingIdentityProviderIsolationRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *MissingIdentityProviderIsolationRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, technicalAsset := range input.TechnicalAssets {
if !technicalAsset.OutOfScope && technicalAsset.Technologies.GetAttribute(types.IsIdentityRelated) {
moreImpact := technicalAsset.Confidentiality == types.StrictlyConfidential ||
@@ -72,7 +72,7 @@ func (r *MissingIdentityProviderIsolationRule) GenerateRisks(input *types.Parsed
return risks
}
-func (r *MissingIdentityProviderIsolationRule) createRisk(techAsset *types.TechnicalAsset, moreImpact bool, sameExecutionEnv bool) types.Risk {
+func (r *MissingIdentityProviderIsolationRule) createRisk(techAsset *types.TechnicalAsset, moreImpact bool, sameExecutionEnv bool) *types.Risk {
impact := types.HighImpact
likelihood := types.Unlikely
others := "in the same network segment"
@@ -83,8 +83,8 @@ func (r *MissingIdentityProviderIsolationRule) createRisk(techAsset *types.Techn
likelihood = types.Likely
others = "in the same execution environment"
}
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(likelihood, impact),
ExploitationLikelihood: likelihood,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/builtin/missing-identity-store-rule.go b/pkg/security/risks/builtin/missing-identity-store-rule.go
index 1b528e32..9571bdcc 100644
--- a/pkg/security/risks/builtin/missing-identity-store-rule.go
+++ b/pkg/security/risks/builtin/missing-identity-store-rule.go
@@ -10,9 +10,9 @@ func NewMissingIdentityStoreRule() *MissingIdentityStoreRule {
return &MissingIdentityStoreRule{}
}
-func (*MissingIdentityStoreRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "missing-identity-store",
+func (*MissingIdentityStoreRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "missing-identity-store",
Title: "Missing Identity Store",
Description: "The modeled architecture does not contain an identity store, which might be the risk of a model missing " +
"critical assets (and thus not seeing their risks).",
@@ -39,8 +39,8 @@ func (*MissingIdentityStoreRule) SupportedTags() []string {
return []string{}
}
-func (r *MissingIdentityStoreRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *MissingIdentityStoreRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, technicalAsset := range input.TechnicalAssets {
if !technicalAsset.OutOfScope && technicalAsset.Technologies.GetAttribute(types.IsIdentityStore) {
// everything fine, no risk, as we have an in-scope identity store in the model
@@ -83,10 +83,10 @@ func (r *MissingIdentityStoreRule) GenerateRisks(input *types.ParsedModel) []typ
return risks
}
-func (r *MissingIdentityStoreRule) createRisk(technicalAsset *types.TechnicalAsset, impact types.RiskExploitationImpact) types.Risk {
+func (r *MissingIdentityStoreRule) createRisk(technicalAsset *types.TechnicalAsset, impact types.RiskExploitationImpact) *types.Risk {
title := "Missing Identity Store in the threat model (referencing asset " + technicalAsset.Title + " as an example)"
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(types.Unlikely, impact),
ExploitationLikelihood: types.Unlikely,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/builtin/missing-network-segmentation-rule.go b/pkg/security/risks/builtin/missing-network-segmentation-rule.go
index 4163bc34..317ff54a 100644
--- a/pkg/security/risks/builtin/missing-network-segmentation-rule.go
+++ b/pkg/security/risks/builtin/missing-network-segmentation-rule.go
@@ -14,9 +14,9 @@ func NewMissingNetworkSegmentationRule() *MissingNetworkSegmentationRule {
return &MissingNetworkSegmentationRule{raaLimit: 50}
}
-func (*MissingNetworkSegmentationRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "missing-network-segmentation",
+func (*MissingNetworkSegmentationRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "missing-network-segmentation",
Title: "Missing Network Segmentation",
Description: "Highly sensitive assets and/or data stores residing in the same network segment than other " +
"lower sensitive assets (like webservers or content management systems etc.) should be better protected " +
@@ -48,8 +48,8 @@ func (*MissingNetworkSegmentationRule) SupportedTags() []string {
return []string{}
}
-func (r *MissingNetworkSegmentationRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *MissingNetworkSegmentationRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
// first create them in memory (see the link replacement below for nested trust boundaries) - otherwise in Go ranging over map is random order
// range over them in sorted (hence re-producible) way:
keys := make([]string, 0)
@@ -92,13 +92,13 @@ func (r *MissingNetworkSegmentationRule) GenerateRisks(input *types.ParsedModel)
return risks
}
-func (r *MissingNetworkSegmentationRule) createRisk(techAsset *types.TechnicalAsset, moreRisky bool) types.Risk {
+func (r *MissingNetworkSegmentationRule) createRisk(techAsset *types.TechnicalAsset, moreRisky bool) *types.Risk {
impact := types.LowImpact
if moreRisky {
impact = types.MediumImpact
}
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(types.Unlikely, impact),
ExploitationLikelihood: types.Unlikely,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/builtin/missing-vault-isolation-rule.go b/pkg/security/risks/builtin/missing-vault-isolation-rule.go
index fdc6f541..0c2e21a3 100644
--- a/pkg/security/risks/builtin/missing-vault-isolation-rule.go
+++ b/pkg/security/risks/builtin/missing-vault-isolation-rule.go
@@ -10,9 +10,9 @@ func NewMissingVaultIsolationRule() *MissingVaultIsolationRule {
return &MissingVaultIsolationRule{}
}
-func (*MissingVaultIsolationRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "missing-vault-isolation",
+func (*MissingVaultIsolationRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "missing-vault-isolation",
Title: "Missing Vault Isolation",
Description: "Highly sensitive vault assets and their data stores should be isolated from other assets " +
"by their own network segmentation trust-boundary (" + types.ExecutionEnvironment.String() + " boundaries do not count as network isolation).",
@@ -41,8 +41,8 @@ func (*MissingVaultIsolationRule) SupportedTags() []string {
return []string{}
}
-func (r *MissingVaultIsolationRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *MissingVaultIsolationRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, technicalAsset := range input.TechnicalAssets {
if !technicalAsset.OutOfScope && technicalAsset.Technologies.GetAttribute(types.Vault) {
moreImpact := technicalAsset.Confidentiality == types.StrictlyConfidential ||
@@ -72,11 +72,11 @@ func (r *MissingVaultIsolationRule) GenerateRisks(input *types.ParsedModel) []ty
return risks
}
-func isVaultStorage(parsedModel *types.ParsedModel, vault *types.TechnicalAsset, storage *types.TechnicalAsset) bool {
+func isVaultStorage(parsedModel *types.Model, vault *types.TechnicalAsset, storage *types.TechnicalAsset) bool {
return storage.Type == types.Datastore && vault.HasDirectConnection(parsedModel, storage.Id)
}
-func (r *MissingVaultIsolationRule) createRisk(techAsset *types.TechnicalAsset, moreImpact bool, sameExecutionEnv bool) types.Risk {
+func (r *MissingVaultIsolationRule) createRisk(techAsset *types.TechnicalAsset, moreImpact bool, sameExecutionEnv bool) *types.Risk {
impact := types.MediumImpact
likelihood := types.Unlikely
others := "in the same network segment"
@@ -87,8 +87,8 @@ func (r *MissingVaultIsolationRule) createRisk(techAsset *types.TechnicalAsset,
likelihood = types.Likely
others = "in the same execution environment"
}
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(likelihood, impact),
ExploitationLikelihood: likelihood,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/builtin/missing-vault-rule.go b/pkg/security/risks/builtin/missing-vault-rule.go
index 5974d404..02a7f926 100644
--- a/pkg/security/risks/builtin/missing-vault-rule.go
+++ b/pkg/security/risks/builtin/missing-vault-rule.go
@@ -10,9 +10,9 @@ func NewMissingVaultRule() *MissingVaultRule {
return &MissingVaultRule{}
}
-func (*MissingVaultRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "missing-vault",
+func (*MissingVaultRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "missing-vault",
Title: "Missing Vault (Secret Storage)",
Description: "In order to avoid the risk of secret leakage via config files (when attacked through vulnerabilities being able to " +
"read files like Path-Traversal and others), it is best practice to use a separate hardened process with proper authentication, " +
@@ -40,8 +40,8 @@ func (*MissingVaultRule) SupportedTags() []string {
return []string{}
}
-func (r *MissingVaultRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *MissingVaultRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
hasVault := false
var mostRelevantAsset *types.TechnicalAsset
impact := types.LowImpact
@@ -71,10 +71,10 @@ func (r *MissingVaultRule) GenerateRisks(input *types.ParsedModel) []types.Risk
return risks
}
-func (r *MissingVaultRule) createRisk(technicalAsset *types.TechnicalAsset, impact types.RiskExploitationImpact) types.Risk {
+func (r *MissingVaultRule) createRisk(technicalAsset *types.TechnicalAsset, impact types.RiskExploitationImpact) *types.Risk {
title := "Missing Vault (Secret Storage) in the threat model (referencing asset " + technicalAsset.Title + " as an example)"
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(types.Unlikely, impact),
ExploitationLikelihood: types.Unlikely,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/builtin/missing-waf-rule.go b/pkg/security/risks/builtin/missing-waf-rule.go
index b7775d55..60207e1f 100644
--- a/pkg/security/risks/builtin/missing-waf-rule.go
+++ b/pkg/security/risks/builtin/missing-waf-rule.go
@@ -10,9 +10,9 @@ func NewMissingWafRule() *MissingWafRule {
return &MissingWafRule{}
}
-func (*MissingWafRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "missing-waf",
+func (*MissingWafRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "missing-waf",
Title: "Missing Web Application Firewall (WAF)",
Description: "To have a first line of filtering defense, security architectures with web-services or web-applications should include a WAF in front of them. " +
"Even though a WAF is not a replacement for security (all components must be secure even without a WAF) it adds another layer of defense to the overall " +
@@ -39,8 +39,8 @@ func (*MissingWafRule) SupportedTags() []string {
return []string{}
}
-func (r *MissingWafRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *MissingWafRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, technicalAsset := range input.TechnicalAssets {
if !technicalAsset.OutOfScope &&
(technicalAsset.Technologies.GetAttribute(types.WebApplication) || technicalAsset.Technologies.GetAttribute(types.IsWebService)) {
@@ -57,7 +57,7 @@ func (r *MissingWafRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
return risks
}
-func (r *MissingWafRule) createRisk(input *types.ParsedModel, technicalAsset *types.TechnicalAsset) types.Risk {
+func (r *MissingWafRule) createRisk(input *types.Model, technicalAsset *types.TechnicalAsset) *types.Risk {
title := "Missing Web Application Firewall (WAF) risk at " + technicalAsset.Title + ""
likelihood := types.Unlikely
impact := types.LowImpact
@@ -66,8 +66,8 @@ func (r *MissingWafRule) createRisk(input *types.ParsedModel, technicalAsset *ty
technicalAsset.HighestProcessedAvailability(input) == types.MissionCritical {
impact = types.MediumImpact
}
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(likelihood, impact),
ExploitationLikelihood: likelihood,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/builtin/mixed-targets-on-shared-runtime-rule.go b/pkg/security/risks/builtin/mixed-targets-on-shared-runtime-rule.go
index 8e225f3b..5084b6d0 100644
--- a/pkg/security/risks/builtin/mixed-targets-on-shared-runtime-rule.go
+++ b/pkg/security/risks/builtin/mixed-targets-on-shared-runtime-rule.go
@@ -12,9 +12,9 @@ func NewMixedTargetsOnSharedRuntimeRule() *MixedTargetsOnSharedRuntimeRule {
return &MixedTargetsOnSharedRuntimeRule{}
}
-func (*MixedTargetsOnSharedRuntimeRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "mixed-targets-on-shared-runtime",
+func (*MixedTargetsOnSharedRuntimeRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "mixed-targets-on-shared-runtime",
Title: "Mixed Targets on Shared Runtime",
Description: "Different attacker targets (like frontend and backend/datastore components) should not be running on the same " +
"shared (underlying) runtime.",
@@ -44,8 +44,8 @@ func (*MixedTargetsOnSharedRuntimeRule) SupportedTags() []string {
return []string{}
}
-func (r *MixedTargetsOnSharedRuntimeRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *MixedTargetsOnSharedRuntimeRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
// as in Go ranging over map is random order, range over them in sorted (hence reproducible) way:
keys := make([]string, 0)
for k := range input.SharedRuntimes {
@@ -79,13 +79,13 @@ func (r *MixedTargetsOnSharedRuntimeRule) GenerateRisks(input *types.ParsedModel
return risks
}
-func (r *MixedTargetsOnSharedRuntimeRule) createRisk(input *types.ParsedModel, sharedRuntime *types.SharedRuntime) types.Risk {
+func (r *MixedTargetsOnSharedRuntimeRule) createRisk(input *types.Model, sharedRuntime *types.SharedRuntime) *types.Risk {
impact := types.LowImpact
if isMoreRisky(input, sharedRuntime) {
impact = types.MediumImpact
}
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(types.Unlikely, impact),
ExploitationLikelihood: types.Unlikely,
ExploitationImpact: impact,
@@ -99,7 +99,7 @@ func (r *MixedTargetsOnSharedRuntimeRule) createRisk(input *types.ParsedModel, s
return risk
}
-func isMoreRisky(input *types.ParsedModel, sharedRuntime *types.SharedRuntime) bool {
+func isMoreRisky(input *types.Model, sharedRuntime *types.SharedRuntime) bool {
for _, techAssetId := range sharedRuntime.TechnicalAssetsRunning {
techAsset := input.TechnicalAssets[techAssetId]
if techAsset.Confidentiality == types.StrictlyConfidential || techAsset.Integrity == types.MissionCritical ||
diff --git a/pkg/security/risks/builtin/path-traversal-rule.go b/pkg/security/risks/builtin/path-traversal-rule.go
index d1f91db9..630271eb 100644
--- a/pkg/security/risks/builtin/path-traversal-rule.go
+++ b/pkg/security/risks/builtin/path-traversal-rule.go
@@ -10,9 +10,9 @@ func NewPathTraversalRule() *PathTraversalRule {
return &PathTraversalRule{}
}
-func (*PathTraversalRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "path-traversal",
+func (*PathTraversalRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "path-traversal",
Title: "Path-Traversal",
Description: "When a filesystem is accessed Path-Traversal or Local-File-Inclusion (LFI) risks might arise. " +
"The risk rating depends on the sensitivity of the technical asset itself and of the data assets processed.",
@@ -41,8 +41,8 @@ func (*PathTraversalRule) SupportedTags() []string {
return []string{}
}
-func (r *PathTraversalRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *PathTraversalRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, id := range input.SortedTechnicalAssetIDs() {
technicalAsset := input.TechnicalAssets[id]
if !technicalAsset.Technologies.GetAttribute(types.IsFileStorage) {
@@ -63,7 +63,7 @@ func (r *PathTraversalRule) GenerateRisks(input *types.ParsedModel) []types.Risk
return risks
}
-func (r *PathTraversalRule) createRisk(input *types.ParsedModel, technicalAsset *types.TechnicalAsset, incomingFlow *types.CommunicationLink, likelihood types.RiskExploitationLikelihood) types.Risk {
+func (r *PathTraversalRule) createRisk(input *types.Model, technicalAsset *types.TechnicalAsset, incomingFlow *types.CommunicationLink, likelihood types.RiskExploitationLikelihood) *types.Risk {
caller := input.TechnicalAssets[incomingFlow.SourceId]
title := "Path-Traversal risk at " + caller.Title + " against filesystem " + technicalAsset.Title + "" +
" via " + incomingFlow.Title + ""
@@ -71,8 +71,8 @@ func (r *PathTraversalRule) createRisk(input *types.ParsedModel, technicalAsset
if technicalAsset.HighestProcessedConfidentiality(input) == types.StrictlyConfidential || technicalAsset.HighestProcessedIntegrity(input) == types.MissionCritical {
impact = types.HighImpact
}
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(likelihood, impact),
ExploitationLikelihood: likelihood,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/builtin/push-instead-of-pull-deployment-rule.go b/pkg/security/risks/builtin/push-instead-of-pull-deployment-rule.go
index 1da63a1d..8d523fea 100644
--- a/pkg/security/risks/builtin/push-instead-of-pull-deployment-rule.go
+++ b/pkg/security/risks/builtin/push-instead-of-pull-deployment-rule.go
@@ -10,9 +10,9 @@ func NewPushInsteadPullDeploymentRule() *PushInsteadPullDeploymentRule {
return &PushInsteadPullDeploymentRule{}
}
-func (*PushInsteadPullDeploymentRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "push-instead-of-pull-deployment",
+func (*PushInsteadPullDeploymentRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "push-instead-of-pull-deployment",
Title: "Push instead of Pull Deployment",
Description: "When comparing push-based vs. pull-based deployments from a security perspective, pull-based " +
"deployments improve the overall security of the deployment targets. Every exposed interface of a production system to accept a deployment " +
@@ -41,8 +41,8 @@ func (*PushInsteadPullDeploymentRule) SupportedTags() []string {
return []string{}
}
-func (r *PushInsteadPullDeploymentRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *PushInsteadPullDeploymentRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
impact := types.LowImpact
for _, buildPipeline := range input.TechnicalAssets {
if buildPipeline.Technologies.GetAttribute(types.BuildPipeline) {
@@ -63,10 +63,10 @@ func (r *PushInsteadPullDeploymentRule) GenerateRisks(input *types.ParsedModel)
return risks
}
-func (r *PushInsteadPullDeploymentRule) createRisk(buildPipeline *types.TechnicalAsset, deploymentTarget *types.TechnicalAsset, deploymentCommLink *types.CommunicationLink, impact types.RiskExploitationImpact) types.Risk {
+func (r *PushInsteadPullDeploymentRule) createRisk(buildPipeline *types.TechnicalAsset, deploymentTarget *types.TechnicalAsset, deploymentCommLink *types.CommunicationLink, impact types.RiskExploitationImpact) *types.Risk {
title := "Push instead of Pull Deployment at " + deploymentTarget.Title + " via build pipeline asset " + buildPipeline.Title + ""
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(types.Unlikely, impact),
ExploitationLikelihood: types.Unlikely,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/builtin/search-query-injection-rule.go b/pkg/security/risks/builtin/search-query-injection-rule.go
index 34c96bfc..cec0f4f1 100644
--- a/pkg/security/risks/builtin/search-query-injection-rule.go
+++ b/pkg/security/risks/builtin/search-query-injection-rule.go
@@ -10,9 +10,9 @@ func NewSearchQueryInjectionRule() *SearchQueryInjectionRule {
return &SearchQueryInjectionRule{}
}
-func (*SearchQueryInjectionRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "search-query-injection",
+func (*SearchQueryInjectionRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "search-query-injection",
Title: "Search-Query Injection",
Description: "When a search engine server is accessed Search-Query Injection risks might arise." +
"
See for example https://github.com/veracode-research/solr-injection and " +
@@ -42,8 +42,8 @@ func (*SearchQueryInjectionRule) SupportedTags() []string {
return []string{}
}
-func (r *SearchQueryInjectionRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *SearchQueryInjectionRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, id := range input.SortedTechnicalAssetIDs() {
technicalAsset := input.TechnicalAssets[id]
if technicalAsset.Technologies.GetAttribute(types.IsSearchRelated) {
@@ -66,7 +66,7 @@ func (r *SearchQueryInjectionRule) GenerateRisks(input *types.ParsedModel) []typ
return risks
}
-func (r *SearchQueryInjectionRule) createRisk(input *types.ParsedModel, technicalAsset *types.TechnicalAsset, incomingFlow *types.CommunicationLink, likelihood types.RiskExploitationLikelihood) types.Risk {
+func (r *SearchQueryInjectionRule) createRisk(input *types.Model, technicalAsset *types.TechnicalAsset, incomingFlow *types.CommunicationLink, likelihood types.RiskExploitationLikelihood) *types.Risk {
caller := input.TechnicalAssets[incomingFlow.SourceId]
title := "Search Query Injection risk at " + caller.Title + " against search engine server " + technicalAsset.Title + "" +
" via " + incomingFlow.Title + ""
@@ -76,8 +76,8 @@ func (r *SearchQueryInjectionRule) createRisk(input *types.ParsedModel, technica
} else if technicalAsset.HighestProcessedConfidentiality(input) <= types.Internal && technicalAsset.HighestProcessedIntegrity(input) == types.Operational {
impact = types.LowImpact
}
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(likelihood, impact),
ExploitationLikelihood: likelihood,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/builtin/server-side-request-forgery-rule.go b/pkg/security/risks/builtin/server-side-request-forgery-rule.go
index 5e6cd0a0..02a9b03a 100644
--- a/pkg/security/risks/builtin/server-side-request-forgery-rule.go
+++ b/pkg/security/risks/builtin/server-side-request-forgery-rule.go
@@ -10,9 +10,9 @@ func NewServerSideRequestForgeryRule() *ServerSideRequestForgeryRule {
return &ServerSideRequestForgeryRule{}
}
-func (*ServerSideRequestForgeryRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "server-side-request-forgery",
+func (*ServerSideRequestForgeryRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "server-side-request-forgery",
Title: "Server-Side Request Forgery (SSRF)",
Description: "When a server system (i.e. not a client) is accessing other server systems via typical web protocols " +
"Server-Side Request Forgery (SSRF) or Local-File-Inclusion (LFI) or Remote-File-Inclusion (RFI) risks might arise. ",
@@ -41,8 +41,8 @@ func (*ServerSideRequestForgeryRule) SupportedTags() []string {
return []string{}
}
-func (r *ServerSideRequestForgeryRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *ServerSideRequestForgeryRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, id := range input.SortedTechnicalAssetIDs() {
technicalAsset := input.TechnicalAssets[id]
if technicalAsset.OutOfScope || technicalAsset.Technologies.GetAttribute(types.IsClient) || technicalAsset.Technologies.GetAttribute(types.LoadBalancer) {
@@ -57,7 +57,7 @@ func (r *ServerSideRequestForgeryRule) GenerateRisks(input *types.ParsedModel) [
return risks
}
-func (r *ServerSideRequestForgeryRule) createRisk(input *types.ParsedModel, technicalAsset *types.TechnicalAsset, outgoingFlow *types.CommunicationLink) types.Risk {
+func (r *ServerSideRequestForgeryRule) createRisk(input *types.Model, technicalAsset *types.TechnicalAsset, outgoingFlow *types.CommunicationLink) *types.Risk {
target := input.TechnicalAssets[outgoingFlow.TargetId]
title := "Server-Side Request Forgery (SSRF) risk at " + technicalAsset.Title + " server-side web-requesting " +
"the target " + target.Title + " via " + outgoingFlow.Title + ""
@@ -93,8 +93,8 @@ func (r *ServerSideRequestForgeryRule) createRisk(input *types.ParsedModel, tech
if outgoingFlow.Usage == types.DevOps {
likelihood = types.Unlikely
}
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(likelihood, impact),
ExploitationLikelihood: likelihood,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/builtin/service-registry-poisoning-rule.go b/pkg/security/risks/builtin/service-registry-poisoning-rule.go
index 60df4f7d..c47ef7ea 100644
--- a/pkg/security/risks/builtin/service-registry-poisoning-rule.go
+++ b/pkg/security/risks/builtin/service-registry-poisoning-rule.go
@@ -10,9 +10,9 @@ func NewServiceRegistryPoisoningRule() *ServiceRegistryPoisoningRule {
return &ServiceRegistryPoisoningRule{}
}
-func (*ServiceRegistryPoisoningRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "service-registry-poisoning",
+func (*ServiceRegistryPoisoningRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "service-registry-poisoning",
Title: "Service Registry Poisoning",
Description: "When a service registry used for discovery of trusted service endpoints Service Registry Poisoning risks might arise.",
Impact: "If this risk remains unmitigated, attackers might be able to poison the service registry with malicious service endpoints or " +
@@ -38,8 +38,8 @@ func (*ServiceRegistryPoisoningRule) SupportedTags() []string {
return []string{}
}
-func (r *ServiceRegistryPoisoningRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *ServiceRegistryPoisoningRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, id := range input.SortedTechnicalAssetIDs() {
technicalAsset := input.TechnicalAssets[id]
if !technicalAsset.OutOfScope && technicalAsset.Technologies.GetAttribute(types.ServiceRegistry) {
@@ -50,7 +50,7 @@ func (r *ServiceRegistryPoisoningRule) GenerateRisks(input *types.ParsedModel) [
return risks
}
-func (r *ServiceRegistryPoisoningRule) createRisk(input *types.ParsedModel, technicalAsset *types.TechnicalAsset, incomingFlows []*types.CommunicationLink) types.Risk {
+func (r *ServiceRegistryPoisoningRule) createRisk(input *types.Model, technicalAsset *types.TechnicalAsset, incomingFlows []*types.CommunicationLink) *types.Risk {
title := "Service Registry Poisoning risk at " + technicalAsset.Title + ""
impact := types.LowImpact
@@ -64,8 +64,8 @@ func (r *ServiceRegistryPoisoningRule) createRisk(input *types.ParsedModel, tech
}
}
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(types.Unlikely, impact),
ExploitationLikelihood: types.Unlikely,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/builtin/sql-nosql-injection-rule.go b/pkg/security/risks/builtin/sql-nosql-injection-rule.go
index 29886175..d32a58fd 100644
--- a/pkg/security/risks/builtin/sql-nosql-injection-rule.go
+++ b/pkg/security/risks/builtin/sql-nosql-injection-rule.go
@@ -10,9 +10,9 @@ func NewSqlNoSqlInjectionRule() *SqlNoSqlInjectionRule {
return &SqlNoSqlInjectionRule{}
}
-func (*SqlNoSqlInjectionRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "sql-nosql-injection",
+func (*SqlNoSqlInjectionRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "sql-nosql-injection",
Title: "SQL/NoSQL-Injection",
Description: "When a database is accessed via database access protocols SQL/NoSQL-Injection risks might arise. " +
"The risk rating depends on the sensitivity technical asset itself and of the data assets processed.",
@@ -38,8 +38,8 @@ func (*SqlNoSqlInjectionRule) SupportedTags() []string {
return []string{}
}
-func (r *SqlNoSqlInjectionRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *SqlNoSqlInjectionRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, id := range input.SortedTechnicalAssetIDs() {
technicalAsset := input.TechnicalAssets[id]
incomingFlows := input.IncomingTechnicalCommunicationLinksMappedByTargetId[technicalAsset.Id]
@@ -56,7 +56,7 @@ func (r *SqlNoSqlInjectionRule) GenerateRisks(input *types.ParsedModel) []types.
return risks
}
-func (r *SqlNoSqlInjectionRule) createRisk(input *types.ParsedModel, technicalAsset *types.TechnicalAsset, incomingFlow *types.CommunicationLink) types.Risk {
+func (r *SqlNoSqlInjectionRule) createRisk(input *types.Model, technicalAsset *types.TechnicalAsset, incomingFlow *types.CommunicationLink) *types.Risk {
caller := input.TechnicalAssets[incomingFlow.SourceId]
title := "SQL/NoSQL-Injection risk at " + caller.Title + " against database " + technicalAsset.Title + "" +
" via " + incomingFlow.Title + ""
@@ -68,8 +68,8 @@ func (r *SqlNoSqlInjectionRule) createRisk(input *types.ParsedModel, technicalAs
if incomingFlow.Usage == types.DevOps {
likelihood = types.Likely
}
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(likelihood, impact),
ExploitationLikelihood: likelihood,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/builtin/unchecked-deployment-rule.go b/pkg/security/risks/builtin/unchecked-deployment-rule.go
index 484f509d..61edff11 100644
--- a/pkg/security/risks/builtin/unchecked-deployment-rule.go
+++ b/pkg/security/risks/builtin/unchecked-deployment-rule.go
@@ -10,9 +10,9 @@ func NewUncheckedDeploymentRule() *UncheckedDeploymentRule {
return &UncheckedDeploymentRule{}
}
-func (*UncheckedDeploymentRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "unchecked-deployment",
+func (*UncheckedDeploymentRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "unchecked-deployment",
Title: "Unchecked Deployment",
Description: "For each build-pipeline component Unchecked Deployment risks might arise when the build-pipeline " +
"does not include established DevSecOps best-practices. DevSecOps best-practices scan as part of CI/CD pipelines for " +
@@ -41,8 +41,8 @@ func (*UncheckedDeploymentRule) SupportedTags() []string {
return []string{}
}
-func (r *UncheckedDeploymentRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *UncheckedDeploymentRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, technicalAsset := range input.TechnicalAssets {
if technicalAsset.Technologies.GetAttribute(types.IsDevelopmentRelevant) {
risks = append(risks, r.createRisk(input, technicalAsset))
@@ -51,7 +51,7 @@ func (r *UncheckedDeploymentRule) GenerateRisks(input *types.ParsedModel) []type
return risks
}
-func (r *UncheckedDeploymentRule) createRisk(input *types.ParsedModel, technicalAsset *types.TechnicalAsset) types.Risk {
+func (r *UncheckedDeploymentRule) createRisk(input *types.Model, technicalAsset *types.TechnicalAsset) *types.Risk {
title := "Unchecked Deployment risk at " + technicalAsset.Title + ""
// impact is depending on highest rating
impact := types.LowImpact
@@ -81,8 +81,8 @@ func (r *UncheckedDeploymentRule) createRisk(input *types.ParsedModel, technical
dataBreachTechnicalAssetIDs = append(dataBreachTechnicalAssetIDs, key)
}
// create risk
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(types.Unlikely, impact),
ExploitationLikelihood: types.Unlikely,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/builtin/unencrypted-asset-rule.go b/pkg/security/risks/builtin/unencrypted-asset-rule.go
index ed8cbde3..63773b12 100644
--- a/pkg/security/risks/builtin/unencrypted-asset-rule.go
+++ b/pkg/security/risks/builtin/unencrypted-asset-rule.go
@@ -10,9 +10,9 @@ func NewUnencryptedAssetRule() *UnencryptedAssetRule {
return &UnencryptedAssetRule{}
}
-func (*UnencryptedAssetRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "unencrypted-asset",
+func (*UnencryptedAssetRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "unencrypted-asset",
Title: "Unencrypted Technical Assets",
Description: "Due to the confidentiality rating of the technical asset itself and/or the stored data assets " +
"this technical asset must be encrypted. The risk rating depends on the sensitivity technical asset itself and of the data assets stored.",
@@ -44,8 +44,8 @@ func (*UnencryptedAssetRule) SupportedTags() []string {
// check for technical assets that should be encrypted due to their confidentiality
-func (r *UnencryptedAssetRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *UnencryptedAssetRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, id := range input.SortedTechnicalAssetIDs() {
technicalAsset := input.TechnicalAssets[id]
if !technicalAsset.OutOfScope && !isEncryptionWaiver(technicalAsset) && len(technicalAsset.DataAssetsStored) > 0 &&
@@ -76,13 +76,13 @@ func isEncryptionWaiver(asset *types.TechnicalAsset) bool {
return asset.Technologies.GetAttribute(types.IsNoStorageAtRest) || asset.Technologies.GetAttribute(types.IsEmbeddedComponent)
}
-func (r *UnencryptedAssetRule) createRisk(technicalAsset *types.TechnicalAsset, impact types.RiskExploitationImpact, requiresEndUserKey bool) types.Risk {
+func (r *UnencryptedAssetRule) createRisk(technicalAsset *types.TechnicalAsset, impact types.RiskExploitationImpact, requiresEndUserKey bool) *types.Risk {
title := "Unencrypted Technical Asset named " + technicalAsset.Title + ""
if requiresEndUserKey {
title += " missing end user individual encryption with " + types.DataWithEndUserIndividualKey.String()
}
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(types.Unlikely, impact),
ExploitationLikelihood: types.Unlikely,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/builtin/unencrypted-communication-rule.go b/pkg/security/risks/builtin/unencrypted-communication-rule.go
index 59d6d64c..08254a22 100644
--- a/pkg/security/risks/builtin/unencrypted-communication-rule.go
+++ b/pkg/security/risks/builtin/unencrypted-communication-rule.go
@@ -10,9 +10,9 @@ func NewUnencryptedCommunicationRule() *UnencryptedCommunicationRule {
return &UnencryptedCommunicationRule{}
}
-func (*UnencryptedCommunicationRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "unencrypted-communication",
+func (*UnencryptedCommunicationRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "unencrypted-communication",
Title: "Unencrypted Communication",
Description: "Due to the confidentiality and/or integrity rating of the data assets transferred over the " +
"communication link this connection must be encrypted.",
@@ -40,8 +40,8 @@ func (*UnencryptedCommunicationRule) SupportedTags() []string {
// check for communication links that should be encrypted due to their confidentiality and/or integrity
-func (r *UnencryptedCommunicationRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *UnencryptedCommunicationRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, technicalAsset := range input.TechnicalAssets {
for _, dataFlow := range technicalAsset.CommunicationLinks {
transferringAuthData := dataFlow.Authentication != types.NoneAuthentication
@@ -83,7 +83,7 @@ func (r *UnencryptedCommunicationRule) GenerateRisks(input *types.ParsedModel) [
return risks
}
-func (r *UnencryptedCommunicationRule) createRisk(input *types.ParsedModel, technicalAsset *types.TechnicalAsset, dataFlow *types.CommunicationLink, highRisk bool, transferringAuthData bool) types.Risk {
+func (r *UnencryptedCommunicationRule) createRisk(input *types.Model, technicalAsset *types.TechnicalAsset, dataFlow *types.CommunicationLink, highRisk bool, transferringAuthData bool) *types.Risk {
impact := types.MediumImpact
if highRisk {
impact = types.HighImpact
@@ -101,8 +101,8 @@ func (r *UnencryptedCommunicationRule) createRisk(input *types.ParsedModel, tech
if dataFlow.IsAcrossTrustBoundaryNetworkOnly(input) {
likelihood = types.Likely
}
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(likelihood, impact),
ExploitationLikelihood: likelihood,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/builtin/unguarded-access-from-internet-rule.go b/pkg/security/risks/builtin/unguarded-access-from-internet-rule.go
index cc66cdd2..67fb0fd7 100644
--- a/pkg/security/risks/builtin/unguarded-access-from-internet-rule.go
+++ b/pkg/security/risks/builtin/unguarded-access-from-internet-rule.go
@@ -12,9 +12,9 @@ func NewUnguardedAccessFromInternetRule() *UnguardedAccessFromInternetRule {
return &UnguardedAccessFromInternetRule{}
}
-func (*UnguardedAccessFromInternetRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "unguarded-access-from-internet",
+func (*UnguardedAccessFromInternetRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "unguarded-access-from-internet",
Title: "Unguarded Access From Internet",
Description: "Internet-exposed assets must be guarded by a protecting service, application, " +
"or reverse-proxy.",
@@ -50,8 +50,8 @@ func (*UnguardedAccessFromInternetRule) SupportedTags() []string {
return []string{}
}
-func (r *UnguardedAccessFromInternetRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *UnguardedAccessFromInternetRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, id := range input.SortedTechnicalAssetIDs() {
technicalAsset := input.TechnicalAssets[id]
if !technicalAsset.OutOfScope {
@@ -88,13 +88,13 @@ func (r *UnguardedAccessFromInternetRule) GenerateRisks(input *types.ParsedModel
}
func (r *UnguardedAccessFromInternetRule) createRisk(dataStore *types.TechnicalAsset, dataFlow *types.CommunicationLink,
- clientFromInternet *types.TechnicalAsset, moreRisky bool) types.Risk {
+ clientFromInternet *types.TechnicalAsset, moreRisky bool) *types.Risk {
impact := types.LowImpact
if moreRisky || dataStore.RAA > 40 {
impact = types.MediumImpact
}
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(types.VeryLikely, impact),
ExploitationLikelihood: types.VeryLikely,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/builtin/unguarded-direct-datastore-access-rule.go b/pkg/security/risks/builtin/unguarded-direct-datastore-access-rule.go
index 157ba27d..68a28f40 100644
--- a/pkg/security/risks/builtin/unguarded-direct-datastore-access-rule.go
+++ b/pkg/security/risks/builtin/unguarded-direct-datastore-access-rule.go
@@ -10,9 +10,9 @@ func NewUnguardedDirectDatastoreAccessRule() *UnguardedDirectDatastoreAccessRule
return &UnguardedDirectDatastoreAccessRule{}
}
-func (*UnguardedDirectDatastoreAccessRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "unguarded-direct-datastore-access",
+func (*UnguardedDirectDatastoreAccessRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "unguarded-direct-datastore-access",
Title: "Unguarded Direct Datastore Access",
Description: "Data stores accessed across trust boundaries must be guarded by some protecting service or application.",
Impact: "If this risk is unmitigated, attackers might be able to directly attack sensitive data stores without any protecting components in-between.",
@@ -42,8 +42,8 @@ func (*UnguardedDirectDatastoreAccessRule) SupportedTags() []string {
// check for data stores that should not be accessed directly across trust boundaries
-func (r *UnguardedDirectDatastoreAccessRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *UnguardedDirectDatastoreAccessRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, id := range input.SortedTechnicalAssetIDs() {
technicalAsset := input.TechnicalAssets[id]
if !technicalAsset.OutOfScope && technicalAsset.Type == types.Datastore {
@@ -68,7 +68,7 @@ func (r *UnguardedDirectDatastoreAccessRule) GenerateRisks(input *types.ParsedMo
return risks
}
-func isSharingSameParentTrustBoundary(input *types.ParsedModel, left, right *types.TechnicalAsset) bool {
+func isSharingSameParentTrustBoundary(input *types.Model, left, right *types.TechnicalAsset) bool {
tbIDLeft, tbIDRight := left.GetTrustBoundaryId(input), right.GetTrustBoundaryId(input)
if len(tbIDLeft) == 0 && len(tbIDRight) > 0 {
return false
@@ -99,13 +99,13 @@ func fileServerAccessViaFTP(technicalAsset *types.TechnicalAsset, incomingAccess
(incomingAccess.Protocol == types.FTP || incomingAccess.Protocol == types.FTPS || incomingAccess.Protocol == types.SFTP)
}
-func (r *UnguardedDirectDatastoreAccessRule) createRisk(dataStore *types.TechnicalAsset, dataFlow *types.CommunicationLink, clientOutsideTrustBoundary *types.TechnicalAsset, moreRisky bool) types.Risk {
+func (r *UnguardedDirectDatastoreAccessRule) createRisk(dataStore *types.TechnicalAsset, dataFlow *types.CommunicationLink, clientOutsideTrustBoundary *types.TechnicalAsset, moreRisky bool) *types.Risk {
impact := types.LowImpact
if moreRisky || dataStore.RAA > 40 {
impact = types.MediumImpact
}
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(types.Likely, impact),
ExploitationLikelihood: types.Likely,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/builtin/unnecessary-communication-link-rule.go b/pkg/security/risks/builtin/unnecessary-communication-link-rule.go
index e2b8c030..6725cc68 100644
--- a/pkg/security/risks/builtin/unnecessary-communication-link-rule.go
+++ b/pkg/security/risks/builtin/unnecessary-communication-link-rule.go
@@ -10,9 +10,9 @@ func NewUnnecessaryCommunicationLinkRule() *UnnecessaryCommunicationLinkRule {
return &UnnecessaryCommunicationLinkRule{}
}
-func (*UnnecessaryCommunicationLinkRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "unnecessary-communication-link",
+func (*UnnecessaryCommunicationLinkRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "unnecessary-communication-link",
Title: "Unnecessary Communication Link",
Description: "When a technical communication link does not send or receive any data assets, this is " +
"an indicator for an unnecessary communication link (or for an incomplete model).",
@@ -36,8 +36,8 @@ func (*UnnecessaryCommunicationLinkRule) SupportedTags() []string {
return []string{}
}
-func (r *UnnecessaryCommunicationLinkRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *UnnecessaryCommunicationLinkRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, id := range input.SortedTechnicalAssetIDs() {
technicalAsset := input.TechnicalAssets[id]
for _, commLink := range technicalAsset.CommunicationLinks {
@@ -51,10 +51,10 @@ func (r *UnnecessaryCommunicationLinkRule) GenerateRisks(input *types.ParsedMode
return risks
}
-func (r *UnnecessaryCommunicationLinkRule) createRisk(technicalAsset *types.TechnicalAsset, commLink *types.CommunicationLink) types.Risk {
+func (r *UnnecessaryCommunicationLinkRule) createRisk(technicalAsset *types.TechnicalAsset, commLink *types.CommunicationLink) *types.Risk {
title := "Unnecessary Communication Link titled " + commLink.Title + " at technical asset " + technicalAsset.Title + ""
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(types.Unlikely, types.LowImpact),
ExploitationLikelihood: types.Unlikely,
ExploitationImpact: types.LowImpact,
diff --git a/pkg/security/risks/builtin/unnecessary-data-asset-rule.go b/pkg/security/risks/builtin/unnecessary-data-asset-rule.go
index dcb6cc3d..64c1aab7 100644
--- a/pkg/security/risks/builtin/unnecessary-data-asset-rule.go
+++ b/pkg/security/risks/builtin/unnecessary-data-asset-rule.go
@@ -12,9 +12,9 @@ func NewUnnecessaryDataAssetRule() *UnnecessaryDataAssetRule {
return &UnnecessaryDataAssetRule{}
}
-func (*UnnecessaryDataAssetRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "unnecessary-data-asset",
+func (*UnnecessaryDataAssetRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "unnecessary-data-asset",
Title: "Unnecessary Data Asset",
Description: "When a data asset is not processed by any data assets and also not transferred by any " +
"communication links, this is an indicator for an unnecessary data asset (or for an incomplete model).",
@@ -40,8 +40,8 @@ func (*UnnecessaryDataAssetRule) SupportedTags() []string {
return []string{}
}
-func (r *UnnecessaryDataAssetRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *UnnecessaryDataAssetRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
// first create them in memory - otherwise in Go ranging over map is random order
// range over them in sorted (hence re-producible) way:
unusedDataAssetIDs := make(map[string]bool)
@@ -75,11 +75,11 @@ func (r *UnnecessaryDataAssetRule) GenerateRisks(input *types.ParsedModel) []typ
return risks
}
-func (r *UnnecessaryDataAssetRule) createRisk(input *types.ParsedModel, unusedDataAssetID string) types.Risk {
+func (r *UnnecessaryDataAssetRule) createRisk(input *types.Model, unusedDataAssetID string) *types.Risk {
unusedDataAsset := input.DataAssets[unusedDataAssetID]
title := "Unnecessary Data Asset named " + unusedDataAsset.Title + ""
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(types.Unlikely, types.LowImpact),
ExploitationLikelihood: types.Unlikely,
ExploitationImpact: types.LowImpact,
diff --git a/pkg/security/risks/builtin/unnecessary-data-transfer-rule.go b/pkg/security/risks/builtin/unnecessary-data-transfer-rule.go
index 88a4d36b..bb31b2dc 100644
--- a/pkg/security/risks/builtin/unnecessary-data-transfer-rule.go
+++ b/pkg/security/risks/builtin/unnecessary-data-transfer-rule.go
@@ -12,9 +12,9 @@ func NewUnnecessaryDataTransferRule() *UnnecessaryDataTransferRule {
return &UnnecessaryDataTransferRule{}
}
-func (*UnnecessaryDataTransferRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "unnecessary-data-transfer",
+func (*UnnecessaryDataTransferRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "unnecessary-data-transfer",
Title: "Unnecessary Data Transfer",
Description: "When a technical asset sends or receives data assets, which it neither processes or stores this is " +
"an indicator for unnecessarily transferred data (or for an incomplete model). When the unnecessarily " +
@@ -45,8 +45,8 @@ func (*UnnecessaryDataTransferRule) SupportedTags() []string {
return []string{}
}
-func (r *UnnecessaryDataTransferRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *UnnecessaryDataTransferRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, id := range input.SortedTechnicalAssetIDs() {
technicalAsset := input.TechnicalAssets[id]
if technicalAsset.OutOfScope {
@@ -74,12 +74,11 @@ func (r *UnnecessaryDataTransferRule) GenerateRisks(input *types.ParsedModel) []
return risks
}
-func (r *UnnecessaryDataTransferRule) checkRisksAgainstTechnicalAsset(input *types.ParsedModel, risks []types.Risk, technicalAsset *types.TechnicalAsset,
- dataFlow *types.CommunicationLink, inverseDirection bool) []types.Risk {
+func (r *UnnecessaryDataTransferRule) checkRisksAgainstTechnicalAsset(input *types.Model, risks []*types.Risk, technicalAsset *types.TechnicalAsset, dataFlow *types.CommunicationLink, inverseDirection bool) []*types.Risk {
for _, transferredDataAssetId := range dataFlow.DataAssetsSent {
if !technicalAsset.ProcessesOrStoresDataAsset(transferredDataAssetId) {
transferredDataAsset := input.DataAssets[transferredDataAssetId]
- //fmt.Print("--->>> Checking "+technicalAsset.Id+": "+transferredDataAsset.Id+" sent via "+dataFlow.Id+"\n")
+ //fmt.Print("--->>> Checking "+technicalAsset.ID+": "+transferredDataAsset.ID+" sent via "+dataFlow.ID+"\n")
if transferredDataAsset.Confidentiality >= types.Confidential || transferredDataAsset.Integrity >= types.Critical {
commPartnerId := dataFlow.TargetId
if inverseDirection {
@@ -96,7 +95,7 @@ func (r *UnnecessaryDataTransferRule) checkRisksAgainstTechnicalAsset(input *typ
for _, transferredDataAssetId := range dataFlow.DataAssetsReceived {
if !technicalAsset.ProcessesOrStoresDataAsset(transferredDataAssetId) {
transferredDataAsset := input.DataAssets[transferredDataAssetId]
- //fmt.Print("--->>> Checking "+technicalAsset.Id+": "+transferredDataAsset.Id+" received via "+dataFlow.Id+"\n")
+ //fmt.Print("--->>> Checking "+technicalAsset.ID+": "+transferredDataAsset.ID+" received via "+dataFlow.ID+"\n")
if transferredDataAsset.Confidentiality >= types.Confidential || transferredDataAsset.Integrity >= types.Critical {
commPartnerId := dataFlow.TargetId
if inverseDirection {
@@ -113,7 +112,7 @@ func (r *UnnecessaryDataTransferRule) checkRisksAgainstTechnicalAsset(input *typ
return risks
}
-func isNewRisk(risks []types.Risk, risk types.Risk) bool {
+func isNewRisk(risks []*types.Risk, risk *types.Risk) bool {
for _, check := range risks {
if check.SyntheticId == risk.SyntheticId {
return false
@@ -122,7 +121,7 @@ func isNewRisk(risks []types.Risk, risk types.Risk) bool {
return true
}
-func (r *UnnecessaryDataTransferRule) createRisk(technicalAsset *types.TechnicalAsset, dataAssetTransferred *types.DataAsset, commPartnerAsset *types.TechnicalAsset) types.Risk {
+func (r *UnnecessaryDataTransferRule) createRisk(technicalAsset *types.TechnicalAsset, dataAssetTransferred *types.DataAsset, commPartnerAsset *types.TechnicalAsset) *types.Risk {
moreRisky := dataAssetTransferred.Confidentiality == types.StrictlyConfidential || dataAssetTransferred.Integrity == types.MissionCritical
impact := types.LowImpact
@@ -132,8 +131,8 @@ func (r *UnnecessaryDataTransferRule) createRisk(technicalAsset *types.Technical
title := "Unnecessary Data Transfer of " + dataAssetTransferred.Title + " data at " + technicalAsset.Title + " " +
"from/to " + commPartnerAsset.Title + ""
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(types.Unlikely, impact),
ExploitationLikelihood: types.Unlikely,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/builtin/unnecessary-technical-asset-rule.go b/pkg/security/risks/builtin/unnecessary-technical-asset-rule.go
index 7a3470e6..9caca9f1 100644
--- a/pkg/security/risks/builtin/unnecessary-technical-asset-rule.go
+++ b/pkg/security/risks/builtin/unnecessary-technical-asset-rule.go
@@ -10,9 +10,9 @@ func NewUnnecessaryTechnicalAssetRule() *UnnecessaryTechnicalAssetRule {
return &UnnecessaryTechnicalAssetRule{}
}
-func (*UnnecessaryTechnicalAssetRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "unnecessary-technical-asset",
+func (*UnnecessaryTechnicalAssetRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "unnecessary-technical-asset",
Title: "Unnecessary Technical Asset",
Description: "When a technical asset does not process any data assets, this is " +
"an indicator for an unnecessary technical asset (or for an incomplete model). " +
@@ -37,8 +37,8 @@ func (*UnnecessaryTechnicalAssetRule) SupportedTags() []string {
return []string{}
}
-func (r *UnnecessaryTechnicalAssetRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *UnnecessaryTechnicalAssetRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, id := range input.SortedTechnicalAssetIDs() {
technicalAsset := input.TechnicalAssets[id]
if len(technicalAsset.DataAssetsProcessed) == 0 && len(technicalAsset.DataAssetsStored) == 0 ||
@@ -49,10 +49,10 @@ func (r *UnnecessaryTechnicalAssetRule) GenerateRisks(input *types.ParsedModel)
return risks
}
-func (r *UnnecessaryTechnicalAssetRule) createRisk(technicalAsset *types.TechnicalAsset) types.Risk {
+func (r *UnnecessaryTechnicalAssetRule) createRisk(technicalAsset *types.TechnicalAsset) *types.Risk {
title := "Unnecessary Technical Asset named " + technicalAsset.Title + ""
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(types.Unlikely, types.LowImpact),
ExploitationLikelihood: types.Unlikely,
ExploitationImpact: types.LowImpact,
diff --git a/pkg/security/risks/builtin/untrusted-deserialization-rule.go b/pkg/security/risks/builtin/untrusted-deserialization-rule.go
index 742ed31c..d7f3ff02 100644
--- a/pkg/security/risks/builtin/untrusted-deserialization-rule.go
+++ b/pkg/security/risks/builtin/untrusted-deserialization-rule.go
@@ -10,9 +10,9 @@ func NewUntrustedDeserializationRule() *UntrustedDeserializationRule {
return &UntrustedDeserializationRule{}
}
-func (*UntrustedDeserializationRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "untrusted-deserialization",
+func (*UntrustedDeserializationRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "untrusted-deserialization",
Title: "Untrusted Deserialization",
Description: "When a technical asset accepts data in a specific serialized form (like Java or .NET serialization), " +
"Untrusted Deserialization risks might arise." +
@@ -42,8 +42,8 @@ func (*UntrustedDeserializationRule) SupportedTags() []string {
return []string{}
}
-func (r *UntrustedDeserializationRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *UntrustedDeserializationRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, id := range input.SortedTechnicalAssetIDs() {
technicalAsset := input.TechnicalAssets[id]
if technicalAsset.OutOfScope {
@@ -77,7 +77,7 @@ func (r *UntrustedDeserializationRule) GenerateRisks(input *types.ParsedModel) [
return risks
}
-func (r *UntrustedDeserializationRule) createRisk(parsedModel *types.ParsedModel, technicalAsset *types.TechnicalAsset, acrossTrustBoundary bool, commLinkTitle string) types.Risk {
+func (r *UntrustedDeserializationRule) createRisk(parsedModel *types.Model, technicalAsset *types.TechnicalAsset, acrossTrustBoundary bool, commLinkTitle string) *types.Risk {
title := "Untrusted Deserialization risk at " + technicalAsset.Title + ""
impact := types.HighImpact
likelihood := types.Likely
@@ -90,8 +90,8 @@ func (r *UntrustedDeserializationRule) createRisk(parsedModel *types.ParsedModel
technicalAsset.HighestProcessedAvailability(parsedModel) == types.MissionCritical {
impact = types.VeryHighImpact
}
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(likelihood, impact),
ExploitationLikelihood: likelihood,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/builtin/wrong-communication-link-content-rule.go b/pkg/security/risks/builtin/wrong-communication-link-content-rule.go
index de1422e2..2205d0df 100644
--- a/pkg/security/risks/builtin/wrong-communication-link-content-rule.go
+++ b/pkg/security/risks/builtin/wrong-communication-link-content-rule.go
@@ -10,9 +10,9 @@ func NewWrongCommunicationLinkContentRule() *WrongCommunicationLinkContentRule {
return &WrongCommunicationLinkContentRule{}
}
-func (*WrongCommunicationLinkContentRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "wrong-communication-link-content",
+func (*WrongCommunicationLinkContentRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "wrong-communication-link-content",
Title: "Wrong Communication Link Content",
Description: "When a communication link is defined as readonly, but does not receive any data asset, " +
"or when it is defined as not readonly, but does not send any data asset, it is likely to be a model failure.",
@@ -37,8 +37,8 @@ func (*WrongCommunicationLinkContentRule) SupportedTags() []string {
return []string{}
}
-func (r *WrongCommunicationLinkContentRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *WrongCommunicationLinkContentRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, techAsset := range input.TechnicalAssets {
for _, commLink := range techAsset.CommunicationLinks {
// check readonly consistency
@@ -72,11 +72,11 @@ func (r *WrongCommunicationLinkContentRule) GenerateRisks(input *types.ParsedMod
return risks
}
-func (r *WrongCommunicationLinkContentRule) createRisk(technicalAsset *types.TechnicalAsset, commLink *types.CommunicationLink, reason string) types.Risk {
+func (r *WrongCommunicationLinkContentRule) createRisk(technicalAsset *types.TechnicalAsset, commLink *types.CommunicationLink, reason string) *types.Risk {
title := "Wrong Communication Link Content " + reason + " at " + technicalAsset.Title + " " +
"regarding communication link " + commLink.Title + ""
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(types.Unlikely, types.LowImpact),
ExploitationLikelihood: types.Unlikely,
ExploitationImpact: types.LowImpact,
diff --git a/pkg/security/risks/builtin/wrong-trust-boundary-content.go b/pkg/security/risks/builtin/wrong-trust-boundary-content.go
index 368c5cae..86fd462f 100644
--- a/pkg/security/risks/builtin/wrong-trust-boundary-content.go
+++ b/pkg/security/risks/builtin/wrong-trust-boundary-content.go
@@ -10,9 +10,9 @@ func NewWrongTrustBoundaryContentRule() *WrongTrustBoundaryContentRule {
return &WrongTrustBoundaryContentRule{}
}
-func (*WrongTrustBoundaryContentRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "wrong-trust-boundary-content",
+func (*WrongTrustBoundaryContentRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "wrong-trust-boundary-content",
Title: "Wrong Trust Boundary Content",
Description: "When a trust boundary of type " + types.NetworkPolicyNamespaceIsolation.String() + " contains " +
"non-container assets it is likely to be a model failure.",
@@ -36,8 +36,8 @@ func (*WrongTrustBoundaryContentRule) SupportedTags() []string {
return []string{}
}
-func (r *WrongTrustBoundaryContentRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *WrongTrustBoundaryContentRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, trustBoundary := range input.TrustBoundaries {
if trustBoundary.Type == types.NetworkPolicyNamespaceIsolation {
for _, techAssetID := range trustBoundary.TechnicalAssetsInside {
@@ -51,10 +51,10 @@ func (r *WrongTrustBoundaryContentRule) GenerateRisks(input *types.ParsedModel)
return risks
}
-func (r *WrongTrustBoundaryContentRule) createRisk(technicalAsset *types.TechnicalAsset) types.Risk {
+func (r *WrongTrustBoundaryContentRule) createRisk(technicalAsset *types.TechnicalAsset) *types.Risk {
title := "Wrong Trust Boundary Content (non-container asset inside container trust boundary) at " + technicalAsset.Title + ""
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(types.Unlikely, types.LowImpact),
ExploitationLikelihood: types.Unlikely,
ExploitationImpact: types.LowImpact,
diff --git a/pkg/security/risks/builtin/xml-external-entity-rule.go b/pkg/security/risks/builtin/xml-external-entity-rule.go
index 9d80fd73..62a4accb 100644
--- a/pkg/security/risks/builtin/xml-external-entity-rule.go
+++ b/pkg/security/risks/builtin/xml-external-entity-rule.go
@@ -10,9 +10,9 @@ func NewXmlExternalEntityRule() *XmlExternalEntityRule {
return &XmlExternalEntityRule{}
}
-func (*XmlExternalEntityRule) Category() types.RiskCategory {
- return types.RiskCategory{
- Id: "xml-external-entity",
+func (*XmlExternalEntityRule) Category() *types.RiskCategory {
+ return &types.RiskCategory{
+ ID: "xml-external-entity",
Title: "XML External Entity (XXE)",
Description: "When a technical asset accepts data in XML format, XML External Entity (XXE) risks might arise.",
Impact: "If this risk is unmitigated, attackers might be able to read sensitive files (configuration data, key/credential files, deployment files, " +
@@ -40,8 +40,8 @@ func (*XmlExternalEntityRule) SupportedTags() []string {
return []string{}
}
-func (r *XmlExternalEntityRule) GenerateRisks(input *types.ParsedModel) []types.Risk {
- risks := make([]types.Risk, 0)
+func (r *XmlExternalEntityRule) GenerateRisks(input *types.Model) []*types.Risk {
+ risks := make([]*types.Risk, 0)
for _, id := range input.SortedTechnicalAssetIDs() {
technicalAsset := input.TechnicalAssets[id]
if technicalAsset.OutOfScope {
@@ -56,7 +56,7 @@ func (r *XmlExternalEntityRule) GenerateRisks(input *types.ParsedModel) []types.
return risks
}
-func (r *XmlExternalEntityRule) createRisk(parsedModel *types.ParsedModel, technicalAsset *types.TechnicalAsset) types.Risk {
+func (r *XmlExternalEntityRule) createRisk(parsedModel *types.Model, technicalAsset *types.TechnicalAsset) *types.Risk {
title := "XML External Entity (XXE) risk at " + technicalAsset.Title + ""
impact := types.MediumImpact
if technicalAsset.HighestProcessedConfidentiality(parsedModel) == types.StrictlyConfidential ||
@@ -64,8 +64,8 @@ func (r *XmlExternalEntityRule) createRisk(parsedModel *types.ParsedModel, techn
technicalAsset.HighestProcessedAvailability(parsedModel) == types.MissionCritical {
impact = types.HighImpact
}
- risk := types.Risk{
- CategoryId: r.Category().Id,
+ risk := &types.Risk{
+ CategoryId: r.Category().ID,
Severity: types.CalculateSeverity(types.VeryLikely, impact),
ExploitationLikelihood: types.VeryLikely,
ExploitationImpact: impact,
diff --git a/pkg/security/risks/risk-rule.go b/pkg/security/risks/risk-rule.go
index 536fa9ca..35921b45 100644
--- a/pkg/security/risks/risk-rule.go
+++ b/pkg/security/risks/risk-rule.go
@@ -3,7 +3,17 @@ package risks
import "github.com/threagile/threagile/pkg/security/types"
type RiskRule interface {
- Category() types.RiskCategory
+ Category() *types.RiskCategory
SupportedTags() []string
- GenerateRisks(*types.ParsedModel) []types.Risk
+ GenerateRisks(*types.Model) []*types.Risk
+}
+
+type RiskRules map[string]RiskRule
+
+func (what RiskRules) Merge(rules RiskRules) RiskRules {
+ for key, value := range rules {
+ what[key] = value
+ }
+
+ return what
}
diff --git a/pkg/security/risks/risks.go b/pkg/security/risks/risks.go
index d9c788f9..6ee80db9 100644
--- a/pkg/security/risks/risks.go
+++ b/pkg/security/risks/risks.go
@@ -4,8 +4,9 @@ import (
"github.com/threagile/threagile/pkg/security/risks/builtin"
)
-func GetBuiltInRiskRules() []RiskRule {
- return []RiskRule{
+func GetBuiltInRiskRules() RiskRules {
+ rules := make(RiskRules)
+ for _, rule := range []RiskRule{
builtin.NewAccidentalSecretLeakRule(),
builtin.NewCodeBackdooringRule(),
builtin.NewContainerBaseImageBackdooringRule(),
@@ -48,5 +49,9 @@ func GetBuiltInRiskRules() []RiskRule {
builtin.NewWrongCommunicationLinkContentRule(),
builtin.NewWrongTrustBoundaryContentRule(),
builtin.NewXmlExternalEntityRule(),
+ } {
+ rules[rule.Category().ID] = rule
}
+
+ return rules
}
diff --git a/pkg/security/types/authentication.go b/pkg/security/types/authentication.go
index fba8fa65..88f47cae 100644
--- a/pkg/security/types/authentication.go
+++ b/pkg/security/types/authentication.go
@@ -5,7 +5,9 @@ Copyright © 2023 NAME HERE
package types
import (
+ "encoding/json"
"fmt"
+ "gopkg.in/yaml.v3"
"strings"
)
@@ -66,3 +68,37 @@ func (what Authentication) Find(value string) (Authentication, error) {
return Authentication(0), fmt.Errorf("unknown authentication value %q", value)
}
+
+func (what Authentication) MarshalJSON() ([]byte, error) {
+ return json.Marshal(what.String())
+}
+
+func (what *Authentication) UnmarshalJSON(data []byte) error {
+ var text string
+ unmarshalError := json.Unmarshal(data, &text)
+ if unmarshalError != nil {
+ return unmarshalError
+ }
+
+ value, findError := what.Find(text)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
+
+func (what Authentication) MarshalYAML() (interface{}, error) {
+ return what.String(), nil
+}
+
+func (what *Authentication) UnmarshalYAML(node *yaml.Node) error {
+ value, findError := what.Find(node.Value)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
diff --git a/pkg/security/types/authorization.go b/pkg/security/types/authorization.go
index 60928d2d..303f0de6 100644
--- a/pkg/security/types/authorization.go
+++ b/pkg/security/types/authorization.go
@@ -5,7 +5,9 @@ Copyright © 2023 NAME HERE
package types
import (
+ "encoding/json"
"fmt"
+ "gopkg.in/yaml.v3"
"strings"
)
@@ -53,3 +55,37 @@ func (what Authorization) Find(value string) (Authorization, error) {
return Authorization(0), fmt.Errorf("unknown authorization value %q", value)
}
+
+func (what Authorization) MarshalJSON() ([]byte, error) {
+ return json.Marshal(what.String())
+}
+
+func (what *Authorization) UnmarshalJSON(data []byte) error {
+ var text string
+ unmarshalError := json.Unmarshal(data, &text)
+ if unmarshalError != nil {
+ return unmarshalError
+ }
+
+ value, findError := what.Find(text)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
+
+func (what Authorization) MarshalYAML() (interface{}, error) {
+ return what.String(), nil
+}
+
+func (what *Authorization) UnmarshalYAML(node *yaml.Node) error {
+ value, findError := what.Find(node.Value)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
diff --git a/pkg/security/types/communication_link.go b/pkg/security/types/communication_link.go
index c7e5396a..a36adb35 100644
--- a/pkg/security/types/communication_link.go
+++ b/pkg/security/types/communication_link.go
@@ -36,13 +36,13 @@ func (what CommunicationLink) IsTaggedWithBaseTag(baseTag string) bool {
return IsTaggedWithBaseTag(what.Tags, baseTag)
}
-func (what CommunicationLink) IsAcrossTrustBoundary(parsedModel *ParsedModel) bool {
+func (what CommunicationLink) IsAcrossTrustBoundary(parsedModel *Model) bool {
trustBoundaryOfSourceAsset := parsedModel.DirectContainingTrustBoundaryMappedByTechnicalAssetId[what.SourceId]
trustBoundaryOfTargetAsset := parsedModel.DirectContainingTrustBoundaryMappedByTechnicalAssetId[what.TargetId]
return trustBoundaryOfSourceAsset.Id != trustBoundaryOfTargetAsset.Id
}
-func (what CommunicationLink) IsAcrossTrustBoundaryNetworkOnly(parsedModel *ParsedModel) bool {
+func (what CommunicationLink) IsAcrossTrustBoundaryNetworkOnly(parsedModel *Model) bool {
trustBoundaryOfSourceAsset := parsedModel.DirectContainingTrustBoundaryMappedByTechnicalAssetId[what.SourceId]
if !trustBoundaryOfSourceAsset.Type.IsNetworkBoundary() { // find and use the parent boundary then
trustBoundaryOfSourceAsset = parsedModel.TrustBoundaries[trustBoundaryOfSourceAsset.ParentTrustBoundaryID(parsedModel)]
@@ -54,7 +54,7 @@ func (what CommunicationLink) IsAcrossTrustBoundaryNetworkOnly(parsedModel *Pars
return trustBoundaryOfSourceAsset.Id != trustBoundaryOfTargetAsset.Id && trustBoundaryOfTargetAsset.Type.IsNetworkBoundary()
}
-func (what CommunicationLink) HighestConfidentiality(parsedModel *ParsedModel) Confidentiality {
+func (what CommunicationLink) HighestConfidentiality(parsedModel *Model) Confidentiality {
highest := Public
for _, dataId := range what.DataAssetsSent {
dataAsset := parsedModel.DataAssets[dataId]
@@ -71,7 +71,7 @@ func (what CommunicationLink) HighestConfidentiality(parsedModel *ParsedModel) C
return highest
}
-func (what CommunicationLink) HighestIntegrity(parsedModel *ParsedModel) Criticality {
+func (what CommunicationLink) HighestIntegrity(parsedModel *Model) Criticality {
highest := Archive
for _, dataId := range what.DataAssetsSent {
dataAsset := parsedModel.DataAssets[dataId]
@@ -88,7 +88,7 @@ func (what CommunicationLink) HighestIntegrity(parsedModel *ParsedModel) Critica
return highest
}
-func (what CommunicationLink) HighestAvailability(parsedModel *ParsedModel) Criticality {
+func (what CommunicationLink) HighestAvailability(parsedModel *Model) Criticality {
highest := Archive
for _, dataId := range what.DataAssetsSent {
dataAsset := parsedModel.DataAssets[dataId]
@@ -105,7 +105,7 @@ func (what CommunicationLink) HighestAvailability(parsedModel *ParsedModel) Crit
return highest
}
-func (what CommunicationLink) DataAssetsSentSorted(parsedModel *ParsedModel) []*DataAsset {
+func (what CommunicationLink) DataAssetsSentSorted(parsedModel *Model) []*DataAsset {
result := make([]*DataAsset, 0)
for _, assetID := range what.DataAssetsSent {
result = append(result, parsedModel.DataAssets[assetID])
@@ -114,7 +114,7 @@ func (what CommunicationLink) DataAssetsSentSorted(parsedModel *ParsedModel) []*
return result
}
-func (what CommunicationLink) DataAssetsReceivedSorted(parsedModel *ParsedModel) []*DataAsset {
+func (what CommunicationLink) DataAssetsReceivedSorted(parsedModel *Model) []*DataAsset {
result := make([]*DataAsset, 0)
for _, assetID := range what.DataAssetsReceived {
result = append(result, parsedModel.DataAssets[assetID])
diff --git a/pkg/security/types/confidentiality.go b/pkg/security/types/confidentiality.go
index bf97b2c0..f94229d4 100644
--- a/pkg/security/types/confidentiality.go
+++ b/pkg/security/types/confidentiality.go
@@ -5,7 +5,9 @@ Copyright © 2023 NAME HERE
package types
import (
+ "encoding/json"
"fmt"
+ "gopkg.in/yaml.v3"
"strings"
)
@@ -93,3 +95,37 @@ func (what Confidentiality) Find(value string) (Confidentiality, error) {
return Confidentiality(0), fmt.Errorf("unknown confidentiality value %q", value)
}
+
+func (what Confidentiality) MarshalJSON() ([]byte, error) {
+ return json.Marshal(what.String())
+}
+
+func (what *Confidentiality) UnmarshalJSON(data []byte) error {
+ var text string
+ unmarshalError := json.Unmarshal(data, &text)
+ if unmarshalError != nil {
+ return unmarshalError
+ }
+
+ value, findError := what.Find(text)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
+
+func (what Confidentiality) MarshalYAML() (interface{}, error) {
+ return what.String(), nil
+}
+
+func (what *Confidentiality) UnmarshalYAML(node *yaml.Node) error {
+ value, findError := what.Find(node.Value)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
diff --git a/pkg/security/types/criticality.go b/pkg/security/types/criticality.go
index 841d0388..8f38cf6e 100644
--- a/pkg/security/types/criticality.go
+++ b/pkg/security/types/criticality.go
@@ -5,7 +5,9 @@ Copyright © 2023 NAME HERE
package types
import (
+ "encoding/json"
"fmt"
+ "gopkg.in/yaml.v3"
"strings"
)
@@ -93,3 +95,37 @@ func (what Criticality) Find(value string) (Criticality, error) {
return Criticality(0), fmt.Errorf("unknown criticality value %q", value)
}
+
+func (what Criticality) MarshalJSON() ([]byte, error) {
+ return json.Marshal(what.String())
+}
+
+func (what *Criticality) UnmarshalJSON(data []byte) error {
+ var text string
+ unmarshalError := json.Unmarshal(data, &text)
+ if unmarshalError != nil {
+ return unmarshalError
+ }
+
+ value, findError := what.Find(text)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
+
+func (what Criticality) MarshalYAML() (interface{}, error) {
+ return what.String(), nil
+}
+
+func (what *Criticality) UnmarshalYAML(node *yaml.Node) error {
+ value, findError := what.Find(node.Value)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
diff --git a/pkg/security/types/data_asset.go b/pkg/security/types/data_asset.go
index 185a17da..b7d21a72 100644
--- a/pkg/security/types/data_asset.go
+++ b/pkg/security/types/data_asset.go
@@ -66,7 +66,7 @@ func (what DataAsset) IdentifiedRiskSeverityStillAtRisk() RiskSeverity {
}
*/
-func (what DataAsset) IdentifiedRisksByResponsibleTechnicalAssetId(model *ParsedModel) map[string][]Risk {
+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 {
@@ -79,14 +79,14 @@ func (what DataAsset) IdentifiedRisksByResponsibleTechnicalAssetId(model *Parsed
}
}
- result := make(map[string][]Risk)
+ 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 *ParsedModel) bool {
+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) {
@@ -97,7 +97,7 @@ func (what DataAsset) IsDataBreachPotentialStillAtRisk(parsedModel *ParsedModel)
return false
}
-func (what DataAsset) IdentifiedDataBreachProbability(parsedModel *ParsedModel) DataBreachProbability {
+func (what DataAsset) IdentifiedDataBreachProbability(parsedModel *Model) DataBreachProbability {
highestProbability := Improbable
for _, risk := range AllRisks(parsedModel) {
for _, techAsset := range risk.DataBreachTechnicalAssetIDs {
@@ -112,7 +112,7 @@ func (what DataAsset) IdentifiedDataBreachProbability(parsedModel *ParsedModel)
return highestProbability
}
-func (what DataAsset) IdentifiedDataBreachProbabilityStillAtRisk(parsedModel *ParsedModel) DataBreachProbability {
+func (what DataAsset) IdentifiedDataBreachProbabilityStillAtRisk(parsedModel *Model) DataBreachProbability {
highestProbability := Improbable
for _, risk := range FilteredByStillAtRisk(parsedModel) {
for _, techAsset := range risk.DataBreachTechnicalAssetIDs {
@@ -127,8 +127,8 @@ func (what DataAsset) IdentifiedDataBreachProbabilityStillAtRisk(parsedModel *Pa
return highestProbability
}
-func (what DataAsset) IdentifiedDataBreachProbabilityRisksStillAtRisk(parsedModel *ParsedModel) []Risk {
- result := make([]Risk, 0)
+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) {
@@ -140,8 +140,8 @@ func (what DataAsset) IdentifiedDataBreachProbabilityRisksStillAtRisk(parsedMode
return result
}
-func (what DataAsset) IdentifiedDataBreachProbabilityRisks(parsedModel *ParsedModel) []Risk {
- result := make([]Risk, 0)
+func (what DataAsset) IdentifiedDataBreachProbabilityRisks(parsedModel *Model) []*Risk {
+ result := make([]*Risk, 0)
for _, risk := range AllRisks(parsedModel) {
for _, techAsset := range risk.DataBreachTechnicalAssetIDs {
if contains(parsedModel.TechnicalAssets[techAsset].DataAssetsProcessed, what.Id) {
@@ -153,7 +153,7 @@ func (what DataAsset) IdentifiedDataBreachProbabilityRisks(parsedModel *ParsedMo
return result
}
-func (what DataAsset) ProcessedByTechnicalAssetsSorted(parsedModel *ParsedModel) []*TechnicalAsset {
+func (what DataAsset) ProcessedByTechnicalAssetsSorted(parsedModel *Model) []*TechnicalAsset {
result := make([]*TechnicalAsset, 0)
for _, technicalAsset := range parsedModel.TechnicalAssets {
for _, candidateID := range technicalAsset.DataAssetsProcessed {
@@ -166,7 +166,7 @@ func (what DataAsset) ProcessedByTechnicalAssetsSorted(parsedModel *ParsedModel)
return result
}
-func (what DataAsset) StoredByTechnicalAssetsSorted(parsedModel *ParsedModel) []*TechnicalAsset {
+func (what DataAsset) StoredByTechnicalAssetsSorted(parsedModel *Model) []*TechnicalAsset {
result := make([]*TechnicalAsset, 0)
for _, technicalAsset := range parsedModel.TechnicalAssets {
for _, candidateID := range technicalAsset.DataAssetsStored {
@@ -179,7 +179,7 @@ func (what DataAsset) StoredByTechnicalAssetsSorted(parsedModel *ParsedModel) []
return result
}
-func (what DataAsset) SentViaCommLinksSorted(parsedModel *ParsedModel) []*CommunicationLink {
+func (what DataAsset) SentViaCommLinksSorted(parsedModel *Model) []*CommunicationLink {
result := make([]*CommunicationLink, 0)
for _, technicalAsset := range parsedModel.TechnicalAssets {
for _, commLink := range technicalAsset.CommunicationLinks {
@@ -194,7 +194,7 @@ func (what DataAsset) SentViaCommLinksSorted(parsedModel *ParsedModel) []*Commun
return result
}
-func (what DataAsset) ReceivedViaCommLinksSorted(parsedModel *ParsedModel) []*CommunicationLink {
+func (what DataAsset) ReceivedViaCommLinksSorted(parsedModel *Model) []*CommunicationLink {
result := make([]*CommunicationLink, 0)
for _, technicalAsset := range parsedModel.TechnicalAssets {
for _, commLink := range technicalAsset.CommunicationLinks {
@@ -209,7 +209,7 @@ func (what DataAsset) ReceivedViaCommLinksSorted(parsedModel *ParsedModel) []*Co
return result
}
-func SortByDataAssetDataBreachProbabilityAndTitle(parsedModel *ParsedModel, assets []*DataAsset) {
+func SortByDataAssetDataBreachProbabilityAndTitle(parsedModel *Model, assets []*DataAsset) {
sort.Slice(assets, func(i, j int) bool {
highestDataBreachProbabilityLeft := assets[i].IdentifiedDataBreachProbability(parsedModel)
highestDataBreachProbabilityRight := assets[j].IdentifiedDataBreachProbability(parsedModel)
@@ -220,7 +220,7 @@ func SortByDataAssetDataBreachProbabilityAndTitle(parsedModel *ParsedModel, asse
})
}
-func SortByDataAssetDataBreachProbabilityAndTitleStillAtRisk(parsedModel *ParsedModel, assets []*DataAsset) {
+func SortByDataAssetDataBreachProbabilityAndTitleStillAtRisk(parsedModel *Model, assets []*DataAsset) {
sort.Slice(assets, func(i, j int) bool {
risksLeft := assets[i].IdentifiedDataBreachProbabilityRisksStillAtRisk(parsedModel)
risksRight := assets[j].IdentifiedDataBreachProbabilityRisksStillAtRisk(parsedModel)
diff --git a/pkg/security/types/data_breach_probability.go b/pkg/security/types/data_breach_probability.go
index ae7fa92e..4ed39f81 100644
--- a/pkg/security/types/data_breach_probability.go
+++ b/pkg/security/types/data_breach_probability.go
@@ -5,7 +5,9 @@ Copyright © 2023 NAME HERE
package types
import (
+ "encoding/json"
"fmt"
+ "gopkg.in/yaml.v3"
"strings"
)
@@ -61,3 +63,37 @@ func (what DataBreachProbability) Find(value string) (DataBreachProbability, err
return DataBreachProbability(0), fmt.Errorf("unknown data breach probability value %q", value)
}
+
+func (what DataBreachProbability) MarshalJSON() ([]byte, error) {
+ return json.Marshal(what.String())
+}
+
+func (what *DataBreachProbability) UnmarshalJSON(data []byte) error {
+ var text string
+ unmarshalError := json.Unmarshal(data, &text)
+ if unmarshalError != nil {
+ return unmarshalError
+ }
+
+ value, findError := what.Find(text)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
+
+func (what DataBreachProbability) MarshalYAML() (interface{}, error) {
+ return what.String(), nil
+}
+
+func (what *DataBreachProbability) UnmarshalYAML(node *yaml.Node) error {
+ value, findError := what.Find(node.Value)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
diff --git a/pkg/security/types/data_format.go b/pkg/security/types/data_format.go
index d8493255..12e08774 100644
--- a/pkg/security/types/data_format.go
+++ b/pkg/security/types/data_format.go
@@ -5,7 +5,9 @@ Copyright © 2023 NAME HERE
package types
import (
+ "encoding/json"
"fmt"
+ "gopkg.in/yaml.v3"
"strings"
)
@@ -72,3 +74,47 @@ func (what ByDataFormatAcceptedSort) Swap(i, j int) { what[i], what[j] = what[j]
func (what ByDataFormatAcceptedSort) Less(i, j int) bool {
return what[i].String() < what[j].String()
}
+
+func (what DataFormat) MarshalJSON() ([]byte, error) {
+ return json.Marshal(what.String())
+}
+
+func (what *DataFormat) UnmarshalJSON(data []byte) error {
+ var text string
+ unmarshalError := json.Unmarshal(data, &text)
+ if unmarshalError != nil {
+ return unmarshalError
+ }
+
+ value, findError := what.find(text)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
+
+func (what DataFormat) MarshalYAML() (interface{}, error) {
+ return what.String(), nil
+}
+
+func (what *DataFormat) UnmarshalYAML(node *yaml.Node) error {
+ value, findError := what.find(node.Value)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
+
+func (what DataFormat) find(value string) (DataFormat, error) {
+ for index, description := range DataFormatTypeDescription {
+ if strings.EqualFold(value, description.Name) {
+ return DataFormat(index), nil
+ }
+ }
+
+ return DataFormat(0), fmt.Errorf("unknown data format value %q", value)
+}
diff --git a/pkg/security/types/date.go b/pkg/security/types/date.go
index f430d450..345f5ac2 100644
--- a/pkg/security/types/date.go
+++ b/pkg/security/types/date.go
@@ -1,9 +1,45 @@
package types
import (
+ "gopkg.in/yaml.v3"
"time"
)
+const (
+ jsonDateFormat = `"2006-01-02"`
+ yamlDateFormat = `2006-01-02`
+)
+
type Date struct {
time.Time
}
+
+func (what Date) MarshalJSON() ([]byte, error) {
+ return []byte(what.Format(jsonDateFormat)), nil
+}
+
+func (what *Date) UnmarshalJSON(bytes []byte) error {
+ date, parseError := time.Parse(jsonDateFormat, string(bytes))
+ if parseError != nil {
+ return parseError
+ }
+
+ what.Time = date
+
+ return nil
+}
+
+func (what Date) MarshalYAML() (interface{}, error) {
+ return what.Format(yamlDateFormat), nil
+}
+
+func (what *Date) UnmarshalYAML(node *yaml.Node) error {
+ date, parseError := time.Parse(yamlDateFormat, node.Value)
+ if parseError != nil {
+ return parseError
+ }
+
+ what.Time = date
+
+ return nil
+}
diff --git a/pkg/security/types/encryption_style.go b/pkg/security/types/encryption_style.go
index 8df8ee7d..2abbb989 100644
--- a/pkg/security/types/encryption_style.go
+++ b/pkg/security/types/encryption_style.go
@@ -5,7 +5,9 @@ Copyright © 2023 NAME HERE
package types
import (
+ "encoding/json"
"fmt"
+ "gopkg.in/yaml.v3"
"strings"
)
@@ -63,3 +65,37 @@ func (what EncryptionStyle) Find(value string) (EncryptionStyle, error) {
return EncryptionStyle(0), fmt.Errorf("unknown encryption style value %q", value)
}
+
+func (what EncryptionStyle) MarshalJSON() ([]byte, error) {
+ return json.Marshal(what.String())
+}
+
+func (what *EncryptionStyle) UnmarshalJSON(data []byte) error {
+ var text string
+ unmarshalError := json.Unmarshal(data, &text)
+ if unmarshalError != nil {
+ return unmarshalError
+ }
+
+ value, findError := what.Find(text)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
+
+func (what EncryptionStyle) MarshalYAML() (interface{}, error) {
+ return what.String(), nil
+}
+
+func (what *EncryptionStyle) UnmarshalYAML(node *yaml.Node) error {
+ value, findError := what.Find(node.Value)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
diff --git a/pkg/security/types/model.go b/pkg/security/types/model.go
index 65129e7d..a1959056 100644
--- a/pkg/security/types/model.go
+++ b/pkg/security/types/model.go
@@ -18,7 +18,7 @@ import (
// rename parsedModel to model or something like this to emphasize that it's just a model
// maybe
-type ParsedModel struct {
+type Model struct {
ThreagileVersion string `yaml:"threagile_version,omitempty" json:"threagile_version,omitempty"`
Includes []string `yaml:"includes,omitempty" json:"includes,omitempty"`
Title string `json:"title,omitempty" yaml:"title,omitempty"`
@@ -38,9 +38,9 @@ type ParsedModel struct {
TechnicalAssets map[string]*TechnicalAsset `json:"technical_assets,omitempty" yaml:"technical_assets,omitempty"`
TrustBoundaries map[string]*TrustBoundary `json:"trust_boundaries,omitempty" yaml:"trust_boundaries,omitempty"`
SharedRuntimes map[string]*SharedRuntime `json:"shared_runtimes,omitempty" yaml:"shared_runtimes,omitempty"`
- IndividualRiskCategories map[string]RiskCategory `json:"individual_risk_categories,omitempty" yaml:"individual_risk_categories,omitempty"`
- BuiltInRiskCategories map[string]RiskCategory `json:"built_in_risk_categories,omitempty" yaml:"built_in_risk_categories,omitempty"`
- RiskTracking map[string]RiskTracking `json:"risk_tracking,omitempty" yaml:"risk_tracking,omitempty"`
+ CustomRiskCategories RiskCategories `json:"custom_risk_categories,omitempty" yaml:"custom_risk_categories,omitempty"`
+ BuiltInRiskCategories RiskCategories `json:"built_in_risk_categories,omitempty" yaml:"built_in_risk_categories,omitempty"`
+ RiskTracking map[string]*RiskTracking `json:"risk_tracking,omitempty" yaml:"risk_tracking,omitempty"`
CommunicationLinks map[string]*CommunicationLink `json:"communication_links,omitempty" yaml:"communication_links,omitempty"`
AllSupportedTags map[string]bool `json:"all_supported_tags,omitempty" yaml:"all_supported_tags,omitempty"`
DiagramTweakNodesep int `json:"diagram_tweak_nodesep,omitempty" yaml:"diagram_tweak_nodesep,omitempty"`
@@ -54,18 +54,18 @@ type ParsedModel struct {
// TODO: those are generated based on items above and needs to be private
IncomingTechnicalCommunicationLinksMappedByTargetId map[string][]*CommunicationLink `json:"incoming_technical_communication_links_mapped_by_target_id,omitempty" yaml:"incoming_technical_communication_links_mapped_by_target_id,omitempty"`
DirectContainingTrustBoundaryMappedByTechnicalAssetId map[string]*TrustBoundary `json:"direct_containing_trust_boundary_mapped_by_technical_asset_id,omitempty" yaml:"direct_containing_trust_boundary_mapped_by_technical_asset_id,omitempty"`
- GeneratedRisksByCategory map[string][]Risk `json:"generated_risks_by_category,omitempty" yaml:"generated_risks_by_category,omitempty"`
- GeneratedRisksBySyntheticId map[string]Risk `json:"generated_risks_by_synthetic_id,omitempty" yaml:"generated_risks_by_synthetic_id,omitempty"`
+ GeneratedRisksByCategory map[string][]*Risk `json:"generated_risks_by_category,omitempty" yaml:"generated_risks_by_category,omitempty"`
+ GeneratedRisksBySyntheticId map[string]*Risk `json:"generated_risks_by_synthetic_id,omitempty" yaml:"generated_risks_by_synthetic_id,omitempty"`
}
-func (parsedModel *ParsedModel) AddToListOfSupportedTags(tags []string) {
+func (parsedModel *Model) AddToListOfSupportedTags(tags []string) {
for _, tag := range tags {
parsedModel.AllSupportedTags[tag] = true
}
}
-func (parsedModel *ParsedModel) GetDeferredRiskTrackingDueToWildcardMatching() map[string]RiskTracking {
- deferredRiskTrackingDueToWildcardMatching := make(map[string]RiskTracking)
+func (parsedModel *Model) GetDeferredRiskTrackingDueToWildcardMatching() map[string]*RiskTracking {
+ deferredRiskTrackingDueToWildcardMatching := make(map[string]*RiskTracking)
for syntheticRiskId, riskTracking := range parsedModel.RiskTracking {
if strings.Contains(syntheticRiskId, "*") { // contains a wildcard char
deferredRiskTrackingDueToWildcardMatching[syntheticRiskId] = riskTracking
@@ -75,14 +75,14 @@ func (parsedModel *ParsedModel) GetDeferredRiskTrackingDueToWildcardMatching() m
return deferredRiskTrackingDueToWildcardMatching
}
-func (parsedModel *ParsedModel) HasNotYetAnyDirectNonWildcardRiskTracking(syntheticRiskId string) bool {
+func (parsedModel *Model) HasNotYetAnyDirectNonWildcardRiskTracking(syntheticRiskId string) bool {
if _, ok := parsedModel.RiskTracking[syntheticRiskId]; ok {
return false
}
return true
}
-func (parsedModel *ParsedModel) CheckTags(tags []string, where string) ([]string, error) {
+func (parsedModel *Model) CheckTags(tags []string, where string) ([]string, error) {
var tagsUsed = make([]string, 0)
if tags != nil {
tagsUsed = make([]string, len(tags))
@@ -98,7 +98,7 @@ func (parsedModel *ParsedModel) CheckTags(tags []string, where string) ([]string
return tagsUsed, nil
}
-func (parsedModel *ParsedModel) ApplyWildcardRiskTrackingEvaluation(ignoreOrphanedRiskTracking bool, progressReporter ProgressReporter) error {
+func (parsedModel *Model) ApplyWildcardRiskTrackingEvaluation(ignoreOrphanedRiskTracking bool, progressReporter ProgressReporter) error {
progressReporter.Info("Executing risk tracking evaluation")
for syntheticRiskIdPattern, riskTracking := range parsedModel.GetDeferredRiskTrackingDueToWildcardMatching() {
progressReporter.Infof("Applying wildcard risk tracking for risk id: %v", syntheticRiskIdPattern)
@@ -108,7 +108,7 @@ func (parsedModel *ParsedModel) ApplyWildcardRiskTrackingEvaluation(ignoreOrphan
for syntheticRiskId := range parsedModel.GeneratedRisksBySyntheticId {
if matchingRiskIdExpression.Match([]byte(syntheticRiskId)) && parsedModel.HasNotYetAnyDirectNonWildcardRiskTracking(syntheticRiskId) {
foundSome = true
- parsedModel.RiskTracking[syntheticRiskId] = RiskTracking{
+ parsedModel.RiskTracking[syntheticRiskId] = &RiskTracking{
SyntheticRiskId: strings.TrimSpace(syntheticRiskId),
Justification: riskTracking.Justification,
CheckedBy: riskTracking.CheckedBy,
@@ -130,7 +130,7 @@ func (parsedModel *ParsedModel) ApplyWildcardRiskTrackingEvaluation(ignoreOrphan
return nil
}
-func (parsedModel *ParsedModel) CheckRiskTracking(ignoreOrphanedRiskTracking bool, progressReporter ProgressReporter) error {
+func (parsedModel *Model) CheckRiskTracking(ignoreOrphanedRiskTracking bool, progressReporter ProgressReporter) error {
progressReporter.Info("Checking risk tracking")
for _, tracking := range parsedModel.RiskTracking {
if _, ok := parsedModel.GeneratedRisksBySyntheticId[tracking.SyntheticRiskId]; !ok {
@@ -151,52 +151,45 @@ func (parsedModel *ParsedModel) CheckRiskTracking(ignoreOrphanedRiskTracking boo
}
}
- // save also the risk-category-id and risk-status directly in the risk for better JSON marshalling
- for category := range parsedModel.GeneratedRisksByCategory {
- for i := range parsedModel.GeneratedRisksByCategory[category] {
- // context.parsedModel.GeneratedRisksByCategory[category][i].CategoryId = category
- parsedModel.GeneratedRisksByCategory[category][i].RiskStatus = parsedModel.GeneratedRisksByCategory[category][i].GetRiskTrackingStatusDefaultingUnchecked(parsedModel)
- }
- }
return nil
}
-func (parsedModel *ParsedModel) CheckTagExists(referencedTag, where string) error {
+func (parsedModel *Model) CheckTagExists(referencedTag, where string) error {
if !slices.Contains(parsedModel.TagsAvailable, referencedTag) {
return fmt.Errorf("missing referenced tag in overall tag list at %v: %v", where, referencedTag)
}
return nil
}
-func (parsedModel *ParsedModel) CheckDataAssetTargetExists(referencedAsset, where string) error {
+func (parsedModel *Model) CheckDataAssetTargetExists(referencedAsset, where string) error {
if _, ok := parsedModel.DataAssets[referencedAsset]; !ok {
return fmt.Errorf("missing referenced data asset target at %v: %v", where, referencedAsset)
}
return nil
}
-func (parsedModel *ParsedModel) CheckTrustBoundaryExists(referencedId, where string) error {
+func (parsedModel *Model) CheckTrustBoundaryExists(referencedId, where string) error {
if _, ok := parsedModel.TrustBoundaries[referencedId]; !ok {
return fmt.Errorf("missing referenced trust boundary at %v: %v", where, referencedId)
}
return nil
}
-func (parsedModel *ParsedModel) CheckSharedRuntimeExists(referencedId, where string) error {
+func (parsedModel *Model) CheckSharedRuntimeExists(referencedId, where string) error {
if _, ok := parsedModel.SharedRuntimes[referencedId]; !ok {
return fmt.Errorf("missing referenced shared runtime at %v: %v", where, referencedId)
}
return nil
}
-func (parsedModel *ParsedModel) CheckCommunicationLinkExists(referencedId, where string) error {
+func (parsedModel *Model) CheckCommunicationLinkExists(referencedId, where string) error {
if _, ok := parsedModel.CommunicationLinks[referencedId]; !ok {
return fmt.Errorf("missing referenced communication link at %v: %v", where, referencedId)
}
return nil
}
-func (parsedModel *ParsedModel) CheckTechnicalAssetExists(referencedAsset, where string, onlyForTweak bool) error {
+func (parsedModel *Model) CheckTechnicalAssetExists(referencedAsset, where string, onlyForTweak bool) error {
if _, ok := parsedModel.TechnicalAssets[referencedAsset]; !ok {
suffix := ""
if onlyForTweak {
@@ -207,7 +200,7 @@ func (parsedModel *ParsedModel) CheckTechnicalAssetExists(referencedAsset, where
return nil
}
-func (parsedModel *ParsedModel) CheckNestedTrustBoundariesExisting() error {
+func (parsedModel *Model) CheckNestedTrustBoundariesExisting() error {
for _, trustBoundary := range parsedModel.TrustBoundaries {
for _, nestedId := range trustBoundary.TrustBoundariesNested {
if _, ok := parsedModel.TrustBoundaries[nestedId]; !ok {
@@ -235,7 +228,7 @@ func CalculateSeverity(likelihood RiskExploitationLikelihood, impact RiskExploit
return CriticalSeverity
}
-func (parsedModel *ParsedModel) InScopeTechnicalAssets() []*TechnicalAsset {
+func (parsedModel *Model) InScopeTechnicalAssets() []*TechnicalAsset {
result := make([]*TechnicalAsset, 0)
for _, asset := range parsedModel.TechnicalAssets {
if !asset.OutOfScope {
@@ -245,7 +238,7 @@ func (parsedModel *ParsedModel) InScopeTechnicalAssets() []*TechnicalAsset {
return result
}
-func (parsedModel *ParsedModel) SortedTechnicalAssetIDs() []string {
+func (parsedModel *Model) SortedTechnicalAssetIDs() []string {
res := make([]string, 0)
for id := range parsedModel.TechnicalAssets {
res = append(res, id)
@@ -254,7 +247,7 @@ func (parsedModel *ParsedModel) SortedTechnicalAssetIDs() []string {
return res
}
-func (parsedModel *ParsedModel) TagsActuallyUsed() []string {
+func (parsedModel *Model) TagsActuallyUsed() []string {
result := make([]string, 0)
for _, tag := range parsedModel.TagsAvailable {
if len(parsedModel.TechnicalAssetsTaggedWithAny(tag)) > 0 ||
@@ -268,7 +261,7 @@ func (parsedModel *ParsedModel) TagsActuallyUsed() []string {
return result
}
-func (parsedModel *ParsedModel) TechnicalAssetsTaggedWithAny(tags ...string) []*TechnicalAsset {
+func (parsedModel *Model) TechnicalAssetsTaggedWithAny(tags ...string) []*TechnicalAsset {
result := make([]*TechnicalAsset, 0)
for _, candidate := range parsedModel.TechnicalAssets {
if candidate.IsTaggedWithAny(tags...) {
@@ -278,7 +271,7 @@ func (parsedModel *ParsedModel) TechnicalAssetsTaggedWithAny(tags ...string) []*
return result
}
-func (parsedModel *ParsedModel) CommunicationLinksTaggedWithAny(tags ...string) []*CommunicationLink {
+func (parsedModel *Model) CommunicationLinksTaggedWithAny(tags ...string) []*CommunicationLink {
result := make([]*CommunicationLink, 0)
for _, asset := range parsedModel.TechnicalAssets {
for _, candidate := range asset.CommunicationLinks {
@@ -290,7 +283,7 @@ func (parsedModel *ParsedModel) CommunicationLinksTaggedWithAny(tags ...string)
return result
}
-func (parsedModel *ParsedModel) DataAssetsTaggedWithAny(tags ...string) []*DataAsset {
+func (parsedModel *Model) DataAssetsTaggedWithAny(tags ...string) []*DataAsset {
result := make([]*DataAsset, 0)
for _, candidate := range parsedModel.DataAssets {
if candidate.IsTaggedWithAny(tags...) {
@@ -300,7 +293,7 @@ func (parsedModel *ParsedModel) DataAssetsTaggedWithAny(tags ...string) []*DataA
return result
}
-func (parsedModel *ParsedModel) TrustBoundariesTaggedWithAny(tags ...string) []*TrustBoundary {
+func (parsedModel *Model) TrustBoundariesTaggedWithAny(tags ...string) []*TrustBoundary {
result := make([]*TrustBoundary, 0)
for _, candidate := range parsedModel.TrustBoundaries {
if candidate.IsTaggedWithAny(tags...) {
@@ -310,7 +303,7 @@ func (parsedModel *ParsedModel) TrustBoundariesTaggedWithAny(tags ...string) []*
return result
}
-func (parsedModel *ParsedModel) SharedRuntimesTaggedWithAny(tags ...string) []*SharedRuntime {
+func (parsedModel *Model) SharedRuntimesTaggedWithAny(tags ...string) []*SharedRuntime {
result := make([]*SharedRuntime, 0)
for _, candidate := range parsedModel.SharedRuntimes {
if candidate.IsTaggedWithAny(tags...) {
@@ -320,7 +313,7 @@ func (parsedModel *ParsedModel) SharedRuntimesTaggedWithAny(tags ...string) []*S
return result
}
-func (parsedModel *ParsedModel) OutOfScopeTechnicalAssets() []*TechnicalAsset {
+func (parsedModel *Model) OutOfScopeTechnicalAssets() []*TechnicalAsset {
assets := make([]*TechnicalAsset, 0)
for _, asset := range parsedModel.TechnicalAssets {
if asset.OutOfScope {
@@ -331,7 +324,7 @@ func (parsedModel *ParsedModel) OutOfScopeTechnicalAssets() []*TechnicalAsset {
return assets
}
-func (parsedModel *ParsedModel) RisksOfOnlySTRIDEInformationDisclosure(risksByCategory map[string][]Risk) map[string][]Risk {
+func (parsedModel *Model) RisksOfOnlySTRIDEInformationDisclosure(risksByCategory map[string][]Risk) map[string][]Risk {
result := make(map[string][]Risk)
for categoryId, categoryRisks := range risksByCategory {
for _, risk := range categoryRisks {
@@ -344,7 +337,7 @@ func (parsedModel *ParsedModel) RisksOfOnlySTRIDEInformationDisclosure(risksByCa
return result
}
-func (parsedModel *ParsedModel) RisksOfOnlySTRIDEDenialOfService(risksByCategory map[string][]Risk) map[string][]Risk {
+func (parsedModel *Model) RisksOfOnlySTRIDEDenialOfService(risksByCategory map[string][]Risk) map[string][]Risk {
result := make(map[string][]Risk)
for categoryId, categoryRisks := range risksByCategory {
for _, risk := range categoryRisks {
@@ -357,7 +350,7 @@ func (parsedModel *ParsedModel) RisksOfOnlySTRIDEDenialOfService(risksByCategory
return result
}
-func (parsedModel *ParsedModel) RisksOfOnlySTRIDEElevationOfPrivilege(risksByCategory map[string][]Risk) map[string][]Risk {
+func (parsedModel *Model) RisksOfOnlySTRIDEElevationOfPrivilege(risksByCategory map[string][]Risk) map[string][]Risk {
result := make(map[string][]Risk)
for categoryId, categoryRisks := range risksByCategory {
for _, risk := range categoryRisks {
@@ -370,7 +363,7 @@ func (parsedModel *ParsedModel) RisksOfOnlySTRIDEElevationOfPrivilege(risksByCat
return result
}
-func (parsedModel *ParsedModel) RisksOfOnlyBusinessSide(risksByCategory map[string][]Risk) map[string][]Risk {
+func (parsedModel *Model) RisksOfOnlyBusinessSide(risksByCategory map[string][]Risk) map[string][]Risk {
result := make(map[string][]Risk)
for categoryId, categoryRisks := range risksByCategory {
for _, risk := range categoryRisks {
@@ -383,7 +376,7 @@ func (parsedModel *ParsedModel) RisksOfOnlyBusinessSide(risksByCategory map[stri
return result
}
-func (parsedModel *ParsedModel) RisksOfOnlyArchitecture(risksByCategory map[string][]Risk) map[string][]Risk {
+func (parsedModel *Model) RisksOfOnlyArchitecture(risksByCategory map[string][]Risk) map[string][]Risk {
result := make(map[string][]Risk)
for categoryId, categoryRisks := range risksByCategory {
for _, risk := range categoryRisks {
@@ -396,7 +389,7 @@ func (parsedModel *ParsedModel) RisksOfOnlyArchitecture(risksByCategory map[stri
return result
}
-func (parsedModel *ParsedModel) RisksOfOnlyDevelopment(risksByCategory map[string][]Risk) map[string][]Risk {
+func (parsedModel *Model) RisksOfOnlyDevelopment(risksByCategory map[string][]Risk) map[string][]Risk {
result := make(map[string][]Risk)
for categoryId, categoryRisks := range risksByCategory {
for _, risk := range categoryRisks {
@@ -409,7 +402,7 @@ func (parsedModel *ParsedModel) RisksOfOnlyDevelopment(risksByCategory map[strin
return result
}
-func (parsedModel *ParsedModel) RisksOfOnlyOperation(risksByCategory map[string][]Risk) map[string][]Risk {
+func (parsedModel *Model) RisksOfOnlyOperation(risksByCategory map[string][]Risk) map[string][]Risk {
result := make(map[string][]Risk)
for categoryId, categoryRisks := range risksByCategory {
for _, risk := range categoryRisks {
diff --git a/pkg/security/types/protocol.go b/pkg/security/types/protocol.go
index d538307f..af3a3743 100644
--- a/pkg/security/types/protocol.go
+++ b/pkg/security/types/protocol.go
@@ -5,7 +5,9 @@ Copyright © 2023 NAME HERE
package types
import (
+ "encoding/json"
"fmt"
+ "gopkg.in/yaml.v3"
"strings"
)
@@ -206,3 +208,47 @@ func (what Protocol) IsPotentialDatabaseAccessProtocol(includingLaxDatabaseProto
func (what Protocol) IsPotentialWebAccessProtocol() bool {
return what == HTTP || what == HTTPS || what == WS || what == WSS || what == ReverseProxyWebProtocol || what == ReverseProxyWebProtocolEncrypted
}
+
+func (what Protocol) MarshalJSON() ([]byte, error) {
+ return json.Marshal(what.String())
+}
+
+func (what *Protocol) UnmarshalJSON(data []byte) error {
+ var text string
+ unmarshalError := json.Unmarshal(data, &text)
+ if unmarshalError != nil {
+ return unmarshalError
+ }
+
+ value, findError := what.find(text)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
+
+func (what Protocol) MarshalYAML() (interface{}, error) {
+ return what.String(), nil
+}
+
+func (what *Protocol) UnmarshalYAML(node *yaml.Node) error {
+ value, findError := what.find(node.Value)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
+
+func (what Protocol) find(value string) (Protocol, error) {
+ for index, description := range ProtocolTypeDescription {
+ if strings.EqualFold(value, description.Name) {
+ return Protocol(index), nil
+ }
+ }
+
+ return Protocol(0), fmt.Errorf("unknown protocol value %q", value)
+}
diff --git a/pkg/security/types/quantity.go b/pkg/security/types/quantity.go
index b3c1a432..eb1df364 100644
--- a/pkg/security/types/quantity.go
+++ b/pkg/security/types/quantity.go
@@ -5,7 +5,9 @@ Copyright © 2023 NAME HERE
package types
import (
+ "encoding/json"
"fmt"
+ "gopkg.in/yaml.v3"
"strings"
)
@@ -65,3 +67,37 @@ func (what Quantity) Find(value string) (Quantity, error) {
return Quantity(0), fmt.Errorf("unknown quantity value %q", value)
}
+
+func (what Quantity) MarshalJSON() ([]byte, error) {
+ return json.Marshal(what.String())
+}
+
+func (what *Quantity) UnmarshalJSON(data []byte) error {
+ var text string
+ unmarshalError := json.Unmarshal(data, &text)
+ if unmarshalError != nil {
+ return unmarshalError
+ }
+
+ value, findError := what.Find(text)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
+
+func (what Quantity) MarshalYAML() (interface{}, error) {
+ return what.String(), nil
+}
+
+func (what *Quantity) UnmarshalYAML(node *yaml.Node) error {
+ value, findError := what.Find(node.Value)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
diff --git a/pkg/security/types/risk-category.go b/pkg/security/types/risk-category.go
index 58a17b2c..562a6190 100644
--- a/pkg/security/types/risk-category.go
+++ b/pkg/security/types/risk-category.go
@@ -1,21 +1,40 @@
package types
+import "strings"
+
type RiskCategory struct {
- // TODO: refactor all "Id" here and elsewhere to "ID"
- Id string `json:"id,omitempty" yaml:"id,omitempty"`
- Title string `json:"title,omitempty" yaml:"title,omitempty"`
- Description string `json:"description,omitempty" yaml:"description,omitempty"`
- Impact string `json:"impact,omitempty" yaml:"impact,omitempty"`
- ASVS string `json:"asvs,omitempty" yaml:"asvs,omitempty"`
- CheatSheet string `json:"cheat_sheet,omitempty" yaml:"cheat_sheet,omitempty"`
- Action string `json:"action,omitempty" yaml:"action,omitempty"`
- Mitigation string `json:"mitigation,omitempty" yaml:"mitigation,omitempty"`
- Check string `json:"check,omitempty" yaml:"check,omitempty"`
- DetectionLogic string `json:"detection_logic,omitempty" yaml:"detection_logic,omitempty"`
- RiskAssessment string `json:"risk_assessment,omitempty" yaml:"risk_assessment,omitempty"`
- FalsePositives string `json:"false_positives,omitempty" yaml:"false_positives,omitempty"`
- Function RiskFunction `json:"function,omitempty" yaml:"function,omitempty"`
- STRIDE STRIDE `json:"stride,omitempty" yaml:"stride,omitempty"`
- ModelFailurePossibleReason bool `json:"model_failure_possible_reason,omitempty" yaml:"model_failure_possible_reason,omitempty"`
- CWE int `json:"cwe,omitempty" yaml:"cwe,omitempty"`
+ ID string `json:"id,omitempty" yaml:"id,omitempty"`
+ Title string `json:"title,omitempty" yaml:"title,omitempty"`
+ Description string `json:"description,omitempty" yaml:"description,omitempty"`
+ Impact string `json:"impact,omitempty" yaml:"impact,omitempty"`
+ ASVS string `json:"asvs,omitempty" yaml:"asvs,omitempty"`
+ CheatSheet string `json:"cheat_sheet,omitempty" yaml:"cheat_sheet,omitempty"`
+ Action string `json:"action,omitempty" yaml:"action,omitempty"`
+ Mitigation string `json:"mitigation,omitempty" yaml:"mitigation,omitempty"`
+ Check string `json:"check,omitempty" yaml:"check,omitempty"`
+ Function RiskFunction `json:"function,omitempty" yaml:"function,omitempty"`
+ STRIDE STRIDE `json:"stride,omitempty" yaml:"stride,omitempty"`
+ DetectionLogic string `json:"detection_logic,omitempty" yaml:"detection_logic,omitempty"`
+ RiskAssessment string `json:"risk_assessment,omitempty" yaml:"risk_assessment,omitempty"`
+ FalsePositives string `json:"false_positives,omitempty" yaml:"false_positives,omitempty"`
+ ModelFailurePossibleReason bool `json:"model_failure_possible_reason,omitempty" yaml:"model_failure_possible_reason,omitempty"`
+ CWE int `json:"cwe,omitempty" yaml:"cwe,omitempty"`
+ IsBuiltIn bool `json:"is_built_in,omitempty" yaml:"is_built_in,omitempty"`
+ Script map[string]any `json:"script,omitempty" yaml:"script,omitempty"`
+}
+
+type RiskCategories []*RiskCategory
+
+func (what *RiskCategories) Add(categories ...*RiskCategory) bool {
+ for _, newCategory := range categories {
+ for _, existingCategory := range *what {
+ if strings.EqualFold(existingCategory.ID, newCategory.ID) {
+ return false
+ }
+ }
+
+ *what = append(*what, newCategory)
+ }
+
+ return true
}
diff --git a/pkg/security/types/risk.go b/pkg/security/types/risk.go
index c98863ed..26908be1 100644
--- a/pkg/security/types/risk.go
+++ b/pkg/security/types/risk.go
@@ -15,25 +15,24 @@ type Risk struct {
MostRelevantCommunicationLinkId string `yaml:"most_relevant_communication_link,omitempty" json:"most_relevant_communication_link,omitempty"`
DataBreachProbability DataBreachProbability `yaml:"data_breach_probability,omitempty" json:"data_breach_probability,omitempty"`
DataBreachTechnicalAssetIDs []string `yaml:"data_breach_technical_assets,omitempty" json:"data_breach_technical_assets,omitempty"`
- // TODO: refactor all "Id" here to "ID"?
+ // TODO: refactor all "ID" here to "ID"?
}
-func (what Risk) GetRiskTracking(model *ParsedModel) RiskTracking { // TODO: Unify function naming regarding Get etc.
- var result RiskTracking
+func (what Risk) GetRiskTracking(model *Model) *RiskTracking { // TODO: Unify function naming regarding Get etc.
if riskTracking, ok := model.RiskTracking[what.SyntheticId]; ok {
- result = riskTracking
+ return riskTracking
}
- return result
+ return nil
}
-func (what Risk) GetRiskTrackingStatusDefaultingUnchecked(model *ParsedModel) RiskStatus {
+func (what Risk) GetRiskTrackingWithDefault(model *Model) RiskTracking { // TODO: Unify function naming regarding Get etc.
if riskTracking, ok := model.RiskTracking[what.SyntheticId]; ok {
- return riskTracking.Status
+ return *riskTracking
}
- return Unchecked
+ return RiskTracking{}
}
-func (what Risk) IsRiskTracked(model *ParsedModel) bool {
+func (what Risk) IsRiskTracked(model *Model) bool {
if _, ok := model.RiskTracking[what.SyntheticId]; ok {
return true
}
diff --git a/pkg/security/types/risk_exploitation_impact.go b/pkg/security/types/risk_exploitation_impact.go
index 8a7c016d..b27e6240 100644
--- a/pkg/security/types/risk_exploitation_impact.go
+++ b/pkg/security/types/risk_exploitation_impact.go
@@ -5,7 +5,9 @@ Copyright © 2023 NAME HERE
package types
import (
+ "encoding/json"
"fmt"
+ "gopkg.in/yaml.v3"
"strings"
)
@@ -68,3 +70,37 @@ func (what RiskExploitationImpact) Find(value string) (RiskExploitationImpact, e
return RiskExploitationImpact(0), fmt.Errorf("unknown risk exploitation impact value %q", value)
}
+
+func (what RiskExploitationImpact) MarshalJSON() ([]byte, error) {
+ return json.Marshal(what.String())
+}
+
+func (what *RiskExploitationImpact) UnmarshalJSON(data []byte) error {
+ var text string
+ unmarshalError := json.Unmarshal(data, &text)
+ if unmarshalError != nil {
+ return unmarshalError
+ }
+
+ value, findError := what.Find(text)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
+
+func (what RiskExploitationImpact) MarshalYAML() (interface{}, error) {
+ return what.String(), nil
+}
+
+func (what *RiskExploitationImpact) UnmarshalYAML(node *yaml.Node) error {
+ value, findError := what.Find(node.Value)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
diff --git a/pkg/security/types/risk_exploitation_likelihood.go b/pkg/security/types/risk_exploitation_likelihood.go
index 93a5e377..86966d64 100644
--- a/pkg/security/types/risk_exploitation_likelihood.go
+++ b/pkg/security/types/risk_exploitation_likelihood.go
@@ -5,7 +5,9 @@ Copyright © 2023 NAME HERE
package types
import (
+ "encoding/json"
"fmt"
+ "gopkg.in/yaml.v3"
"strings"
)
@@ -68,3 +70,37 @@ func (what RiskExploitationLikelihood) Find(value string) (RiskExploitationLikel
return RiskExploitationLikelihood(0), fmt.Errorf("unknown risk exploration likelihood value %q", value)
}
+
+func (what RiskExploitationLikelihood) MarshalJSON() ([]byte, error) {
+ return json.Marshal(what.String())
+}
+
+func (what *RiskExploitationLikelihood) UnmarshalJSON(data []byte) error {
+ var text string
+ unmarshalError := json.Unmarshal(data, &text)
+ if unmarshalError != nil {
+ return unmarshalError
+ }
+
+ value, findError := what.Find(text)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
+
+func (what RiskExploitationLikelihood) MarshalYAML() (interface{}, error) {
+ return what.String(), nil
+}
+
+func (what *RiskExploitationLikelihood) UnmarshalYAML(node *yaml.Node) error {
+ value, findError := what.Find(node.Value)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
diff --git a/pkg/security/types/risk_function.go b/pkg/security/types/risk_function.go
index 78f6288a..e48e484d 100644
--- a/pkg/security/types/risk_function.go
+++ b/pkg/security/types/risk_function.go
@@ -5,7 +5,9 @@ Copyright © 2023 NAME HERE
package types
import (
+ "encoding/json"
"fmt"
+ "gopkg.in/yaml.v3"
"strings"
)
@@ -56,3 +58,47 @@ func (what RiskFunction) Explain() string {
func (what RiskFunction) Title() string {
return [...]string{"Business Side", "Architecture", "Development", "Operations"}[what]
}
+
+func (what RiskFunction) MarshalJSON() ([]byte, error) {
+ return json.Marshal(what.String())
+}
+
+func (what *RiskFunction) UnmarshalJSON(data []byte) error {
+ var text string
+ unmarshalError := json.Unmarshal(data, &text)
+ if unmarshalError != nil {
+ return unmarshalError
+ }
+
+ value, findError := what.find(text)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
+
+func (what RiskFunction) MarshalYAML() (interface{}, error) {
+ return what.String(), nil
+}
+
+func (what *RiskFunction) UnmarshalYAML(node *yaml.Node) error {
+ value, findError := what.find(node.Value)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
+
+func (what RiskFunction) find(value string) (RiskFunction, error) {
+ for index, description := range RiskFunctionTypeDescription {
+ if strings.EqualFold(value, description.Name) {
+ return RiskFunction(index), nil
+ }
+ }
+
+ return RiskFunction(0), fmt.Errorf("unknown risk function value %q", value)
+}
diff --git a/pkg/security/types/risk_severity.go b/pkg/security/types/risk_severity.go
index d87cce9e..488a4b03 100644
--- a/pkg/security/types/risk_severity.go
+++ b/pkg/security/types/risk_severity.go
@@ -5,7 +5,9 @@ Copyright © 2023 NAME HERE
package types
import (
+ "encoding/json"
"fmt"
+ "gopkg.in/yaml.v3"
"strings"
)
@@ -67,3 +69,37 @@ func (what RiskSeverity) Find(value string) (RiskSeverity, error) {
return RiskSeverity(0), fmt.Errorf("unknown risk severity value %q", value)
}
+
+func (what RiskSeverity) MarshalJSON() ([]byte, error) {
+ return json.Marshal(what.String())
+}
+
+func (what *RiskSeverity) UnmarshalJSON(data []byte) error {
+ var text string
+ unmarshalError := json.Unmarshal(data, &text)
+ if unmarshalError != nil {
+ return unmarshalError
+ }
+
+ value, findError := what.Find(text)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
+
+func (what RiskSeverity) MarshalYAML() (interface{}, error) {
+ return what.String(), nil
+}
+
+func (what *RiskSeverity) UnmarshalYAML(node *yaml.Node) error {
+ value, findError := what.Find(node.Value)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
diff --git a/pkg/security/types/risk_status.go b/pkg/security/types/risk_status.go
index 022f9e45..bdddd221 100644
--- a/pkg/security/types/risk_status.go
+++ b/pkg/security/types/risk_status.go
@@ -5,7 +5,9 @@ Copyright © 2023 NAME HERE
package types
import (
+ "encoding/json"
"fmt"
+ "gopkg.in/yaml.v3"
"strings"
)
@@ -66,3 +68,47 @@ func (what RiskStatus) Title() string {
func (what RiskStatus) IsStillAtRisk() bool {
return what == Unchecked || what == InDiscussion || what == Accepted || what == InProgress
}
+
+func (what RiskStatus) MarshalJSON() ([]byte, error) {
+ return json.Marshal(what.String())
+}
+
+func (what *RiskStatus) UnmarshalJSON(data []byte) error {
+ var text string
+ unmarshalError := json.Unmarshal(data, &text)
+ if unmarshalError != nil {
+ return unmarshalError
+ }
+
+ value, findError := what.find(text)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
+
+func (what RiskStatus) MarshalYAML() (interface{}, error) {
+ return what.String(), nil
+}
+
+func (what *RiskStatus) UnmarshalYAML(node *yaml.Node) error {
+ value, findError := what.find(node.Value)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
+
+func (what RiskStatus) find(value string) (RiskStatus, error) {
+ for index, description := range RiskStatusTypeDescription {
+ if strings.EqualFold(value, description.Name) {
+ return RiskStatus(index), nil
+ }
+ }
+
+ return RiskStatus(0), fmt.Errorf("unknown risk status value %q", value)
+}
diff --git a/pkg/security/types/risks.go b/pkg/security/types/risks.go
index e9018f04..39d1b5ca 100644
--- a/pkg/security/types/risks.go
+++ b/pkg/security/types/risks.go
@@ -6,36 +6,39 @@ package types
import (
"sort"
+ "strings"
)
-func GetRiskCategory(parsedModel *ParsedModel, categoryID string) *RiskCategory {
- if len(parsedModel.IndividualRiskCategories) > 0 {
- custom, customOk := parsedModel.IndividualRiskCategories[categoryID]
- if customOk {
- return &custom
+func GetRiskCategory(parsedModel *Model, categoryID string) *RiskCategory {
+ if len(parsedModel.CustomRiskCategories) > 0 {
+ for _, custom := range parsedModel.CustomRiskCategories {
+ if strings.EqualFold(custom.ID, categoryID) {
+ return custom
+ }
}
}
if len(parsedModel.BuiltInRiskCategories) > 0 {
- builtIn, builtInOk := parsedModel.BuiltInRiskCategories[categoryID]
- if builtInOk {
- return &builtIn
+ for _, builtIn := range parsedModel.BuiltInRiskCategories {
+ if strings.EqualFold(builtIn.ID, categoryID) {
+ return builtIn
+ }
}
}
return nil
}
-func GetRiskCategories(parsedModel *ParsedModel, categoryIDs []string) []RiskCategory {
- categoryMap := make(map[string]RiskCategory)
+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
+ categoryMap[categoryId] = category
}
}
- categories := make([]RiskCategory, 0)
+ categories := make([]*RiskCategory, 0)
for categoryId := range categoryMap {
categories = append(categories, categoryMap[categoryId])
}
@@ -43,25 +46,25 @@ func GetRiskCategories(parsedModel *ParsedModel, categoryIDs []string) []RiskCat
return categories
}
-func AllRisks(parsedModel *ParsedModel) []Risk {
- result := make([]Risk, 0)
+func AllRisks(parsedModel *Model) []*Risk {
+ result := make([]*Risk, 0)
for _, risks := range parsedModel.GeneratedRisksByCategory {
result = append(result, risks...)
}
return result
}
-func ReduceToOnlyStillAtRisk(parsedModel *ParsedModel, risks []Risk) []Risk {
- filteredRisks := make([]Risk, 0)
+func ReduceToOnlyStillAtRisk(parsedModel *Model, risks []*Risk) []*Risk {
+ filteredRisks := make([]*Risk, 0)
for _, risk := range risks {
- if risk.GetRiskTrackingStatusDefaultingUnchecked(parsedModel).IsStillAtRisk() {
+ if risk.RiskStatus.IsStillAtRisk() {
filteredRisks = append(filteredRisks, risk)
}
}
return filteredRisks
}
-func HighestExploitationLikelihood(risks []Risk) RiskExploitationLikelihood {
+func HighestExploitationLikelihood(risks []*Risk) RiskExploitationLikelihood {
result := Unlikely
for _, risk := range risks {
if risk.ExploitationLikelihood > result {
@@ -71,7 +74,7 @@ func HighestExploitationLikelihood(risks []Risk) RiskExploitationLikelihood {
return result
}
-func HighestExploitationImpact(risks []Risk) RiskExploitationImpact {
+func HighestExploitationImpact(risks []*Risk) RiskExploitationImpact {
result := LowImpact
for _, risk := range risks {
if risk.ExploitationImpact > result {
@@ -81,17 +84,17 @@ func HighestExploitationImpact(risks []Risk) RiskExploitationImpact {
return result
}
-func HighestSeverityStillAtRisk(model *ParsedModel, risks []Risk) RiskSeverity {
+func HighestSeverityStillAtRisk(model *Model, risks []*Risk) RiskSeverity {
result := LowSeverity
for _, risk := range risks {
- if risk.Severity > result && risk.GetRiskTrackingStatusDefaultingUnchecked(model).IsStillAtRisk() {
+ if risk.Severity > result && risk.RiskStatus.IsStillAtRisk() {
result = risk.Severity
}
}
return result
}
-type ByRiskCategoryTitleSort []RiskCategory
+type ByRiskCategoryTitleSort []*RiskCategory
func (what ByRiskCategoryTitleSort) Len() int { return len(what) }
func (what ByRiskCategoryTitleSort) Swap(i, j int) {
@@ -101,10 +104,10 @@ func (what ByRiskCategoryTitleSort) Less(i, j int) bool {
return what[i].Title < what[j].Title
}
-func SortByRiskCategoryHighestContainingRiskSeveritySortStillAtRisk(parsedModel *ParsedModel, riskCategories []RiskCategory) {
+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, parsedModel.GeneratedRisksByCategory[riskCategories[i].ID])
+ risksRight := ReduceToOnlyStillAtRisk(parsedModel, parsedModel.GeneratedRisksByCategory[riskCategories[j].ID])
highestLeft := HighestSeverityStillAtRisk(parsedModel, risksLeft)
highestRight := HighestSeverityStillAtRisk(parsedModel, risksRight)
if highestLeft == highestRight {
@@ -125,11 +128,11 @@ type RiskStatistics struct {
Risks map[string]map[string]int `yaml:"risks" json:"risks"`
}
-func SortByRiskSeverity(risks []Risk, parsedModel *ParsedModel) {
+func SortByRiskSeverity(risks []*Risk, parsedModel *Model) {
sort.Slice(risks, func(i, j int) bool {
if risks[i].Severity == risks[j].Severity {
- trackingStatusLeft := risks[i].GetRiskTrackingStatusDefaultingUnchecked(parsedModel)
- trackingStatusRight := risks[j].GetRiskTrackingStatusDefaultingUnchecked(parsedModel)
+ trackingStatusLeft := risks[i].RiskStatus
+ trackingStatusRight := risks[j].RiskStatus
if trackingStatusLeft == trackingStatusRight {
impactLeft := risks[i].ExploitationImpact
impactRight := risks[j].ExploitationImpact
@@ -153,12 +156,12 @@ func SortByRiskSeverity(risks []Risk, parsedModel *ParsedModel) {
})
}
-func SortByDataBreachProbability(risks []Risk, parsedModel *ParsedModel) {
+func SortByDataBreachProbability(risks []*Risk, parsedModel *Model) {
sort.Slice(risks, func(i, j int) bool {
if risks[i].DataBreachProbability == risks[j].DataBreachProbability {
- trackingStatusLeft := risks[i].GetRiskTrackingStatusDefaultingUnchecked(parsedModel)
- trackingStatusRight := risks[j].GetRiskTrackingStatusDefaultingUnchecked(parsedModel)
+ trackingStatusLeft := risks[i].RiskStatus
+ trackingStatusRight := risks[j].RiskStatus
if trackingStatusLeft == trackingStatusRight {
return risks[i].Title < risks[j].Title
} else {
@@ -171,16 +174,16 @@ func SortByDataBreachProbability(risks []Risk, parsedModel *ParsedModel) {
// as in Go ranging over map is random order, range over them in sorted (hence reproducible) way:
-func SortedRiskCategories(parsedModel *ParsedModel) []RiskCategory {
- categoryMap := make(map[string]RiskCategory)
+func SortedRiskCategories(parsedModel *Model) []*RiskCategory {
+ categoryMap := make(map[string]*RiskCategory)
for categoryId := range parsedModel.GeneratedRisksByCategory {
category := GetRiskCategory(parsedModel, categoryId)
if category != nil {
- categoryMap[categoryId] = *category
+ categoryMap[categoryId] = category
}
}
- categories := make([]RiskCategory, 0)
+ categories := make([]*RiskCategory, 0)
for categoryId := range categoryMap {
categories = append(categories, categoryMap[categoryId])
}
@@ -189,13 +192,13 @@ func SortedRiskCategories(parsedModel *ParsedModel) []RiskCategory {
return categories
}
-func SortedRisksOfCategory(parsedModel *ParsedModel, category RiskCategory) []Risk {
- risks := parsedModel.GeneratedRisksByCategory[category.Id]
+func SortedRisksOfCategory(parsedModel *Model, category *RiskCategory) []*Risk {
+ risks := parsedModel.GeneratedRisksByCategory[category.ID]
SortByRiskSeverity(risks, parsedModel)
return risks
}
-func CountRisks(risksByCategory map[string][]Risk) int {
+func CountRisks(risksByCategory map[string][]*Risk) int {
result := 0
for _, risks := range risksByCategory {
result += len(risks)
@@ -203,8 +206,8 @@ func CountRisks(risksByCategory map[string][]Risk) int {
return result
}
-func RisksOfOnlySTRIDESpoofing(parsedModel *ParsedModel, risksByCategory map[string][]Risk) map[string][]Risk {
- result := make(map[string][]Risk)
+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)
@@ -218,8 +221,8 @@ func RisksOfOnlySTRIDESpoofing(parsedModel *ParsedModel, risksByCategory map[str
return result
}
-func RisksOfOnlySTRIDETampering(parsedModel *ParsedModel, risksByCategory map[string][]Risk) map[string][]Risk {
- result := make(map[string][]Risk)
+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)
@@ -233,8 +236,8 @@ func RisksOfOnlySTRIDETampering(parsedModel *ParsedModel, risksByCategory map[st
return result
}
-func RisksOfOnlySTRIDERepudiation(parsedModel *ParsedModel, risksByCategory map[string][]Risk) map[string][]Risk {
- result := make(map[string][]Risk)
+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)
@@ -246,8 +249,8 @@ func RisksOfOnlySTRIDERepudiation(parsedModel *ParsedModel, risksByCategory map[
return result
}
-func RisksOfOnlySTRIDEInformationDisclosure(parsedModel *ParsedModel, risksByCategory map[string][]Risk) map[string][]Risk {
- result := make(map[string][]Risk)
+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)
@@ -259,8 +262,8 @@ func RisksOfOnlySTRIDEInformationDisclosure(parsedModel *ParsedModel, risksByCat
return result
}
-func RisksOfOnlySTRIDEDenialOfService(parsedModel *ParsedModel, risksByCategory map[string][]Risk) map[string][]Risk {
- result := make(map[string][]Risk)
+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)
@@ -272,8 +275,8 @@ func RisksOfOnlySTRIDEDenialOfService(parsedModel *ParsedModel, risksByCategory
return result
}
-func RisksOfOnlySTRIDEElevationOfPrivilege(parsedModel *ParsedModel, risksByCategory map[string][]Risk) map[string][]Risk {
- result := make(map[string][]Risk)
+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)
@@ -285,8 +288,8 @@ func RisksOfOnlySTRIDEElevationOfPrivilege(parsedModel *ParsedModel, risksByCate
return result
}
-func RisksOfOnlyBusinessSide(parsedModel *ParsedModel, risksByCategory map[string][]Risk) map[string][]Risk {
- result := make(map[string][]Risk)
+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)
@@ -298,8 +301,8 @@ func RisksOfOnlyBusinessSide(parsedModel *ParsedModel, risksByCategory map[strin
return result
}
-func RisksOfOnlyArchitecture(parsedModel *ParsedModel, risksByCategory map[string][]Risk) map[string][]Risk {
- result := make(map[string][]Risk)
+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)
@@ -311,8 +314,8 @@ func RisksOfOnlyArchitecture(parsedModel *ParsedModel, risksByCategory map[strin
return result
}
-func RisksOfOnlyDevelopment(parsedModel *ParsedModel, risksByCategory map[string][]Risk) map[string][]Risk {
- result := make(map[string][]Risk)
+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)
@@ -324,8 +327,8 @@ func RisksOfOnlyDevelopment(parsedModel *ParsedModel, risksByCategory map[string
return result
}
-func RisksOfOnlyOperation(parsedModel *ParsedModel, risksByCategory map[string][]Risk) map[string][]Risk {
- result := make(map[string][]Risk)
+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)
@@ -337,11 +340,11 @@ func RisksOfOnlyOperation(parsedModel *ParsedModel, risksByCategory map[string][
return result
}
-func CategoriesOfOnlyRisksStillAtRisk(parsedModel *ParsedModel, risksByCategory map[string][]Risk) []string {
+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.GetRiskTrackingStatusDefaultingUnchecked(parsedModel).IsStillAtRisk() {
+ if !risk.RiskStatus.IsStillAtRisk() {
continue
}
categories[categoryId] = struct{}{}
@@ -351,11 +354,11 @@ func CategoriesOfOnlyRisksStillAtRisk(parsedModel *ParsedModel, risksByCategory
return keysAsSlice(categories)
}
-func CategoriesOfOnlyCriticalRisks(parsedModel *ParsedModel, risksByCategory map[string][]Risk, initialRisks bool) []string {
+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.GetRiskTrackingStatusDefaultingUnchecked(parsedModel).IsStillAtRisk() {
+ if !initialRisks && !risk.RiskStatus.IsStillAtRisk() {
continue
}
if risk.Severity == CriticalSeverity {
@@ -367,11 +370,11 @@ func CategoriesOfOnlyCriticalRisks(parsedModel *ParsedModel, risksByCategory map
return keysAsSlice(categories)
}
-func CategoriesOfOnlyHighRisks(parsedModel *ParsedModel, risksByCategory map[string][]Risk, initialRisks bool) []string {
+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.GetRiskTrackingStatusDefaultingUnchecked(parsedModel).IsStillAtRisk() {
+ if !initialRisks && !risk.RiskStatus.IsStillAtRisk() {
continue
}
highest := HighestSeverity(parsedModel.GeneratedRisksByCategory[categoryId])
@@ -387,11 +390,11 @@ func CategoriesOfOnlyHighRisks(parsedModel *ParsedModel, risksByCategory map[str
return keysAsSlice(categories)
}
-func CategoriesOfOnlyElevatedRisks(parsedModel *ParsedModel, risksByCategory map[string][]Risk, initialRisks bool) []string {
+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.GetRiskTrackingStatusDefaultingUnchecked(parsedModel).IsStillAtRisk() {
+ if !initialRisks && !risk.RiskStatus.IsStillAtRisk() {
continue
}
highest := HighestSeverity(parsedModel.GeneratedRisksByCategory[categoryId])
@@ -407,11 +410,11 @@ func CategoriesOfOnlyElevatedRisks(parsedModel *ParsedModel, risksByCategory map
return keysAsSlice(categories)
}
-func CategoriesOfOnlyMediumRisks(parsedModel *ParsedModel, risksByCategory map[string][]Risk, initialRisks bool) []string {
+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.GetRiskTrackingStatusDefaultingUnchecked(parsedModel).IsStillAtRisk() {
+ if !initialRisks && !risk.RiskStatus.IsStillAtRisk() {
continue
}
highest := HighestSeverity(parsedModel.GeneratedRisksByCategory[categoryId])
@@ -427,11 +430,11 @@ func CategoriesOfOnlyMediumRisks(parsedModel *ParsedModel, risksByCategory map[s
return keysAsSlice(categories)
}
-func CategoriesOfOnlyLowRisks(parsedModel *ParsedModel, risksByCategory map[string][]Risk, initialRisks bool) []string {
+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.GetRiskTrackingStatusDefaultingUnchecked(parsedModel).IsStillAtRisk() {
+ if !initialRisks && !risk.RiskStatus.IsStillAtRisk() {
continue
}
highest := HighestSeverity(parsedModel.GeneratedRisksByCategory[categoryId])
@@ -447,7 +450,7 @@ func CategoriesOfOnlyLowRisks(parsedModel *ParsedModel, risksByCategory map[stri
return keysAsSlice(categories)
}
-func HighestSeverity(risks []Risk) RiskSeverity {
+func HighestSeverity(risks []*Risk) RiskSeverity {
result := LowSeverity
for _, risk := range risks {
if risk.Severity > result {
@@ -465,8 +468,8 @@ func keysAsSlice(categories map[string]struct{}) []string {
return result
}
-func FilteredByOnlyBusinessSide(parsedModel *ParsedModel) []Risk {
- filteredRisks := make([]Risk, 0)
+func FilteredByOnlyBusinessSide(parsedModel *Model) []*Risk {
+ filteredRisks := make([]*Risk, 0)
for categoryId, risks := range parsedModel.GeneratedRisksByCategory {
for _, risk := range risks {
category := GetRiskCategory(parsedModel, categoryId)
@@ -478,8 +481,8 @@ func FilteredByOnlyBusinessSide(parsedModel *ParsedModel) []Risk {
return filteredRisks
}
-func FilteredByOnlyArchitecture(parsedModel *ParsedModel) []Risk {
- filteredRisks := make([]Risk, 0)
+func FilteredByOnlyArchitecture(parsedModel *Model) []*Risk {
+ filteredRisks := make([]*Risk, 0)
for categoryId, risks := range parsedModel.GeneratedRisksByCategory {
for _, risk := range risks {
category := GetRiskCategory(parsedModel, categoryId)
@@ -491,8 +494,8 @@ func FilteredByOnlyArchitecture(parsedModel *ParsedModel) []Risk {
return filteredRisks
}
-func FilteredByOnlyDevelopment(parsedModel *ParsedModel) []Risk {
- filteredRisks := make([]Risk, 0)
+func FilteredByOnlyDevelopment(parsedModel *Model) []*Risk {
+ filteredRisks := make([]*Risk, 0)
for categoryId, risks := range parsedModel.GeneratedRisksByCategory {
for _, risk := range risks {
category := GetRiskCategory(parsedModel, categoryId)
@@ -504,8 +507,8 @@ func FilteredByOnlyDevelopment(parsedModel *ParsedModel) []Risk {
return filteredRisks
}
-func FilteredByOnlyOperation(parsedModel *ParsedModel) []Risk {
- filteredRisks := make([]Risk, 0)
+func FilteredByOnlyOperation(parsedModel *Model) []*Risk {
+ filteredRisks := make([]*Risk, 0)
for categoryId, risks := range parsedModel.GeneratedRisksByCategory {
for _, risk := range risks {
category := GetRiskCategory(parsedModel, categoryId)
@@ -517,8 +520,8 @@ func FilteredByOnlyOperation(parsedModel *ParsedModel) []Risk {
return filteredRisks
}
-func FilteredByOnlyCriticalRisks(parsedModel *ParsedModel) []Risk {
- filteredRisks := make([]Risk, 0)
+func FilteredByOnlyCriticalRisks(parsedModel *Model) []*Risk {
+ filteredRisks := make([]*Risk, 0)
for _, risks := range parsedModel.GeneratedRisksByCategory {
for _, risk := range risks {
if risk.Severity == CriticalSeverity {
@@ -529,8 +532,8 @@ func FilteredByOnlyCriticalRisks(parsedModel *ParsedModel) []Risk {
return filteredRisks
}
-func FilteredByOnlyHighRisks(parsedModel *ParsedModel) []Risk {
- filteredRisks := make([]Risk, 0)
+func FilteredByOnlyHighRisks(parsedModel *Model) []*Risk {
+ filteredRisks := make([]*Risk, 0)
for _, risks := range parsedModel.GeneratedRisksByCategory {
for _, risk := range risks {
if risk.Severity == HighSeverity {
@@ -541,8 +544,8 @@ func FilteredByOnlyHighRisks(parsedModel *ParsedModel) []Risk {
return filteredRisks
}
-func FilteredByOnlyElevatedRisks(parsedModel *ParsedModel) []Risk {
- filteredRisks := make([]Risk, 0)
+func FilteredByOnlyElevatedRisks(parsedModel *Model) []*Risk {
+ filteredRisks := make([]*Risk, 0)
for _, risks := range parsedModel.GeneratedRisksByCategory {
for _, risk := range risks {
if risk.Severity == ElevatedSeverity {
@@ -553,8 +556,8 @@ func FilteredByOnlyElevatedRisks(parsedModel *ParsedModel) []Risk {
return filteredRisks
}
-func FilteredByOnlyMediumRisks(parsedModel *ParsedModel) []Risk {
- filteredRisks := make([]Risk, 0)
+func FilteredByOnlyMediumRisks(parsedModel *Model) []*Risk {
+ filteredRisks := make([]*Risk, 0)
for _, risks := range parsedModel.GeneratedRisksByCategory {
for _, risk := range risks {
if risk.Severity == MediumSeverity {
@@ -565,8 +568,8 @@ func FilteredByOnlyMediumRisks(parsedModel *ParsedModel) []Risk {
return filteredRisks
}
-func FilteredByOnlyLowRisks(parsedModel *ParsedModel) []Risk {
- filteredRisks := make([]Risk, 0)
+func FilteredByOnlyLowRisks(parsedModel *Model) []*Risk {
+ filteredRisks := make([]*Risk, 0)
for _, risks := range parsedModel.GeneratedRisksByCategory {
for _, risk := range risks {
if risk.Severity == LowSeverity {
@@ -577,8 +580,8 @@ func FilteredByOnlyLowRisks(parsedModel *ParsedModel) []Risk {
return filteredRisks
}
-func FilterByModelFailures(parsedModel *ParsedModel, risksByCat map[string][]Risk) map[string][]Risk {
- result := make(map[string][]Risk)
+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 {
@@ -589,15 +592,15 @@ func FilterByModelFailures(parsedModel *ParsedModel, risksByCat map[string][]Ris
return result
}
-func FlattenRiskSlice(risksByCat map[string][]Risk) []Risk {
- result := make([]Risk, 0)
+func FlattenRiskSlice(risksByCat map[string][]*Risk) []*Risk {
+ result := make([]*Risk, 0)
for _, risks := range risksByCat {
result = append(result, risks...)
}
return result
}
-func TotalRiskCount(parsedModel *ParsedModel) int {
+func TotalRiskCount(parsedModel *Model) int {
count := 0
for _, risks := range parsedModel.GeneratedRisksByCategory {
count += len(risks)
@@ -605,11 +608,11 @@ func TotalRiskCount(parsedModel *ParsedModel) int {
return count
}
-func FilteredByRiskTrackingUnchecked(parsedModel *ParsedModel) []Risk {
- filteredRisks := make([]Risk, 0)
+func FilteredByRiskTrackingUnchecked(parsedModel *Model) []*Risk {
+ filteredRisks := make([]*Risk, 0)
for _, risks := range parsedModel.GeneratedRisksByCategory {
for _, risk := range risks {
- if risk.GetRiskTrackingStatusDefaultingUnchecked(parsedModel) == Unchecked {
+ if risk.RiskStatus == Unchecked {
filteredRisks = append(filteredRisks, risk)
}
}
@@ -617,11 +620,11 @@ func FilteredByRiskTrackingUnchecked(parsedModel *ParsedModel) []Risk {
return filteredRisks
}
-func FilteredByRiskTrackingInDiscussion(parsedModel *ParsedModel) []Risk {
- filteredRisks := make([]Risk, 0)
+func FilteredByRiskTrackingInDiscussion(parsedModel *Model) []*Risk {
+ filteredRisks := make([]*Risk, 0)
for _, risks := range parsedModel.GeneratedRisksByCategory {
for _, risk := range risks {
- if risk.GetRiskTrackingStatusDefaultingUnchecked(parsedModel) == InDiscussion {
+ if risk.RiskStatus == InDiscussion {
filteredRisks = append(filteredRisks, risk)
}
}
@@ -629,11 +632,11 @@ func FilteredByRiskTrackingInDiscussion(parsedModel *ParsedModel) []Risk {
return filteredRisks
}
-func FilteredByRiskTrackingAccepted(parsedModel *ParsedModel) []Risk {
- filteredRisks := make([]Risk, 0)
+func FilteredByRiskTrackingAccepted(parsedModel *Model) []*Risk {
+ filteredRisks := make([]*Risk, 0)
for _, risks := range parsedModel.GeneratedRisksByCategory {
for _, risk := range risks {
- if risk.GetRiskTrackingStatusDefaultingUnchecked(parsedModel) == Accepted {
+ if risk.RiskStatus == Accepted {
filteredRisks = append(filteredRisks, risk)
}
}
@@ -641,11 +644,11 @@ func FilteredByRiskTrackingAccepted(parsedModel *ParsedModel) []Risk {
return filteredRisks
}
-func FilteredByRiskTrackingInProgress(parsedModel *ParsedModel) []Risk {
- filteredRisks := make([]Risk, 0)
+func FilteredByRiskTrackingInProgress(parsedModel *Model) []*Risk {
+ filteredRisks := make([]*Risk, 0)
for _, risks := range parsedModel.GeneratedRisksByCategory {
for _, risk := range risks {
- if risk.GetRiskTrackingStatusDefaultingUnchecked(parsedModel) == InProgress {
+ if risk.RiskStatus == InProgress {
filteredRisks = append(filteredRisks, risk)
}
}
@@ -653,11 +656,11 @@ func FilteredByRiskTrackingInProgress(parsedModel *ParsedModel) []Risk {
return filteredRisks
}
-func FilteredByRiskTrackingMitigated(parsedModel *ParsedModel) []Risk {
- filteredRisks := make([]Risk, 0)
+func FilteredByRiskTrackingMitigated(parsedModel *Model) []*Risk {
+ filteredRisks := make([]*Risk, 0)
for _, risks := range parsedModel.GeneratedRisksByCategory {
for _, risk := range risks {
- if risk.GetRiskTrackingStatusDefaultingUnchecked(parsedModel) == Mitigated {
+ if risk.RiskStatus == Mitigated {
filteredRisks = append(filteredRisks, risk)
}
}
@@ -665,11 +668,11 @@ func FilteredByRiskTrackingMitigated(parsedModel *ParsedModel) []Risk {
return filteredRisks
}
-func FilteredByRiskTrackingFalsePositive(parsedModel *ParsedModel) []Risk {
- filteredRisks := make([]Risk, 0)
+func FilteredByRiskTrackingFalsePositive(parsedModel *Model) []*Risk {
+ filteredRisks := make([]*Risk, 0)
for _, risks := range parsedModel.GeneratedRisksByCategory {
for _, risk := range risks {
- if risk.GetRiskTrackingStatusDefaultingUnchecked(parsedModel) == FalsePositive {
+ if risk.RiskStatus == FalsePositive {
filteredRisks = append(filteredRisks, risk)
}
}
@@ -677,8 +680,8 @@ func FilteredByRiskTrackingFalsePositive(parsedModel *ParsedModel) []Risk {
return filteredRisks
}
-func ReduceToOnlyHighRisk(risks []Risk) []Risk {
- filteredRisks := make([]Risk, 0)
+func ReduceToOnlyHighRisk(risks []*Risk) []*Risk {
+ filteredRisks := make([]*Risk, 0)
for _, risk := range risks {
if risk.Severity == HighSeverity {
filteredRisks = append(filteredRisks, risk)
@@ -687,8 +690,8 @@ func ReduceToOnlyHighRisk(risks []Risk) []Risk {
return filteredRisks
}
-func ReduceToOnlyMediumRisk(risks []Risk) []Risk {
- filteredRisks := make([]Risk, 0)
+func ReduceToOnlyMediumRisk(risks []*Risk) []*Risk {
+ filteredRisks := make([]*Risk, 0)
for _, risk := range risks {
if risk.Severity == MediumSeverity {
filteredRisks = append(filteredRisks, risk)
@@ -697,8 +700,8 @@ func ReduceToOnlyMediumRisk(risks []Risk) []Risk {
return filteredRisks
}
-func ReduceToOnlyLowRisk(risks []Risk) []Risk {
- filteredRisks := make([]Risk, 0)
+func ReduceToOnlyLowRisk(risks []*Risk) []*Risk {
+ filteredRisks := make([]*Risk, 0)
for _, risk := range risks {
if risk.Severity == LowSeverity {
filteredRisks = append(filteredRisks, risk)
@@ -707,71 +710,71 @@ func ReduceToOnlyLowRisk(risks []Risk) []Risk {
return filteredRisks
}
-func ReduceToOnlyRiskTrackingUnchecked(parsedModel *ParsedModel, risks []Risk) []Risk {
- filteredRisks := make([]Risk, 0)
+func ReduceToOnlyRiskTrackingUnchecked(parsedModel *Model, risks []*Risk) []*Risk {
+ filteredRisks := make([]*Risk, 0)
for _, risk := range risks {
- if risk.GetRiskTrackingStatusDefaultingUnchecked(parsedModel) == Unchecked {
+ if risk.RiskStatus == Unchecked {
filteredRisks = append(filteredRisks, risk)
}
}
return filteredRisks
}
-func ReduceToOnlyRiskTrackingInDiscussion(parsedModel *ParsedModel, risks []Risk) []Risk {
- filteredRisks := make([]Risk, 0)
+func ReduceToOnlyRiskTrackingInDiscussion(parsedModel *Model, risks []*Risk) []*Risk {
+ filteredRisks := make([]*Risk, 0)
for _, risk := range risks {
- if risk.GetRiskTrackingStatusDefaultingUnchecked(parsedModel) == InDiscussion {
+ if risk.RiskStatus == InDiscussion {
filteredRisks = append(filteredRisks, risk)
}
}
return filteredRisks
}
-func ReduceToOnlyRiskTrackingAccepted(parsedModel *ParsedModel, risks []Risk) []Risk {
- filteredRisks := make([]Risk, 0)
+func ReduceToOnlyRiskTrackingAccepted(parsedModel *Model, risks []*Risk) []*Risk {
+ filteredRisks := make([]*Risk, 0)
for _, risk := range risks {
- if risk.GetRiskTrackingStatusDefaultingUnchecked(parsedModel) == Accepted {
+ if risk.RiskStatus == Accepted {
filteredRisks = append(filteredRisks, risk)
}
}
return filteredRisks
}
-func ReduceToOnlyRiskTrackingInProgress(parsedModel *ParsedModel, risks []Risk) []Risk {
- filteredRisks := make([]Risk, 0)
+func ReduceToOnlyRiskTrackingInProgress(parsedModel *Model, risks []*Risk) []*Risk {
+ filteredRisks := make([]*Risk, 0)
for _, risk := range risks {
- if risk.GetRiskTrackingStatusDefaultingUnchecked(parsedModel) == InProgress {
+ if risk.RiskStatus == InProgress {
filteredRisks = append(filteredRisks, risk)
}
}
return filteredRisks
}
-func ReduceToOnlyRiskTrackingMitigated(parsedModel *ParsedModel, risks []Risk) []Risk {
- filteredRisks := make([]Risk, 0)
+func ReduceToOnlyRiskTrackingMitigated(parsedModel *Model, risks []*Risk) []*Risk {
+ filteredRisks := make([]*Risk, 0)
for _, risk := range risks {
- if risk.GetRiskTrackingStatusDefaultingUnchecked(parsedModel) == Mitigated {
+ if risk.RiskStatus == Mitigated {
filteredRisks = append(filteredRisks, risk)
}
}
return filteredRisks
}
-func ReduceToOnlyRiskTrackingFalsePositive(parsedModel *ParsedModel, risks []Risk) []Risk {
- filteredRisks := make([]Risk, 0)
+func ReduceToOnlyRiskTrackingFalsePositive(parsedModel *Model, risks []*Risk) []*Risk {
+ filteredRisks := make([]*Risk, 0)
for _, risk := range risks {
- if risk.GetRiskTrackingStatusDefaultingUnchecked(parsedModel) == FalsePositive {
+ if risk.RiskStatus == FalsePositive {
filteredRisks = append(filteredRisks, risk)
}
}
return filteredRisks
}
-func FilteredByStillAtRisk(parsedModel *ParsedModel) []Risk {
- filteredRisks := make([]Risk, 0)
+func FilteredByStillAtRisk(parsedModel *Model) []*Risk {
+ filteredRisks := make([]*Risk, 0)
for _, risks := range parsedModel.GeneratedRisksByCategory {
for _, risk := range risks {
- if risk.GetRiskTrackingStatusDefaultingUnchecked(parsedModel).IsStillAtRisk() {
+ if risk.RiskStatus.IsStillAtRisk() {
filteredRisks = append(filteredRisks, risk)
}
}
@@ -779,7 +782,7 @@ func FilteredByStillAtRisk(parsedModel *ParsedModel) []Risk {
return filteredRisks
}
-func OverallRiskStatistics(parsedModel *ParsedModel) RiskStatistics {
+func OverallRiskStatistics(parsedModel *Model) RiskStatistics {
result := RiskStatistics{}
result.Risks = make(map[string]map[string]int)
result.Risks[CriticalSeverity.String()] = make(map[string]int)
@@ -819,7 +822,7 @@ func OverallRiskStatistics(parsedModel *ParsedModel) RiskStatistics {
result.Risks[LowSeverity.String()][FalsePositive.String()] = 0
for _, risks := range parsedModel.GeneratedRisksByCategory {
for _, risk := range risks {
- result.Risks[risk.Severity.String()][risk.GetRiskTrackingStatusDefaultingUnchecked(parsedModel).String()]++
+ result.Risks[risk.Severity.String()][risk.RiskStatus.String()]++
}
}
return result
diff --git a/pkg/security/types/shared_runtime.go b/pkg/security/types/shared_runtime.go
index 05060656..bd591846 100644
--- a/pkg/security/types/shared_runtime.go
+++ b/pkg/security/types/shared_runtime.go
@@ -24,7 +24,7 @@ func (what SharedRuntime) IsTaggedWithBaseTag(baseTag string) bool {
return IsTaggedWithBaseTag(what.Tags, baseTag)
}
-func (what SharedRuntime) HighestConfidentiality(model *ParsedModel) Confidentiality {
+func (what SharedRuntime) HighestConfidentiality(model *Model) Confidentiality {
highest := Public
for _, id := range what.TechnicalAssetsRunning {
techAsset := model.TechnicalAssets[id]
@@ -35,7 +35,7 @@ func (what SharedRuntime) HighestConfidentiality(model *ParsedModel) Confidentia
return highest
}
-func (what SharedRuntime) HighestIntegrity(model *ParsedModel) Criticality {
+func (what SharedRuntime) HighestIntegrity(model *Model) Criticality {
highest := Archive
for _, id := range what.TechnicalAssetsRunning {
techAsset := model.TechnicalAssets[id]
@@ -46,7 +46,7 @@ func (what SharedRuntime) HighestIntegrity(model *ParsedModel) Criticality {
return highest
}
-func (what SharedRuntime) HighestAvailability(model *ParsedModel) Criticality {
+func (what SharedRuntime) HighestAvailability(model *Model) Criticality {
highest := Archive
for _, id := range what.TechnicalAssetsRunning {
techAsset := model.TechnicalAssets[id]
@@ -57,7 +57,7 @@ func (what SharedRuntime) HighestAvailability(model *ParsedModel) Criticality {
return highest
}
-func (what SharedRuntime) TechnicalAssetWithHighestRAA(model *ParsedModel) *TechnicalAsset {
+func (what SharedRuntime) TechnicalAssetWithHighestRAA(model *Model) *TechnicalAsset {
result := model.TechnicalAssets[what.TechnicalAssetsRunning[0]]
for _, asset := range what.TechnicalAssetsRunning {
candidate := model.TechnicalAssets[asset]
@@ -70,7 +70,7 @@ func (what SharedRuntime) TechnicalAssetWithHighestRAA(model *ParsedModel) *Tech
// as in Go ranging over map is random order, range over them in sorted (hence reproducible) way:
-func SortedKeysOfSharedRuntime(model *ParsedModel) []string {
+func SortedKeysOfSharedRuntime(model *Model) []string {
keys := make([]string, 0)
for k := range model.SharedRuntimes {
keys = append(keys, k)
diff --git a/pkg/security/types/stride.go b/pkg/security/types/stride.go
index 9697d8fb..3602072e 100644
--- a/pkg/security/types/stride.go
+++ b/pkg/security/types/stride.go
@@ -5,7 +5,9 @@ Copyright © 2023 NAME HERE
package types
import (
+ "encoding/json"
"fmt"
+ "gopkg.in/yaml.v3"
"strings"
)
@@ -62,3 +64,47 @@ func (what STRIDE) Explain() string {
func (what STRIDE) Title() string {
return [...]string{"Spoofing", "Tampering", "Repudiation", "Information Disclosure", "Denial of Service", "Elevation of Privilege"}[what]
}
+
+func (what STRIDE) MarshalJSON() ([]byte, error) {
+ return json.Marshal(what.String())
+}
+
+func (what *STRIDE) UnmarshalJSON(data []byte) error {
+ var text string
+ unmarshalError := json.Unmarshal(data, &text)
+ if unmarshalError != nil {
+ return unmarshalError
+ }
+
+ value, findError := what.find(text)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
+
+func (what STRIDE) MarshalYAML() (interface{}, error) {
+ return what.String(), nil
+}
+
+func (what *STRIDE) UnmarshalYAML(node *yaml.Node) error {
+ value, findError := what.find(node.Value)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
+
+func (what STRIDE) find(value string) (STRIDE, error) {
+ for index, description := range StrideTypeDescription {
+ if strings.EqualFold(value, description.Name) {
+ return STRIDE(index), nil
+ }
+ }
+
+ return STRIDE(0), fmt.Errorf("unknown STRIDE value %q", value)
+}
diff --git a/pkg/security/types/technical_asset.go b/pkg/security/types/technical_asset.go
index fe1f144a..45973ee9 100644
--- a/pkg/security/types/technical_asset.go
+++ b/pkg/security/types/technical_asset.go
@@ -51,7 +51,7 @@ func (what TechnicalAsset) IsTaggedWithBaseTag(baseTag string) bool {
// first use the tag(s) of the asset itself, then their trust boundaries (recursively up) and then their shared runtime
-func (what TechnicalAsset) IsTaggedWithAnyTraversingUp(model *ParsedModel, tags ...string) bool {
+func (what TechnicalAsset) IsTaggedWithAnyTraversingUp(model *Model, tags ...string) bool {
if containsCaseInsensitiveAny(what.Tags, tags...) {
return true
}
@@ -69,7 +69,7 @@ func (what TechnicalAsset) IsTaggedWithAnyTraversingUp(model *ParsedModel, tags
return false
}
-func (what TechnicalAsset) IsSameTrustBoundary(parsedModel *ParsedModel, otherAssetId string) bool {
+func (what TechnicalAsset) IsSameTrustBoundary(parsedModel *Model, otherAssetId string) bool {
trustBoundaryOfMyAsset := parsedModel.DirectContainingTrustBoundaryMappedByTechnicalAssetId[what.Id]
trustBoundaryOfOtherAsset := parsedModel.DirectContainingTrustBoundaryMappedByTechnicalAssetId[otherAssetId]
if trustBoundaryOfMyAsset == nil || trustBoundaryOfOtherAsset == nil {
@@ -78,7 +78,7 @@ func (what TechnicalAsset) IsSameTrustBoundary(parsedModel *ParsedModel, otherAs
return trustBoundaryOfMyAsset.Id == trustBoundaryOfOtherAsset.Id
}
-func (what TechnicalAsset) IsSameExecutionEnvironment(parsedModel *ParsedModel, otherAssetId string) bool {
+func (what TechnicalAsset) IsSameExecutionEnvironment(parsedModel *Model, otherAssetId string) bool {
trustBoundaryOfMyAsset := parsedModel.DirectContainingTrustBoundaryMappedByTechnicalAssetId[what.Id]
trustBoundaryOfOtherAsset := parsedModel.DirectContainingTrustBoundaryMappedByTechnicalAssetId[otherAssetId]
if trustBoundaryOfMyAsset != nil && trustBoundaryOfMyAsset.Type == ExecutionEnvironment && trustBoundaryOfOtherAsset != nil && trustBoundaryOfOtherAsset.Type == ExecutionEnvironment {
@@ -87,7 +87,7 @@ func (what TechnicalAsset) IsSameExecutionEnvironment(parsedModel *ParsedModel,
return false
}
-func (what TechnicalAsset) IsSameTrustBoundaryNetworkOnly(parsedModel *ParsedModel, otherAssetId string) bool {
+func (what TechnicalAsset) IsSameTrustBoundaryNetworkOnly(parsedModel *Model, otherAssetId string) bool {
trustBoundaryOfMyAsset := parsedModel.DirectContainingTrustBoundaryMappedByTechnicalAssetId[what.Id]
if trustBoundaryOfMyAsset != nil && !trustBoundaryOfMyAsset.Type.IsNetworkBoundary() { // find and use the parent boundary then
trustBoundaryOfMyAsset = parsedModel.TrustBoundaries[trustBoundaryOfMyAsset.ParentTrustBoundaryID(parsedModel)]
@@ -108,7 +108,7 @@ func (what TechnicalAsset) HighestSensitivityScore() float64 {
what.Availability.AttackerAttractivenessForAsset()
}
-func (what TechnicalAsset) HighestConfidentiality(model *ParsedModel) Confidentiality {
+func (what TechnicalAsset) HighestConfidentiality(model *Model) Confidentiality {
highest := what.Confidentiality
highestProcessed := what.HighestProcessedConfidentiality(model)
if highest < highestProcessed {
@@ -123,7 +123,7 @@ func (what TechnicalAsset) HighestConfidentiality(model *ParsedModel) Confidenti
return highest
}
-func (what TechnicalAsset) HighestProcessedConfidentiality(parsedModel *ParsedModel) Confidentiality {
+func (what TechnicalAsset) HighestProcessedConfidentiality(parsedModel *Model) Confidentiality {
highest := what.Confidentiality
for _, dataId := range what.DataAssetsProcessed {
dataAsset := parsedModel.DataAssets[dataId]
@@ -134,7 +134,7 @@ func (what TechnicalAsset) HighestProcessedConfidentiality(parsedModel *ParsedMo
return highest
}
-func (what TechnicalAsset) HighestStoredConfidentiality(parsedModel *ParsedModel) Confidentiality {
+func (what TechnicalAsset) HighestStoredConfidentiality(parsedModel *Model) Confidentiality {
highest := what.Confidentiality
for _, dataId := range what.DataAssetsStored {
dataAsset := parsedModel.DataAssets[dataId]
@@ -145,7 +145,7 @@ func (what TechnicalAsset) HighestStoredConfidentiality(parsedModel *ParsedModel
return highest
}
-func (what TechnicalAsset) DataAssetsProcessedSorted(parsedModel *ParsedModel) []*DataAsset {
+func (what TechnicalAsset) DataAssetsProcessedSorted(parsedModel *Model) []*DataAsset {
result := make([]*DataAsset, 0)
for _, assetID := range what.DataAssetsProcessed {
result = append(result, parsedModel.DataAssets[assetID])
@@ -154,7 +154,7 @@ func (what TechnicalAsset) DataAssetsProcessedSorted(parsedModel *ParsedModel) [
return result
}
-func (what TechnicalAsset) DataAssetsStoredSorted(parsedModel *ParsedModel) []*DataAsset {
+func (what TechnicalAsset) DataAssetsStoredSorted(parsedModel *Model) []*DataAsset {
result := make([]*DataAsset, 0)
for _, assetID := range what.DataAssetsStored {
result = append(result, parsedModel.DataAssets[assetID])
@@ -177,7 +177,7 @@ func (what TechnicalAsset) CommunicationLinksSorted() []*CommunicationLink {
return result
}
-func (what TechnicalAsset) HighestIntegrity(model *ParsedModel) Criticality {
+func (what TechnicalAsset) HighestIntegrity(model *Model) Criticality {
highest := what.Integrity
highestProcessed := what.HighestProcessedIntegrity(model)
if highest < highestProcessed {
@@ -192,7 +192,7 @@ func (what TechnicalAsset) HighestIntegrity(model *ParsedModel) Criticality {
return highest
}
-func (what TechnicalAsset) HighestProcessedIntegrity(model *ParsedModel) Criticality {
+func (what TechnicalAsset) HighestProcessedIntegrity(model *Model) Criticality {
highest := what.Integrity
for _, dataId := range what.DataAssetsProcessed {
dataAsset := model.DataAssets[dataId]
@@ -203,7 +203,7 @@ func (what TechnicalAsset) HighestProcessedIntegrity(model *ParsedModel) Critica
return highest
}
-func (what TechnicalAsset) HighestStoredIntegrity(model *ParsedModel) Criticality {
+func (what TechnicalAsset) HighestStoredIntegrity(model *Model) Criticality {
highest := what.Integrity
for _, dataId := range what.DataAssetsStored {
dataAsset := model.DataAssets[dataId]
@@ -214,7 +214,7 @@ func (what TechnicalAsset) HighestStoredIntegrity(model *ParsedModel) Criticalit
return highest
}
-func (what TechnicalAsset) HighestAvailability(model *ParsedModel) Criticality {
+func (what TechnicalAsset) HighestAvailability(model *Model) Criticality {
highest := what.Availability
highestProcessed := what.HighestProcessedAvailability(model)
if highest < highestProcessed {
@@ -229,7 +229,7 @@ func (what TechnicalAsset) HighestAvailability(model *ParsedModel) Criticality {
return highest
}
-func (what TechnicalAsset) HighestProcessedAvailability(model *ParsedModel) Criticality {
+func (what TechnicalAsset) HighestProcessedAvailability(model *Model) Criticality {
highest := what.Availability
for _, dataId := range what.DataAssetsProcessed {
dataAsset := model.DataAssets[dataId]
@@ -240,7 +240,7 @@ func (what TechnicalAsset) HighestProcessedAvailability(model *ParsedModel) Crit
return highest
}
-func (what TechnicalAsset) HighestStoredAvailability(model *ParsedModel) Criticality {
+func (what TechnicalAsset) HighestStoredAvailability(model *Model) Criticality {
highest := what.Availability
for _, dataId := range what.DataAssetsStored {
dataAsset := model.DataAssets[dataId]
@@ -251,7 +251,7 @@ func (what TechnicalAsset) HighestStoredAvailability(model *ParsedModel) Critica
return highest
}
-func (what TechnicalAsset) HasDirectConnection(parsedModel *ParsedModel, otherAssetId string) bool {
+func (what TechnicalAsset) HasDirectConnection(parsedModel *Model, otherAssetId string) bool {
for _, dataFlow := range parsedModel.IncomingTechnicalCommunicationLinksMappedByTargetId[what.Id] {
if dataFlow.SourceId == otherAssetId {
return true
@@ -266,8 +266,8 @@ func (what TechnicalAsset) HasDirectConnection(parsedModel *ParsedModel, otherAs
return false
}
-func (what TechnicalAsset) GeneratedRisks(parsedModel *ParsedModel) []Risk {
- resultingRisks := make([]Risk, 0)
+func (what TechnicalAsset) GeneratedRisks(parsedModel *Model) []*Risk {
+ resultingRisks := make([]*Risk, 0)
if len(SortedRiskCategories(parsedModel)) == 0 {
fmt.Println("Uh, strange, no risks generated (yet?) and asking for them by tech asset...")
}
@@ -340,7 +340,7 @@ func (what TechnicalAsset) QuickWins() float64 {
}
*/
-func (what TechnicalAsset) GetTrustBoundaryId(model *ParsedModel) string {
+func (what TechnicalAsset) GetTrustBoundaryId(model *Model) string {
for _, trustBoundary := range model.TrustBoundaries {
for _, techAssetInside := range trustBoundary.TechnicalAssetsInside {
if techAssetInside == what.Id {
@@ -351,7 +351,7 @@ func (what TechnicalAsset) GetTrustBoundaryId(model *ParsedModel) string {
return ""
}
-func SortByTechnicalAssetRiskSeverityAndTitleStillAtRisk(assets []*TechnicalAsset, parsedModel *ParsedModel) {
+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))
diff --git a/pkg/security/types/technical_asset_machine.go b/pkg/security/types/technical_asset_machine.go
index 921073ff..75f2be4d 100644
--- a/pkg/security/types/technical_asset_machine.go
+++ b/pkg/security/types/technical_asset_machine.go
@@ -5,7 +5,9 @@ Copyright © 2023 NAME HERE
package types
import (
+ "encoding/json"
"fmt"
+ "gopkg.in/yaml.v3"
"strings"
)
@@ -51,3 +53,47 @@ func (what TechnicalAssetMachine) String() string {
func (what TechnicalAssetMachine) Explain() string {
return TechnicalAssetMachineTypeDescription[what].Description
}
+
+func (what TechnicalAssetMachine) MarshalJSON() ([]byte, error) {
+ return json.Marshal(what.String())
+}
+
+func (what *TechnicalAssetMachine) UnmarshalJSON(data []byte) error {
+ var text string
+ unmarshalError := json.Unmarshal(data, &text)
+ if unmarshalError != nil {
+ return unmarshalError
+ }
+
+ value, findError := what.find(text)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
+
+func (what TechnicalAssetMachine) MarshalYAML() (interface{}, error) {
+ return what.String(), nil
+}
+
+func (what *TechnicalAssetMachine) UnmarshalYAML(node *yaml.Node) error {
+ value, findError := what.find(node.Value)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
+
+func (what TechnicalAssetMachine) find(value string) (TechnicalAssetMachine, error) {
+ for index, description := range TechnicalAssetMachineTypeDescription {
+ if strings.EqualFold(value, description.Name) {
+ return TechnicalAssetMachine(index), nil
+ }
+ }
+
+ return TechnicalAssetMachine(0), fmt.Errorf("unknown technical asset machine value %q", value)
+}
diff --git a/pkg/security/types/technical_asset_size.go b/pkg/security/types/technical_asset_size.go
index 45d9d607..bf81519d 100644
--- a/pkg/security/types/technical_asset_size.go
+++ b/pkg/security/types/technical_asset_size.go
@@ -5,7 +5,9 @@ Copyright © 2023 NAME HERE
package types
import (
+ "encoding/json"
"fmt"
+ "gopkg.in/yaml.v3"
"strings"
)
@@ -56,3 +58,37 @@ func (what TechnicalAssetSize) Find(value string) (TechnicalAssetSize, error) {
return TechnicalAssetSize(0), fmt.Errorf("unknown technical asset size value %q", value)
}
+
+func (what TechnicalAssetSize) MarshalJSON() ([]byte, error) {
+ return json.Marshal(what.String())
+}
+
+func (what *TechnicalAssetSize) UnmarshalJSON(data []byte) error {
+ var text string
+ unmarshalError := json.Unmarshal(data, &text)
+ if unmarshalError != nil {
+ return unmarshalError
+ }
+
+ value, findError := what.Find(text)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
+
+func (what TechnicalAssetSize) MarshalYAML() (interface{}, error) {
+ return what.String(), nil
+}
+
+func (what *TechnicalAssetSize) UnmarshalYAML(node *yaml.Node) error {
+ value, findError := what.Find(node.Value)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
diff --git a/pkg/security/types/technical_asset_type.go b/pkg/security/types/technical_asset_type.go
index 8a2a0c35..0d08d03e 100644
--- a/pkg/security/types/technical_asset_type.go
+++ b/pkg/security/types/technical_asset_type.go
@@ -5,7 +5,9 @@ Copyright © 2023 NAME HERE
package types
import (
+ "encoding/json"
"fmt"
+ "gopkg.in/yaml.v3"
"strings"
)
@@ -49,3 +51,47 @@ func ParseTechnicalAssetType(value string) (technicalAssetType TechnicalAssetTyp
}
return technicalAssetType, fmt.Errorf("unable to parse into type: %v", value)
}
+
+func (what TechnicalAssetType) MarshalJSON() ([]byte, error) {
+ return json.Marshal(what.String())
+}
+
+func (what *TechnicalAssetType) UnmarshalJSON(data []byte) error {
+ var text string
+ unmarshalError := json.Unmarshal(data, &text)
+ if unmarshalError != nil {
+ return unmarshalError
+ }
+
+ value, findError := what.find(text)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
+
+func (what TechnicalAssetType) MarshalYAML() (interface{}, error) {
+ return what.String(), nil
+}
+
+func (what *TechnicalAssetType) UnmarshalYAML(node *yaml.Node) error {
+ value, findError := what.find(node.Value)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
+
+func (what TechnicalAssetType) find(value string) (TechnicalAssetType, error) {
+ for index, description := range TechnicalAssetTypeDescription {
+ if strings.EqualFold(value, description.Name) {
+ return TechnicalAssetType(index), nil
+ }
+ }
+
+ return TechnicalAssetType(0), fmt.Errorf("unknown technical asset type value %q", value)
+}
diff --git a/pkg/security/types/trust_boundary.go b/pkg/security/types/trust_boundary.go
index 0c9811af..3cad0f47 100644
--- a/pkg/security/types/trust_boundary.go
+++ b/pkg/security/types/trust_boundary.go
@@ -18,7 +18,7 @@ type TrustBoundary struct {
TrustBoundariesNested []string `json:"trust_boundaries_nested,omitempty" yaml:"trust_boundaries_nested,omitempty"`
}
-func (what TrustBoundary) RecursivelyAllTechnicalAssetIDsInside(model *ParsedModel) []string {
+func (what TrustBoundary) RecursivelyAllTechnicalAssetIDsInside(model *Model) []string {
result := make([]string, 0)
what.addAssetIDsRecursively(model, &result)
return result
@@ -32,7 +32,7 @@ func (what TrustBoundary) IsTaggedWithBaseTag(baseTag string) bool {
return IsTaggedWithBaseTag(what.Tags, baseTag)
}
-func (what TrustBoundary) IsTaggedWithAnyTraversingUp(model *ParsedModel, tags ...string) bool {
+func (what TrustBoundary) IsTaggedWithAnyTraversingUp(model *Model, tags ...string) bool {
if what.IsTaggedWithAny(tags...) {
return true
}
@@ -43,7 +43,7 @@ func (what TrustBoundary) IsTaggedWithAnyTraversingUp(model *ParsedModel, tags .
return false
}
-func (what TrustBoundary) ParentTrustBoundaryID(model *ParsedModel) string {
+func (what TrustBoundary) ParentTrustBoundaryID(model *Model) string {
var result string
for _, candidate := range model.TrustBoundaries {
if contains(candidate.TrustBoundariesNested, what.Id) {
@@ -54,7 +54,7 @@ func (what TrustBoundary) ParentTrustBoundaryID(model *ParsedModel) string {
return result
}
-func (what TrustBoundary) HighestConfidentiality(model *ParsedModel) Confidentiality {
+func (what TrustBoundary) HighestConfidentiality(model *Model) Confidentiality {
highest := Public
for _, id := range what.RecursivelyAllTechnicalAssetIDsInside(model) {
techAsset := model.TechnicalAssets[id]
@@ -65,7 +65,7 @@ func (what TrustBoundary) HighestConfidentiality(model *ParsedModel) Confidentia
return highest
}
-func (what TrustBoundary) HighestIntegrity(model *ParsedModel) Criticality {
+func (what TrustBoundary) HighestIntegrity(model *Model) Criticality {
highest := Archive
for _, id := range what.RecursivelyAllTechnicalAssetIDsInside(model) {
techAsset := model.TechnicalAssets[id]
@@ -76,7 +76,7 @@ func (what TrustBoundary) HighestIntegrity(model *ParsedModel) Criticality {
return highest
}
-func (what TrustBoundary) HighestAvailability(model *ParsedModel) Criticality {
+func (what TrustBoundary) HighestAvailability(model *Model) Criticality {
highest := Archive
for _, id := range what.RecursivelyAllTechnicalAssetIDsInside(model) {
techAsset := model.TechnicalAssets[id]
@@ -87,20 +87,20 @@ func (what TrustBoundary) HighestAvailability(model *ParsedModel) Criticality {
return highest
}
-func (what TrustBoundary) AllParentTrustBoundaryIDs(model *ParsedModel) []string {
+func (what TrustBoundary) AllParentTrustBoundaryIDs(model *Model) []string {
result := make([]string, 0)
what.addTrustBoundaryIDsRecursively(model, &result)
return result
}
-func (what TrustBoundary) addAssetIDsRecursively(model *ParsedModel, result *[]string) {
+func (what TrustBoundary) addAssetIDsRecursively(model *Model, result *[]string) {
*result = append(*result, what.TechnicalAssetsInside...)
for _, nestedBoundaryID := range what.TrustBoundariesNested {
model.TrustBoundaries[nestedBoundaryID].addAssetIDsRecursively(model, result)
}
}
-func (what TrustBoundary) addTrustBoundaryIDsRecursively(model *ParsedModel, result *[]string) {
+func (what TrustBoundary) addTrustBoundaryIDsRecursively(model *Model, result *[]string) {
*result = append(*result, what.Id)
parentID := what.ParentTrustBoundaryID(model)
if len(parentID) > 0 {
@@ -110,7 +110,7 @@ func (what TrustBoundary) addTrustBoundaryIDsRecursively(model *ParsedModel, res
// as in Go ranging over map is random order, range over them in sorted (hence reproducible) way:
-func SortedKeysOfTrustBoundaries(model *ParsedModel) []string {
+func SortedKeysOfTrustBoundaries(model *Model) []string {
keys := make([]string, 0)
for k := range model.TrustBoundaries {
keys = append(keys, k)
diff --git a/pkg/security/types/trust_boundary_type.go b/pkg/security/types/trust_boundary_type.go
index 2c109356..90711ce7 100644
--- a/pkg/security/types/trust_boundary_type.go
+++ b/pkg/security/types/trust_boundary_type.go
@@ -5,7 +5,9 @@ Copyright © 2023 NAME HERE
package types
import (
+ "encoding/json"
"fmt"
+ "gopkg.in/yaml.v3"
"strings"
)
@@ -70,3 +72,47 @@ func (what TrustBoundaryType) IsNetworkBoundary() bool {
func (what TrustBoundaryType) IsWithinCloud() bool {
return what == NetworkCloudProvider || what == NetworkCloudSecurityGroup
}
+
+func (what TrustBoundaryType) MarshalJSON() ([]byte, error) {
+ return json.Marshal(what.String())
+}
+
+func (what *TrustBoundaryType) UnmarshalJSON(data []byte) error {
+ var text string
+ unmarshalError := json.Unmarshal(data, &text)
+ if unmarshalError != nil {
+ return unmarshalError
+ }
+
+ value, findError := what.find(text)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
+
+func (what TrustBoundaryType) MarshalYAML() (interface{}, error) {
+ return what.String(), nil
+}
+
+func (what *TrustBoundaryType) UnmarshalYAML(node *yaml.Node) error {
+ value, findError := what.find(node.Value)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
+
+func (what TrustBoundaryType) find(value string) (TrustBoundaryType, error) {
+ for index, description := range TrustBoundaryTypeDescription {
+ if strings.EqualFold(value, description.Name) {
+ return TrustBoundaryType(index), nil
+ }
+ }
+
+ return TrustBoundaryType(0), fmt.Errorf("unknown trust boundary type value %q", value)
+}
diff --git a/pkg/security/types/usage.go b/pkg/security/types/usage.go
index ec70908f..863f5c99 100644
--- a/pkg/security/types/usage.go
+++ b/pkg/security/types/usage.go
@@ -5,7 +5,9 @@ Copyright © 2023 NAME HERE
package types
import (
+ "encoding/json"
"fmt"
+ "gopkg.in/yaml.v3"
"strings"
)
@@ -51,3 +53,47 @@ func (what Usage) Explain() string {
func (what Usage) Title() string {
return [...]string{"Business", "DevOps"}[what]
}
+
+func (what Usage) MarshalJSON() ([]byte, error) {
+ return json.Marshal(what.String())
+}
+
+func (what *Usage) UnmarshalJSON(data []byte) error {
+ var text string
+ unmarshalError := json.Unmarshal(data, &text)
+ if unmarshalError != nil {
+ return unmarshalError
+ }
+
+ value, findError := what.find(text)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
+
+func (what Usage) MarshalYAML() (interface{}, error) {
+ return what.String(), nil
+}
+
+func (what *Usage) UnmarshalYAML(node *yaml.Node) error {
+ value, findError := what.find(node.Value)
+ if findError != nil {
+ return findError
+ }
+
+ *what = value
+ return nil
+}
+
+func (what Usage) find(value string) (Usage, error) {
+ for index, description := range UsageTypeDescription {
+ if strings.EqualFold(value, description.Name) {
+ return Usage(index), nil
+ }
+ }
+
+ return Usage(0), fmt.Errorf("unknown usage type value %q", value)
+}
diff --git a/pkg/server/execute.go b/pkg/server/execute.go
index 6f145ebe..d3c29d5c 100644
--- a/pkg/server/execute.go
+++ b/pkg/server/execute.go
@@ -179,7 +179,7 @@ func (s *server) doItViaRuntimeCall(modelFile string, outputDir string,
dpi int) {
// Remember to also add the same args to the exec based sub-process calls!
var cmd *exec.Cmd
- args := []string{"-model", modelFile, "-output", outputDir, "-execute-model-macro", s.config.ExecuteModelMacro, "-raa-run", s.config.RAAPlugin, "-custom-risk-rules-plugins", strings.Join(s.config.RiskRulesPlugins, ","), "-skip-risk-rules", s.config.SkipRiskRules, "-diagram-dpi", strconv.Itoa(dpi)}
+ args := []string{"-model", modelFile, "-output", outputDir, "-execute-model-macro", s.config.ExecuteModelMacro, "-raa-run", s.config.RAAPlugin, "-custom-risk-rules-plugins", strings.Join(s.config.RiskRulesPlugins, ","), "-skip-risk-rules", strings.Join(s.config.SkipRiskRules, ","), "-diagram-dpi", strconv.Itoa(dpi)}
if s.config.Verbose {
args = append(args, "-verbose")
}
diff --git a/pkg/server/model.go b/pkg/server/model.go
index 1af70560..b6901513 100644
--- a/pkg/server/model.go
+++ b/pkg/server/model.go
@@ -507,14 +507,14 @@ func (s *server) deleteDataAsset(ginContext *gin.Context) {
}
}
}
- for individualRiskCatTitle, individualRiskCat := range modelInput.IndividualRiskCategories {
+ for _, individualRiskCat := range modelInput.CustomRiskCategories {
if individualRiskCat.RisksIdentified != nil {
for individualRiskInstanceTitle, individualRiskInstance := range individualRiskCat.RisksIdentified {
if individualRiskInstance.MostRelevantDataAsset == dataAsset.ID { // apply the removal
referencesDeleted = true
- x := modelInput.IndividualRiskCategories[individualRiskCatTitle].RisksIdentified[individualRiskInstanceTitle]
+ x := individualRiskCat.RisksIdentified[individualRiskInstanceTitle]
x.MostRelevantDataAsset = "" // TODO needs more testing
- modelInput.IndividualRiskCategories[individualRiskCatTitle].RisksIdentified[individualRiskInstanceTitle] = x
+ individualRiskCat.RisksIdentified[individualRiskInstanceTitle] = x
}
}
}
@@ -603,13 +603,13 @@ func (s *server) setDataAsset(ginContext *gin.Context) {
}
}
}
- for individualRiskCatTitle, individualRiskCat := range modelInput.IndividualRiskCategories {
+ for _, individualRiskCat := range modelInput.CustomRiskCategories {
if individualRiskCat.RisksIdentified != nil {
for individualRiskInstanceTitle, individualRiskInstance := range individualRiskCat.RisksIdentified {
if individualRiskInstance.MostRelevantDataAsset == dataAsset.ID { // apply the ID change
- x := modelInput.IndividualRiskCategories[individualRiskCatTitle].RisksIdentified[individualRiskInstanceTitle]
+ x := individualRiskCat.RisksIdentified[individualRiskInstanceTitle]
x.MostRelevantDataAsset = dataAssetInput.ID // TODO needs more testing
- modelInput.IndividualRiskCategories[individualRiskCatTitle].RisksIdentified[individualRiskInstanceTitle] = x
+ individualRiskCat.RisksIdentified[individualRiskInstanceTitle] = x
}
}
}
@@ -777,13 +777,13 @@ func (s *server) setSharedRuntime(ginContext *gin.Context) {
modelInput.SharedRuntimes[payload.Title] = sharedRuntimeInput
idChanged := sharedRuntimeInput.ID != sharedRuntime.ID
if idChanged { // ID-CHANGE-PROPAGATION
- for individualRiskCatTitle, individualRiskCat := range modelInput.IndividualRiskCategories {
+ for _, individualRiskCat := range modelInput.CustomRiskCategories {
if individualRiskCat.RisksIdentified != nil {
for individualRiskInstanceTitle, individualRiskInstance := range individualRiskCat.RisksIdentified {
if individualRiskInstance.MostRelevantSharedRuntime == sharedRuntime.ID { // apply the ID change
- x := modelInput.IndividualRiskCategories[individualRiskCatTitle].RisksIdentified[individualRiskInstanceTitle]
+ x := individualRiskCat.RisksIdentified[individualRiskInstanceTitle]
x.MostRelevantSharedRuntime = sharedRuntimeInput.ID // TODO needs more testing
- modelInput.IndividualRiskCategories[individualRiskCatTitle].RisksIdentified[individualRiskInstanceTitle] = x
+ individualRiskCat.RisksIdentified[individualRiskInstanceTitle] = x
}
}
}
@@ -928,14 +928,14 @@ func (s *server) deleteSharedRuntime(ginContext *gin.Context) {
for title, sharedRuntime := range modelInput.SharedRuntimes {
if sharedRuntime.ID == ginContext.Param("shared-runtime-id") {
// also remove all usages of this shared runtime !!
- for individualRiskCatTitle, individualRiskCat := range modelInput.IndividualRiskCategories {
+ for _, individualRiskCat := range modelInput.CustomRiskCategories {
if individualRiskCat.RisksIdentified != nil {
for individualRiskInstanceTitle, individualRiskInstance := range individualRiskCat.RisksIdentified {
if individualRiskInstance.MostRelevantSharedRuntime == sharedRuntime.ID { // apply the removal
referencesDeleted = true
- x := modelInput.IndividualRiskCategories[individualRiskCatTitle].RisksIdentified[individualRiskInstanceTitle]
+ x := individualRiskCat.RisksIdentified[individualRiskInstanceTitle]
x.MostRelevantSharedRuntime = "" // TODO needs more testing
- modelInput.IndividualRiskCategories[individualRiskCatTitle].RisksIdentified[individualRiskInstanceTitle] = x
+ individualRiskCat.RisksIdentified[individualRiskInstanceTitle] = x
}
}
}
diff --git a/pkg/server/server.go b/pkg/server/server.go
index b9747b3f..201ba8ec 100644
--- a/pkg/server/server.go
+++ b/pkg/server/server.go
@@ -35,7 +35,7 @@ type server struct {
mapFolderNameToTokenHash map[string]string
extremeShortTimeoutsForTesting bool
locksByFolderName map[string]*sync.Mutex
- customRiskRules map[string]*model.CustomRisk
+ customRiskRules risks.RiskRules
}
func RunServer(config *common.Config) {
diff --git a/test/all.json b/test/all.json
index b33364e3..e1bb05cf 100644
--- a/test/all.json
+++ b/test/all.json
@@ -1051,8 +1051,8 @@
]
}
},
- "individual_risk_categories": {
- "something-strange": {
+ "custom_risk_categories": [
+ {
"id": "something-strange",
"title": "Some Individual Risk Example",
"description": "Some text describing the risk category...",
@@ -1066,9 +1066,10 @@
"risk_assessment": "Some text describing the risk assessment...",
"false_positives": "Some text describing the most common types of false positives...",
"stride": "repudiation",
- "cwe": 693
+ "cwe": 693,
+ "is_built_in": true
}
- },
+ ],
"built_in_risk_categories": {
"accidental-secret-leak": {
"id": "accidental-secret-leak",
diff --git a/test/all.yaml b/test/all.yaml
index 73264af8..b26af1a7 100644
--- a/test/all.yaml
+++ b/test/all.yaml
@@ -1239,12 +1239,10 @@ shared_runtimes:
+custom_risk_categories: # used for adding custom risk categories
-individual_risk_categories: # used for adding custom manually identified risks
-
-
- Some Individual Risk Example:
- id: something-strange
+ - id: something-strange
+ title: Some Individual Risk Example
description: Some text describing the risk category...
impact: Some text describing the impact...
asvs: V0 - Something Strange
@@ -1259,6 +1257,7 @@ individual_risk_categories: # used for adding custom manually identified risks
false_positives: Some text describing the most common types of false positives...
model_failure_possible_reason: false
cwe: 693
+ is_built_in: true
risks_identified:
Example Individual Risk at Database:
severity: critical # values: low, medium, elevated, high, critical
@@ -1290,8 +1289,8 @@ individual_risk_categories: # used for adding custom manually identified risks
# For risk tracking each risk-id needs to be defined (the string with the @ sign in it). These unique risk IDs
# are visible in the PDF report (the small grey string under each risk), the Excel (column "ID"), as well as the JSON responses.
# Some risk IDs have only one @ sign in them, while others multiple. The idea is to allow for unique but still speaking IDs.
-# Therefore each risk instance creates its individual ID by taking all affected elements causing the risk to be within an @-delimited part.
-# Using wildcards (the * sign) for parts delimited by @ signs allows to handle groups of certain risks at once. Best is to lookup the IDs
+# Therefore, each risk instance creates its individual ID by taking all affected elements causing the risk to be within an @-delimited part.
+# Using wildcards (the * sign) for parts delimited by @ signs allows to handle groups of certain risks at once. Best is to look up the IDs
# to use in the created Excel file. Alternatively a model macro "seed-risk-tracking" is available that helps in initially
# seeding the risk tracking part here based on already identified and not yet handled risks.
risk_tracking:
diff --git a/test/risk-category.yaml b/test/risk-category.yaml
index feae907b..40ad149b 100644
--- a/test/risk-category.yaml
+++ b/test/risk-category.yaml
@@ -1,140 +1,140 @@
-individual_risk_categories:
- accidental-secret-leak:
- id: accidental-secret-leak
- title: Accidental Secret Leak
- function: operations
- stride: information-disclosure
- cwe: 200
- description:
- Sourcecode repositories (including their histories) as well as artifact registries can accidentally contain
- secrets like checked-in or packaged-in passwords, API tokens, certificates, crypto keys, etc.
- impact:
- If this risk is unmitigated, attackers which have access to affected sourcecode repositories or artifact
- registries might find secrets accidentally checked-in.
- asvs:
- V14 - Configuration Verification Requirements
- cheat_sheet:
- https://cheatsheetseries.owasp.org/cheatsheets/Attack_Surface_Analysis_Cheat_Sheet.html
- action:
- Build Pipeline Hardening
- mitigation:
- Establish measures preventing accidental check-in or package-in of secrets into sourcecode repositories and
- artifact registries. This starts by using good .gitignore and .dockerignore files, but does not stop there.
- See for example tools like \"git-secrets\" or \"Talisman\" to have check-in preventive measures for
- secrets. Consider also to regularly scan your repositories for secrets accidentally checked-in using
- scanning tools like "gitleaks" or "gitrob".
- check:
- Are recommendations from the linked cheat sheet and referenced ASVS chapter applied?
- detection_logic:
- In-scope sourcecode repositories and artifact registries.
- risk_assessment:
- The risk rating depends on the sensitivity of the technical asset itself and of the data assets processed.
- false_positives:
- Usually no false positives.
+id: accidental-secret-leak
+title: Accidental Secret Leak
+function: operations
+stride: information-disclosure
+cwe: 200
+description:
+ Sourcecode repositories (including their histories) as well as artifact registries can accidentally contain
+ secrets like checked-in or packaged-in passwords, API tokens, certificates, crypto keys, etc.
+impact:
+ If this risk is unmitigated, attackers which have access to affected sourcecode repositories or artifact
+ registries might find secrets accidentally checked-in.
+asvs:
+ V14 - Configuration Verification Requirements
+cheat_sheet:
+ https://cheatsheetseries.owasp.org/cheatsheets/Attack_Surface_Analysis_Cheat_Sheet.html
+action:
+ Build Pipeline Hardening
+mitigation:
+ Establish measures preventing accidental check-in or package-in of secrets into sourcecode repositories and
+ artifact registries. This starts by using good .gitignore and .dockerignore files, but does not stop there.
+ See for example tools like \"git-secrets\" or \"Talisman\" to have check-in preventive measures for
+ secrets. Consider also to regularly scan your repositories for secrets accidentally checked-in using
+ scanning tools like "gitleaks" or "gitrob".
+check:
+ Are recommendations from the linked cheat sheet and referenced ASVS chapter applied?
+detection_logic:
+ In-scope sourcecode repositories and artifact registries.
+risk_assessment:
+ The risk rating depends on the sensitivity of the technical asset itself and of the data assets processed.
+false_positives:
+ Usually no false positives.
+is_built_in: true
- risk:
- parameter: tech_asset
- id: "{$risk.id}@{tech_asset.id}"
- title: "get_title({tech_asset})"
- severity: "calculate_severity(unlikely, get_impact({tech_asset}))"
- exploitation_likelihood: unlikely
- exploitation_impact: "get_impact({tech_asset})"
- data_breach_probability: probable
- data_breach_technical_assets:
- - "{tech_asset.id}"
- most_relevant_data_asset: "{tech_asset.id}"
+script:
+ risk:
+ parameter: tech_asset
+ id: "{$risk.id}@{tech_asset.id}"
+ title: "get_title({tech_asset})"
+ severity: "calculate_severity(unlikely, get_impact({tech_asset}))"
+ exploitation_likelihood: unlikely
+ exploitation_impact: "get_impact({tech_asset})"
+ data_breach_probability: probable
+ data_breach_technical_assets:
+ - "{tech_asset.id}"
+ most_relevant_data_asset: "{tech_asset.id}"
- match:
- parameter: tech_asset
- do:
- if:
- and:
- - false: "{tech_asset.out_of_scope}"
- - contains:
- item: "{tech_asset.technology}"
- in:
- - sourcecode-repository
- - artifact-registry
- then:
- return: true
+ match:
+ parameter: tech_asset
+ do:
+ if:
+ and:
+ - false: "{tech_asset.out_of_scope}"
+ - contains:
+ item: "{tech_asset.technology}"
+ in:
+ - sourcecode-repository
+ - artifact-registry
+ then:
+ return: true
- utils:
- get_title:
- parameters:
- - tech_asset
- do:
- - if:
- contains:
- item: git
- in: "{tech_asset.tags}"
- then:
- - return:
- "Accidental Secret Leak(Git) risk at {tech_asset.title}: Git Leak Prevention"
- else:
- - return:
- "Accidental Secret Leak risk at {tech_asset.title}"
+ utils:
+ get_title:
+ parameters:
+ - tech_asset
+ do:
+ - if:
+ contains:
+ item: git
+ in: "{tech_asset.tags}"
+ then:
+ - return:
+ "Accidental Secret Leak(Git) risk at {tech_asset.title}: Git Leak Prevention"
+ else:
+ - return:
+ "Accidental Secret Leak risk at {tech_asset.title}"
- get_impact:
- parameters:
- - tech_asset
- do:
- - assign:
- - impact: low
- - highest_confidentiality: "get_highest({tech_asset}, confidentiality)"
- - highest_integrity: "get_highest({tech_asset}, integrity)"
- - highest_availability: "get_highest({tech_asset}, availability)"
- - if:
- or:
- - equal-or-greater:
- as: confidentiality
- first: "{highest_confidentiality}"
- second: confidential
- - equal-or-greater:
- as: integrity
- first: "{highest_integrity}"
- second: critical
- - equal-or-greater:
- as: availability
- first: "{highest_availability}"
- second: critical
- then:
- - assign:
- impact: medium
- - if:
- or:
- - equal-or-greater:
- as: confidentiality
- first: "{highest_confidentiality}"
- second: strictly-confidential
- - equal-or-greater:
- as: integrity
- first: "{highest_integrity}"
- second: mission-critical
- - equal-or-greater:
- as: availability
- first: "{highest_availability}"
- second: mission-critical
- then:
- - assign:
- impact: high
- - return: "{impact}"
+ get_impact:
+ parameters:
+ - tech_asset
+ do:
+ - assign:
+ - impact: low
+ - highest_confidentiality: "get_highest({tech_asset}, confidentiality)"
+ - highest_integrity: "get_highest({tech_asset}, integrity)"
+ - highest_availability: "get_highest({tech_asset}, availability)"
+ - if:
+ or:
+ - equal-or-greater:
+ as: confidentiality
+ first: "{highest_confidentiality}"
+ second: confidential
+ - equal-or-greater:
+ as: integrity
+ first: "{highest_integrity}"
+ second: critical
+ - equal-or-greater:
+ as: availability
+ first: "{highest_availability}"
+ second: critical
+ then:
+ - assign:
+ impact: medium
+ - if:
+ or:
+ - equal-or-greater:
+ as: confidentiality
+ first: "{highest_confidentiality}"
+ second: strictly-confidential
+ - equal-or-greater:
+ as: integrity
+ first: "{highest_integrity}"
+ second: mission-critical
+ - equal-or-greater:
+ as: availability
+ first: "{highest_availability}"
+ second: mission-critical
+ then:
+ - assign:
+ impact: high
+ - return: "{impact}"
- get_highest:
- parameters:
- - tech_asset
- - "type"
- do:
- - assign:
- - value: "{tech_asset.{type}}"
- - loop:
- in: "{tech_asset.data_assets_processed}"
- item: data_id
- do:
- if:
- greater:
- first: "{$model.data_assets.{data_id}.{type}}"
- second: "{value}"
- then:
- - assign:
- value: "{$model.data_assets.{data_id}.{type}}"
- - return: "{value}"
+ get_highest:
+ parameters:
+ - tech_asset
+ - "type"
+ do:
+ - assign:
+ - value: "{tech_asset.{type}}"
+ - loop:
+ in: "{tech_asset.data_assets_processed}"
+ item: data_id
+ do:
+ if:
+ greater:
+ first: "{$model.data_assets.{data_id}.{type}}"
+ second: "{value}"
+ then:
+ - assign:
+ value: "{$model.data_assets.{data_id}.{type}}"
+ - return: "{value}"
diff --git a/test/risk_categories.yaml b/test/risk_categories.yaml
index 572534c3..49b99b8e 100644
--- a/test/risk_categories.yaml
+++ b/test/risk_categories.yaml
@@ -1,8 +1,7 @@
+custom_risk_categories: # used for adding custom risk categories
-individual_risk_categories: # used for adding custom manually identified risks
-
- Some Individual Risk Example:
- id: something-strange
+ - id: something-strange
+ title: Some Individual Risk Example
description: Some text describing the risk category...
impact: Some text describing the impact...
asvs: V0 - Something Strange