@@ -15,13 +15,61 @@ import (
15
15
)
16
16
17
17
func WriteDataFlowDiagramGraphvizDOT (parsedModel * types.Model ,
18
- diagramFilenameDOT string , dpi int , addModelTitle bool ,
18
+ diagramFilenameDOT string , dpi int , addModelTitle bool , addLegend bool ,
19
19
progressReporter progressReporter ) (* os.File , error ) {
20
20
progressReporter .Info ("Writing data flow diagram input" )
21
21
22
22
var dotContent strings.Builder
23
23
dotContent .WriteString ("digraph generatedModel { concentrate=false \n " )
24
24
25
+ if addLegend {
26
+ dotContent .WriteString ("subgraph cluster_shape_legend { \n " )
27
+ dotContent .WriteString (" label=\" Shape legend\" ; \n " )
28
+ dotContent .WriteString (" color=\" lightgrey\" ; \n " )
29
+ dotContent .WriteString (" style=\" dashed\" ; \n " )
30
+ dotContent .WriteString (makeLegendNode ("external_entity_item" , "External Entity" , "0" , "black" , "box" , "solid" , "filled" , "2.0" , VeryLightGray , "1" , Black ))
31
+ dotContent .WriteString (makeLegendNode ("process_item" , "Process" , "0" , "black" , "ellipse" , "solid" , "filled" , "2.0" , VeryLightGray , "1" , Black ))
32
+ dotContent .WriteString (makeLegendNode ("datastore_item" , "Datastore" , "0" , "black" , "cylinder" , "solid" , "filled" , "2.0" , VeryLightGray , "1" , Black ))
33
+ dotContent .WriteString (makeLegendNode ("used_as_client_item" , "Used as client" , "0" , "black" , "octagon" , "solid" , "filled" , "2.0" , VeryLightGray , "1" , Black ))
34
+ dotContent .WriteString ("} \n " )
35
+
36
+ dotContent .WriteString ("subgraph cluster_tenant_legend { \n " )
37
+ dotContent .WriteString (" label=\" Tenant legend\" ; \n " )
38
+ dotContent .WriteString (" color=\" lightgrey\" ; \n " )
39
+ dotContent .WriteString (" style=\" dashed\" ; \n " )
40
+ dotContent .WriteString (makeLegendNode ("single_tenant" , "Single tenant" , "0" , "black" , "box" , "solid" , "filled" , "2.0" , VeryLightGray , "1" , Black ))
41
+ dotContent .WriteString (makeLegendNode ("multi_tenant" , "Multitenant" , "1" , "black" , "box" , "solid" , "filled" , "2.0" , VeryLightGray , "1" , Black ))
42
+ dotContent .WriteString ("} \n " )
43
+
44
+ dotContent .WriteString ("subgraph cluster_label_legend { \n " )
45
+ dotContent .WriteString (" label=\" Label color legend\" ; \n " )
46
+ dotContent .WriteString (" color=\" lightgrey\" ; \n " )
47
+ dotContent .WriteString (" style=\" dashed\" ; \n " )
48
+ dotContent .WriteString (makeLegendNode ("mission_critical" , "Mission Critical Asset" , "0" , Red , "box" , "solid" , "filled" , "3.0" , VeryLightGray , "1" , Red ))
49
+ dotContent .WriteString (makeLegendNode ("critical" , "Critical Asset" , "0" , Amber , "box" , "solid" , "filled" , "3.0" , VeryLightGray , "1" , Amber ))
50
+ dotContent .WriteString (makeLegendNode ("other" , "Important and Other Assets" , "0" , Black , "box" , "solid" , "filled" , "2.0" , VeryLightGray , "1" , Black ))
51
+ dotContent .WriteString ("} \n " )
52
+
53
+ dotContent .WriteString ("subgraph cluster_border_line_legend { \n " )
54
+ dotContent .WriteString (" label=\" Label color legend\" ; \n " )
55
+ dotContent .WriteString (" color=\" lightgrey\" ; \n " )
56
+ dotContent .WriteString (" style=\" dashed\" ; \n " )
57
+ dotContent .WriteString (makeLegendNode ("dotted" , "Model forgery attempt" , "0" , Black , "box" , "dotted " , "filled" , "2.0" , VeryLightGray , "1" , Black ))
58
+ dotContent .WriteString (makeLegendNode ("solid" , "Normal" , "0" , Black , "box" , "solid" , "filled" , "2.0" , VeryLightGray , "1" , Black ))
59
+ dotContent .WriteString ("} \n " )
60
+
61
+ dotContent .WriteString ("subgraph cluster_fill_legend { \n " )
62
+ dotContent .WriteString (" label=\" Shape fill legend (darker for physical machines, brighter for container and even more brighter for serverless\" ; \n " )
63
+ dotContent .WriteString (" color=\" lightgrey\" ; \n " )
64
+ dotContent .WriteString (" style=\" dashed\" ; \n " )
65
+ dotContent .WriteString (makeLegendNode ("invalid_item" , "No data processed or stored, or using unknown technology, or no communication links" , "0" , "black" , "box" , "solid" , "filled" , "2.0" , LightPink , "1" , Black ))
66
+ dotContent .WriteString (makeLegendNode ("internet" , "Asset used over the internet" , "0" , "black" , "box" , "solid" , "filled" , "2.0" , ExtremeLightBlue , "1" , Black ))
67
+ dotContent .WriteString (makeLegendNode ("out_of_scope" , "Out of scope" , "0" , "black" , "box" , "solid" , "filled" , "2.0" , OutOfScopeFancy , "1" , Black ))
68
+ dotContent .WriteString (makeLegendNode ("custom_developed_part" , "Custom developed part" , "0" , "black" , "box" , "solid" , "filled" , "2.0" , CustomDevelopedParts , "1" , Black ))
69
+ dotContent .WriteString (makeLegendNode ("other_assets" , "Other assets" , "0" , "black" , "box" , "solid" , "filled" , "2.0" , VeryLightGray , "1" , Black ))
70
+ dotContent .WriteString ("} \n " )
71
+ }
72
+
25
73
// Metadata init ===============================================================================
26
74
tweaks := ""
27
75
if parsedModel .DiagramTweakNodesep > 0 {
@@ -252,11 +300,8 @@ func WriteDataFlowDiagramGraphvizDOT(parsedModel *types.Model,
252
300
return nil , fmt .Errorf ("error while making diagram same-rank node tweaks: %w" , err )
253
301
}
254
302
dotContent .WriteString (diagramSameRankNodeTweaks )
255
-
256
303
dotContent .WriteString ("}" )
257
304
258
- //fmt.Println(dotContent.String())
259
-
260
305
// Write the DOT file
261
306
file , err := os .Create (filepath .Clean (diagramFilenameDOT ))
262
307
if err != nil {
@@ -270,6 +315,14 @@ func WriteDataFlowDiagramGraphvizDOT(parsedModel *types.Model,
270
315
return file , nil
271
316
}
272
317
318
+ func makeLegendNode (id , title , compartmentBorder , labelColor , shape , borderLineStyle , shapeStyle , borderPenWidth , shapeFillColor , shapePeripheries , shapeBorderColor string ) string {
319
+ return " " + id + ` [
320
+ label=<<table border="0" cellborder="` + compartmentBorder + `" cellpadding="2" cellspacing="0"><tr><td><font point-size="15" color="` + DarkBlue + `">list of technologies` + `</font><br/><font point-size="15" color="` + LightGray + `">technical asset size</font></td></tr><tr><td><b><font color="` + labelColor + `">` + encode (title ) + `</font></b><br/></td></tr><tr><td>attacker attractiveness level</td></tr></table>>
321
+ shape=` + shape + ` style="` + borderLineStyle + `,` + shapeStyle + `" penwidth="` + borderPenWidth + `" fillcolor="` + shapeFillColor + `"
322
+ peripheries=` + shapePeripheries + `
323
+ color="` + shapeBorderColor + "\" \n ]; "
324
+ }
325
+
273
326
// Pen Widths:
274
327
275
328
func determineArrowPenWidth (cl * types.CommunicationLink , parsedModel * types.Model ) string {
@@ -362,35 +415,6 @@ func determineArrowColor(cl *types.CommunicationLink, parsedModel *types.Model)
362
415
}
363
416
// default
364
417
return Black
365
- /*
366
- } else if dataFlow.Authentication != NoneAuthentication {
367
- return Black
368
- } else {
369
- // check for red
370
- for _, sentDataAsset := range dataFlow.DataAssetsSent { // first check if any red?
371
- if ParsedModelRoot.DataAssets[sentDataAsset].Integrity == MissionCritical {
372
- return Red
373
- }
374
- }
375
- for _, receivedDataAsset := range dataFlow.DataAssetsReceived { // first check if any red?
376
- if ParsedModelRoot.DataAssets[receivedDataAsset].Integrity == MissionCritical {
377
- return Red
378
- }
379
- }
380
- // check for amber
381
- for _, sentDataAsset := range dataFlow.DataAssetsSent { // then check if any amber?
382
- if ParsedModelRoot.DataAssets[sentDataAsset].Integrity == Critical {
383
- return Amber
384
- }
385
- }
386
- for _, receivedDataAsset := range dataFlow.DataAssetsReceived { // then check if any amber?
387
- if ParsedModelRoot.DataAssets[receivedDataAsset].Integrity == Critical {
388
- return Amber
389
- }
390
- }
391
- return Black
392
- }
393
- */
394
418
}
395
419
396
420
func GenerateDataFlowDiagramGraphvizImage (dotFile * os.File , targetDir string ,
@@ -638,48 +662,48 @@ func makeTechAssetNode(parsedModel *types.Model, technicalAsset *types.Technical
638
662
return " " + hash (technicalAsset .Id ) + ` [ shape="box" style="filled" fillcolor="` + color + `"
639
663
label=<<b>` + encode (technicalAsset .Title ) + `</b>> penwidth="3.0" color="` + color + `" ];
640
664
`
641
- } else {
642
- var shape , title string
643
- var lineBreak = ""
644
- switch technicalAsset .Type {
645
- case types .ExternalEntity :
646
- shape = "box"
647
- title = technicalAsset .Title
648
- case types .Process :
649
- shape = "ellipse"
650
- title = technicalAsset .Title
651
- case types .Datastore :
652
- shape = "cylinder"
653
- title = technicalAsset .Title
654
- if technicalAsset .Redundant {
655
- lineBreak = "<br/>"
656
- }
657
- }
665
+ }
658
666
659
- if technicalAsset .UsedAsClientByHuman {
660
- shape = "octagon"
667
+ var shape , title string
668
+ var lineBreak = ""
669
+ switch technicalAsset .Type {
670
+ case types .ExternalEntity :
671
+ shape = "box"
672
+ title = technicalAsset .Title
673
+ case types .Process :
674
+ shape = "ellipse"
675
+ title = technicalAsset .Title
676
+ case types .Datastore :
677
+ shape = "cylinder"
678
+ title = technicalAsset .Title
679
+ if technicalAsset .Redundant {
680
+ lineBreak = "<br/>"
661
681
}
682
+ }
662
683
663
- // RAA = Relative Attacker Attractiveness
664
- raa := technicalAsset .RAA
665
- var attackerAttractivenessLabel string
666
- if technicalAsset .OutOfScope {
667
- attackerAttractivenessLabel = "<font point-size=\" 15\" color=\" #603112\" >RAA: out of scope</font>"
668
- } else {
669
- attackerAttractivenessLabel = "<font point-size=\" 15\" color=\" #603112\" >RAA: " + fmt .Sprintf ("%.0f" , raa ) + " %</font>"
670
- }
684
+ if technicalAsset .UsedAsClientByHuman {
685
+ shape = "octagon"
686
+ }
671
687
672
- compartmentBorder := "0"
673
- if technicalAsset .MultiTenant {
674
- compartmentBorder = "1"
675
- }
688
+ // RAA = Relative Attacker Attractiveness
689
+ raa := technicalAsset .RAA
690
+ var attackerAttractivenessLabel string
691
+ if technicalAsset .OutOfScope {
692
+ attackerAttractivenessLabel = "<font point-size=\" 15\" color=\" #603112\" >RAA: out of scope</font>"
693
+ } else {
694
+ attackerAttractivenessLabel = "<font point-size=\" 15\" color=\" #603112\" >RAA: " + fmt .Sprintf ("%.0f" , raa ) + " %</font>"
695
+ }
676
696
677
- return " " + hash (technicalAsset .Id ) + ` [
678
- label=<<table border="0" cellborder="` + compartmentBorder + `" cellpadding="2" cellspacing="0"><tr><td><font point-size="15" color="` + DarkBlue + `">` + lineBreak + technicalAsset .Technologies .String () + `</font><br/><font point-size="15" color="` + LightGray + `">` + technicalAsset .Size .String () + `</font></td></tr><tr><td><b><font color="` + determineTechnicalAssetLabelColor (technicalAsset , parsedModel ) + `">` + encode (title ) + `</font></b><br/></td></tr><tr><td>` + attackerAttractivenessLabel + `</td></tr></table>>
679
- shape=` + shape + ` style="` + determineShapeBorderLineStyle (technicalAsset ) + `,` + determineShapeStyle (technicalAsset ) + `" penwidth="` + determineShapeBorderPenWidth (technicalAsset , parsedModel ) + `" fillcolor="` + determineShapeFillColor (technicalAsset , parsedModel ) + `"
680
- peripheries=` + strconv .Itoa (determineShapePeripheries (technicalAsset )) + `
681
- color="` + determineShapeBorderColor (technicalAsset , parsedModel ) + "\" \n ]; "
697
+ compartmentBorder := "0"
698
+ if technicalAsset .MultiTenant {
699
+ compartmentBorder = "1"
682
700
}
701
+
702
+ return " " + hash (technicalAsset .Id ) + ` [
703
+ label=<<table border="0" cellborder="` + compartmentBorder + `" cellpadding="2" cellspacing="0"><tr><td><font point-size="15" color="` + DarkBlue + `">` + lineBreak + technicalAsset .Technologies .String () + `</font><br/><font point-size="15" color="` + LightGray + `">` + technicalAsset .Size .String () + `</font></td></tr><tr><td><b><font color="` + determineTechnicalAssetLabelColor (technicalAsset , parsedModel ) + `">` + encode (title ) + `</font></b><br/></td></tr><tr><td>` + attackerAttractivenessLabel + `</td></tr></table>>
704
+ shape=` + shape + ` style="` + determineShapeBorderLineStyle (technicalAsset ) + `,` + determineShapeStyle (technicalAsset ) + `" penwidth="` + determineShapeBorderPenWidth (technicalAsset , parsedModel ) + `" fillcolor="` + determineShapeFillColor (technicalAsset , parsedModel ) + `"
705
+ peripheries=` + strconv .Itoa (determineShapePeripheries (technicalAsset )) + `
706
+ color="` + determineShapeBorderColor (technicalAsset , parsedModel ) + "\" \n ]; "
683
707
}
684
708
685
709
func determineShapeStyle (ta * types.TechnicalAsset ) string {
@@ -744,29 +768,6 @@ func determineShapeBorderColor(ta *types.TechnicalAsset, parsedModel *types.Mode
744
768
}
745
769
}
746
770
return Black
747
- /*
748
- if what.Integrity == MissionCritical {
749
- for _, dataFlow := range IncomingTechnicalCommunicationLinksMappedByTargetId[what.ID] {
750
- if !dataFlow.Readonly && dataFlow.Authentication == NoneAuthentication {
751
- return Red
752
- }
753
- }
754
- }
755
-
756
- if what.Integrity == Critical {
757
- for _, dataFlow := range IncomingTechnicalCommunicationLinksMappedByTargetId[what.ID] {
758
- if !dataFlow.Readonly && dataFlow.Authentication == NoneAuthentication {
759
- return Amber
760
- }
761
- }
762
- }
763
-
764
- if len(what.DataAssetsProcessed) == 0 && len(what.DataAssetsStored) == 0 {
765
- return Pink // pink, because it's strange when too many technical assets process no data... some are ok, but many in a diagram is a sign of model forgery...
766
- }
767
-
768
- return Black
769
- */
770
771
}
771
772
772
773
func determineShapePeripheries (ta * types.TechnicalAsset ) int {
@@ -786,7 +787,6 @@ func determineShapeBorderLineStyle(ta *types.TechnicalAsset) string {
786
787
787
788
// red when >= confidential data stored in unencrypted technical asset
788
789
func determineTechnicalAssetLabelColor (ta * types.TechnicalAsset , model * types.Model ) string {
789
- // TODO: Just move into main.go and let the generated risk determine the color, don't duplicate the logic here
790
790
// Check for red
791
791
if ta .Integrity == types .MissionCritical {
792
792
return Red
@@ -816,29 +816,6 @@ func determineTechnicalAssetLabelColor(ta *types.TechnicalAsset, model *types.Mo
816
816
}
817
817
}
818
818
return Black
819
- /*
820
- if what.Encrypted {
821
- return Black
822
- } else {
823
- if what.Confidentiality == StrictlyConfidential {
824
- return Red
825
- }
826
- for _, storedDataAsset := range what.DataAssetsStored {
827
- if ParsedModelRoot.DataAssets[storedDataAsset].Confidentiality == StrictlyConfidential {
828
- return Red
829
- }
830
- }
831
- if what.Confidentiality == Confidential {
832
- return Amber
833
- }
834
- for _, storedDataAsset := range what.DataAssetsStored {
835
- if ParsedModelRoot.DataAssets[storedDataAsset].Confidentiality == Confidential {
836
- return Amber
837
- }
838
- }
839
- return Black
840
- }
841
- */
842
819
}
843
820
844
821
func GenerateDataAssetDiagramGraphvizImage (dotFile * os.File , targetDir string ,
0 commit comments