Security Model
This page documents the controls that are implemented in the current shipping codebase.
Threat model
The server can inspect and mutate live WPF UI state. That makes these risks relevant:
- a malicious or prompt-injected MCP client issuing direct
tools/callrequests - unauthorized access to the inspector pipe
- loading an unexpected inspector DLL during
connect - leaking credentials or certificates through local files
- man-in-the-middle or impersonation on the named-pipe channel
The MCP client is untrusted by default. Tool descriptions, annotations, and prompts are guidance only; security decisions are enforced by server-side policy gates before process discovery details, UI text, screenshots, ViewModel values, or runtime mutations are returned.
Implemented controls
DLL validation
connect validates the inspector DLL before loading it.
- Debug builds: trusted local paths skip signature verification to keep local development practical.
- Release builds: signature verification is enforced.
- Path validation: the shipping server accepts only trusted roots.
Raw injection target policy
Raw DLL injection into arbitrary same-user WPF processes is blocked by default.
- The shipping server does not implicitly trust project-scoped targets discovered under the current repository root.
- When the target executable is not explicitly allowlisted,
connect()fails closed witherrorCode: SecurityErrorandrequiresExplicitTargetOptIn: trueinstead of injecting if no earlier default-pipe compatibility failure has already stopped the connection attempt. - If a stale or incompatible default-pipe host is already advertising the expected pipe,
connect()can returnerrorCode: CompatibilityErrorbefore the raw-injection policy denial, but raw injection still remains blocked. - Set
WPFDEVTOOLS_INJECTION_ALLOWED_TARGETSto a semicolon-separated list of exact local absolute executable paths only when raw injection into a specific app is an intentional production decision; malformed configured entries fail closed witherrorCode: InvalidPolicyConfiguration. - Prefer the SDK-hosted reuse path with
InspectorSdk.Initialize()when you need production diagnostics for an external target without broadening raw injection scope.
MCP tool and target policy gates
The server evaluates high-risk MCP tools/call requests before dispatching them to tool implementations.
| Gate | Default | Enables | Main risk controlled | Common first tool |
|---|---|---|---|---|
WPFDEVTOOLS_MCP_ALLOWED_TARGETS |
Required for target access | connect() targets by exact local absolute executable path |
Process identity and window metadata disclosure | connect |
WPFDEVTOOLS_MCP_ALLOW_SENSITIVE_READS |
false |
UI text, binding values, DP values, event payloads, scene/tree summaries, runtime state | Sensitive UI or application data leaving the target process | get_ui_summary |
WPFDEVTOOLS_MCP_ALLOW_SCREENSHOTS |
false |
Screenshot metadata/file/base64 output through element_screenshot |
Pixel data leakage | element_screenshot |
WPFDEVTOOLS_MCP_ALLOW_VIEWMODEL_INSPECTION |
false |
ViewModel, command, DataContext chain, and ViewModel-triggered snapshot/batch operations | Runtime business state exposure or command misuse | get_viewmodel |
WPFDEVTOOLS_MCP_ALLOW_DESTRUCTIVE_TOOLS |
false |
Approved interaction, mutation, render measurement, state snapshots, event drain, and batch mutation | Target app state changes | click_element, batch_mutate |
WPFDEVTOOLS_INJECTION_ALLOWED_TARGETS |
Disabled | Raw injection fallback into exact reviewed executable paths | Unexpected code loading into an app process | connect |
Use the smallest gate set that matches the diagnostic task. For example, scene-level binding triage usually needs the target allowlist and sensitive reads, but not screenshots, ViewModel inspection, destructive tools, or raw injection.
WPFDEVTOOLS_MCP_ALLOWED_TARGETSrestricts allconnect()targets to exact local absolute executable paths and applies before SDK-hosted reuse or raw injection. Unset values fail closed withSecurityError; malformed configured entries fail closed withInvalidPolicyConfiguration.get_processesandconnect()auto-discovery apply this target policy before returning process names, window titles, architecture/runtime metadata, or candidate details. Denied targets are redacted to aggregate counts.WPFDEVTOOLS_MCP_ALLOW_DESTRUCTIVE_TOOLS=trueopts into runtime mutation, interaction, render-measurement, and session state-consuming tools, includingset_dp_value,click_element,execute_command,measure_element_render_time,capture_state_snapshot,restore_state_snapshot,drain_events, andbatch_mutate.WPFDEVTOOLS_MCP_ALLOW_SCREENSHOTS=trueopts intoelement_screenshotat the MCP boundary.WPFDEVTOOLS_MCP_ALLOW_SENSITIVE_READS=trueopts into target UI text, DependencyProperty and binding values, routed-event payloads, tree/scene summaries, and runtime state snapshots. This is the per-session diagnostic profile gate for read-heavy tools such asget_ui_summary,get_visual_tree,get_bindings, andget_state_diff.WPFDEVTOOLS_MCP_ALLOW_VIEWMODEL_INSPECTION=trueopts intoget_viewmodel,get_commands,get_datacontext_chain,modify_viewmodel, andexecute_command. The same gate applies whencapture_state_snapshotrequestsviewModelPropertyNames, whenbatch_mutatecaptures or mutates ViewModel state, and whenwait_for_dp_change_after_mutationuses a ViewModel mutation trigger.WPFDEVTOOLS_MCP_ALLOW_COMPOSER_RUNTIME_APPROVALS=trueletspreview_ui_blueprintaccept an exact reviewed content-bound token for one request.WPFDEVTOOLS_COMPOSER_TRUSTED_RUNTIME_PACKSremains the operator-controlled server-start option. Each token binds the exact pack root, id, version, and fingerprint; preview still requiresWPFDEVTOOLS_MCP_ALLOW_DESTRUCTIVE_TOOLS=true.- Unset, false, or invalid boolean gates fail closed for the affected category.
MCP JSON-RPC envelope boundary
The raw MCP JSON-RPC envelope for STDIO requests is parsed by the MCP C# SDK before this server receives typed requests. Pre-dispatch envelope fields such as id and method on initialize, resources/read, and tools/list are therefore SDK-owned. This project validates tool-call names and arguments after SDK parsing, then validates Inspector IPC request ids, methods, and correlation ids before dispatching requests into the injected or SDK-hosted Inspector host.
Do not treat this as a blanket input-validation gap for tool execution. The project-owned boundary starts at typed MCP request filters and tool wrappers, where oversized tool names, unsupported tools, tool arguments, process target policy, sensitive-read gates, screenshot gates, ViewModel gates, and destructive gates are enforced. The downstream named-pipe IPC boundary also enforces request id, method, correlation id, framing, and authentication constraints.
Screenshot capture is additionally bounded by resource lifecycle controls. element_screenshot defaults to metadata-only output. Inline base64 output is capped for small PNG payloads; larger pixel captures must use outputMode: "file", which returns a wpf://screenshots/{screenshotId} resource handle instead of a local path. MCP element_screenshot file mode creates MCP server-owned retained screenshot resources: the server issues a per-process server-issued lease root, passes only that root to the Inspector, then SessionManager registers the returned PNG, expires it after 24 hours, caps each MCP server session at 100 resources, and deletes retained PNG files and detached lease roots when evicted or expired. Ordinary target-owned resources are purged when the target session disconnects. The preview-only exception is a successful preview_ui_blueprint request with screenshotOutputMode="file": that one registered resource is detached from the temporary process before disconnect so clients can read its resourceUri with resources/read; it remains bounded by expiry, eviction, and server session-manager disposal. Failed preview captures are not detached. These retained resources are cleaned up by SessionManager, not by the Inspector default screenshot cache. The Inspector default screenshot cache under %LOCALAPPDATA%\WpfDevTools\tmp\screenshots, or WPFDEVTOOLS_SCREENSHOT_DIR when configured, applies only to Inspector file output that runs without a server-issued lease root. full-uninstall removes that default current-user cache; auth secrets and certificates remain intentionally manual cleanup items.
IPC payload size is bounded at the framing layer. MessageFraming.MaxMessageSizeBytes is a 10 MB hard per-frame limit for UTF-8 named-pipe payloads. Treat this as an abuse and memory boundary, not as a tuning knob. Large responses must be reduced before crossing IPC with tool-level caps, truncation metadata, compact modes, or resource handles such as screenshot file-mode resources. Do not raise the frame limit without first designing and testing streaming or chunking, including failure recovery and release documentation for clients.
Safe deployment profiles
Use these profiles as deployment templates for production or shared test workstations. Every target path must be an exact local absolute executable path. Boolean gates not listed for a profile should stay unset or false. Prefer SDK-hosted reuse with matching WPFDEVTOOLS_AUTH_SECRET and WPFDEVTOOLS_CERT_DIR for the first four profiles; use raw injection only for the emergency profile after separate approval.
Read-only diagnostics
Use this profile for scene, tree, binding, DP, and state reads where target UI text may leave the process.
Set:
WPFDEVTOOLS_MCP_ALLOWED_TARGETS=<exact target exe>WPFDEVTOOLS_MCP_ALLOW_SENSITIVE_READS=true
Keep unset or false:
WPFDEVTOOLS_MCP_ALLOW_SCREENSHOTSWPFDEVTOOLS_MCP_ALLOW_VIEWMODEL_INSPECTIONWPFDEVTOOLS_MCP_ALLOW_DESTRUCTIVE_TOOLSWPFDEVTOOLS_INJECTION_ALLOWED_TARGETS
Blocked:
element_screenshotget_viewmodelandmodify_viewmodelset_dp_value,click_element, andbatch_mutate- raw-injection fallback
Allowed:
get_ui_summaryand other sensitive read tools, only for the allowlisted target.
Screenshot-enabled diagnostics
Use this profile when metadata and scene summaries are insufficient and pixel capture is approved for a reviewed target.
Set:
- All read-only diagnostics gates
WPFDEVTOOLS_MCP_ALLOW_SCREENSHOTS=true
Use element_screenshot with outputMode: "metadata" or "file" by default.
Blocked:
- ViewModel tools
modify_viewmodel,set_dp_value,click_element, andbatch_mutate- raw-injection fallback
ViewModel-enabled diagnostics
Use this profile to inspect commands, the DataContext chain, and ViewModel state without mutating the running app.
Set:
- All read-only diagnostics gates
WPFDEVTOOLS_MCP_ALLOW_VIEWMODEL_INSPECTION=true
Keep WPFDEVTOOLS_MCP_ALLOW_DESTRUCTIVE_TOOLS unset unless mutation is separately approved.
Blocked:
modify_viewmodelexecute_commandset_dp_value,click_element, andbatch_mutatemutation steps
Mutation-enabled diagnostics
Use this profile only for approved workflows where UI or ViewModel changes are rollback-safe.
Set:
- All read-only diagnostics gates
WPFDEVTOOLS_MCP_ALLOW_DESTRUCTIVE_TOOLS=true
Add only when required:
WPFDEVTOOLS_MCP_ALLOW_VIEWMODEL_INSPECTION=truefor ViewModel tools, ViewModel snapshot fields, ViewModel batch steps, or ViewModel wait-after-mutation triggers.WPFDEVTOOLS_MCP_ALLOW_SCREENSHOTS=truewhen pixel evidence is required.
Blocked:
- Any capability whose gate stays unset.
- raw-injection fallback, unless the emergency profile is also explicitly approved.
Raw-injection emergency diagnostics
Use this profile as the last resort for a reviewed local target that cannot host the SDK inspector.
Set:
WPFDEVTOOLS_MCP_ALLOWED_TARGETS=<exact target exe>WPFDEVTOOLS_INJECTION_ALLOWED_TARGETS=<same exact target exe>- Only the minimum
WPFDEVTOOLS_MCP_ALLOW_*gates required from the profiles above.
Blocked:
- Non-allowlisted targets.
element_screenshot,get_viewmodel,modify_viewmodel,set_dp_value,click_element, andbatch_mutateunless their exact profile gates are also enabled.
Named-pipe authentication
Injection-based connect sessions use HMAC challenge-response authentication by default.
- The secret must be base64 encoded and decode to at least 32 decoded bytes (256 bits).
- When
WPFDEVTOOLS_AUTH_SECRETis not set, the server generates a default secret once and reuses it across server restarts for the current user profile. - Set
WPFDEVTOOLS_AUTH_SECRETwhen you need to override the generated secret with a deterministic shared value. - During injection-based bootstrap, the server writes the short-lived auth-secret handoff file as a DPAPI-protected payload and the native bootstrapper deletes it after loading. This prevents direct plaintext disclosure from the temp file, but code already running as the same Windows user remains inside the local trust boundary.
- The default persisted auth-secret file is
%APPDATA%\WpfDevTools\auth\shared-secret.bin. - For
connect()to reuse an SDK-hosted Inspector, setWPFDEVTOOLS_AUTH_SECRETandWPFDEVTOOLS_CERT_DIRtogether on both sides before callingInspectorSdk.Initialize(). The default-hardened MCP server will not reuse a plaintext SDK host. - If either value is missing, or both are unset,
InspectorSdk.Initialize()now fails closed instead of starting a plaintext SDK host.
TLS over named pipes
Injection-based connect sessions use TLS for the inspector connection by default.
- The secure named-pipe transport currently pins TLS 1.2 for compatibility across .NET 8 and .NET Framework 4.8 runtime paths.
- Named-pipe TLS negotiation is verified by
scripts/tests/Test-TlsNegotiation.ps1for thenet8-net8,net8-net48, andnet48-net8runtime pairs. Do not enable TLS 1.3 inSecureTransportProtocols.InspectorTransportuntil the same harness proves stable negotiation for every supported pair and the release notes identify the verified Windows/.NET matrix. - The server creates or reuses a certificate in that directory.
- If
WPFDEVTOOLS_CERT_DIRis not set, the server uses the default certificate directory under%APPDATA%\WpfDevTools\certs. - If you set
WPFDEVTOOLS_CERT_DIR, it must be a local absolute directory. Network paths are not allowed; UNC paths and mapped network drives are rejected. - Persisted PFX files stay in the protected local certificate directory, but runtime certificate imports use non-exportable private key storage. The transport does not fall back to
Exportablekey imports. - The client validates the subject and pins the expected thumbprint.
WPFDEVTOOLS_CERT_THUMBPRINTcan override the expected thumbprint.connect()can reuse an existing SDK-hosted Inspector only when the target app callsInspectorSdk.Initialize()with matchingWPFDEVTOOLS_AUTH_SECRETvalues and the same local absoluteWPFDEVTOOLS_CERT_DIRvalue.- Even outside SDK-host reuse, any default-pipe
connect()attempt validates that the named-pipe server is owned by the requested target process and reports a compatible protocol/build fingerprint before the client accepts the connection. - Before reusing an existing host, the client verifies that the named-pipe server is owned by the requested target process and that the host reports a compatible protocol/build fingerprint.
Package uninstall removes client registration. Package full-uninstall removes installer-owned payloads and generated registration artifacts, but it does not delete current-user transport state because the same server profile may reuse it across package upgrades. To remove the default persisted auth secret and TLS certificate store intentionally, run:
Remove-Item -LiteralPath "$env:APPDATA\WpfDevTools\auth\shared-secret.bin" -Force
Remove-Item -LiteralPath "$env:APPDATA\WpfDevTools\certs" -Recurse -Force
Pipe access limits and server-side controls
- Pipe ACLs are scoped to the current user and SYSTEM.
- Requests are serialized and bounded by framing limits.
- Session-level rate limiting is enforced by the server.
- Tool policy gates can block destructive tools, screenshots, sensitive reads, ViewModel inspection, and non-allowlisted targets before any target-process request is sent.
Recommended production posture
- Run a
Releasebuild. - Authenticode-sign the inspector DLL.
- Keep the default injection-based transport hardening enabled.
- Set
WPFDEVTOOLS_AUTH_SECRETwhen you need deterministic secret rotation or SDK-mode coordination. - Set
WPFDEVTOOLS_CERT_DIRto the same local absolute directory in both processes when certificate storage must be deterministic or shared with SDK mode. - Optionally set
WPFDEVTOOLS_CERT_THUMBPRINT. - Keep raw injection disabled by default; use
WPFDEVTOOLS_INJECTION_ALLOWED_TARGETSonly for explicitly reviewed exact local absolute executable paths. - Set
WPFDEVTOOLS_MCP_ALLOWED_TARGETSto the reviewed exact local absolute executable paths the server may connect to. - Disable destructive tools, screenshots, sensitive reads, or ViewModel inspection with the
WPFDEVTOOLS_MCP_ALLOW_*gates when those capabilities are not needed. - Restrict who can launch the server on the workstation or VM.
Important limitations
- TLS uses locally managed certificates, not an enterprise PKI by default.
- SDK-hosted inspectors require matching transport configuration before
connect()can reuse the existing host, including the same local absoluteWPFDEVTOOLS_CERT_DIRvalue when TLS is enabled. Network paths are not allowed. - The current shipping transport is STDIO + named-pipe inspector communication; HTTP transport is not part of the current binary.