Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
d7e3ce3
Implement import for threat modeling files
Benny3009 Jan 8, 2026
8321faa
Implement export of violated constraints
Benny3009 Jan 8, 2026
5df2876
Implement labeling process UI
Benny3009 Jan 9, 2026
491157f
Improve UI; Implement provisional output pin label assignment
Benny3009 Jan 12, 2026
c505734
Implement shape highlighting during labeling process; Update label as…
Benny3009 Jan 13, 2026
5b5f4a9
Remove unnecessary css classes
Benny3009 Jan 13, 2026
00a50dd
Refactor 'excludes' to enable better handling in the future
Benny3009 Jan 20, 2026
94ee721
Improve dialog and assignment
Benny3009 Jan 22, 2026
2ddca8d
Fix threat modeling label assignments to nodes
Benny3009 Jan 23, 2026
c5066ea
Add additional information on hover
Benny3009 Jan 23, 2026
fd12a1e
Fix dark mode colors for additional information
Benny3009 Jan 23, 2026
b7678ef
Reset labeling process when loading a new threat modeling file
Benny3009 Jan 23, 2026
c4975a1
Clean up some code
Benny3009 Jan 23, 2026
4e6e1ee
Add color to assigned elements; Add information about colors
Benny3009 Jan 23, 2026
ba56eab
Refactor complex commands
Benny3009 Jan 23, 2026
fd5cad4
Clean up code; Fix bug where adding behavior to empty behavior result…
Benny3009 Jan 25, 2026
57033ce
Color elements when collisions are detected during labeling process
Benny3009 Jan 25, 2026
5b5e4f2
Remove duplicates from collision computation on output ports
Benny3009 Jan 25, 2026
ba81d8a
Fix backend analysis (drawback: labeling process after analysis not p…
Benny3009 Jan 26, 2026
8610fbf
Integrate LINDDUN threat modeling file into command palette
Benny3009 Jan 26, 2026
0e8e057
Import newer linddun file version
Benny3009 Jan 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.idea/
23 changes: 15 additions & 8 deletions frontend/webEditor/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frontend/webEditor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"husky": "^9.1.7",
"inversify": "^6.2.2",
"lint-staged": "^16.2.7",
"marked": "^17.0.1",
"monaco-editor": "^0.52.2",
"prettier": "^3.7.4",
"reflect-metadata": "^0.2.2",
Expand Down
14 changes: 14 additions & 0 deletions frontend/webEditor/src/commandPalette/commandPaletteProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import { LayoutMethod } from "../layout/layoutMethod";
import { LayoutModelAction } from "../layout/command";
import { SaveJsonFileAction } from "../serialize/saveJsonFile";
import { SaveDfdAndDdFileAction } from "../serialize/saveDfdAndDdFile";
import { SaveThreatsTableAction } from "../serialize/saveThreatsTable.ts";
import { LoadThreatModelingUserFileAction } from "../serialize/loadThreatModelingUserFile.ts";
import { LoadThreatModelingLinddunFileAction } from "../serialize/loadThreatModelingLinddunFile.ts";

/**
* Provides possible actions for the command palette.
Expand All @@ -31,6 +34,16 @@ export class WebEditorCommandPaletteActionProvider implements ICommandPaletteAct
[LoadPalladioFileAction.create(), commitAction],
"fa-puzzle-piece",
),
new LabeledAction(
"Load Threat Modeling File (JSON)",
[LoadThreatModelingUserFileAction.create(), commitAction],
"fa-triangle-exclamation"
),
new LabeledAction(
"Load LINDDUN Threat Modeling File",
[LoadThreatModelingLinddunFileAction.create(), commitAction],
"fa-triangle-exclamation"
),
],
"go-to-file",
),
Expand All @@ -40,6 +53,7 @@ export class WebEditorCommandPaletteActionProvider implements ICommandPaletteAct
new LabeledAction("Save diagram as JSON", [SaveJsonFileAction.create()], "json"),
new LabeledAction("Save diagram as DFD and DD", [SaveDfdAndDdFileAction.create()], "coffee"),
//new LabeledAction("Save viewport as image", [SaveImageAction.create()], "device-camera"),
new LabeledAction("Save threats table", [SaveThreatsTableAction.create()], "fa-triangle-exclamation")
],
"save",
),
Expand Down
9 changes: 9 additions & 0 deletions frontend/webEditor/src/diagram/ports/DfdOutputPort.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ export interface DfdOutputPort extends SPort {

@injectable()
export class DfdOutputPortImpl extends DfdPortImpl {
static readonly PORT_COLOR = "var(--color-primary)";

private color?: string;
private behavior: string = "";
private validBehavior: boolean = true;
private tree?: LanguageTreeNode<VerifyWord>[];
Expand Down Expand Up @@ -52,6 +55,8 @@ export class DfdOutputPortImpl extends DfdPortImpl {
style["--port-color"] = "#ff6961";
}

if (this.color) style["--port-color"] = this.color

return style;
}

Expand All @@ -75,6 +80,10 @@ export class DfdOutputPortImpl extends DfdPortImpl {
public getBehavior() {
return this.behavior;
}

public setColor(color: string, override: boolean = true) {
if (override || this.color === DfdOutputPortImpl.PORT_COLOR) this.color = color;
}
}

@injectable()
Expand Down
2 changes: 2 additions & 0 deletions frontend/webEditor/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { constraintModule } from "./constraint/di.config";
import { assignmentModule } from "./assignment/di.config";
import { editorModeOverwritesModule } from "./editModeOverwrites/di.config";
import { loadingIndicatorModule } from "./loadingIndicator/di.config";
import { labelingProcessModule } from "./labelingProcess/di.config.ts";

const container = new Container();

Expand All @@ -48,6 +49,7 @@ container.load(
layoutModule,
fileNameModule,
settingsModule,
labelingProcessModule,
toolPaletteModule,
constraintModule,
assignmentModule,
Expand Down
24 changes: 24 additions & 0 deletions frontend/webEditor/src/labelingProcess/di.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { ContainerModule } from "inversify";
import { configureCommand, TYPES } from "sprotty";
import { LabelingProcessUi } from "./labelingProcessUi.ts";
import { LabelingProcessCommand } from "./labelingProcessCommand.ts";
import { EDITOR_TYPES } from "../editorTypes.ts";
import { ThreatModelingLabelAssignmentToOutputPortCommand } from "./threatModelingLabelAssignmentToOutputPortCommand.ts";
import { LabelingProcessMouseListener } from "./labelingProcessMouseListener.ts";
import { ExcludesDialog } from "./excludesDialog.ts";
import { ThreatModelingLabelAssignmentCommand } from "./threatModelingLabelAssignmentCommand.ts";

export const labelingProcessModule = new ContainerModule((bind, _, isBound) => {
bind(LabelingProcessUi).toSelf().inSingletonScope();
bind(TYPES.IUIExtension).toService(LabelingProcessUi);
bind(EDITOR_TYPES.DefaultUIElement).to(LabelingProcessUi);

bind(ExcludesDialog).toSelf().inSingletonScope();
bind(TYPES.IUIExtension).toService(ExcludesDialog);

bind(TYPES.MouseListener).to(LabelingProcessMouseListener).inSingletonScope();

configureCommand({bind, isBound}, LabelingProcessCommand)
configureCommand({bind, isBound}, ThreatModelingLabelAssignmentCommand);
configureCommand({bind, isBound}, ThreatModelingLabelAssignmentToOutputPortCommand);
})
54 changes: 54 additions & 0 deletions frontend/webEditor/src/labelingProcess/dialog.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
.dialog-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 100;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
}

.dialog {
z-index: 101;
position: relative;

max-width: 50%;

background-color: var(--color-background);
border-radius: 5px;
padding: 16px;

display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 4px;
}

.dialog-text {
text-align: center;
}

.dialog-buttons {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 4px;
}

.dialog-button {
background-color: green;
color: white;
border: none;
border-radius: 8px;
padding: 5px 10px;
text-align: center;
text-decoration: none;
display: inline-block;
width: fit-content;
cursor: pointer;
}
104 changes: 104 additions & 0 deletions frontend/webEditor/src/labelingProcess/excludesDialog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { ThreatModelingLabelType, ThreatModelingLabelTypeValue } from "../labels/ThreatModelingLabelType.ts";
import { AbstractUIExtension, IActionDispatcher, TYPES } from "sprotty";
import "./dialog.css";
import { inject } from "inversify";
import { marked } from "marked";
import { Action } from "sprotty-protocol";

export type ExcludesDialogData = {
previousLabelAssignments: { labelType: ThreatModelingLabelType; labelTypeValue: ThreatModelingLabelTypeValue }[];
newLabelAssignment: { labelType: ThreatModelingLabelType; labelTypeValue: ThreatModelingLabelTypeValue };
confirmAction: Action
};

export class ExcludesDialog extends AbstractUIExtension {
protected textContainer: HTMLDivElement = document.createElement("div");
protected buttonContainer: HTMLDivElement = document.createElement("div");

private state?: ExcludesDialogData;

constructor(@inject(TYPES.IActionDispatcher) private readonly actionDispatcher: IActionDispatcher) {
super();
}

id(): string {
return "excludes-collision-dialog";
}

containerClass(): string {
return "dialog-container";
}

protected initializeContents(containerElement: HTMLElement): void {
const dialog = document.createElement("div");
dialog.classList.add("dialog");
containerElement.appendChild(dialog);

this.textContainer.classList.add("dialog-text");
dialog.appendChild(this.textContainer);

this.buttonContainer.classList.add("dialog-buttons");
dialog.appendChild(this.buttonContainer);

this.update();
}

public update(state?: ExcludesDialogData) {
if (!this.containerElement) {
if (!this.initialize()) return;
}

this.state = state;
this.updateText();
this.updateButtons();
}

private updateText(): void {
if (!this.state) {
this.textContainer.innerText = "Something went wrong: This dialog has no state.";
return;
}

this.textContainer.innerHTML = marked.parse(
"This element already has the labels " +
this.state.previousLabelAssignments
.map((assignment) => `**${assignment.labelType.name}.${assignment.labelTypeValue.text}**`)
.join(", ") +
" assigned to it.\n" +
"The label " +
`**${this.state.newLabelAssignment.labelType.name}.${this.state.newLabelAssignment.labelTypeValue.text}**` +
" cannot be assigned at the same time, since they exclude each other.",
{ async: false },
);
}

private updateButtons(): void {
if (!this.state) {
const closeButton = document.createElement("button");
closeButton.classList.add("dialog-button");
closeButton.innerText = "Close";
closeButton.addEventListener("click", () => this.hide());
this.buttonContainer.replaceChildren(closeButton);
return;
}

const keepPreviousLabelButton = document.createElement("button");
keepPreviousLabelButton.classList.add("dialog-button");
keepPreviousLabelButton.innerText = `Keep previous labels`;
keepPreviousLabelButton.addEventListener("click", () => this.hide());

const overwriteWithNewLabelButton = document.createElement("button");
overwriteWithNewLabelButton.classList.add("dialog-button");
overwriteWithNewLabelButton.innerText =
"Replace with " +
`${this.state.newLabelAssignment.labelType.name}.${this.state.newLabelAssignment.labelTypeValue.text}`;

const confirmAction = this.state.confirmAction
overwriteWithNewLabelButton.addEventListener("click", () => {
this.hide();
this.actionDispatcher.dispatch(confirmAction)
});

this.buttonContainer.replaceChildren(keepPreviousLabelButton, overwriteWithNewLabelButton);
}
}
Loading