From 34540ad4df88c98507c44ee1f36201511fd4b7e2 Mon Sep 17 00:00:00 2001 From: Simon Date: Sat, 20 May 2023 11:31:42 +0200 Subject: [PATCH] v0.4.12 --- CHANGELOG.md | 9 + app/package-lock.json | 4 +- app/package.json | 2 +- package.json | 2 +- .../configuration/configuration.component.ts | 15 +- .../results-analysis.component.scss | 1 + src/app/home/home.component.html | 18 +- src/app/model/component.ts | 23 +- src/app/model/database.ts | 10 +- src/app/model/dfd-model.ts | 5 +- src/app/model/diagram.ts | 2 + src/app/model/system-context.ts | 1 + .../threat-identification.component.html | 10 +- .../threat-identification.component.ts | 7 + .../threat-sources.component.html | 9 +- .../threat-sources.component.ts | 7 + .../attack-scenario.component.html | 22 +- .../container-tree.component.html | 22 +- .../container-tree.component.scss | 9 + .../container-tree.component.ts | 15 +- .../countermeasure-table.component.html | 4 +- .../countermeasure-table.component.scss | 46 +--- .../countermeasure-table.component.ts | 34 ++- .../countermeasure.component.html | 22 +- .../device-assets.component.html | 10 +- .../device-assets/device-assets.component.ts | 7 + src/app/modeling/diagram/diagram.component.ts | 31 ++- .../issue-table/issue-table.component.html | 2 +- .../issue-table/issue-table.component.scss | 12 +- .../issue-table/issue-table.component.ts | 18 +- .../mitigation-process.component.html | 23 +- src/app/modeling/modeling.component.html | 10 +- src/app/modeling/modeling.component.scss | 2 +- src/app/modeling/modeling.component.ts | 19 ++ .../properties/properties.component.html | 2 + .../question-dialog.component.html | 7 +- .../question-dialog.component.scss | 7 + .../question-dialog.component.ts | 9 +- src/app/modeling/stack/stack.component.html | 23 +- src/app/modeling/stack/stack.component.scss | 16 +- src/app/modeling/stack/stack.component.ts | 41 +++- .../stencil-palette.component.html | 2 +- .../stencil-palette.component.scss | 10 +- .../test-case-table.component.html | 2 +- .../test-case-table.component.scss | 32 +-- .../test-case-table.component.ts | 18 +- .../test-case/test-case.component.html | 20 +- .../modeling/testing/testing.component.html | 7 +- src/app/modeling/testing/testing.component.ts | 7 + .../threat-table/threat-table.component.html | 4 +- .../threat-table/threat-table.component.scss | 42 +--- .../threat-table/threat-table.component.ts | 20 +- .../cve-search/cve-search.component.html | 1 + .../cve-search/cve-search.component.ts | 10 +- .../file-info-dialog.component.html | 37 --- .../model-changes.component.html | 11 + .../model-changes.component.scss} | 0 .../model-changes.component.spec.ts} | 12 +- .../model-changes/model-changes.component.ts} | 12 +- .../model-info/model-info.component.html | 228 ++++++++++-------- .../model-info/model-info.component.ts | 26 +- .../model-tasks/model-tasks.component.html | 11 + .../model-tasks/model-tasks.component.scss | 0 .../model-tasks/model-tasks.component.spec.ts | 23 ++ .../model-tasks/model-tasks.component.ts | 20 ++ .../nav-tree/nav-tree.component.html | 6 +- .../components/nav-tree/nav-tree.component.ts | 3 + .../progress-tracker.component.html | 4 +- .../status-bar/status-bar.component.html | 14 +- src/app/shared/shared.module.ts | 5 +- src/app/util/data.service.ts | 2 +- src/app/util/dialog.service.ts | 34 ++- src/app/util/local-storage.service.ts | 1 + src/assets/default-config-file.json | 38 ++- src/assets/i18n/de.json | 15 +- src/assets/i18n/en.json | 15 +- src/assets/version.json | 2 +- src/styles.scss | 34 +++ src/stylesPropList.scss | 4 + src/stylesTable.scss | 49 ++++ src/stylesTheme.scss | 3 +- 81 files changed, 879 insertions(+), 443 deletions(-) delete mode 100644 src/app/shared/components/file-info-dialog/file-info-dialog.component.html create mode 100644 src/app/shared/components/model-info/model-changes/model-changes.component.html rename src/app/shared/components/{file-info-dialog/file-info-dialog.component.scss => model-info/model-changes/model-changes.component.scss} (100%) rename src/app/shared/components/{file-info-dialog/file-info-dialog.component.spec.ts => model-info/model-changes/model-changes.component.spec.ts} (50%) rename src/app/shared/components/{file-info-dialog/file-info-dialog.component.ts => model-info/model-changes/model-changes.component.ts} (76%) create mode 100644 src/app/shared/components/model-info/model-tasks/model-tasks.component.html create mode 100644 src/app/shared/components/model-info/model-tasks/model-tasks.component.scss create mode 100644 src/app/shared/components/model-info/model-tasks/model-tasks.component.spec.ts create mode 100644 src/app/shared/components/model-info/model-tasks/model-tasks.component.ts create mode 100644 src/stylesTable.scss diff --git a/CHANGELOG.md b/CHANGELOG.md index c60fee5f..176cb9b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## 0.4.12 + +* Project information: separate tabs for general, tasks and notes, changes, and progress tracker +* Out of scope: visualized as dotted line +* Diagram: allow flexible resizing +* Object Tree: filter for element/component +* UI improvements: name field width, reorder components, table row height, visualization of optional steps, software component with optional port +* Fix: undefined generation settings in new diagram + ## 0.4.11 * Added file info dialog with (incomplete) list of changes diff --git a/app/package-lock.json b/app/package-lock.json index 4be49076..0ff3b0a8 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -1,12 +1,12 @@ { "name": "ttmodeler", - "version": "0.4.11", + "version": "0.4.12", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "ttmodeler", - "version": "0.4.11" + "version": "0.4.12" } } } diff --git a/app/package.json b/app/package.json index 65b15b6d..10a1e268 100644 --- a/app/package.json +++ b/app/package.json @@ -1,6 +1,6 @@ { "name": "ttmodeler", - "version": "0.4.11", + "version": "0.4.12", "description": "Thing Threat Modeler for Internet of Things Devices", "homepage": "https://www.simon-liebl.de/TTM", "author": { diff --git a/package.json b/package.json index 2a539400..0b89d16d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ttmodeler", - "version": "0.4.11", + "version": "0.4.12", "description": "Thing Threat Modeler for Internet of Things Devices", "homepage": "https://www.simon-liebl.de/TTM", "author": { diff --git a/src/app/configuration/configuration.component.ts b/src/app/configuration/configuration.component.ts index 5e6c4475..71731e11 100644 --- a/src/app/configuration/configuration.component.ts +++ b/src/app/configuration/configuration.component.ts @@ -7,6 +7,8 @@ import { LocalStorageService, LocStorageKeys } from '../util/local-storage.servi import { ThemeService } from '../util/theme.service'; import { WarningDialogComponent } from './warning-dialog/warning-dialog.component'; +import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'; +import { NavTreeBase } from '../shared/components/nav-tree/nav-tree-base'; @Component({ selector: 'app-configuration', @@ -22,7 +24,8 @@ export class ConfigurationComponent extends SideNavBase implements OnInit { this._selectedNode = val; } - constructor(public theme: ThemeService, public dataService: DataService, private locStorageService: LocalStorageService, private dialog: MatDialog) { + constructor(public theme: ThemeService, public dataService: DataService, private locStorageService: LocalStorageService, private dialog: MatDialog, + private router: Router, private route: ActivatedRoute) { super(); } @@ -48,6 +51,16 @@ export class ConfigurationComponent extends SideNavBase implements OnInit { }); } } + + this.router.events.subscribe(event => { + if (event instanceof NavigationEnd) { + this.route.queryParams.subscribe(params => { + if (params['index'] != null) { + this.SetSelectedTabIndex(params['index']); + } + }); + } + }); } public GetSelectedTabIndex() { diff --git a/src/app/dashboard/results-analysis/results-analysis.component.scss b/src/app/dashboard/results-analysis/results-analysis.component.scss index 916a6069..21b0a026 100644 --- a/src/app/dashboard/results-analysis/results-analysis.component.scss +++ b/src/app/dashboard/results-analysis/results-analysis.component.scss @@ -1,4 +1,5 @@ @import 'stylesTheme.scss'; +@import 'stylesTable.scss'; .selected-entry { td { diff --git a/src/app/home/home.component.html b/src/app/home/home.component.html index 28fc712d..64f110e2 100644 --- a/src/app/home/home.component.html +++ b/src/app/home/home.component.html @@ -24,11 +24,11 @@

{{'pages.home.menu.saveProject' | translate}} @@ -58,7 +58,7 @@

@@ -78,7 +78,7 @@

cloud_queue - in {{GetRepoName(proj)}} + in {{GetRepoName(proj)}} edit_off - in {{GetFilePath(proj)}} + in {{GetFilePath(proj)}} @@ -127,7 +127,7 @@

@@ -137,7 +137,7 @@

cloud_queue - in {{GetRepoName(conf)}} + in {{GetRepoName(conf)}} edit_off
+
{{ 'general.Threats' | translate }} + + + + + +
@@ -24,7 +30,7 @@ [class.highlight-light]="selectedThreat === dt && !theme.IsDarkMode" [class.highlight-dark]="selectedThreat === dt && theme.IsDarkMode" matTooltip="{{dt.Name}}" matTooltipShowDelay="1000"> chevron_right -
{{dt.Name}}
+
{{dt.GetLongName()}}
{{'properties.Impact' | translate}}: {{GetLMHName(dt.Impact) | translate}}
diff --git a/src/app/modeling/analysis/threat-identification/threat-identification.component.ts b/src/app/modeling/analysis/threat-identification/threat-identification.component.ts index cf80013d..e2818de4 100644 --- a/src/app/modeling/analysis/threat-identification/threat-identification.component.ts +++ b/src/app/modeling/analysis/threat-identification/threat-identification.component.ts @@ -94,6 +94,13 @@ export class ThreatIdentificationComponent implements OnInit { this.selectedThreat = dt; } + public ResetNumbers() { + const arr = this.systemThreats; + for (let i = 0; i < arr.length; i++) { + arr[i].Number = (i+1).toString(); + } + } + public GetLMHValues() { return LowMediumHighNumberUtil.GetKeys(); } diff --git a/src/app/modeling/analysis/threat-sources/threat-sources.component.html b/src/app/modeling/analysis/threat-sources/threat-sources.component.html index 28f43eaf..15d6431c 100644 --- a/src/app/modeling/analysis/threat-sources/threat-sources.component.html +++ b/src/app/modeling/analysis/threat-sources/threat-sources.component.html @@ -17,7 +17,8 @@
-
{{'general.ThreatSources' | translate}} +
{{'general.ThreatSources' | translate}} + @@ -25,11 +26,15 @@ + + + +
chevron_right -
{{src.Name}}
+
{{src.GetLongName()}}
{{'general.Likelihood' | translate}}: {{GetLMHName(src.Likelihood) | translate}}
diff --git a/src/app/modeling/analysis/threat-sources/threat-sources.component.ts b/src/app/modeling/analysis/threat-sources/threat-sources.component.ts index e3d52216..47bb5a3b 100644 --- a/src/app/modeling/analysis/threat-sources/threat-sources.component.ts +++ b/src/app/modeling/analysis/threat-sources/threat-sources.component.ts @@ -45,6 +45,13 @@ export class ThreatSourcesComponent implements OnInit { this.dataService.Project.DeleteThreatActor(ta); } + public ResetNumbers() { + const arr = this.threatSources.Sources; + for (let i = 0; i < arr.length; i++) { + arr[i].Number = (i+1).toString(); + } + } + public drop(event: CdkDragDrop) { moveItemInArray(this.threatSources.Data['sourceIDs'], event.previousIndex, event.currentIndex); } diff --git a/src/app/modeling/attack-scenario/attack-scenario.component.html b/src/app/modeling/attack-scenario/attack-scenario.component.html index d95cce68..83f1813c 100644 --- a/src/app/modeling/attack-scenario/attack-scenario.component.html +++ b/src/app/modeling/attack-scenario/attack-scenario.component.html @@ -1,16 +1,12 @@
- + {{ 'properties.Name' | translate }} + - - {{'properties.Status' | translate}} - - {{GetThreatStateName(state) | translate}} - - - sync_problem - + {{ 'general.Number' | translate }} @@ -18,6 +14,14 @@
+ + {{'properties.Status' | translate}} + + {{GetThreatStateName(state) | translate}} + + + sync_problem +
{{ 'general.Target' | translate }} diff --git a/src/app/modeling/container-tree/container-tree.component.html b/src/app/modeling/container-tree/container-tree.component.html index 288fdf49..b136c417 100644 --- a/src/app/modeling/container-tree/container-tree.component.html +++ b/src/app/modeling/container-tree/container-tree.component.html @@ -2,20 +2,32 @@ - - {{node.GetProperty('Name')}} {{GetNodeInfo(node)}} - bolt + + {{node.GetProperty('Name')}} {{GetNodeInfo(node)}} + bolt + + -
+
{{node.GetProperty('Name')}} - bolt + bolt + +
diff --git a/src/app/modeling/container-tree/container-tree.component.scss b/src/app/modeling/container-tree/container-tree.component.scss index ff7442cc..213f65ec 100644 --- a/src/app/modeling/container-tree/container-tree.component.scss +++ b/src/app/modeling/container-tree/container-tree.component.scss @@ -14,6 +14,15 @@ list-style-type: none; } +.onHover { + .hoverIcon { + display: none; + } + &:hover .hoverIcon { + display: block; + } +} + /* * This padding sets alignment of the nested nodes. */ diff --git a/src/app/modeling/container-tree/container-tree.component.ts b/src/app/modeling/container-tree/container-tree.component.ts index 61f6679e..d6279784 100644 --- a/src/app/modeling/container-tree/container-tree.component.ts +++ b/src/app/modeling/container-tree/container-tree.component.ts @@ -13,7 +13,7 @@ import { ThemeService } from '../../util/theme.service'; styleUrls: ['./container-tree.component.scss'] }) export class ContainerTreeComponent implements OnInit { - + private _filteredElement: ViewElementBase; private subscription: Subscription; private _elements: IContainer; private infoMap = new Map(); @@ -34,15 +34,28 @@ export class ContainerTreeComponent implements OnInit { public set elements(val: IContainer) { this._elements = val; this.selectedElement = null; + this.filteredElement = null; this.RefreshTree(); }; @Input() public selectedElement: ViewElementBase; + public get filteredElement(): ViewElementBase { + return this._filteredElement; + } + @Input() + public set filteredElement(val: ViewElementBase) { + this._filteredElement = val; + this.filterChanged.emit(val); + } + @Output() public selectionChanged = new EventEmitter(); + @Output() + public filterChanged = new EventEmitter(); + constructor(public dataService: DataService, public theme: ThemeService) { } diff --git a/src/app/modeling/countermeasure-table/countermeasure-table.component.html b/src/app/modeling/countermeasure-table/countermeasure-table.component.html index cd5a2360..102017d3 100644 --- a/src/app/modeling/countermeasure-table/countermeasure-table.component.html +++ b/src/app/modeling/countermeasure-table/countermeasure-table.component.html @@ -57,7 +57,7 @@ - @@ -109,7 +109,7 @@ {{GetTargets(entry)}} - diff --git a/src/app/modeling/countermeasure-table/countermeasure-table.component.scss b/src/app/modeling/countermeasure-table/countermeasure-table.component.scss index e008b850..3ea1136c 100644 --- a/src/app/modeling/countermeasure-table/countermeasure-table.component.scss +++ b/src/app/modeling/countermeasure-table/countermeasure-table.component.scss @@ -1,45 +1 @@ -@import 'stylesTheme.scss'; - -.selected-countermeasure { - td { - @extend .primary-color; - } -} - -.removed-countermeasure { - opacity: 0.3; -} - -.tools { - display: block; - float: left; - height: 27px; - width: calc(100% - 20px); - font-size: 12px; -} - -.toolBtn { - width: 30px; - min-width: 30px; - padding: 0px; - line-height: 25px; - margin-left: 5px; -} - -.toolBtn-Selected { - background-color: rgba($color: #fff, $alpha: 0.15); -} - -.filterInput { - margin-left: 5px; - width: 200px; - height: 25px; -} - -th, td { - font-size: small; -} - -.disable { - pointer-events: none; -} \ No newline at end of file +@import 'stylesTable.scss'; \ No newline at end of file diff --git a/src/app/modeling/countermeasure-table/countermeasure-table.component.ts b/src/app/modeling/countermeasure-table/countermeasure-table.component.ts index a7b57fe5..6712e44c 100644 --- a/src/app/modeling/countermeasure-table/countermeasure-table.component.ts +++ b/src/app/modeling/countermeasure-table/countermeasure-table.component.ts @@ -25,7 +25,9 @@ export class CountermeasureTableComponent implements OnInit { private isCalculatingCountermeasures = false; private _selectedNode: INavigationNode; private _selectedObject: ViewElementBase; + private _filteredObject: ViewElementBase; private _countermeasures: Countermeasure[] = []; + private _unfilteredCountermeasuers: Countermeasure[] = []; private _selectedCountermeasures: Countermeasure[] = []; public displayedColumns = []; @@ -41,8 +43,9 @@ export class CountermeasureTableComponent implements OnInit { public get Countermeasures(): Countermeasure[] { return this._countermeasures; } public set Countermeasures(val: Countermeasure[]) { + if (this._filteredObject) val = val.filter(x => x.Targets.includes(this._filteredObject)); this._countermeasures = val; - let mySort = (data: Countermeasure, sortHeaderId: string) => { + const mySort = (data: Countermeasure, sortHeaderId: string) => { if (sortHeaderId == 'name') return data.Name; if (sortHeaderId == 'number') return Number(data.Number); if (sortHeaderId == 'state') return data.MappingState; @@ -53,7 +56,7 @@ export class CountermeasureTableComponent implements OnInit { if (sortHeaderId == 'targets') return this.GetTargets(data); console.error('Missing sorting header'); }; - let myFilter = (data: Countermeasure, filter: string) => { + const myFilter = (data: Countermeasure, filter: string) => { let search = filter.trim().toLowerCase(); let res = data.Name.toLowerCase().indexOf(search); if (res == -1 && data.Control) res = data.Control.Name.toLowerCase().indexOf(search); @@ -95,7 +98,11 @@ export class CountermeasureTableComponent implements OnInit { @Input() public set selectedObject(val: ViewElementBase) { if (val && this._selectedObject?.ID == val.ID) return; this._selectedObject = val; - this.selectedCountermeasures = this.Countermeasures.filter(x => x.Targets.includes(val)); + this.selectedCountermeasures = this._unfilteredCountermeasuers.filter(x => x.Targets.includes(val)); + } + @Input() public set filteredObject(val: ViewElementBase) { + this._filteredObject = val; + this.RefreshCountermeasures(); } @Output() public selectedObjectChanged = new EventEmitter(); @@ -135,18 +142,19 @@ export class CountermeasureTableComponent implements OnInit { public RefreshCountermeasures() { setTimeout(() => { this.Countermeasures = []; - if (this._selectedNode?.data) { - if (this._selectedNode?.data instanceof Diagram) { - this.Countermeasures = this.mitigationEngine.GenerateDiagramMitigations(this._selectedNode.data); - } - else if (this._selectedNode?.data instanceof MyComponentStack) { - this.Countermeasures = this.mitigationEngine.GenerateStackMitigations(this._selectedNode.data); + this._unfilteredCountermeasuers = []; + if (this._selectedNode?.data) { + if (this._selectedNode?.data instanceof Diagram) { + this.Countermeasures = this._unfilteredCountermeasuers = this.mitigationEngine.GenerateDiagramMitigations(this._selectedNode.data); + } + else if (this._selectedNode?.data instanceof MyComponentStack) { + this.Countermeasures = this._unfilteredCountermeasuers = this.mitigationEngine.GenerateStackMitigations(this._selectedNode.data); + } } - } - this.countermeasureCountChanged.emit(this.dataSourceActive.data.length); - - this.isCalculatingCountermeasures = false; + this.countermeasureCountChanged.emit(this.dataSourceActive.data.length); + + this.isCalculatingCountermeasures = false; }, 10); } diff --git a/src/app/modeling/countermeasure/countermeasure.component.html b/src/app/modeling/countermeasure/countermeasure.component.html index b03b6ca7..b0187355 100644 --- a/src/app/modeling/countermeasure/countermeasure.component.html +++ b/src/app/modeling/countermeasure/countermeasure.component.html @@ -1,9 +1,20 @@
- + {{ 'properties.Name' | translate }} + - + + {{ 'general.Number' | translate }} + + + {{'messages.error.numberAlreadyExists' | translate}} + + +
+ {{'properties.Status' | translate}} {{GetMitigationStateName(state) | translate}} @@ -23,13 +34,6 @@ add - - {{ 'general.Number' | translate }} - - - {{'messages.error.numberAlreadyExists' | translate}} - -
{{ 'general.Targets' | translate }} diff --git a/src/app/modeling/device-assets/device-assets.component.html b/src/app/modeling/device-assets/device-assets.component.html index 3fe458e6..2aea6fe0 100644 --- a/src/app/modeling/device-assets/device-assets.component.html +++ b/src/app/modeling/device-assets/device-assets.component.html @@ -102,12 +102,18 @@
-
{{ 'general.Datas' | translate }}
+
{{ 'general.Datas' | translate }} + + + + + +
chevron_right -
{{data.Name}}
+
{{data.GetLongName()}}
{{'properties.Sensitivity' | translate}}: {{GetSensitivity(data.Sensitivity) | translate}}
diff --git a/src/app/modeling/device-assets/device-assets.component.ts b/src/app/modeling/device-assets/device-assets.component.ts index 049e676f..f8e2cc6d 100644 --- a/src/app/modeling/device-assets/device-assets.component.ts +++ b/src/app/modeling/device-assets/device-assets.component.ts @@ -137,6 +137,13 @@ export class DeviceAssetsComponent implements OnInit { this.SelectObject(event, data); } + public ResetNumbers() { + const arr = this.assetGroup.GetMyDataFlat(); + for (let i = 0; i < arr.length; i++) { + arr[i].Number = (i+1).toString(); + } + } + public GetSensitivity(val: LowMediumHighNumber): string { return LowMediumHighNumberUtil.ToString(val); } diff --git a/src/app/modeling/diagram/diagram.component.ts b/src/app/modeling/diagram/diagram.component.ts index c7ae1094..9af332fa 100644 --- a/src/app/modeling/diagram/diagram.component.ts +++ b/src/app/modeling/diagram/diagram.component.ts @@ -640,6 +640,7 @@ export abstract class CanvasBase { this.Canvas.selection = false; this.Canvas.selectionFullyContained = true; this.Canvas.targetFindTolerance = 2; + this.Canvas.uniformScaling = false; // scale not proportionally this.Canvas.setWidth(cc.clientWidth); this.CanvasScreenWidth = cc.clientWidth; @@ -715,6 +716,7 @@ export abstract class CanvasBase { this.Diagram.Elements.GetChildrenFlat().forEach(x => { x.NameChanged.subscribe(y => this.changeObjectName(x.ID)); + x.OutOfScopeChanged.subscribe(y => this.changeObjectBorder(x.ID)); if (x instanceof DFDElement) { x.TypeChanged.subscribe(y => this.changeObjectType(x.ID, y.Name)); x.PhysicalElementChanged.subscribe(y => this.changeObjectPhysicalElement(x.ID, y)); @@ -1301,6 +1303,7 @@ export abstract class CanvasBase { flow.ShowName = this.ShowName; let flowObj = this.createFlow(this.tmpFlowLine.x1, this.tmpFlowLine.y1, start[0], start[1], flow); flow.NameChanged.subscribe(x => this.changeObjectName(flow.ID)); + flow.OutOfScopeChanged.subscribe(x => this.changeObjectBorder(flow.ID)); flow.LineTypeChanged?.subscribe(x => this.flowChangeLineType(flow.ID, x)); flow.ArrowPosChanged?.subscribe(y => this.flowUpdateFlowArrow(flow.ID)); flow.BendFlowChanged?.subscribe(y => this.flowChangeBending(flow.ID, y)); @@ -2006,6 +2009,30 @@ export abstract class CanvasBase { } } + protected changeObjectBorder(id: string) { + const obj = this.getCanvasElementByID(id); + const ele = this.getViewBaseElement(id); + let border = null; + if (obj['_objects']) { + border = obj['_objects'].find(x => x[CProps.myType] == CTypes.ElementBorder); + } + else if (obj[CProps.myType] == CTypes.DataFlowLine) { + border = obj; + } + + if (border && ele) { + if (ele.OutOfScope) { + border.set('strokeDashArray', [2, 2]); + } + else { + delete border['strokeDashArray']; + } + + border.set('dirty', true); + this.Canvas.requestRenderAll(); + } + } + protected renderIcon(ctx: CanvasRenderingContext2D, left, top, styleOverride, fabricObject) { //if (fabricObject[CProps.myType] == 'Annotation') return; var size = 24; @@ -2218,7 +2245,7 @@ export class HWDFCanvas extends CanvasBase { if (!this.Diagram.Canvas && this.Diagram.DiagramType == DiagramTypes.Hardware) { let stencil = this.dataService.Config.GetStencilTypes().find(x => x.ElementTypeID == ElementTypeIDs.PhyTrustArea && x.Name == 'Device Casing'); if (!stencil) stencil = this.dataService.Config.GetStencilTypes().find(x => x.IsDefault && x.ElementTypeID == ElementTypeIDs.PhyTrustArea); - let element = this.createElement({ stencilRef: { name: '', stencilID: stencil.ID } }, 100, 10); + const element = this.createElement({ stencilRef: { name: '', stencilID: stencil.ID } }, 5, 5); let obj = this.getCanvasElementByID(element.ID); element.Name = this.dataService.Project.FindDeviceOfDiagram(this.Diagram)?.Name + "'s Casing"; obj.set('scaleX', (cc.clientWidth - 200) / obj.width); @@ -2275,6 +2302,7 @@ export class HWDFCanvas extends CanvasBase { } element.NameChanged.subscribe(x => this.changeObjectName(element.ID)); + element.OutOfScopeChanged.subscribe(x => this.changeObjectBorder(element.ID)); element.TypeChanged.subscribe(x => this.changeObjectType(element.ID, x.Name)); element.PhysicalElementChanged.subscribe(x => this.changeObjectPhysicalElement(element.ID, x)); let x = 0; //ev.e.dataTransfer.getData("offsetX"); @@ -2892,6 +2920,7 @@ export class CtxCanvas extends CanvasBase { } element.NameChanged.subscribe(x => this.changeObjectName(element.ID)); + element.OutOfScopeChanged.subscribe(x => this.changeObjectBorder(element.ID)); element.TypeChanged.subscribe(x => this.changeObjectType(element.ID, ContextElementTypeUtil.ToString(element.Type))); this.Diagram.Elements.AddChild(element); diff --git a/src/app/modeling/issue-table/issue-table.component.html b/src/app/modeling/issue-table/issue-table.component.html index 8173b501..e5c58864 100644 --- a/src/app/modeling/issue-table/issue-table.component.html +++ b/src/app/modeling/issue-table/issue-table.component.html @@ -14,7 +14,7 @@ - + {{'pages.modeling.issuetable.noIssues' | translate}} diff --git a/src/app/modeling/issue-table/issue-table.component.scss b/src/app/modeling/issue-table/issue-table.component.scss index 15219380..3ea1136c 100644 --- a/src/app/modeling/issue-table/issue-table.component.scss +++ b/src/app/modeling/issue-table/issue-table.component.scss @@ -1,11 +1 @@ -@import 'stylesTheme.scss'; - -.selected-issue { - td { - @extend .primary-color; - } -} - -th, td { - font-size: small; -} \ No newline at end of file +@import 'stylesTable.scss'; \ No newline at end of file diff --git a/src/app/modeling/issue-table/issue-table.component.ts b/src/app/modeling/issue-table/issue-table.component.ts index 54dee4d5..212ba3a7 100644 --- a/src/app/modeling/issue-table/issue-table.component.ts +++ b/src/app/modeling/issue-table/issue-table.component.ts @@ -19,7 +19,9 @@ export class IssueTableComponent implements OnInit { private isCalculatingIssues = false; private _selectedNode: INavigationNode; private _selectedObject: ViewElementBase; - private issues: IDFDIssue[] = []; + private _filteredObject: ViewElementBase; + private _issues: IDFDIssue[] = []; + private _unfilteredIssues: IDFDIssue[] = []; private _selectedIssues: IDFDIssue[] = []; public displayedColumns = []; @@ -41,15 +43,20 @@ export class IssueTableComponent implements OnInit { @Input() public set selectedObject(val: ViewElementBase) { if (val && this._selectedObject?.ID == val.ID) return; this._selectedObject = val; - this.selectedIssues = this.Issues.filter(x => x.Element?.ID == val?.ID); + this.selectedIssues = this._unfilteredIssues.filter(x => x.Element?.ID == val?.ID); + } + @Input() public set filteredObject(val: ViewElementBase) { + this._filteredObject = val; + this.RefreshIssues(); } @Output() public selectedObjectChanged = new EventEmitter(); @Output() public issueCountChanged = new EventEmitter(); - public get Issues(): IDFDIssue[] { return this.issues; } + public get Issues(): IDFDIssue[] { return this._issues; } public set Issues(val: IDFDIssue[]) { - this.issues = val; + if (this._filteredObject) val = val.filter(x => x.Element == this._filteredObject); + this._issues = val; this.dataSource = new MatTableDataSource(val); this.dataSource.sort = this.sort; this.dataSource.sortingDataAccessor = (data: IDFDIssue, sortHeaderId: string) => { @@ -93,7 +100,7 @@ export class IssueTableComponent implements OnInit { public RefreshIssues() { if (this._selectedNode?.data) { if (this._selectedNode?.data instanceof HWDFDiagram) { - this.Issues = this.dfdCop.CheckDFDRules(this._selectedNode.data); + this.Issues = this._unfilteredIssues = this.dfdCop.CheckDFDRules(this._selectedNode.data); } else if (this._selectedNode?.data instanceof MyComponentStack) { //this.Issues = this.dfdCop.CheckDFDRules(this._selectedNode.data); @@ -101,6 +108,7 @@ export class IssueTableComponent implements OnInit { } else { this.Issues = []; + this._unfilteredIssues = []; } setTimeout(() => { diff --git a/src/app/modeling/mitigation-process/mitigation-process.component.html b/src/app/modeling/mitigation-process/mitigation-process.component.html index 6907caac..505753cd 100644 --- a/src/app/modeling/mitigation-process/mitigation-process.component.html +++ b/src/app/modeling/mitigation-process/mitigation-process.component.html @@ -1,15 +1,12 @@
- + {{ 'properties.Name' | translate }} + - - {{'properties.Status' | translate}} - - {{GetMitigationProcessStateName(state) | translate}} - - - + {{ 'general.Number' | translate }} @@ -18,13 +15,19 @@
+ {{'properties.Status' | translate}} + + {{GetMitigationProcessStateName(state) | translate}} + + + north_east + + {{'general.Countermeasures' | translate}} {{map.Name}} - north_east - {{ 'properties.Description' | translate }} diff --git a/src/app/modeling/modeling.component.html b/src/app/modeling/modeling.component.html index ded30cdf..a82b91fa 100644 --- a/src/app/modeling/modeling.component.html +++ b/src/app/modeling/modeling.component.html @@ -13,7 +13,7 @@ - + @@ -56,25 +56,25 @@ {{'general.AttackScenarios' | translate}} - + {{'general.Countermeasures' | translate}} - + {{'general.TestCases' | translate}} - + {{'general.Issues' | translate}} - + diff --git a/src/app/modeling/modeling.component.scss b/src/app/modeling/modeling.component.scss index 915d46a0..3e658f2c 100644 --- a/src/app/modeling/modeling.component.scss +++ b/src/app/modeling/modeling.component.scss @@ -14,7 +14,7 @@ } ::ng-deep .mat-tab-labels .mat-tab-label { - min-width: 150px !important; + min-width: 160px !important; } } diff --git a/src/app/modeling/modeling.component.ts b/src/app/modeling/modeling.component.ts index 301e6759..5eed6fbe 100644 --- a/src/app/modeling/modeling.component.ts +++ b/src/app/modeling/modeling.component.ts @@ -53,6 +53,7 @@ interface ITabContainer { export class ModelingComponent extends SideNavBase implements OnInit { private nodes: INavigationNode[]; private _selectedObject: any; + private _filteredObject: ViewElementBase; private _selectedTabIndex = 0; public get selectedNode(): INavigationNode { @@ -87,6 +88,11 @@ export class ModelingComponent extends SideNavBase implements OnInit { this._selectedObject = val; } + public get filteredObject(): ViewElementBase { return this._filteredObject; } + public set filteredObject(val: ViewElementBase) { + this._filteredObject = val; + } + public get selectedComponent(): MyComponent { return this.selectedObject instanceof MyComponent ? this.selectedObject as MyComponent : null; } public hasBottomTabGroup: boolean = true; @@ -177,6 +183,7 @@ export class ModelingComponent extends SideNavBase implements OnInit { this.selectedTabIndex = event; this.selectedObject = null; + this.filteredObject = null; if (this.tabs.length > previousIndex && this.tabs[previousIndex] && !this.tabs[previousIndex].keepOpen) { setTimeout(() => { @@ -749,6 +756,8 @@ export class ModelingComponent extends SideNavBase implements OnInit { children: [ { name: () => 'Characterization & Scope', + nameExtension: '(opt)', + tooltipExtension: this.translate.instant('pages.modeling.optionalStep'), canSelect: true, iconAlignLeft: true, icon: 'edit_note', @@ -757,6 +766,8 @@ export class ModelingComponent extends SideNavBase implements OnInit { }, { name: () => 'Business Objectives & Impact', + nameExtension: '(opt)', + tooltipExtension: this.translate.instant('pages.modeling.optionalStep'), canSelect: true, iconAlignLeft: true, icon: 'outlined_flag', @@ -765,6 +776,8 @@ export class ModelingComponent extends SideNavBase implements OnInit { }, { name: () => 'System Interaction', + nameExtension: '(opt)', + tooltipExtension: this.translate.instant('pages.modeling.optionalStep'), canSelect: true, iconAlignLeft: true, icon: 'signpost', @@ -773,6 +786,8 @@ export class ModelingComponent extends SideNavBase implements OnInit { }, { name: () => 'Use Cases', + nameExtension: '(opt)', + tooltipExtension: this.translate.instant('pages.modeling.optionalStep'), canSelect: true, iconAlignLeft: true, icon: 'explore', @@ -781,6 +796,8 @@ export class ModelingComponent extends SideNavBase implements OnInit { }, { name: () => 'Assets', + nameExtension: '(opt*)', + tooltipExtension: this.translate.instant('pages.modeling.optionalAsset'), canSelect: true, iconAlignLeft: true, icon: AssetGroup.Icon, @@ -803,6 +820,8 @@ export class ModelingComponent extends SideNavBase implements OnInit { }, { name: () => 'Threat Sources', + nameExtension: '(opt)', + tooltipExtension: this.translate.instant('pages.modeling.optionalStep'), canSelect: true, iconAlignLeft: true, icon: 'portrait', diff --git a/src/app/modeling/properties/properties.component.html b/src/app/modeling/properties/properties.component.html index 9cc075ba..df46f004 100644 --- a/src/app/modeling/properties/properties.component.html +++ b/src/app/modeling/properties/properties.component.html @@ -64,6 +64,8 @@ + + {{protocol.Name}} diff --git a/src/app/modeling/stack/question-dialog/question-dialog.component.html b/src/app/modeling/stack/question-dialog/question-dialog.component.html index af26baec..c09ea38a 100644 --- a/src/app/modeling/stack/question-dialog/question-dialog.component.html +++ b/src/app/modeling/stack/question-dialog/question-dialog.component.html @@ -31,7 +31,12 @@

{{'general.Notes' | translate}}

- {{'pages.modeling.stack.questiondialog.noConfiguredQuestions' | translate}} + + {{'pages.modeling.stack.questiondialog.noConfiguredQuestions' | translate}} + + diff --git a/src/app/modeling/stack/question-dialog/question-dialog.component.scss b/src/app/modeling/stack/question-dialog/question-dialog.component.scss index e69de29b..93aba249 100644 --- a/src/app/modeling/stack/question-dialog/question-dialog.component.scss +++ b/src/app/modeling/stack/question-dialog/question-dialog.component.scss @@ -0,0 +1,7 @@ +.toolBtn { + width: 30px; + min-width: 30px; + padding: 0px; + line-height: 25px; + margin-left: 5px; +} \ No newline at end of file diff --git a/src/app/modeling/stack/question-dialog/question-dialog.component.ts b/src/app/modeling/stack/question-dialog/question-dialog.component.ts index b2a48a1c..813cb474 100644 --- a/src/app/modeling/stack/question-dialog/question-dialog.component.ts +++ b/src/app/modeling/stack/question-dialog/question-dialog.component.ts @@ -4,6 +4,7 @@ import { MyComponent } from '../../../model/component'; import { OptionTypes, OptionTypesUtil, RuleTypes, AttackVector, ThreatQuestion } from '../../../model/threat-model'; import { DataService } from '../../../util/data.service'; import { DialogService } from '../../../util/dialog.service'; +import { Router } from '@angular/router'; @Component({ selector: 'app-question-dialog', @@ -34,7 +35,7 @@ export class QuestionDialogComponent implements OnInit { return this.selectedComponent != this.components[0]; } - constructor(public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data, public dataService: DataService, private dialog: DialogService) { + constructor(public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data, public dataService: DataService, private dialog: DialogService, private router: Router) { } ngOnInit(): void { @@ -121,4 +122,10 @@ export class QuestionDialogComponent implements OnInit { public Prev() { this.selectedComponent = this.components[this.components.indexOf(this.selectedComponent)-1]; } + + public NavigateToSettings() { + this.router.navigate(['configuration'], { + queryParams: { index: this.selectedComponent.Type.ComponentTypeID } + }); + } } diff --git a/src/app/modeling/stack/stack.component.html b/src/app/modeling/stack/stack.component.html index 4e351271..0c5b1b9a 100644 --- a/src/app/modeling/stack/stack.component.html +++ b/src/app/modeling/stack/stack.component.html @@ -25,12 +25,15 @@

{{group.Name}}

- - + +
+ +
{{GetComponentPort(comp)}}
+
@@ -58,6 +61,14 @@ security {{'pages.modeling.diagram.addCountermeasure' | translate}} + + diff --git a/src/app/modeling/stack/stack.component.scss b/src/app/modeling/stack/stack.component.scss index 50704efa..c0efc72c 100644 --- a/src/app/modeling/stack/stack.component.scss +++ b/src/app/modeling/stack/stack.component.scss @@ -25,12 +25,18 @@ max-width: 100px; } -.component { +.component-base { background: none; margin: 5px; cursor: pointer; + border-radius: 5px; +} + +.component { + @extend .component-base; font-size: large; height: 75px; + min-width: 130px; border-radius: 10px; } @@ -43,6 +49,14 @@ background-color: rgba(255, 255, 255, 0.1); } +.component-port { + position: absolute; + left: 4px; + bottom: 4px; + border: 2px solid; + padding: 0 2px; +} + .tools { display: block; float: left; diff --git a/src/app/modeling/stack/stack.component.ts b/src/app/modeling/stack/stack.component.ts index 2f74c5ce..0ba31972 100644 --- a/src/app/modeling/stack/stack.component.ts +++ b/src/app/modeling/stack/stack.component.ts @@ -1,13 +1,14 @@ import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { MatMenuTrigger } from '@angular/material/menu'; -import { MyComponentTypeGroup, MyComponent, MyComponentStack, MyComponentType } from '../../model/component'; +import { MyComponentTypeGroup, MyComponent, MyComponentStack, MyComponentType, MyComponentTypeIDs } from '../../model/component'; import { DataService } from '../../util/data.service'; import { DialogService } from '../../util/dialog.service'; import { StringExtension } from '../../util/string-extension'; import { ThemeService } from '../../util/theme.service'; import { IDragDropData } from '../stencil-palette/stencil-palette.component'; import { QuestionDialogComponent } from './question-dialog/question-dialog.component'; +import { PropertyEditTypes } from '../../model/database'; @Component({ selector: 'app-stack', @@ -68,11 +69,12 @@ export class StackComponent implements OnInit { } public OnDrop(event, group: MyComponentTypeGroup) { - let data: IDragDropData = JSON.parse(event.dataTransfer.getData('dragDropData')); + const data: IDragDropData = JSON.parse(event.dataTransfer.getData('dragDropData')); let type: MyComponentType; if (data.componentTypeID) type = this.dataService.Config.GetMyComponentType(data.componentTypeID); if (!type) type = this.dataService.Config.CreateMyComponentType(group); - let c = this.dataService.Project.CreateComponent(type); + const c = this.dataService.Project.CreateComponent(type); + c.SyncNameToTypeName = true; c.Name = type.Name; c.IsActive = true; c.IsThirdParty = false; @@ -89,6 +91,15 @@ export class StackComponent implements OnInit { else return 'black'; } + public GetComponentPort(comp: MyComponent): string { + if (comp.TypeID == MyComponentTypeIDs.Software) { + const port = comp.GetProperties().find(x => x.Type == PropertyEditTypes.PortBox); + if (port) return comp.GetProperty(port.ID); + } + + return null; + } + public AddThreat(item: MyComponent) { if (!item) item = this.selectedComponent; if (item) { @@ -139,6 +150,30 @@ export class StackComponent implements OnInit { }); } + public MoveLeft(item: MyComponent) { + const group = this.GetGroups().find(x => this.GetComponents(x).includes(item)); + const groupComps = this.GetComponents(group); + const allComps = this.stack.GetChildren(); + const prevIndex = allComps.indexOf(item); + const newIndex = allComps.indexOf(groupComps[groupComps.indexOf(item)-1]); + if (prevIndex > 0 && newIndex >= 0) { + const arr = this.stack.Data['childrenIDs']; + arr.splice(newIndex, 0, arr.splice(prevIndex, 1)[0]); + } + } + + public MoveRight(item: MyComponent) { + const group = this.GetGroups().find(x => this.GetComponents(x).includes(item)); + const groupComps = this.GetComponents(group); + const allComps = this.stack.GetChildren(); + const prevIndex = allComps.indexOf(item); + const newIndex = allComps.indexOf(groupComps[groupComps.indexOf(item)+1]); + if (prevIndex < allComps.length-1 && newIndex >= 0) { + const arr = this.stack.Data['childrenIDs']; + arr.splice(newIndex, 0, arr.splice(prevIndex, 1)[0]); + } + } + public OpenContextMenu(event, comp: MyComponent) { event.preventDefault(); diff --git a/src/app/modeling/stencil-palette/stencil-palette.component.html b/src/app/modeling/stencil-palette/stencil-palette.component.html index 817c7b67..7b257b23 100644 --- a/src/app/modeling/stencil-palette/stencil-palette.component.html +++ b/src/app/modeling/stencil-palette/stencil-palette.component.html @@ -1,4 +1,4 @@ - + diff --git a/src/app/modeling/stencil-palette/stencil-palette.component.scss b/src/app/modeling/stencil-palette/stencil-palette.component.scss index 8758fcdf..52712c02 100644 --- a/src/app/modeling/stencil-palette/stencil-palette.component.scss +++ b/src/app/modeling/stencil-palette/stencil-palette.component.scss @@ -1,9 +1,13 @@ +.stencil-headers-align .mat-expansion-panel-header { + height: 35px !important; +} -.example-headers-align .mat-expansion-panel-header-description { +.stencil-headers-align .mat-expansion-panel-header-description { justify-content: right; + flex-grow: 1; } -.example-headers-align .mat-expansion-panel { +.stencil-headers-align .mat-expansion-panel { background-color: transparent; } @@ -74,7 +78,7 @@ } .draggable-element { - margin: 3px; + margin: 2px 2px -3px 2px; cursor: pointer; } diff --git a/src/app/modeling/test-case-table/test-case-table.component.html b/src/app/modeling/test-case-table/test-case-table.component.html index 615b85c8..7ca5f701 100644 --- a/src/app/modeling/test-case-table/test-case-table.component.html +++ b/src/app/modeling/test-case-table/test-case-table.component.html @@ -36,7 +36,7 @@ - + {{'pages.modeling.testcasetable.noTestCases' | translate}} diff --git a/src/app/modeling/test-case-table/test-case-table.component.scss b/src/app/modeling/test-case-table/test-case-table.component.scss index 1ff0def5..3ea1136c 100644 --- a/src/app/modeling/test-case-table/test-case-table.component.scss +++ b/src/app/modeling/test-case-table/test-case-table.component.scss @@ -1,31 +1 @@ -@import 'stylesTheme.scss'; - -.selected-test-case { - td { - @extend .primary-color; - } -} - -th, td { - font-size: small; -} - -.tools { - display: block; - float: left; - height: 27px; - width: calc(100% - 20px); - font-size: 12px; -} - -.toolBtn { - width: 30px; - min-width: 30px; - padding: 0px; - line-height: 25px; - margin-left: 5px; -} - -.toolBtn-Selected { - background-color: rgba($color: #fff, $alpha: 0.15); -} \ No newline at end of file +@import 'stylesTable.scss'; \ No newline at end of file diff --git a/src/app/modeling/test-case-table/test-case-table.component.ts b/src/app/modeling/test-case-table/test-case-table.component.ts index 853e726d..bfd3de7c 100644 --- a/src/app/modeling/test-case-table/test-case-table.component.ts +++ b/src/app/modeling/test-case-table/test-case-table.component.ts @@ -19,7 +19,9 @@ export class TestCaseTableComponent implements OnInit { private isCalculatingTestCases = false; private _selectedNode: INavigationNode; private _selectedObject: ViewElementBase; - private testCases: TestCase[] = []; + private _filteredObject: ViewElementBase; + private _testCases: TestCase[] = []; + private _unfilteredTestCases: TestCase[] = []; private _selectedTestCases: TestCase[] = []; public autoRefreshTestCases = true; @@ -46,15 +48,20 @@ export class TestCaseTableComponent implements OnInit { @Input() public set selectedObject(val: ViewElementBase) { if (val && this._selectedObject?.ID == val.ID) return; this._selectedObject = val; - this.selectedTestCases = this.TestCases.filter(x => this.GetElements(x).includes(val)); + this.selectedTestCases = this._unfilteredTestCases.filter(x => this.GetElements(x).includes(val)); + } + @Input() public set filteredObject(val: ViewElementBase) { + this._filteredObject = val; + this.RefreshTestCases(); } @Output() public selectedObjectChanged = new EventEmitter(); @Output() public testCaseCountChanged = new EventEmitter(); - public get TestCases(): TestCase[] { return this.testCases; } + public get TestCases(): TestCase[] { return this._testCases; } public set TestCases(val: TestCase[]) { - this.testCases = val; + if (this._filteredObject) val = val.filter(x => x.LinkedElements.includes(this._filteredObject)); + this._testCases = val; this.dataSource = new MatTableDataSource(val); this.dataSource.sort = this.sort; this.dataSource.sortingDataAccessor = (data: TestCase, sortHeaderId: string) => { @@ -102,8 +109,9 @@ export class TestCaseTableComponent implements OnInit { public RefreshTestCases() { setTimeout(() => { this.TestCases = []; + this._unfilteredTestCases = []; if (this._selectedNode?.data) { - this.TestCases = this.dataService.Project.GetTestCases().filter(x => this.GetElements(x).length > 0); + this.TestCases = this._unfilteredTestCases = this.dataService.Project.GetTestCases().filter(x => this.GetElements(x).length > 0); } this.testCaseCountChanged.emit(this.TestCases.length); diff --git a/src/app/modeling/test-case/test-case.component.html b/src/app/modeling/test-case/test-case.component.html index 68c172b2..2c20e6f4 100644 --- a/src/app/modeling/test-case/test-case.component.html +++ b/src/app/modeling/test-case/test-case.component.html @@ -1,25 +1,27 @@
- + {{'general.Name' | translate}} - - {{'properties.Status' | translate}} - - {{GetTestCaseStateName(state) | translate}} - - - - + {{ 'general.Number' | translate }} {{'messages.error.numberAlreadyExists' | translate}} +
+ + {{'properties.Status' | translate}} + + {{GetTestCaseStateName(state) | translate}} + + + +
{{ 'properties.Description' | translate }} diff --git a/src/app/modeling/testing/testing.component.html b/src/app/modeling/testing/testing.component.html index 8ac26601..ca34ea4e 100644 --- a/src/app/modeling/testing/testing.component.html +++ b/src/app/modeling/testing/testing.component.html @@ -2,7 +2,12 @@
-
{{'general.TestCases' | translate}} +
{{'general.TestCases' | translate}} + + + + +
diff --git a/src/app/modeling/testing/testing.component.ts b/src/app/modeling/testing/testing.component.ts index c964b78a..007e6ea1 100644 --- a/src/app/modeling/testing/testing.component.ts +++ b/src/app/modeling/testing/testing.component.ts @@ -31,6 +31,13 @@ export class TestingComponent implements OnInit { this.dataService.Project.DeleteTestCase(tc); } + public ResetNumbers() { + const arr = this.dataService.Project.GetTestCases(); + for (let i = 0; i < arr.length; i++) { + arr[i].Number = (i+1).toString(); + } + } + public drop(event: CdkDragDrop) { moveItemInArray(this.testing.Data['testCaseIDs'], event.previousIndex, event.currentIndex); } diff --git a/src/app/modeling/threat-table/threat-table.component.html b/src/app/modeling/threat-table/threat-table.component.html index 7d8ba9eb..59165e43 100644 --- a/src/app/modeling/threat-table/threat-table.component.html +++ b/src/app/modeling/threat-table/threat-table.component.html @@ -63,7 +63,7 @@ - @@ -122,7 +122,7 @@ - diff --git a/src/app/modeling/threat-table/threat-table.component.scss b/src/app/modeling/threat-table/threat-table.component.scss index 230d9f09..3ea1136c 100644 --- a/src/app/modeling/threat-table/threat-table.component.scss +++ b/src/app/modeling/threat-table/threat-table.component.scss @@ -1,41 +1 @@ -@import 'stylesTheme.scss'; - -.selected-threat { - td { - @extend .primary-color; - } -} - -.removed-threat { - opacity: 0.3; -} - -.tools { - display: block; - float: left; - height: 27px; - width: calc(100% - 20px); - font-size: 12px; -} - -.toolBtn { - width: 30px; - min-width: 30px; - padding: 0px; - line-height: 25px; - margin-left: 5px; -} - -.toolBtn-Selected { - background-color: rgba($color: #fff, $alpha: 0.15); -} - -.filterInput { - margin-left: 5px; - width: 200px; - height: 25px; -} - -th, td { - font-size: small; -} \ No newline at end of file +@import 'stylesTable.scss'; \ No newline at end of file diff --git a/src/app/modeling/threat-table/threat-table.component.ts b/src/app/modeling/threat-table/threat-table.component.ts index d297745e..4cc7cec3 100644 --- a/src/app/modeling/threat-table/threat-table.component.ts +++ b/src/app/modeling/threat-table/threat-table.component.ts @@ -12,7 +12,7 @@ import { INavigationNode } from '../../shared/components/nav-tree/nav-tree.compo import { DialogService } from '../../util/dialog.service'; import { DataChangedTypes, ViewElementBase } from '../../model/database'; import { TranslateService } from '@ngx-translate/core'; -import { Control, Countermeasure, MitigationStates } from '../../model/mitigations'; +import { Countermeasure } from '../../model/mitigations'; import { StringExtension } from '../../util/string-extension'; @Component({ @@ -25,7 +25,9 @@ export class ThreatTableComponent implements OnInit { private isCalculatingThreats = false; private _selectedNode: INavigationNode; private _selectedObject: ViewElementBase; + private _filteredObject: ViewElementBase; private _attackScenarios: AttackScenario[] = []; + private _unfilteredAttackScenarios: AttackScenario[] = []; private _selectedThreats: AttackScenario[] = []; private countermeasureCounts = {}; @@ -43,8 +45,9 @@ export class ThreatTableComponent implements OnInit { public get AttackScenarios(): AttackScenario[] { return this._attackScenarios; } public set AttackScenarios(val: AttackScenario[]) { + if (this._filteredObject) val = val.filter(x => x.Targets.includes(this._filteredObject) || x.Target == this._filteredObject); this._attackScenarios = val; - let mySort = (data: AttackScenario, sortHeaderId: string) => { + const mySort = (data: AttackScenario, sortHeaderId: string) => { if (sortHeaderId == 'name') return data.Name; if (sortHeaderId == 'number') return Number(data.Number); if (sortHeaderId == 'state') return data.MappingState; @@ -57,7 +60,7 @@ export class ThreatTableComponent implements OnInit { if (sortHeaderId == 'elements') return this.GetTargets(data); console.error('Missing sorting header'); }; - let myFilter = (data: AttackScenario, filter: string) => { + const myFilter = (data: AttackScenario, filter: string) => { let search = filter.trim().toLowerCase(); let res = data.Name.toLowerCase().indexOf(search); if (res == -1 && data.AttackVector) res = data.AttackVector.Name.toLowerCase().indexOf(search); @@ -104,7 +107,11 @@ export class ThreatTableComponent implements OnInit { @Input() public set selectedObject(val: ViewElementBase) { if (val && this._selectedObject?.ID == val.ID) return; this._selectedObject = val; - this.selectedThreats = this.AttackScenarios.filter(x => x.Targets.includes(val)); + this.selectedThreats = this._unfilteredAttackScenarios.filter(x => x.Targets.includes(val)); + } + @Input() public set filteredObject(val: ViewElementBase) { + this._filteredObject = val; + this.RefreshThreats(); } @Output() public selectedObjectChanged = new EventEmitter(); @@ -152,12 +159,13 @@ export class ThreatTableComponent implements OnInit { public RefreshThreats() { setTimeout(() => { this.AttackScenarios = []; + this._unfilteredAttackScenarios = []; if (this._selectedNode?.data) { if (this._selectedNode?.data instanceof Diagram) { - this.AttackScenarios = this.threatEngine.GenerateDiagramThreats(this._selectedNode.data); + this.AttackScenarios = this._unfilteredAttackScenarios = this.threatEngine.GenerateDiagramThreats(this._selectedNode.data); } else if (this._selectedNode?.data instanceof MyComponentStack) { - this.AttackScenarios = this.threatEngine.GenerateStackThreats(this._selectedNode.data); + this.AttackScenarios = this._unfilteredAttackScenarios = this.threatEngine.GenerateStackThreats(this._selectedNode.data); } } diff --git a/src/app/shared/components/cve-search/cve-search.component.html b/src/app/shared/components/cve-search/cve-search.component.html index 3186e865..e42d19ca 100644 --- a/src/app/shared/components/cve-search/cve-search.component.html +++ b/src/app/shared/components/cve-search/cve-search.component.html @@ -5,6 +5,7 @@ search + diff --git a/src/app/shared/components/cve-search/cve-search.component.ts b/src/app/shared/components/cve-search/cve-search.component.ts index 96b8a949..222dc563 100644 --- a/src/app/shared/components/cve-search/cve-search.component.ts +++ b/src/app/shared/components/cve-search/cve-search.component.ts @@ -40,6 +40,7 @@ export class CveSearchComponent implements OnInit { public SearchString: string = ''; public ExactSearch: boolean = false; + public IsSearching: boolean = false; public NoSearchResult = false; public dataSource: MatTableDataSource; @@ -64,9 +65,11 @@ export class CveSearchComponent implements OnInit { } public Search() { - if (this.SearchString?.length > 0) { + if (this.SearchString?.length > 0 && !this.IsSearching) { + this.IsSearching = true; + this.dataSource = new MatTableDataSource([]); + this.dataSource.paginator = this.paginator; this.http.get('https://services.nvd.nist.gov/rest/json/cves/2.0?keywordSearch=' + this.SearchString).subscribe(res => { - //console.log(res); this.searchHistory[this.element.ID + this.viewID] = this.SearchString; this.locStorage.Set(LocStorageKeys.CVE_SEARCH_HISTORY, JSON.stringify(this.searchHistory)); if (res['vulnerabilities']?.length > 0) { @@ -82,10 +85,9 @@ export class CveSearchComponent implements OnInit { this.NoSearchResult = newArray.length == 0; } else { - this.dataSource = new MatTableDataSource([]); - this.dataSource.paginator = this.paginator; this.NoSearchResult = true; } + this.IsSearching = false; }); } } diff --git a/src/app/shared/components/file-info-dialog/file-info-dialog.component.html b/src/app/shared/components/file-info-dialog/file-info-dialog.component.html deleted file mode 100644 index 23f0f735..00000000 --- a/src/app/shared/components/file-info-dialog/file-info-dialog.component.html +++ /dev/null @@ -1,37 +0,0 @@ -

{{ dataService.Project ? (dataService.Project.Name) : (dataService.SelectedGHConfig ? dataService.SelectedGHConfig.name : dataService.Config?.Name) }}

- -
- - - - {{ 'dialog.modelinfo.title' | translate }} - - - - - - - - {{ 'dialog.fileinfo.Changes' | translate }} - - - -

{{ 'dialog.fileinfo.noChanges' | translate }}

-
- -

{{ 'dialog.fileinfo.incompleteList' | translate }}:

-
    -
  • {{ e }}
  • -
-
-
-
-
-
-
- -
-
- - - \ No newline at end of file diff --git a/src/app/shared/components/model-info/model-changes/model-changes.component.html b/src/app/shared/components/model-info/model-changes/model-changes.component.html new file mode 100644 index 00000000..de89a1b3 --- /dev/null +++ b/src/app/shared/components/model-info/model-changes/model-changes.component.html @@ -0,0 +1,11 @@ +
+ +

{{ 'dialog.modelchanges.noChanges' | translate }}

+
+ +

{{ 'dialog.modelchanges.incompleteList' | translate }}:

+
    +
  • {{ e }}
  • +
+
+
\ No newline at end of file diff --git a/src/app/shared/components/file-info-dialog/file-info-dialog.component.scss b/src/app/shared/components/model-info/model-changes/model-changes.component.scss similarity index 100% rename from src/app/shared/components/file-info-dialog/file-info-dialog.component.scss rename to src/app/shared/components/model-info/model-changes/model-changes.component.scss diff --git a/src/app/shared/components/file-info-dialog/file-info-dialog.component.spec.ts b/src/app/shared/components/model-info/model-changes/model-changes.component.spec.ts similarity index 50% rename from src/app/shared/components/file-info-dialog/file-info-dialog.component.spec.ts rename to src/app/shared/components/model-info/model-changes/model-changes.component.spec.ts index 5d4a30f3..24571f0e 100644 --- a/src/app/shared/components/file-info-dialog/file-info-dialog.component.spec.ts +++ b/src/app/shared/components/model-info/model-changes/model-changes.component.spec.ts @@ -1,18 +1,18 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { FileInfoDialogComponent } from './file-info-dialog.component'; +import { ModelChangesComponent } from './model-changes.component'; -describe('FileInfoDialogComponent', () => { - let component: FileInfoDialogComponent; - let fixture: ComponentFixture; +describe('ModelChangesComponent', () => { + let component: ModelChangesComponent; + let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ FileInfoDialogComponent ] + declarations: [ ModelChangesComponent ] }) .compileComponents(); - fixture = TestBed.createComponent(FileInfoDialogComponent); + fixture = TestBed.createComponent(ModelChangesComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/src/app/shared/components/file-info-dialog/file-info-dialog.component.ts b/src/app/shared/components/model-info/model-changes/model-changes.component.ts similarity index 76% rename from src/app/shared/components/file-info-dialog/file-info-dialog.component.ts rename to src/app/shared/components/model-info/model-changes/model-changes.component.ts index 8eceb6a2..8ad32752 100644 --- a/src/app/shared/components/file-info-dialog/file-info-dialog.component.ts +++ b/src/app/shared/components/model-info/model-changes/model-changes.component.ts @@ -1,14 +1,14 @@ import { Component, OnInit } from '@angular/core'; -import { DataService } from '../../../util/data.service'; import { TranslateService } from '@ngx-translate/core'; -import { StringExtension } from '../../../util/string-extension'; +import { DataService } from '../../../../util/data.service'; +import { StringExtension } from '../../../../util/string-extension'; @Component({ - selector: 'app-file-info-dialog', - templateUrl: './file-info-dialog.component.html', - styleUrls: ['./file-info-dialog.component.scss'] + selector: 'app-model-changes', + templateUrl: './model-changes.component.html', + styleUrls: ['./model-changes.component.scss'] }) -export class FileInfoDialogComponent implements OnInit { +export class ModelChangesComponent implements OnInit { public Changes: string[] = []; diff --git a/src/app/shared/components/model-info/model-info.component.html b/src/app/shared/components/model-info/model-info.component.html index 12c80895..a212090b 100644 --- a/src/app/shared/components/model-info/model-info.component.html +++ b/src/app/shared/components/model-info/model-info.component.html @@ -1,110 +1,130 @@
-

{{Project.Name}}

-
-
-
- - {{ 'properties.Description' | translate }} - - - - {{ 'properties.Version' | translate }} - - -
-
-
- - -
- + + + + {{ 'dialog.modelinfo.general' | translate }} + + +

{{Project.Name}}

+
+
+
+ + {{ 'properties.Description' | translate }} + + + + {{ 'properties.Version' | translate }} + + +
+
+
+ + +
+ +
+
+ +
+
+
-
- +
+
+ +
{{ 'properties.Participants' | translate }}
+ + person +
{{user.Name}}
+ + +
+
+
+
+
+ + {{ 'properties.Name' | translate }} + + + +
+ + {{ 'properties.Email' | translate }} + + +
+
-
-
-
-
- -
{{ 'properties.Participants' | translate }}
- - person -
{{user.Name}}
- - -
-
-
-
-
- - {{ 'properties.Name' | translate }} - - - -
- - {{ 'properties.Email' | translate }} - - +
+

{{'general.Settings' | translate}}

+
+ {{'general.TestCases' | translate}} +
+
+
+ + + + + {{'dialog.modelinfo.History' | translate}} + + + {{GHProject.name}} + history + + + +
+ + + + + + + + +
{{count-index}}){{commit.date | localDateTime}}-{{commit.commiter}}: + {{commit.message}} + +
+ + +
-
-
-
-
-

{{'general.Tasks' | translate}}

- -
-
-

{{'general.Notes' | translate}}

- -
-
-

{{'general.Settings' | translate}}

-
- {{'general.TestCases' | translate}} -
-
-
- - - - - {{'dialog.modelinfo.History' | translate}} - - - {{GHProject.name}} - history - - - - - - - - - - - - -
{{count-index}}){{commit.date | localDateTime}}-{{commit.commiter}}: - {{commit.message}} - -
-
-
-
-
+ + + + + {{ 'status-bar.TasksAndNotes' | translate }} + + + + + + {{ 'dialog.modelchanges.Changes' | translate }} + + + + + + {{ 'dialog.progress.title' | translate }} + +

{{ 'status-bar.progress' | translate }}: {{GetProgress()}}

+ +
+
\ No newline at end of file diff --git a/src/app/shared/components/model-info/model-info.component.ts b/src/app/shared/components/model-info/model-info.component.ts index 1eaf0c70..5d93bf77 100644 --- a/src/app/shared/components/model-info/model-info.component.ts +++ b/src/app/shared/components/model-info/model-info.component.ts @@ -6,6 +6,7 @@ import { ThemeService } from '../../../util/theme.service'; import imageCompression from 'browser-image-compression'; import { DialogService } from '../../../util/dialog.service'; +import { LocStorageKeys, LocalStorageService } from '../../../util/local-storage.service'; @Component({ selector: 'app-model-info', @@ -17,15 +18,13 @@ export class ModelInfoComponent implements OnInit { public GHProject: IGHFile; public Project: ProjectFile; - public isEdtingArray: boolean[][] = [[], []]; - public selectedUser: IUserInfo; - public commits: IGHCommitInfo[]; + @Output() public refreshNodes = new EventEmitter(); - constructor(public dataService: DataService, public theme: ThemeService, private dialog: DialogService) { } + constructor(public dataService: DataService, public theme: ThemeService, private dialog: DialogService, private locStorage: LocalStorageService) { } ngOnInit(): void { this.GHProject = this.dataService.SelectedGHProject; @@ -96,4 +95,23 @@ export class ModelInfoComponent implements OnInit { if (!projImg || projImg['width'] < 25) return '25px'; return (projImg['width'] - 25).toString() + 'px'; } + + public GetProgress() { + if (this.dataService.Project) { + let vals = Object.values(this.dataService.Project.ProgressTracker); + if (vals.length == 0) return '0%'; + + return (100 * vals.filter(x => x == true).length / vals.length).toFixed(0) + '%'; + } + } + + public GetSelectedTabIndex() { + let index = this.locStorage.Get(LocStorageKeys.PAGE_MODELING_MODEL_TAB_INDEX); + if (index != null) return index; + else return 0; + } + + public SetSelectedTabIndex(event) { + this.locStorage.Set(LocStorageKeys.PAGE_MODELING_MODEL_TAB_INDEX, event); + } } diff --git a/src/app/shared/components/model-info/model-tasks/model-tasks.component.html b/src/app/shared/components/model-info/model-tasks/model-tasks.component.html new file mode 100644 index 00000000..ac29b1f4 --- /dev/null +++ b/src/app/shared/components/model-info/model-tasks/model-tasks.component.html @@ -0,0 +1,11 @@ + + + +
+

{{'general.Tasks' | translate}}

+ +
+
+

{{'general.Notes' | translate}}

+ +
\ No newline at end of file diff --git a/src/app/shared/components/model-info/model-tasks/model-tasks.component.scss b/src/app/shared/components/model-info/model-tasks/model-tasks.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/shared/components/model-info/model-tasks/model-tasks.component.spec.ts b/src/app/shared/components/model-info/model-tasks/model-tasks.component.spec.ts new file mode 100644 index 00000000..db9fdc7b --- /dev/null +++ b/src/app/shared/components/model-info/model-tasks/model-tasks.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ModelTasksComponent } from './model-tasks.component'; + +describe('ModelTasksComponent', () => { + let component: ModelTasksComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ModelTasksComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ModelTasksComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/components/model-info/model-tasks/model-tasks.component.ts b/src/app/shared/components/model-info/model-tasks/model-tasks.component.ts new file mode 100644 index 00000000..8407f374 --- /dev/null +++ b/src/app/shared/components/model-info/model-tasks/model-tasks.component.ts @@ -0,0 +1,20 @@ +import { Component, OnInit } from '@angular/core'; +import { ProjectFile } from '../../../../model/project-file'; +import { DataService } from '../../../../util/data.service'; + +@Component({ + selector: 'app-model-tasks', + templateUrl: './model-tasks.component.html', + styleUrls: ['./model-tasks.component.scss'] +}) +export class ModelTasksComponent implements OnInit { + + public Project: ProjectFile; + + constructor(public dataService: DataService) { } + + ngOnInit(): void { + this.Project = this.dataService.Project; + } + +} diff --git a/src/app/shared/components/nav-tree/nav-tree.component.html b/src/app/shared/components/nav-tree/nav-tree.component.html index 2d5c61ff..0b00048a 100644 --- a/src/app/shared/components/nav-tree/nav-tree.component.html +++ b/src/app/shared/components/nav-tree/nav-tree.component.html @@ -7,7 +7,8 @@ [style.font-weight]="node.isBold ? 'bold' : 'normal'" [style.cursor]="CanSelet(node) ? 'pointer' : 'auto'" class="disable-select"> {{node.icon}} - {{node.name()}} + {{node.name()}} + {{node.nameExtension}} @@ -30,7 +31,8 @@ {{node.icon}} - {{node.name()}} + {{node.name()}} + {{node.nameExtension}} diff --git a/src/app/shared/components/nav-tree/nav-tree.component.ts b/src/app/shared/components/nav-tree/nav-tree.component.ts index fc124fd7..55835251 100644 --- a/src/app/shared/components/nav-tree/nav-tree.component.ts +++ b/src/app/shared/components/nav-tree/nav-tree.component.ts @@ -8,6 +8,9 @@ import { NavTreeBase } from './nav-tree-base'; export interface INavigationNode { name: () => string; + tooltip?: string; + nameExtension?: string; + tooltipExtension?: string; canCheck?: boolean; checkEnabled?: boolean; isChecked?: boolean; diff --git a/src/app/shared/components/progress-tracker/progress-tracker.component.html b/src/app/shared/components/progress-tracker/progress-tracker.component.html index 2858f0b6..302692bf 100644 --- a/src/app/shared/components/progress-tracker/progress-tracker.component.html +++ b/src/app/shared/components/progress-tracker/progress-tracker.component.html @@ -15,7 +15,7 @@
{{step.number}}. {{step.name}} - diff --git a/src/app/shared/components/status-bar/status-bar.component.html b/src/app/shared/components/status-bar/status-bar.component.html index 4197b27f..61e1a64b 100644 --- a/src/app/shared/components/status-bar/status-bar.component.html +++ b/src/app/shared/components/status-bar/status-bar.component.html @@ -7,10 +7,13 @@ - - + - - +