Security Planning
MicrosoftOfficialPublished by Microsoft
- Skill
- Tool integration
- Security
- MIT
- Medium risk
- Has scripts
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
Security Planning provides a structured framework for managing application security throughout the development lifecycle. It includes methodologies for STRIDE threat analysis, NIST control mapping, and data classification. The skill supports plan-drift analysis to correlate security baselines with current findings, and provides robust workflows for generating and validating Microsoft Threat Modeling Tool (TM7) files, including an optional Windows-native feedback loop for visual model validation.
What it does
- Classifies application components into operational security buckets.
- Performs STRIDE-based threat analysis with support for AI-specific extensions.
- Maps security findings to industry standards and NIST control families.
- Generates security-focused backlog items for implementation handoff.
- Analyzes plan-drift by comparing security baselines against current repository findings.
- Automates the generation of TM7 threat models and associated markdown reports.
When to use it
- When performing initial security planning or threat modeling for an application.
- When you need to reconcile an existing security plan with current audit or code review findings.
- When preparing security-related work items for a development backlog.
- When generating or updating TM7 threat model files for compliance or audit workflows.
Advantages
- Provides a consistent, repeatable methodology for security planning and threat modeling.
- Reduces manual effort in mapping security findings to regulatory standards.
- Enables automated detection of drift between planned security controls and actual implementation.
- Supports both portable markdown reporting and native integration with the Microsoft Threat Modeling Tool.
Usage examples
- “Analyze my application components and generate a security backlog based on the current STRIDE findings.”
- The agent will classify the components into operational buckets, perform a STRIDE analysis, and output a prioritized list of security work items formatted for your backlog.
- “Compare my existing security baseline with these new code review findings to identify control drift.”
- The agent will correlate the findings, filter out excluded paths, and produce a report detailing validated controls, residual risks, and any identified control drift.
- “Generate a TM7 threat model file from my current YAML threat-model specification.”
- The agent will process the specification and output a .tm7 file compatible with the Microsoft Threat Modeling Tool, along with a corresponding markdown report.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/microsoft/security-planning/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip security-planning.zip -d .agents/skills
Documentation by Microsoft
Security Planning
This skill packages durable security-planning and plan-drift analysis used by Security Planner, Security Reviewer, and Code Review: operational bucket guidance, STRIDE analysis patterns, standards cross-references, NIST control-family references, security-specific backlog formats, and correlation of a Security Planner baseline with current security findings.
When to use
Use this skill when you need to:
Classify application components into the operational security buckets used during planning.
Evaluate threats with STRIDE-based analysis, including AI-specific extensions when
raiEnabledis true.Map bucket findings to standards references and control families without re-embedding long standard tables.
Derive security-specific backlog priorities and RAI work item categories for Phase 5 handoff.
Correlate a Security Planner baseline with caller-supplied current findings to identify validated controls, control drift, residual planned risks, newly introduced threats, and obsolete plan items.
Generate a dual-output TM7 model plus markdown report from a YAML/JSON threat-model spec for human-reviewed audit workflows.
Security plan drift analysis
Use the drift capability when a Security Planner baseline and current-state security findings already exist. It correlates supplied evidence only: it does not scan, profile, select security skills, verify findings, re-rate severity, invoke another agent, or modify source, plan, reviewer, backlog, or state artifacts.
Resolve and extract the baseline through drift-input-contracts.md.
Normalize caller-supplied audit, diff, or Code Review findings without treating plan-mode evidence as repository state.
Filter default exclusions, then classify only conclusions supported by drift-comparison-model.md.
Render the direct, Security Reviewer, Security Planner, or Code Review adaptation from drift-report-contract.md.
Use drift-worked-examples.md for deterministic behavior scenarios and expected category suppression.
Before full conversational or durable drift output, display the Security Planning CAUTION block in this skill verbatim. Report the default exclusions below in the canonical body's Current repository evidence section. A bounded Code Review section may point to these blocks because its containing review already carries the Code-Review disclaimer.
Default exclusions in effect. Planning and agent-customization artifacts are excluded from drift findings:
Paths:
.copilot-tracking/**,docs/planning/**,docs/adrs/**,.github/agents/**,.github/prompts/**,.github/instructions/**,.github/skills/**File globs:
*.prompt.md,*.agent.md,*.instructions.md,SKILL.mdTo override, pass
scope=explicitly. Overlapping user scope wins and is reported as a warning.
A write-capable direct caller may create one sequence-safe report under .copilot-tracking/security-audits/<project-slug>/. Security Reviewer and Security Planner render in conversation. Code Review renders a bounded section and optional security_plan_drift field. Every destination keeps inputs read-only and recommends follow-up without dispatch.
Stop the affected conclusion when a baseline is incomplete, a required fact is unresolved, evidence scope does not cover the claim, or input format drift prevents exclusions. Report suppressed categories as insufficient evidence rather than zero findings. Stop the entire correlation when no baseline resolves or no finding location is available to enforce exclusions.
TM7 generation workflow
When the user asks for a TM7 threat model, the runtime can generate a .tm7 file and a matching markdown report from the same spec. The generator supports the pre-populated-comprehensive and diagram-only-defer-to-tmt modes and can update an existing model with --update. Use the generate_tm7.py and generate_markdown.py entry points with --template to select a profile.
The .tm7 output mirrors the Microsoft Threat Modeling Tool's real SerializableModelData DataContract and deserializes cleanly under the tool's own DataContractSerializer. Fidelity is validated against the tool's own assemblies by scripts/Deserialize-Tm7.ps1, which the pytest suite runs when the tool is installed and skips cleanly otherwise. See references/tm7-generation.md for the verified contract.
Native TM7 visual feedback workflow
The skill also supports an opt-in, Windows-local feedback loop for the native Microsoft Threat Modeling Tool UI. The feature is off by default. The generator and standard validator keep their existing portable behavior when the feedback flags are absent. Native feedback is enabled only when validate_tm7_with_tmt.py is run with --feedback-loop, --spec, and --overlay-output; optional --overlay-input, --max-iterations, and --require-feedback-evidence refine the execution contract.
The workflow requires Microsoft Threat Modeling Tool 7.3.51110.1, a Windows desktop session, and UI Automation access. The loop is bounded to a baseline run plus at most three refinement iterations, and defaults to a baseline plus one. A single run holds the mouse and keyboard continuously across the baseline and every refinement iteration; control returns only at the release notice. Replaying a corrected overlay is a new run and a new takeover. Exit codes, stop reasons, and the discovery-failure rules are defined in the generation reference.
The harness controls TMT windows and may open, close, and reopen the app for save/reopen validation. It emits a start notice before automation begins, progress updates for the baseline and each refinement candidate, and a release notice when the loop completes or aborts so the operator knows when control is returned. These are notices only; the harness does not block on operator acknowledgment. The agent-facing lockout and release obligations that surround a run are owned by references/tm7-generation-workflow.md.
The loop records one evidence bundle per run under the requested evidence directory, with per-iteration screenshots, UI Automation snapshots, summaries, and candidate models. A run that captured at least one surface and either passed the automated gates or stopped for layout exhaustion also emits agent-review-request.json at the bundle root. The overlay payload remains in approval_state: pending, and no runtime path or flag auto-promotes it to approved or rewrites the canonical baseline.
Scoring keeps deterministic geometry gates separate from advisory screenshot heuristics. Screenshot heuristics are not a semantic approval signal.
A Windows-native example uses the skill's locked Windows dependency group:
uv run --project "<security-planning-skill-root>" --group windows \
python "<security-planning-skill-root>/scripts/validate_tm7_with_tmt.py" model.tm7 \
--evidence-dir ./artifacts/feedback \
--feedback-loop \
--spec ./specs/model.yaml \
--overlay-output ./artifacts/feedback/overlay.json \
--max-iterations 3 \
--require-feedback-evidenceResolve <security-planning-skill-root> from the loaded skill location before running the command.
The overlay contract is versioned and deterministic. It carries layout intent in named rule collections and is invalidated unless its full fingerprint block matches, so a stale overlay is rejected rather than replayed onto a changed model.
See references/tm7-generation.md for the full CLI surface, exit codes, stop reasons, geometry thresholds, the evidence-bundle layout, the overlay fingerprint contract, and the operator runbook covering prerequisites, abort, recovery, and rollback.
Agent-assisted visual review
Some layout defects never reach a metric. TM7 persists no connector label geometry and UI Automation exposes no label element, so label collisions, unreadable label text, and visual crowding are invisible to the deterministic gates. An agent that reads the rendered screenshots can see them and author corrections into the overlay the harness publishes.
This review is a default step of every feedback-loop run, not an opt-in extra. A run that captured at least one surface and either passed the automated gates or stopped for layout exhaustion emits agent-review-request.json at the evidence root, carrying per surface the screenshot and UI Automation paths, the node and zone rectangles, the connector handle points, and the predicted label rectangles in model coordinates, plus the coordinate-translation constants and the port convention. The agent reviews from that payload rather than hand-parsing UI Automation trees. automated-ready-pending-human means the automated gates passed and the result awaits review; it is not an approval. The remedy remains documented rather than proven: it has not yet been demonstrated end to end on a real defect.
On the success path the published overlay addresses every captured surface, so a correction can be authored for any of them. When the run found no automated correction, that overlay is the seed shape, carrying an overlay-seed- identifier and empty rule collections. A layout-exhaustion stop publishes the same seed shape; a correctness or environment stop publishes nothing at all.
See references/tm7-generation.md for the protocol, the request payload, the accepted rule fields, the coordinate translation, and the constraints that bound an agent-authored overlay.
[!CAUTION] Disclaimer: This agent is an assistive tool only. It does not provide legal, regulatory, or compliance advice and does not replace professional security review boards, penetration testing teams, compliance auditors, legal counsel, or other qualified human reviewers. The output consists of suggested actions and considerations to support a user's own internal security review and decision‑making. All security plans, threat models, security models, and mitigation recommendations generated by this tool must be independently reviewed and validated by appropriate security and compliance reviewers before use. Outputs from this tool do not constitute security approval, compliance certification, or regulatory sign‑off.
The human-in-the-loop contract governing authorship confirmation, native feedback-loop operator safety, and layout overlay promotion is owned by references/tm7-generation-workflow.md. This skill owns the mechanics only.
Skill layout
Load the reference file that matches the phase or topic you need.
| Reference | Topic |
|---|---|
| references/00-index.md | Navigation catalog and consolidated attribution |
| references/operational-buckets.md | Operational bucket definitions, GS overlay, and classification guidance |
| references/stride-model.md | STRIDE methodology, AI extensions, risk matrix, and data-flow analysis |
| references/standards-cross-reference.md | Bucket-to-standards mapping table and component mapping output format |
| references/nist-control-families.md | NIST 800-53 priority tiers and NIST AI RMF subcategory mappings |
| references/backlog-formats.md | Security-specific prioritization and RAI work item categories |
| references/data-classification.md | Public-safe data-classification taxonomy, tiers/categories/retention, and schema mapping |
| references/threat-model-review.md | Threat-model completeness checklist, PASS/INCOMPLETE verdict, and gap list |
| references/drift-input-contracts.md | Security Planner baseline extraction, normalized current-finding forms, evidence scope, and drift handling |
| references/drift-comparison-model.md | Exclusions, evidence preconditions, five plan-drift categories, matching order, and recommendation-only handoffs |
| references/drift-report-contract.md | Canonical body and direct, Security Reviewer, Security Planner, and Code Review destination adaptations |
| references/drift-worked-examples.md | Synthetic complete, incomplete, malformed, diff-scoped, exclusion, and caller-regression behavior scenarios |
| references/tm7-generation-workflow.md | Human-in-the-loop contract for TM7 generation: authorship confirmation, native feedback-loop operator safety, and layout overlay promotion |
| references/tm7-generation.md | TM7 input schema, dual-output generation contract, profile mapping, emission contract, native feedback loop, overlay and fingerprint contract, and operator runbook |
Bundled executable and data resources:
| Resource | Use |
|---|---|
scripts/generate_tm7.py | Run to build a .tm7 from a threat-model spec |
scripts/generate_markdown.py | Run to render the same spec as a markdown report |
scripts/generate_tb7.py | Run to emit a template file |
scripts/validate_tm7_with_tmt.py | Run for native Windows TMT validation and the opt-in feedback loop |
scripts/Deserialize-Tm7.ps1 | Run to check round-trip fidelity against the tool's own assemblies |
assets/schemas/tm7-layout-overlay.schema.json | Read as the layout overlay schema |
assets/schemas/tm7-agent-review-request.schema.json | Read as the agent visual-review request schema |
assets/schemas/tm7-visual-feedback-manifest.schema.json | Read as the evidence manifest schema |
templates/threat-model-spec-example.yaml | Copy as the starting point for a new spec |
The skill ships public defaults for the taxonomy and the completeness checklist. Organization-specific internal details such as internal data-type taxonomies, internal auth service names, and internal review-gate steps are supplied through a private config overlay referenced by state.overlayConfigPath and are never embedded in the public skill.
Attribution
The durable reference content in this skill is organized by reference file and summarized in references/00-index.md. See that index for the consolidated attribution and delegation notes.
Files
- ATTRIBUTION.mdAdded by AutoGineer847 B
- LICENSELicence file added by AutoGineer1.1 KiB
- README.md5.3 KiB
- SECURITY.md24.9 KiB
- SKILL.md16.9 KiB
- assets/schemas/tm7-agent-review-request.schema.json4.6 KiB
- assets/schemas/tm7-layout-overlay.schema.json6.8 KiB
- assets/schemas/tm7-visual-feedback-manifest.schema.json8.3 KiB
- assets/template-profiles/LICENSE-MIT-REFERENCE.txt366 B
- assets/template-profiles/README.md795 B
- assets/template-profiles/sdl_core_generic.yaml324 B
- assets/templates/LICENSE1.1 KiB
- assets/templates/default-kb.xml1.0 MiB
- assets/templates/default.tb7375.2 KiB
- pyproject.toml1.0 KiB
- references/00-index.md6.7 KiB
- references/backlog-formats.md2.0 KiB
- references/data-classification.md5.3 KiB
- references/drift-comparison-model.md8.0 KiB
- references/drift-input-contracts.md7.0 KiB
- references/drift-report-contract.md5.9 KiB
- references/drift-worked-examples.md11.5 KiB
- references/nist-control-families.md1.1 KiB
- references/operational-buckets.md2.9 KiB
- references/standards-cross-reference.md1.5 KiB
- references/stride-model.md4.0 KiB
- references/threat-model-review.md6.1 KiB
- references/tm7-generation-workflow.md3.9 KiB
- references/tm7-generation.md59.0 KiB
- scripts/Deserialize-Tm7.ps1Script7.8 KiB
- scripts/generate_markdown.pyScript8.0 KiB
- scripts/generate_tb7.pyScript11.6 KiB
- scripts/generate_tm7.pyScript256.5 KiB
- scripts/populate_tm7_threats.pyScript24.9 KiB
- scripts/tm7_threat_contract.pyScript38.3 KiB
- scripts/tm7_visual_feedback.pyScript119.6 KiB
- scripts/validate_gap_citations.pyScript2.5 KiB
- scripts/validate_tm7_with_tmt.pyScript262.9 KiB
- templates/threat-model-spec-example.yaml5.8 KiB
- tests/corpus/minimal-spec.yaml111 B
- tests/corpus/minimal-tm7.xml17 B
- tests/fixtures/README.md3.2 KiB
- tests/fixtures/comprehensive-spec.yaml78.5 KiB
- tests/fixtures/expected.tm71.1 MiB
- tests/fixtures/tmp-sample.tm71.1 MiB
- tests/fixtures/tmt-reference-threats.tm71.3 MiB
- tests/fixtures/tmt-reference.tm71.2 MiB
- tests/fixtures/visual-feedback/valid-manifest.json921 B
- tests/fixtures/visual-feedback/valid-overlay.yaml1.4 KiB
- tests/fuzz_harness.pyScript3.0 KiB
- tests/test_generate_tb7.pyScript11.9 KiB
- tests/test_generate_tm7.pyScript189.8 KiB
- tests/test_populate_tm7_threats.pyScript13.5 KiB
- tests/test_tm7_threat_contract.pyScript36.4 KiB
- tests/test_tm7_visual_feedback.pyScript59.5 KiB
- tests/test_validate_tm7_with_tmt.pyScript208.7 KiB
- uv.lock100.6 KiB
references/tm7-generation.md
title: TM7 Generation Format Contract description: OTM-aligned input schema, mapping reference, template profile contract, and current native feedback workflow for TM7 generation. ms.date: 2026-10-30 ms.topic: reference
TM7 generation reference
This document records the verified wire facts and the current input-contract decisions for the TM7 generation extension. The content is intentionally independent of the upstream OTM schema wording and is aligned to OTM concepts for interoperability rather than copied from the upstream document.
Native TM7 visual feedback workflow
The skill also supports an opt-in, Windows-local feedback loop that replays a validated layout overlay through the generator and validates the result in the native Microsoft Threat Modeling Tool UI. The workflow is disabled unless validate_tm7_with_tmt.py is invoked with --feedback-loop, --spec, and --overlay-output. When those flags are absent, the default generation and validation behavior remains unchanged.
Current CLI contract
generate_tm7.py takes the spec as its positional argument and accepts:
-o/--outputfor the generated.tm7path, defaulting toout.tm7--templateto select a template profile, defaulting to the spec value orsdl_core_generic--modewithpre-populated-comprehensiveordiagram-only-defer-to-tmt--updateto merge the generated output onto an existing TM7 model--overlay-inputto replay a validated layout overlay onto the generated model--threat-generation-enabledto synthesize STRIDE-per-element threats and emitThreatGenerationEnabled=true
validate_tm7_with_tmt.py takes the input model as its positional argument, always requires --evidence-dir, and selects behavior through --mode:
| Harness mode | Purpose |
|---|---|
probe | Confirm TMT is present, trusted, and at the pinned version without validating |
calibration-smoke | Short bounded run used to calibrate capture and layout metrics |
validate | Default mode; open the model in TMT and validate load fidelity |
compare-generation-state | Compare the model against --comparison-model for generation drift |
upgrade-template | Exercise the TMT template-upgrade prompt and write --upgraded-model-output |
The remaining flags group as follows:
Feedback loop:
--feedback-loop,--spec,--overlay-input,--overlay-output,--max-iterations(1through3, default1), and--require-feedback-evidenceto require per-surface screenshot, UIA, metrics, and findings evidence before the run is acceptedTemplate upgrade:
--template-upgrade-policywithfail(default),decline, orapply, plus--delete-stale-threatsto drop stale threats while applying a newer templateEnvironment:
--require-tmtto fail rather than skip when TMT is absent,--pinned-version(default7.3.51110.1),--workspace-rootto relocate the runtime workspace,--timeout-seconds(default60), and--diagnostic-overrideExpectations:
--expected-threat-countand--expected-custom-type-count, both optional assertions that are unset by default so a model of any size is accepted-v/--verbosefor debug logging
The native harness requires the pinned Microsoft Threat Modeling Tool version and treats the workflow as a local UI Automation workflow that must run on Windows with an interactive desktop session. On a non-Windows host, or when TMT cannot be discovered, the run stops as tmt-unavailable under --require-tmt and as skipped without it, and it reports version-mismatch, automation-timeout, or unexpected-modal when the runtime environment diverges from the expected harness contract.
The feedback loop requires operator-safety confirmation from the agent before launch; tm7-generation-workflow.md owns that contract. The harness itself does not block on the operator: it announces the start of automation, surfaces baseline/refinement candidate progress, and emits a release notice once the loop completes or aborts so the operator knows the computer can be used again. The workflow may open, close, and reopen TMT more than once to complete save/reopen validation.
The default is a baseline plus one refinement. A single run holds the mouse and keyboard continuously from the start notice through the baseline and every refinement iteration, and returns control only at the release notice; the operator does not regain control between iterations. Replaying a corrected overlay with --overlay-input is a new run and a new takeover, so a review cycle that produces corrections costs more than one takeover. The agent asks the operator how many rounds to run before launching.
Evidence layout
Each run writes a redacted evidence bundle rooted at the requested evidence directory. The root contains manifest.json, status.json, action.log, and the screenshots/, uia/, exports/, summaries/, and logs/ folders. A run that captured at least one surface and either passed the automated gates or stopped for layout exhaustion also writes agent-review-request.json at the root; see Agent-assisted visual review. The run also writes iterations/00-baseline plus iterations/01 through iterations/03 as needed. Each iteration bundle contains per-surface screenshots, UIA snapshots, summaries, and the generated candidate model, and the loop writes an iteration-scoped overlay.yaml into the iteration bundle plus a final pending overlay to the explicit --overlay-output path; the payload carries approval_state: pending and the runtime never auto-promotes it to approved.
Deterministic metrics, thresholds, and stop reasons
The deterministic geometry gates are separate from the advisory screenshot heuristics. The current implementation treats the following geometry findings as review or warning gates:
overlap_ratio > 0.03becomes a review finding, whileoverlap_ratio >= 0.01becomes a warningedge_node_intersections > 2becomes a review finding, whileedge_node_intersections > 0becomes a warningedge_crossing_count > 2becomes a review finding in non-dense layouts, while dense layouts warn at>= 1min_spacing_ratio < 0.24becomes a review finding, whilemin_spacing_ratio < 0.6becomes a warninga missing or incomplete surface capture is a review finding
Screenshot heuristics are advisory. They can raise a review finding when the image analysis indicates a likely defect, but they do not override deterministic geometry gates or the human semantic review step.
The loop uses the stable stop reasons automated-ready-pending-human, repeated-defect-no-improvement, max-iterations, evidence-incomplete, semantic-regression, candidate-generation-failed, overlay-validation-failed, tmt-unavailable, skipped, version-mismatch, automation-timeout, unexpected-modal, and harness-error. Every outcome the loop assigns is one of these reasons, and an outcome that is not is reported as harness-error rather than collapsed into unexpected-modal; a generator, overlay, or harness failure is never described as a blocking dialog. automated-ready-pending-human is the success reason and states exactly what it means: the automated gates are satisfied and the result is waiting for human review. It is not an approval. A baseline run plus three refinement iterations is the maximum bounded execution. Semantic regression is evaluated against the baseline model identity and blocks promotion even when the geometry score improves.
When TMT is not discovered, --require-tmt decides the outcome: with the flag the run stops as tmt-unavailable with exit 3, and without it the run stops as skipped with exit 0. A run that publishes no overlay removes any file already at --overlay-output, so an earlier run's overlay is never mistaken for the current result. The only exception is a caller that points --overlay-input and --overlay-output at the same file.
Whole-surface refinement
Before spending a native launch on another iteration, the loop asks whether any whole-surface alternative is measurably better than what was drawn. An alternative is a pairing of an orientation with a zone order, because those are the only two whole-surface choices an overlay can carry into layout. The search enumerates the distinct pairs, at most 32 per failing surface per iteration.
Each alternative is laid out in process, on a copy of the model, and scored on geometry measured from the result: canvas clipping, nodes outside their zone, boundary overlap, edges crossing nodes, label collisions, edge crossings, node overlap, and scroll extent, in that order of severity. That is information generation could not have had, because it comes from the shape the layout actually takes. Scoring an alternative on graph topology alone cannot work here: generation already minimized that same score when it chose the shipped layout, so the incumbent wins by construction and no alternative is ever selected.
An alternative the generator refuses to lay out, such as one that overruns the bounded canvas, is rejected as a candidate and the remaining alternatives still run. Rejection is ordinary and can account for a large share of the space on a surface with several zones. A rejected alternative never ends the run.
An alternative is selected only when it strictly improves on the incumbent and preserves every element, flow, and zone identity. A selected alternative becomes a surface_rules entry on the overlay replayed into the next iteration, so the iteration regenerates against the chosen layout rather than the unchanged model. When nothing improves, the loop stops as repeated-defect-no-improvement without launching.
action.log records one line per iteration reporting the surface, how many alternatives were evaluated, how many were pruned as dominated, how many were rejected for identity or for being unrealizable, and whether a launch was earned. Read that line before trusting a repeated-defect-no-improvement stop: it distinguishes a genuine "nothing was better" from a search that evaluated nothing.
Overlay schema and invalidation
The feedback overlay is a versioned payload with schema_version, overlay_type, model_id, overlay_id, applies_to, provenance, approval_state, and invalidation fields. Layout intent is carried in four named rule collections rather than one generic rules list:
| Collection | Governs |
|---|---|
zone_rules | Trust-boundary zone placement, sizing, and containment |
node_rules | Element placement within its owning zone |
connector_rules | Data-flow routing and handle positioning |
surface_rules | Surface-wide layout settings that apply to a whole drawing surface |
Overlays emitted by automation must carry approval_state: pending. Invalidation requires a complete fingerprint block; a partial block is rejected rather than backfilled. All five fingerprints must be present and must match:
spec_fingerprintgenerator_profile_fingerprintsurface_identity_fingerprintsurface_zone_identity_fingerprintsurface_flow_identity_fingerprint
These checks reject stale or tampered overlays before a candidate model is generated.
A stopped run can also publish an overlay seed. When the loop stops for layout exhaustion, meaning repeated-defect-no-improvement or max-iterations, it writes a valid overlay to --overlay-output carrying correct fingerprints, approval_state: pending, and all four rule collections empty. The empty collections are the signal that automation found no correction to publish. A correctness stop such as semantic-regression, an incomplete capture, or an environment failure publishes no seed at all.
Because the five fingerprints are hashes over spec bytes and sorted model identity sets, they are derived rather than authored. The seed is therefore the only practical starting point for a hand-authored or agent-authored overlay.
Windows-local example
uv run --project "<security-planning-skill-root>" --group windows \
python "<security-planning-skill-root>/scripts/validate_tm7_with_tmt.py" model.tm7 \
--evidence-dir ./artifacts/feedback \
--feedback-loop \
--spec ./specs/model.yaml \
--overlay-output ./artifacts/feedback/overlay.json \
--max-iterations 3 \
--require-feedback-evidenceResolve <security-planning-skill-root> from the loaded skill location before running the command.
The automation writes pending overlays only and keeps the canonical baseline unchanged. Promotion is an external action; the agent-facing rule is owned by tm7-generation-workflow.md.
Agent-assisted visual review
Deterministic metrics cannot see every layout defect. TM7 persists no connector label geometry, and UI Automation exposes no label element for a connector: the tree carries only a FocusBorder spanning the whole route. An agent that reads the rendered screenshots can see defects that no metric will ever score, and this section defines how that observation becomes a layout correction.
The review is a default step, not an opt-in one. Every run that captured at least one surface and either passed the automated gates or stopped for layout exhaustion emits agent-review-request.json at the evidence root, and the run is not finished until that review happens. automated-ready-pending-human means the automated gates passed and the result is waiting for review; it is not an approval.
Treat the protocol's remedy as documented rather than proven. Its end-to-end effectiveness has not yet been demonstrated on a real defect.
Protocol
Run the harness with
--feedback-loopand let it write the evidence bundle.Read
agent-review-request.jsonat the evidence root. It carries, per surface, the screenshot and UIA paths, the node and zone rectangles, the connector handle points, and the predicted label rectangles, all in model coordinates, plus the coordinate-translation constants and the port convention. Fall back to the raw UIA tree at each surface'suia_pathonly when the request is absent or a value it does not carry is needed.Read the screenshots the request points at. Judge the render against the defect classes no metric covers, which the request lists: label-on-node collision, label-on-label overlap, unreadable or truncated label text, connector routing through empty space, and visual crowding that leaves individually legible elements unreadable as a whole.
Translate the observation into model coordinates. The request supplies current values, so a displacement can usually be computed without the procedure below; use it when a value must be recovered from a screenshot.
Author rules into the published overlay at
--overlay-output, using any ofzone_rules,node_rules,connector_rules, andsurface_rules. On the success path that overlay addresses every captured surface, so a rule can be authored for any of them; when the run found no automated correction it is the seed shape, carrying anoverlay-seed-identifier and empty rule collections. Leave theinvalidationblock untouched; editing a fingerprint invalidates the overlay.Replay the edited overlay with
--overlay-inputand compare the new screenshots against the previous iteration.Stop after at most three attempts for a given surface, or earlier once the defect is resolved. This bound governs agent attempts and is counted separately from the harness's own iteration budget. Attempts accumulate per surface across the whole review cycle, including replays and separate agent sessions, and reset only when a spec or profile change produces a new baseline. A
surface_rulescanvas enlargement is an attempt like any other.
Request payload
agent-review-request.json is validated by assets/schemas/tm7-agent-review-request.schema.json, which rejects unknown keys.
Per surface it carries surface_id, surface_name, surface_guid, screenshot_path, uia_path, metrics_path, node_rects, predicted_connector_label_rects, connector_handles, zone_content_rects, boundary_rects, viewport_target, diagram_bounds, existing_findings, and review_status.
At run level it carries defect_classes, coordinate_translation, port_convention, overlay_seed_path, replay_command, and agent_round. overlay_seed_path is null unless this run published an overlay. replay_command is reserved and is always null today, because the harness does not reconstruct its own invocation. agent_round is replay depth, 0 on an initial run and 1 when --overlay-input was supplied; it is not a claim about how many agent rounds have run, because the harness cannot see across invocations.
predicted_connector_label_rects are predictions from the generator's own placement search, not measurements of what the tool drew. Treat them as a hint about where a label will land, never as evidence of where it is.
Rule fields
Each collection accepts a fixed key set, and an unknown key is rejected rather than ignored. assets/schemas/tm7-layout-overlay.schema.json is the authoritative schema.
| Collection | Accepted keys |
|---|---|
zone_rules | surface_id, zone_id, region, label_band_height, lane_order |
node_rules | surface_id, node_id, layout_role, absolute_position, relative_placement |
connector_rules | surface_id, flow_id, source_port, target_port, handle_point, label_offset |
surface_rules | surface_id, zone_order, orientation, viewport_target, outer_margin |
Rule values are absolute model coordinates, not displacements. A translation step yields how far an element must move; the rule requires where it must end up. Read the current value from the candidate model in the iteration bundle, such as iterations/00-baseline/candidate-00-baseline.tm7, and add the displacement to it. For a connector, the current handle is the HandleX and HandleY pair on that flow, and agent-review-request.json reports it directly as connector_handles.
An authored connector_rules entry additionally requires non-empty source_port and target_port; validation rejects the rule before it looks at handle_point. Use "auto" for both. handle_point is an object with x and y keys, which is the same shape connector_handles emits, so a displaced handle can be authored without reshaping it.
Coordinate translation
The request payload supplies current node rectangles, zone rectangles, and connector handle points in model coordinates, so most corrections need no recovery step at all. Use this procedure when a value must be measured from a screenshot instead.
feedback-manifest.json carries no node rectangles. Its per-surface keys are capture_path, findings, human_review_required, human_review_status, metrics, surface_guid, surface_id, surface_name, and uia_path; there is no surface_geometry key at all.
Rendered rectangles come from the UI Automation tree at uia_path, one element per line in pipe-delimited form:
8|Custom||VS Code Extension|654|242|885|392The trailing four fields are left, top, right, and bottom in screen pixels, keyed by the element name that precedes them. A drawn node appears as a Custom row whose automation id is empty, which is the empty field between Custom and the name.
TMT renders the model faithfully at a uniform 1.5x zoom. A least-squares fit over eight nodes measured an x scale of 1.5001 and a y scale of 1.4926 with a maximum residual of 1.1 pixels. Model coordinates are therefore recoverable as (screen - offset) / 1.5, and the shared offset cancels whenever two rectangles on the same surface are compared, so a displacement can be computed without ever resolving the origin.
Worked example: a label whose right edge sits 60 screen pixels inside a node's left edge overlaps that node by 60 / 1.5, which is 40 model units. Clearing it with a margin of 10 model units requires a displacement of 50 model units, not 50 pixels. State every displacement in model units, because rule values are model coordinates and a pixel figure pasted into a rule moves the element half again as far as intended.
Connector label placement
Three measured facts govern any rule that moves a connector label:
handle_pointis the only lever that reaches the renderer. It is serialized asHandleXandHandleYon the connector.label_offsethas no effect and is ignored. It formerly displaced the predictedlabel_rectwithout moving the drawn label, so the placement search preferred offsets that appeared to clear obstacles while the render did not change. Measured over the comprehensive spec, 53 of 55 labels had a prediction that disagreed with the render, and the generator scored 2 collisions where TMT drew 46. The generator now searches handles alone, so the predicted rect always equals the rendered rect. The key is still accepted for schema compatibility.TMT draws each connector label centred on its handle point. It does not place the label's top-left corner there. Four labels on the
ctx-01surface were extracted from a render screenshot and all four fitlabel_centre_screen = 1.5 * handle_model + (-4.5, -3)exactly on both axes. A competing hypothesis that labels anchor to the route midpoint was ruled out by the same measurement.
To move a label, move handle_point by the required model displacement and predict the resulting position by assuming centring.
Constraints
approval_statestayspending; no runtime path promotes an overlay.Semantic identity is immutable. A layout correction may move things; it may never add, remove, or rename an element, flow, or zone.
validate_layout_overlayenforces this.surface_rulesmay enlarge the canvas. Enlarging trades a clipped diagram for a scrolled one, and it is the only available remedy for genuine viewport overflow, so record why node and connector rules were insufficient. That rationale goes in the review record accompanying the overlay, not in the overlay itself:provenanceaccepts onlyevidence_ref,generated_at, andapproval_state, and any other key is rejected.An agent-authored overlay is evidence, not truth. Record which iteration and which screenshot motivated each rule.
Operator runbook
Prerequisites
Confirm all of the following before starting a feedback run. The harness stops rather than degrading when any of them is missing.
Windows with an interactive, unlocked desktop session. A remote session that disconnects, a locked workstation, or a screensaver breaks UI Automation and screen capture.
Microsoft Threat Modeling Tool at the pinned version, installed for the current user and launchable from the desktop.
The
windowsdependency group installed, because the UI Automation and capture dependencies are not in the default group.The baseline model and the spec that produced it. The harness regenerates candidates from the spec, so a spec that no longer matches the model invalidates the overlay.
A writable evidence directory on a local disk that either does not already exist or is owned by the harness. Avoid a synced or network folder; capture and manifest writes are frequent, and the harness refuses to remove directories it did not create.
No already-open Threat Modeling Tool windows. The harness opens, closes, and reopens the tool, and a pre-existing window can capture the automation.
No unsaved work in other applications, and no scheduled task, update, or notification popup expected during the run.
An uninterrupted window of operator time, because the operator must not use the mouse, keyboard, or switch windows while the loop runs.
Run --mode probe first. It confirms the tool is present, trusted, and at the pinned version without performing a validation or feedback run, and it is the cheapest way to catch an environment problem.
While the run is active
The harness drives the real TMT window. Once it announces the start of automation, do not use the mouse or keyboard and do not switch windows. The harness reports baseline and refinement candidate progress as it goes, and the loop may open, close, and reopen TMT more than once to complete save and reopen validation. Treat the release notice, emitted when the loop completes or aborts, as the signal that the machine is usable again.
Aborting
Press Ctrl+C in the terminal running the harness. The harness records a stopped run and writes status.json. Closing the TMT window by hand instead is not an abort: the harness reports unexpected-modal or automation-timeout, and the evidence bundle records a failure that did not happen for the reason it names.
Because the tool may be mid-open, expect to close a stray Threat Modeling Tool window by hand after any abort and before starting another run. An aborted run leaves whatever evidence was already written in place; that partial bundle is diagnostic only and does not satisfy --require-feedback-evidence.
Reading the outcome
Read status.json at the evidence root first. It carries the stop reason and the exit code, and it is written even when the run fails, so a missing status.json means the process was killed before it could record an outcome.
| Stop reason | Exit | What it means | Operator action |
|---|---|---|---|
automated-ready-pending-human | 0 | Automated gates passed | Perform the human semantic review before any promotion; this is not an approval |
repeated-defect-no-improvement | 8 | The same defect persisted across iterations without improvement | Inspect the findings and the published overlay seed; the defect needs a spec, profile, or layout change |
max-iterations | 8 | The bounded iteration budget was exhausted before the gates cleared | Review the best candidate manually; more iterations are not available by design |
evidence-incomplete | 7 | Required per-surface evidence was missing under --require-feedback-evidence | Check the iteration bundles for the missing capture, then rerun on a stable desktop session |
semantic-regression | 1 | A candidate diverged from the baseline model identity | Do not promote; treat the overlay as rejected, reconcile the spec, and review the layout intent |
candidate-generation-failed | 2 | Regenerating a candidate from the spec or overlay failed | Fix the generation error reported in status.json; the model and spec are untouched |
overlay-validation-failed | 2 | A produced overlay failed its own schema or fingerprint validation | Report the validation message in status.json; the model and spec are untouched |
harness-error | 2 | The harness itself failed outside the automation contract | Read status.json for the failure message; no overlay was published |
skipped | 0 | TMT was absent and --require-tmt was not set | None; run on a host with the pinned TMT, or pass --require-tmt to make absence a failure |
tmt-unavailable | 3 | TMT was absent or untrusted, or the host is not Windows, under --require-tmt | Verify the install and rerun --mode probe; the harness refuses untrusted executables |
version-mismatch | 4 | The discovered TMT is not the pinned version | Install the pinned version or set --pinned-version deliberately |
automation-timeout | 5 | A UI Automation step exceeded --timeout-seconds | Confirm the session is unlocked and interactive, then retry; raise --timeout-seconds on a slow machine |
unexpected-modal | 6 | An unrecognized dialog blocked automation | Inspect the final screenshot in the bundle, reproduce the dialog manually, dismiss its cause, then rerun |
Evidence
Every run writes a redacted bundle under --evidence-dir. Read status.json for the outcome and stop reason, manifest.json for the per-surface validation record, feedback-manifest.json for the per-surface feedback record, and action.log for the automation sequence. Per-iteration screenshots, UIA snapshots, summaries, and the generated candidate model live under iterations/. Every evidence path is confined to the evidence directory, and an identifier taken from the spec is reduced to a single safe path segment before it names a file.
Redaction covers text evidence only. Screenshots are pixels and are never redacted, so treat them as the most sensitive artifact in the bundle. Capture is refused unless the host can address a single native window handle; where that is unavailable no screenshot is written, and a run under --require-feedback-evidence fails closed rather than continuing without one. Review a bundle before sharing it outside the machine that produced it.
Recovery and rollback
The loop never mutates the canonical baseline model or spec and never promotes an overlay, so recovery is bounded:
To discard a run entirely, delete its evidence directory and the file written to
--overlay-output. Nothing outside those paths changed, and because generation is deterministic for a given spec and generator version, the baseline can always be regenerated from the spec rather than restored from a copy.To retry a failed run, re-run the same command. Evidence directories are per-run; do not reuse one across runs you intend to compare.
To reuse a good layout, pass the pending overlay from
--overlay-outputback in through--overlay-input. If the spec or profile changed since the overlay was written, the fingerprint check rejects it and you regenerate from the baseline instead.If a stray Threat Modeling Tool window or a temporary workspace survives an abort, close the window and delete the workspace directory manually. The harness only auto-removes workspaces it marked as its own.
An overlay is only ever promoted by an explicit human action outside this loop. If a pending overlay was promoted and later found wrong, revert the promotion in version control; no harness flag reverses it.
This schema is an independently authored HVE-Core artifact and is not a verbatim copy of the Open Threat Model (OTM) specification. It is aligned to OTM concepts and terminology for interoperability and was informed by the OTM project maintained by IriusRisk: OpenThreatModel. OTM is licensed under Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0), CC BY-SA 4.0. Any adaptation or redistribution should preserve this attribution and apply the same CC BY-SA terms where applicable.
Canonical input schema
The generator accepts a vendor-neutral YAML or JSON threat-model spec whose top-level structure is intentionally generic and data-driven.
project_metadata:
name: Example service
version: 1.0
summary: Generic example threat model
scope: Service boundary
assumptions:
- Traffic flows over a public network
policy_values:
cryptography_suites:
- TLS 1.3
identity_providers:
- OpenID Connect compatible identity provider
data_classification_labels:
- public
- internal
- restricted
representations:
context_diagrams:
- id: ctx-01
name: Context diagram
description: System boundary and external entities
elements: []
flows: []
trust_zone_ids: []
functional_scenarios:
- id: func-01
name: Primary interaction
description: Primary use case
elements: []
flows: []
trust_zone_ids: []
operational_views:
- id: op-01
name: Deployment and operations
description: Managed deployment and operating model
components: []
trust_zone_ids: []
assets:
- id: asset-01
name: User content
kind: data
description: User-supplied content
sensitivity: internal
category: Content
components:
- id: comp-01
name: Application process
kind: process
asset_ids:
- asset-01
trust_zone_id: tz-01
trust_zones:
- id: tz-01
name: Application boundary
description: In-scope boundary
data_flows:
- id: flow-01
source_ref: ext-01
target_ref: comp-01
ordinal: 1
transport: HTTPS
encryption: TLS 1.3
authentication: bearer token
authorization: scoped access token
data_sensitivity: internal
retention: 90 days
notes: Primary request path
threats:
- id: threat-01
target_ref: comp-01
interaction_ref: flow-01
category: tampering
title: Tampering of request payload
description: An attacker could alter the payload in transit
state: Open
citations:
stride:
- T
nist:
- SC-8
mitre: []
mitigation_ids:
- mitigation-01
mitigations:
- id: mitigation-01
name: Payload signature verification
description: Validate request content integrity
target_refs:
- comp-01
citations:
nist:
- SC-8
abuse_cases:
- id: abuse-01
title: Replay of a signed request
description: An attacker reuses a previously valid request
actor: External adversary
objective: Bypass intended controls
evil_user_story: As an authenticated low-privilege user, I want to manipulate the checkout flow so that I can bypass authorization
flow_ids:
- flow-01
mitigation_ids:
- mitigation-01
security_test_cases:
- id: test-01
title: Negative boundary input
description: Submit malformed input and verify rejection
target_refs:
- comp-01
test_type: negative-input
expected_result: Request is rejected and loggedsecurity_test_cases[].test_type takes one of negative-input,
malformed-message, multi-step-logic, or authorization. The completeness
check RGAP-05 in the threat-model review reference is keyed to this vocabulary.
Semantic target and placement interaction
threats[].target_ref identifies the component or element whose behavior is
threatened. threats[].interaction_ref identifies the concrete data flow used to
place the threat instance in TM7. The semantic target must be the source or target
endpoint of that interaction by default.
When TM7 cannot represent a reviewed component threat without using a nearby carrier interaction, declare the exception rather than relying on same-surface coexistence:
placement_override:
reviewed: true
rationale: The selected interaction is the reviewed TM7 carrier for this component threat.The portable validator rejects non-endpoint placement when the override is absent,
unreviewed, or lacks a rationale. Human review remains required; automated tooling
does not set reviewed: true on behalf of a reviewer.
Data flow endpoints
A data flow must connect two different elements. source_ref and target_ref
that name the same element are rejected during model construction, and the
diagnostic names every offending flow id in sorted order.
Connector anchors are derived from the two endpoint rectangles, so a
self-referencing flow collapses to a zero-length connector. TMT stores that
connector but draws nothing a reviewer can see or select, which silently removes
a declared interaction from the diagram while the run still exits zero. Model a
loop back into the same component as an explicit intermediate element, or as a
component threat with a reviewed placement_override.
Layout geometry
Segment-to-rectangle contact has one production algorithm. The generator holds
rectangles as left/top/width/height mappings and the feedback loop holds
them as left/top/right/bottom tuples; both adapt to the same bounds and
call the same test, so published geometry and scored geometry cannot disagree.
Contact is closed on both operands. A connector that runs exactly along a node border, or that grazes a corner partway along its length, counts as intersecting, because it overlaps that node visually.
Whole-surface layout candidates are the cross product of orientation and zone-order variant. Selection returns the first candidate that satisfies the candidate contract and fails when none does, rather than returning the best-scored candidate unchecked. A surface with no trust zones yields a fallback candidate carrying every member of that same contract.
Authored-base reconciliation
Portable validation operates only on the threat-model specification. When an existing TMT-authored base is supplied, run a separate reconciliation gate. That gate verifies that each placement connector exists on its declared surface, all surface and connector GUIDs are non-null, and connector endpoints match the authored element identities corresponding to the declared source and target.
A portable pass does not imply that an authored base is complete. Missing elements, missing connectors, wrong-surface connectors, and endpoint identity drift are authored-base reconciliation failures.
Surface identity
Reconciliation binds an authored drawing surface to its declared surface through
the deterministic surface GUID, which is derived from the declared surface id.
Authored order never participates: reordering the drawing surfaces in a base
model cannot rebind a connector to a different diagram. A declared surface with
no matching authored surface, and an authored base that presents the same
declared surface twice, both fail the gate. Authored surfaces that match no
declared surface are ignored, so a base model may carry extra diagrams.
Element identity is resolved per surface. There is no cross-surface element fallback, so an endpoint drawn on one diagram cannot satisfy a connector on another.
Native template upgrades
Treat TMT's Threat Model Conversion Confirmation prompt as a controlled
migration boundary. The native harness defaults to fail and requires one of
three explicit policies:
fail: preserve the model and report that a newer template is available.decline: open against the embedded template without changing it.apply: apply the installed template to a harness-owned working copy.
Apply a newer template to a topology-complete model with zero threat instances,
then populate explicit threats only after the upgraded base passes save/reopen and
authored-base reconciliation. Applying a template after explicit population can
retain the threat rows as stale entries whose interaction is Deleted.
TMT's native conversion replaces the embedded KnowledgeBase. When the project uses deterministic custom threat types, compose the upgraded stock template with those custom types before population. The harness must verify the expected custom type count and clean reopen before publishing the composed base.
Stale-threat deletion is independent from template application and remains off by
default. Enable it only through the explicit --delete-stale-threats option after
reviewing the impact on human-authored threat notes and state.
CTM field inventory
| CTM | Field path | Notes |
|---|---|---|
| CTM-1 | representations.context_diagrams | Context/system view with external entities and boundaries |
| CTM-2 | representations.functional_scenarios | Functional/scenario view with trust boundaries and data flows |
| CTM-3 | representations.operational_views | Deployment and operating context |
| CTM-4 | threats and mitigations | Threat analysis with mitigations |
| CTM-5 | data_flows[].ordinal | Numbered flows |
| CTM-6 | data_flows[].transport, data_flows[].encryption, data_flows[].authentication, data_flows[].authorization, data_flows[].data_sensitivity, data_flows[].retention, assets[].category | Per-flow annotation fields and optional asset classification hints |
| CTM-7 | threats[].state, threats[].citations, mitigations | Reviewable triage and status |
| CTM-8 | abuse_cases, abuse_cases[].evil_user_story | Abuse-case and evil-user-story capture |
| CTM-9 | security_test_cases | Negative and boundary-test artifacts |
| CTM-10 | project_metadata, representations, threats, mitigations, abuse_cases, security_test_cases | The complete spec must be rich enough to emit a reviewable model |
CTM-to-carrier mapping
| CTM | Input field(s) | OTM-like carrier | TM7 carrier |
|---|---|---|---|
| CTM-1 | representations.context_diagrams | representation and trust-zone context | drawing surface with external-interactor/process/store/boundary elements |
| CTM-2 | representations.functional_scenarios | scenario-oriented representation | drawing surface with flow and boundary geometry |
| CTM-3 | representations.operational_views | operational representation | drawing surface with deployment-oriented components |
| CTM-4 | threats, mitigations | threats and mitigations | threat instances plus notes/properties |
| CTM-5 | data_flows[].ordinal | numbered dataflow ordering | connector properties and labels |
| CTM-6 | data_flows[].transport, encryption, authentication, authorization, data_sensitivity, retention | dataflow attributes | connector properties |
| CTM-7 | threats[].state, threats[].citations | review state and evidence | threat state and citation-bearing notes/properties |
| CTM-8 | abuse_cases | adversary narrative | threat entries and notes |
| CTM-9 | security_test_cases | test-plan evidence | notes or cross-references in the generated markdown twin |
| CTM-10 | whole spec | complete threat-model interchange payload | the generated .tm7 file itself |
Verified TM7 wire facts
Namespaces
The generator uses the namespaces verified against the Threat Modeling Tool's own SerializableModelData DataContract (ThreatModeling.ExternalStorage.OM.SerializableModelData, serialized as <ThreatModel> in the Model namespace):
Model namespace:
http://schemas.datacontract.org/2004/07/ThreatModeling.ModelAbstracts namespace:
http://schemas.datacontract.org/2004/07/ThreatModeling.Model.AbstractsKnowledgeBase namespace:
http://schemas.datacontract.org/2004/07/ThreatModeling.KnowledgeBaseSerialization arrays namespace:
http://schemas.microsoft.com/2003/10/Serialization/ArraysXML schema-instance namespace:
http://www.w3.org/2001/XMLSchema-instance
Element and threat serialization rules
These rules reflect the DataContract graph verified against the Threat Modeling Tool's own serializer: SerializableModelData.GetSerializer() deserializes the generated file with no SerializationException. They were extracted from a genuine, TMT-loadable reference export (tests/fixtures/tmt-reference.tm7), not a reverse-engineered inference.
Each
DrawingSurfaceModelcarries the base fieldsGenericTypeId(DRAWINGSURFACE),Guid,Properties,TypeId, thenBorders,Header,Lines, and a trailingZoom(default1).Every
Propertiesdisplay attribute is ana:anyTypewith a polymorphici:type(b:HeaderDisplayAttributeorb:StringDisplayAttribute) in the KnowledgeBase namespace. BecauseStringDisplayAttribute.Valueis typed asobject, a present<b:Value>must carryi:type="c:string"(xsd:string); omitting the type hint makes the DataContract serializer reject the file withElement Value ... cannot have child contents to be deserialized as an object.HeaderDisplayAttributeuses an empty<b:Name/>and<b:Value i:nil="true"/>;StringDisplayAttributesets<b:Name>to the property key.Shapes are stored in
Bordersas a GUID-keyed dictionary ofa:KeyValueOfguidanyTypeentries (in the Serialization Arrays namespace). Eacha:Valuecarries a polymorphici:typeofStencilRectangle(external interactor,GE.EI),StencilEllipse(process,GE.P),StencilParallelLines(data store,GE.DS), orBorderBoundary(trust boundary box,GE.TB.B), plus base fieldsGenericTypeId,Guid,Properties,TypeIdand geometryHeight,Left,StrokeDashArray,StrokeThickness,Top,Width.Data flows are stored in
Linesasa:KeyValueOfguidanyTypeentries whosea:Valueusesi:type="Connector"(GE.DF) withSourceGuid,SourceX,SourceY,TargetGuid,TargetX,TargetY,HandleX,HandleY, andStrokeThickness. An unconnected endpoint uses the all-zero GUID sentinel00000000-0000-0000-0000-000000000000.The root model carries, in order,
DrawingSurfaceList,MetaInformation,Notes,ThreatInstances,ThreatGenerationEnabled,Validations,Version,KnowledgeBase, andProfile(<Profile><PromptedKb xmlns=""/></Profile>).The verified
Versionstring is4.3. Allz:Idvalues form a single contiguous sequence: diagram objects are numberedi1..iNin document order, and the embeddedKnowledgeBaserootz:Idis renumbered toi(N+1)at generation time. Because noz:Reftargets the KnowledgeBase root, this keeps every id unique with no model-size ceiling. A fixed start offset would instead cap a diagram at the object count below that offset.
Threats, the embedded KnowledgeBase, and citations
A loadable .tm7 derives its threats from an embedded DataContract KnowledgeBase, exactly as the reference file does. It does not hand-serialize threat instances.
With the default flags, generated output emits an empty
<ThreatInstances/>and<ThreatGenerationEnabled>false</ThreatGenerationEnabled>, matching the loadable reference, and TMT generates threats from the embedded KnowledgeBase when the model is opened. This is a consequence of the flag defaults rather than a fixed property of the format; see Mode-flag behavior for the cases that populate the element.The generator embeds a verbatim DataContract
<KnowledgeBase>from the bundledassets/templates/default-kb.xmlasset. That asset was extracted from the genuine TMT reference export; it is Azure-flavored but includes the genericGE.*stencils this generator emits. Swapping in a smaller MIT SDL-core DataContract KnowledgeBase is tracked as follow-up work.Per-threat STRIDE + NIST SP 800-53 (+ optional MITRE ATT&CK/CAPEC) citations are surfaced in the synchronized markdown twin (
generate_markdown.py), which reuses the generator's deterministic threat derivation. Thethreats[].citationsfield in the input spec remains the source-of-truth structure for those citations.The
bundled .tb7underassets/templates/is a no-namespace XmlSerializer template and is NOT spliced into the.tm7directly; the DataContractdefault-kb.xmlis the embeddable KnowledgeBase.
STRIDE-per-element matrix
| Element kind | Baseline STRIDE categories |
|---|---|
| External interactor | Spoofing |
| Process | Spoofing, Tampering, Repudiation, Information Disclosure, Denial of Service, Elevation of Privilege |
| Data store | Tampering, Information Disclosure, Denial of Service, Repudiation for logging stores |
| Data flow | Tampering, Information Disclosure, Denial of Service |
| Trust boundary | No direct threat by itself; use it as a grouping and routing anchor |
Mode-flag behavior
Two independent controls decide what lands in <ThreatInstances>: the --mode flag and the --threat-generation-enabled flag. They are not aliases for each other and must not be treated as one control.
--mode | --threat-generation-enabled | <ThreatInstances> content |
|---|---|---|
pre-populated-comprehensive | absent (default) | The threats declared in the spec's own threats: list |
pre-populated-comprehensive | present | A synthesized STRIDE-per-element set derived from the model |
diagram-only-defer-to-tmt | absent or present | Always empty; TMT generates threats when the model is opened |
pre-populated-comprehensive is the default mode. In its default flag state it does not synthesize anything: it maps the spec's declared threats onto the resolved model elements and flows, and it fails closed when a declared target_ref resolves to no element or flow. A spec that declares no threats therefore produces an empty <ThreatInstances/> in either mode, which is why the bundled example template generates an empty element.
The serialized <ThreatGenerationEnabled> field is a separate value read from --threat-generation-enabled when supplied, otherwise from the spec's threat_generation_enabled key, and it defaults to false. Note that the same flag name drives two consumers: it switches the generator into STRIDE synthesis, and it sets the field TMT reads to decide whether to auto-generate its own threats. Leaving it at the default keeps the generator and TMT from populating the same threat set twice.
Threat-instance identity
Every spec-sourced threat must reach <ThreatInstances>. A threat whose interaction_ref resolves to no model flow, or whose flow lacks a SourceGuid, FlowGuid, or TargetGuid, fails generation and names every affected threat in one sorted diagnostic. The generator also reconciles the emitted instance count against the spec-sourced threat count, so a partial array cannot ship.
The serialized <Id> member is the identifier reviewers and CSV exports cite, so it is derived rather than positional. The generator takes the SHA-256 digest of the UTF-8 bytes of the exact normalized source threat id and maps it into the closed interval [1, 2147483647], which is the positive half of the signed 32-bit range TMT stores. Zero stays reserved as the unassigned sentinel. Because the identifier depends only on the source id, it survives insertion, reordering, and separate processes. Two source ids that derive the same numeric id fail generation deterministically, naming both.
Template-profile abstraction
The generator should support a pluggable profile abstraction where each profile maps generic DFD semantics to a concrete template's stencil TypeId values and a knowledge-base reference.
template_profiles:
sdl_core_generic:
description: Generic SDL/Core profile for broad threat modeling
asset_source: microsoft/threat-modeling-templates/default.tb7
knowledge_base: default
type_ids:
external_interactor: GE.EI
process: GE.P
data_store: GE.DS
data_flow: GE.DF
trust_boundary_line: GE.TB.L
trust_boundary_box: GE.TB.B
annotation: GE.ADefault shipped profile
The default profile is the generic SDL/Core profile and uses the verified generic stencil TypeIds:
External interactor:
GE.EIProcess:
GE.PData store:
GE.DSData flow:
GE.DFTrust boundary line:
GE.TB.LTrust boundary box:
GE.TB.BAnnotation:
GE.A
Reference-only profile tables
The following profiles are documented as mapping-table references and are not bundled as redistributed template files because they are either domain-specific or not confirmed as bundled MIT artifacts.
| Profile | Status | Notes |
|---|---|---|
| Azure | Reference-only mapping table | Public Azure-oriented profile with examples such as SE.P.TMCore.AzureAppServiceWebApp, SE.P.TMCore.AzureAD, and SE.P.TMCore.Host from public tooling documentation |
| PatrickGallucci | Reference-only mapping table | Public profile catalog with a richer cloud-oriented threat vocabulary; keep as a mapping-table reference until a confirmed redistribution path is chosen |
Standards rationale and public citations
The generated TM7 file and its markdown twin are justified by public, vendor-neutral standards and guidance:
OWASP Threat Modeling Cheat Sheet: DFD completeness, trust boundaries, data flows, data stores, processes, and external entities. Source: https://cheatsheetseries.owasp.org/cheatsheets/Threat_Modeling_Cheat_Sheet.html
OWASP Abuse Case Cheat Sheet: abuse cases, adversary narratives, and security test cases. Source: https://cheatsheetseries.owasp.org/cheatsheets/Abuse_Case_Cheat_Sheet.html
Threat Modeling Manifesto: methodology-neutral review questions and representation-driven analysis. Source: https://www.threatmodelingmanifesto.org/
NIST Risk Management Framework: data-sensitivity and impact-based categorization. Source: https://csrc.nist.gov/Projects/risk-management/about-rmf
MITRE ATT&CK and CAPEC: optional enrichment for abuse-case-driven threats. Source: https://attack.mitre.org/ and https://capec.mitre.org/
The extension therefore uses a vendor-neutral, standards-aligned input contract and preserves traceability in the emitted TM7 model without copying the upstream OTM schema text.
Verifying TMT deserialization
Generated files are validated against the Threat Modeling Tool's own assemblies rather than a hand-crafted schema. scripts/Deserialize-Tm7.ps1 locates the installed ThreatModeling.ExternalStorage.Local.dll, verifies each candidate assembly carries a valid Microsoft Authenticode signature before loading it, obtains the exact DataContractSerializer from SerializableModelData.GetSerializer(), and calls ReadObject on a target .tm7. The TMT assemblies are 32-bit, so the harness re-launches itself under the 32-bit Windows PowerShell host when invoked from a 64-bit process. It prints DESERIALIZE_OK (exit 0) on success, DESERIALIZE_FAIL: <message> (exit 1) on a contract mismatch, and TMT_ASSEMBLIES_NOT_FOUND (exit 3) when the tool is not installed.
The pytest suite drives this harness in test_given_generated_tm7_when_deserialized_by_tmt_then_round_trips for both generation modes and against the reference fixture. The tests skip cleanly on non-Windows hosts or when the assemblies are absent (for example, in CI) and run automatically when the tool is installed locally. Structural-parity tests additionally assert that the generated root and DrawingSurfaceModel member order match tests/fixtures/tmt-reference.tm7.
Requirements and compatibility
Written for: GitHub Copilot, Claude Code
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Claude Code frontmatter, Needs Python, Needs Node.js
The author says: Generation requires Python 3.11+ and uv. The native TM7 feedback loop additionally requires Windows with an interactive desktop session and the pinned Microsoft Threat Modeling Tool 7.3.51110.1.
Security
What our scanner noticed· Medium risk
- Large base64-encoded blobs
- Scripts that your agent may run
This skill includes scripts that your agent may run. Read them before you use it.
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Published by
- Microsoft
- Publisher
- Microsoft (Official)
- Source
- microsoft/hve-core · .github/skills/project-planning/security-planning · commit f425e94
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Microsoft or Microsoft.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download security-planning.zip