Articles
Making ThreatSketch feel like Microsoft's Threat Modeling Tool
Why I got involved
Our cybersecurity course uses Microsoft's Threat Modeling Tool (TMT), which only runs on Windows. If you use a Mac or Linux machine, your only option is setting up a virtual machine, installing Windows, and running TMT inside it. On an older laptop, that experience ranges from painfully sluggish to completely unusable.
My classmate Eric Sunu ran into this exact wall and decided to fix it. He created ThreatSketch: you draw your data-flow diagram in a web browser (or a lightweight desktop app), and STRIDE threats populate automatically as you draw. Because it uses the exact same SDL template file as TMT, its threat logic matches Microsoft's tool. Eric shared his story in I couldn't run Microsoft's Threat Modeling Tool on my Mac, so I rebuilt it for the browser.
When Eric shared ThreatSketch with our class, I immediately tried it out. The underlying engine was remarkably solid, but the user experience did not quite feel like TMT yet. The screens were arranged differently, diagram styling felt unfamiliar, and the threat table was missing key columns. When you are a student trying to follow step-by-step lab instructions written specifically for TMT, those UI differences create real friction.
Our current semester was already wrapping up, so we would not get much personal use out of it. But future cohorts definitely would. If ThreatSketch could match TMT's behavior, upcoming classes could ditch virtual machines completely while following the same lab guides. That was all the motivation I needed to jump in.
First question: what is this thing, really?
Before touching any code, I wanted to understand how ThreatSketch was structured. At first glance, I assumed it was a wrapper around existing open-source tools like OWASP Threat Dragon that required separate installations.
In reality, ThreatSketch is completely self-contained:
- A Python threat engine that parses Microsoft's official SDL template (released under the MIT license) and evaluates TMT's threat generation rules against your diagram.
- Compatibility with Threat Dragon's schema, allowing models to open across both tools without bundling Threat Dragon itself.
- A modern frontend and app shell built with Svelte, AntV X6 for diagramming, and Tauri for desktop packaging. In the browser, it runs the Python engine directly using Pyodide.
This clean separation was encouraging. If the underlying threat generation was already sound, the rough edges were entirely in the frontend and interface, which were straightforward to adjust.
To verify the engine, I loaded a real coursework model I had previously built in TMT (a mobile banking app model spanning two diagrams). TMT had identified 232 threats. ThreatSketch generated the exact same 232 threats. The core logic was already spot on; what it needed was better interface fidelity.
The approach
1. Plan around the real tool as the reference
I started by drafting a feature parity plan in the repository (docs/TMT-PARITY.md). It broke down the main areas: application layout, diagramming canvas, threat list, reports, and template editing. Each task had an explicit acceptance criterion ending with [TMT], meaning it had to be verified against the real Windows tool rather than my memory.
Having access to a Windows machine with TMT installed made this straightforward. I gathered concrete reference material:
- Screenshots of every open menu, sidebar pane, dialog, stencil palette, and property sheet.
- Real project files: original
.tm7model files, CSV exports, and HTML reports. - Before-and-after diffs: saving a model in TMT, triaging a few threats, and re-exporting to observe how the underlying file structure changed.
2. Prioritize behavior over visual design
Eric had developed his own visual identity for ThreatSketch, with custom colors and typography. I did not want my first pull request to say, "I threw out your visual design."
Instead, we separated functionality from aesthetics. All the functional parity work (menu structures, view toggles, shape semantics, stencil icons, column layouts, and file compatibility) stayed within Eric's visual theme. A pixel-for-pixel TMT visual theme (using Segoe UI and classic Windows colors) was prepared as a separate, optional layer that Eric could choose to adopt or leave out.
3. Write tests using real TMT files
The project already followed a helpful discipline: write a failing test before applying a fix, and document any file format quirks in the specification.
I extended this by adding private test suites on my local machine using real coursework files. Keeping these files out of version control protected academic integrity while giving me high-confidence sanity checks:
- Generated threats had to match TMT's list exactly.
- Exported CSV files had to match TMT's CSV output row for row.
- Round-tripping a
.tm7file (importing and re-exporting) had to reproduce the exact XML structure TMT generated.
Whenever these tests caught a bug, I created a sanitized public test using sample data so CI could prevent regressions without exposing student coursework.
4. Deliver small, incremental pull requests
Rather than dropping a massive, unreviewable pull request, I broke the work into seven stacked PRs. We started with file fidelity (small, obviously correct, and easy to review), then moved progressively through diagram rendering, window structure, threat table columns, threat editing, timestamps, and note persistence.
What real-file testing uncovered
This was the most satisfying part of the project. Comparing ThreatSketch against real TMT files exposed subtle discrepancies that would have been nearly impossible to spot by reading code alone:
- Resetting data flow order: TMT assigns each data flow a sequential "Dataflow Order" number. The existing exporter was writing all of them back as 0. Our XML round-trip test flagged this immediately.
- Threat ID zero handling: One of my test models stored a threat with ID 0 in the XML. In TMT's exported CSV, that threat was displayed with ID 242 (one higher than the highest assigned ID) while retaining ID 0 internally. ThreatSketch now follows this exact display convention, making its CSV output match TMT row for row.
- Capturing triage timestamps: The original code left the "Last Modified" field blank when triaging a threat. By triaging threats in TMT and inspecting the output, I saw that TMT records both the user (
MACHINE\user) and a localized timestamp (13/10/2026 9:56:59 pm). Inside the.tm7XML, this timestamp uses ISO 8601 with seven-digit microsecond precision and timezone offsets. We updated ThreatSketch to record both formats identically. - Missing threat justifications: This was the most critical bug. TMT 7.3 stores user-provided justifications inside a nested property list rather than the top-level property where the importer looked. Because earlier tests relied on hand-crafted XML fixtures with the property at the top level, real justifications written in TMT were disappearing on import. We fixed this parser bug, along with dropped notes and an issue where disabled threat generation was always re-enabled on save.
With these fixes in place, a .tm7 file triaged in TMT with notes and custom justifications round-trips through ThreatSketch with zero data loss.
What changed in ThreatSketch 0.6.0
Here is what landed in the 0.6.0 release. For reference, this is TMT's own sample model in its Design view:

And this is ThreatSketch 0.6.0 with its example model in the TMT layout: the same menu bar, a toolbar in TMT's order, the stencil icons on each shape, and flow names in TMT's green callouts.

- Official stencil icons on the canvas: Microsoft's SDL template already bundles 58 distinct stencil icons. ThreatSketch now renders them in the exact positions TMT uses: centered on process bubbles, at the bottom right of interactors and data stores, and preceding labels on flows and trust boundaries. Flow names also render inside TMT's familiar pale green callout boxes.
- Natural text wrapping: The diagramming library was breaking long words mid-syllable (such as "Authenticatio / n"). We replaced this with proper word-boundary wrapping.
- Familiar menu bar and toolbar: Standardized menus (File, Edit, View, Diagram, Reports, and Help) with TMT's keyboard shortcuts and toolbar layout.
- Design and Analysis view modes: Just like TMT, Design mode displays stencil palettes for diagramming, while Analysis mode brings up the threat table. Individual panels can be toggled on or off from the View menu.
- Complete threat table parity: Displays all eleven columns in TMT's original sequence, includes column-level filtering dropdowns, and matches TMT's status footer ("Export Csv", "232 Threats Displayed, 232 Total").
- Expanded threat editing: Allows editing threat title, category, and description fields in addition to state, priority, and justification.
- Structured element properties: Groups element attributes into "Predefined Static Attributes" and "Configurable Attributes" matching TMT's inspector.
- Model metadata support: Added support for TMT's Notes panel, Assumptions, and External Dependencies, ensuring these fields persist across
.tm7saves.
Here is the Analysis view after triaging three threats. The Last modified column appears once something has been triaged, and the right-hand pane edits the selected threat's title, state, priority, category, justification and description, as TMT's Threat Properties does.

Eric kept his simpler layout as the default, so threats still appear beside the diagram as you draw. The TMT layout is one click away, from the layout button in the header or from View > TMT layout, and ThreatSketch remembers the choice.

Assumptions I got wrong
Not every issue I initially flagged was an actual bug in ThreatSketch:
- I suspected ThreatSketch was renumbering threat IDs on import. In reality, my comparison screenshot of TMT was simply scrolled down by one row.
- I thought our canvas was incorrectly overlapping imported elements. Inspecting the raw coordinates showed that TMT itself had positioned those two shapes only 4 pixels apart.
- I thought data store shapes were drawing with truncated border lines. It turned out three data stores in my test model were stacked just 8 pixels apart, exactly as TMT rendered them.
In each situation, checking the underlying file coordinates settled the question within minutes. That is the real value of using the genuine tool and its data files as your baseline: it catches your own mistaken assumptions just as quickly as it catches bugs in the code.
How it concluded
Eric reviewed the seven pull requests, added his own refinements, and merged them into the release of ThreatSketch 0.6.0. He kindly credited the parity work and thanked the classmates who helped test it in the release announcement.
The largest remaining gap is TMT's Template Editor, where users create custom stencils and author threat generation rules. I documented the entire editor interface from TMT (including every tab, validation warning, and dialog flow). Building it properly requires a sample template file saved directly from TMT to validate against.
To make sure that research is not lost, I documented the full specification in a GitHub issue (#16). The issue outlines how TMT's editor operates, proposes a step-by-step roadmap (schema parser first, then validation logic, then user interface), and notes smaller pending items like toggling threat generation, screen-reader diagram descriptions, model information dialogs, and custom reports.
That felt like a natural handoff point. When future contributors or students from upcoming cohorts want to expand the project, they will have a clear implementation guide rather than starting from scratch.
Takeaways for contributing to clone and parity tools
- Verify assumptions before refactoring: I initially wondered if the threat engine needed substantial work. In reality, the engine was already completely accurate. Testing the core first saved days of misplaced effort.
- Rely on real files, not just screenshots: Visual captures tell you what an interface looks like, but saved files reveal the underlying data model. Comparing serialized XML and CSV outputs uncovered critical bugs that visual inspection never would have caught.
- Respect the maintainer's vision: Separate functional behavior from aesthetic changes. Delivering functional compatibility within the maintainer's existing design makes pull requests far easier to review and merge.
- Protect confidential test data: Using real coursework models was vital for finding edge cases, but those files never belonged in public version control. Keeping private fixtures local and recreating minimal reproducing examples for public test suites protected academic integrity while safeguarding continuous integration.
Links and resources
- Try ThreatSketch online (runs client-side offline, no installation needed): https://elikplimsunu.github.io/threatsketch/
- Project repository: https://github.com/ElikplimSunu/threatsketch
- Eric's background article: I couldn't run Microsoft's Threat Modeling Tool on my Mac, so I rebuilt it for the browser
If you use macOS or Linux and your security class requires Microsoft's Threat Modeling Tool, give ThreatSketch a spin. If you spot anything that differs from TMT, feel free to open an issue.