Security Cheatsheet
The one-page lookup. The order security checks run in, the configuration knob behind each layer, a checklist to run before you ship, a glossary, and where to read deeper. Bookmark this page.
The seven layers at a glance
Each layer answers one question and is implemented by different code. This is the map; click through for the detail.
| # | Layer | Answers | Lives in |
|---|---|---|---|
| 1 | Identity & Access | Who is calling? | Infrastructure.AI.MCPServer, Infrastructure.AI/A2A, Presentation.AgentHub |
| 2 | Autonomy & Governance | How much may it do alone? | Domain.AI/Governance, Application.AI.Common/MediatRBehaviors |
| 3 | Tools & Permissions | Which tools, right now? | Infrastructure.AI/Permissions, Application.Core/Permissions |
| 4 | Sandbox & Execution | What can its code touch? | Infrastructure.AI/Sandbox, Infrastructure.AI/Attestation |
| 5 | Egress & SSRF | Where on the network? | Infrastructure.AI/Egress, Infrastructure.AI.MCP/Services |
| 6 | Content Safety | Is the text safe, in and out? | Application.AI.Common/MediatRBehaviors, Infrastructure.AI.Governance |
| 7 | Data Protection | What can it see and keep? | Infrastructure.AI.KnowledgeGraph/Scoping, .../Compliance |
The security pipeline order
Most runtime security runs as MediatR pipeline behaviors — interceptors
that wrap every command. The agent behaviors are registered in
src/Content/Application/Application.AI.Common/DependencyInjection.cs and are
deliberately registered first so they form the outermost wrapper. A request
descends through them on the way in; the response climbs back out through them in reverse.
A wrapper that runs around a command handler — like middleware, but for in-process commands. Each behavior can inspect the request, short-circuit it (return early without calling the handler), or modify the response on the way out. Registration order is execution order, outer to inner.
The security-relevant behaviors, in the order they execute (outer → inner):
-
AuditTrail→ContentSafety→PromptInjection -
The command handler runs (the LLM turn / tool call)
-
ResponseSanitizationacts on the result as it unwinds back out
So input is screened (content safety, injection scan)
before the handler ever calls the model, and tool output is sanitized
after the handler, before it can re-enter the model's context. Two more
Application-level behaviors —
RequestValidation (FluentValidation at the boundary) and
Authorization — run on the inner ring for every command.
Tool permission and governance are not MediatR behaviors. They run on the live
tool-execution path: every agent tool is wrapped by GovernedAIFunction, whose
InvokeCoreAsync runs three ambient gates —
IToolInvocationGovernor (authorization),
IToolClassificationGate (data-classification
DLP), and IProgressEvaluator (spin guard) — before the tool runs. Two earlier
behaviors named ToolPermissionBehavior and GovernancePolicyBehavior
were removed as dead code (they keyed on a marker nothing implemented).
If you add a new security behavior, where you register it decides what it can see.
A check that must run before the LLM call belongs among the inbound screening
behaviors; a check on model output belongs at or after
ResponseSanitization. Registering it in the wrong place can silently
make it a no-op.
Configuration knobs, by layer
The harness ships closed-by-default; opening it up is a set of deliberate config choices. These are the security-relevant settings and where they live. For the full configuration reference, see the Developer Guide's Configuration page.
| Layer | Knob | What it controls |
|---|---|---|
| Identity | MCP.Server.Authentication (Authority / Audience / ValidIssuers) |
JWT validation for inbound MCP clients. Production requires it. |
| Identity | AgentHub:Cors:AllowedOrigins |
The CORS origin allowlist. Never wildcard in production. |
| Identity | Auth:Disabled (Development only) |
Disables auth — only honored when IsDevelopment() is also true. |
| Autonomy | Autonomy tier (TierPolicies / agent SubagentType) |
Restricted / Supervised / Autonomous default posture per agent. |
| Tools | Plugin AllowedTools / DeniedTools / AutonomyLevel |
Per-plugin tool boundary. DeniedTools is bypass-immune. |
| Sandbox | SandboxConfig → ResourceLimits |
Memory / CPU-time / subprocess / disk caps; isolation mode. |
| Sandbox | AttestationKeyOptions:HmacKeys (User Secrets / Key Vault) |
HMAC signing keys for tool-result attestation. Never in appsettings. |
| Egress | Skill manifest egress.allowlist |
Additive per-skill hostname allowlist over the frozen default-deny baseline. |
| Content safety | GovernanceConfig: Enabled, EnablePromptInjectionDetection, EnableResponseSanitization, InjectionBlockThreshold, ResponseBlockThreshold |
Toggles and thresholds for injection scanning and response sanitization. |
| Data | AI:Governance:DataClassification:Mode (Off / Audit / Enforce) |
Purview data-classification DLP gate on the tool path. Off = inert; Audit = log verdicts; Enforce = block or redact by sensitivity label. Requires a classification provider enabled. |
| Data | MultiTenantIsolation |
Enables per-tenant / per-owner record filtering. Off = single-tenant mode. |
| Data | GraphRag.ProvenanceEnabled |
Stamps source pipeline / task / timestamp on every graph write. |
| Secrets | AzureKeyVaultUri |
Loads secrets from Key Vault via managed identity (non-DEBUG builds). |
Config keys are listed here by their conceptual path. Casing and nesting can drift
between versions — confirm the exact key in your appsettings.json and
the AppConfig hierarchy before relying on it. The layer pages link the
class that reads each one.
Pre-merge security checklist
Before a change that touches agent behavior, tools, or data goes in, walk this list. The first item is enforced mechanically; the rest are judgment.
-
OWASP Agentic evals pass. Run
dotnet test --filter "Category=OwaspAgentic". A failure blocks the merge by default. See OWASP Agentic Evals. -
No hardcoded secrets. Secrets come from User Secrets (dev) or Key Vault
(prod) — never appsettings or source. Run
dotnet list package --vulnerablewhen adding dependencies. -
New tool? It declares its capabilities
(
[ToolCapabilityAttribute]) and registers with a keyed-DI string key. By default it can touch nothing. See Sandbox. - New outbound host? It is on an egress allowlist. The default is deny-all; an un-allowlisted host is blocked at runtime. See Egress & SSRF.
-
New command? It has a FluentValidation validator and the right
[Authorize]/ autonomy posture. Input is validated at the boundary, not inside the handler. - New security behavior? It is registered in the correct pipeline position (see the pipeline order above).
- Destructive or high-risk action? It is gated behind an autonomy tier or an escalation strategy — never auto-approved. See Autonomy & Governance.
Glossary
DeniedTools and the sandbox safety gates are bypass-immune.
Deep-dive references
Longer, code-level writeups that sit alongside this guide:
-
SSRF Defense — threat model & defense matrix
(Markdown source,
documentation/security/ssrf-defense.md) — the complete egress threat model behind Chapter 06. -
OWASP Agentic Top-10 Evals — full spec
(Markdown source,
documentation/security/owasp-agentic-top-10-evals.md) — fixture-by-fixture detail behind Chapter 09. -
MCP Tool-Definition Scanning
(Markdown source,
documentation/security/mcp-tool-definition-scanning.md) — how tool definitions from external MCP servers are scanned at discovery, what the detection rules were calibrated against, and the limits they knowingly carry. -
Gating Tools By Declared Behaviour
(Markdown source,
documentation/security/tool-behavior-gating.md) — requiring approval for every tool not declared read-only, why a read-only claim from an unvouched-for MCP server buys nothing, and how the exemption list works. - Architecture Guide → Networking & Security — the deployment side: VNets, private endpoints, Key Vault, managed identity.
- Developer Guide — Configuration reference, the message journey, and how to extend the harness.
Everything the model reads or writes is untrusted; every capability is closed until granted; every control fails closed; and no control stands alone. If a change you're making violates one of those four, it's probably a security regression.