# Defense-in-Depth Score: Agent Governance Toolkit

**Repo:** https://github.com/microsoft/agent-governance-toolkit · **Commit:** `c3e8229dfb19c697468cfb790495d7174ef8bc45` (5.0.0) · **Reviewed:** 2026-10-04
**What it is:** Microsoft's multi-language toolkit of policy enforcement, identity, audit, sandboxing and SRE primitives for governing AI agent tool calls.
**Category:** Agent Frameworks
**Scored configuration:** Python core, README-led govern(fn, policy=...) wrapper with default arguments (agent_id='*', in-memory audit, no ring, no approval handler, no advisory), using the README's example policy.
**Agent surface (default):** code execution opt-in · filesystem write yes · network egress yes · external credentials yes · persistent memory no · untrusted input yes · third party extensions opt-in · sub agents opt-in · external communication yes

## Score: 3.5 / 10.0 (Minimal)

| # | Criterion | S | C | D | B | Raw | Cap | Score | Confidence |
|---|---|---|---|---|---|---|---|---|---|
| C1 | Identity & least privilege | L0 | L1 | L1 | L1 | 0.17 | — | **0.17** | Medium |
| C2 | Approval gates | L2 | L1 | L0 | L1 | 0.28 | G1 | **0.28** | High |
| C3 | Tool & action scoping | L2 | L2 | L2 | L1 | 0.45 | — | **0.45** | High |
| C4 | Code-execution isolation | L3 | L1 | L2 | L3 | 0.55 | G1 | **0.50** (alt) | High |
| C5 | Untrusted input blast radius | L1 | L1 | L0 | L0 | 0.15 | C5-WORSTCASE | **0.15** | High |
| C6 | Memory, context & configuration integrity | L2 | L2 | L2 | L3 | 0.55 | — | **0.55** | Medium |
| C7 | Third-party extensions | L2 | L1 | L2 | L1 | 0.38 | — | **0.38** | High |
| C8 | Secrets & sensitive-data protection | L1 | L1 | L2 | L1 | 0.30 | — | **0.30** | Medium |
| C9 | Audit & traceability | L1 | L2 | L2 | L1 | 0.38 | — | **0.38** | High |
| C10 | Limits & kill switch | L1 | L1 | L2 | L1 | 0.30 | G1 | **0.30** (alt) | High |


AGT's govern() wrapper is a real, deterministic, deny-by-default policy gate with careful fail-closed handling, but almost every other safety primitive in the toolkit is opt-in. With default arguments there is no human approval, no sandbox, no untrusted-input handling, no budgets, and an audit log that lives only in memory and omits call arguments. The README's lead policy sets default_action: allow, which turns the gate into a denylist. The main risk is assuming the toolkit's broad feature list is active when only the policy check is.

## Critical gaps
- Governed tools and the core ExecutionSandbox run in the host process; the sandbox is self-described as soft, so an escape reaches the host process's credentials, network and files. (ASI05; C4) — [agent-governance-python/agent-os/src/agent_os/sandbox.py:10](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-os/src/agent_os/sandbox.py#L10); [agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py:505](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py#L505)
- No untrusted-input control is wired into govern(); a hijacked agent can exfiltrate data and take irreversible actions through governed tools unless the developer's own policy forbids it. (ASI01, LLM01; C5) — [agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py:505](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py#L505); [README.md:100](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/README.md#L100)

## Criterion details

### C1 Identity & least privilege — 0.17 (medium)

govern() puts a deterministic, deny-by-default policy check in front of each wrapped tool call, but it does nothing to narrow the credentials the tool itself uses. The default agent identity is the wildcard "*", so no per-agent or per-user authority is checked unless the developer sets one, and the toolkit's short-lived agent credentials are for agent-to-agent trust, not for the external systems tools reach. Wrapped tools and any subprocesses they start keep the host application's full environment and credentials.

- **S L0:** Wrapped tools act with the host's ambient credentials; the default identity is the wildcard '*', and the code-level policy gate never scopes or swaps the credential the tool uses. — [agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py:247](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py#L247); [agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py:430](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py#L430) (verified)
  - *To reach the next level:* No dedicated identity by default and no credential scoping; the gate keys on caller-supplied action labels rather than the provider's permission model.
- **C L1:** Only callables explicitly wrapped with govern() are checked; govern() has no environment scrubbing, so subprocesses started by tools inherit the full process environment. — [agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py:505](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py#L505); searched `rg -n 'environ'` in `agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py` → 3 hits (all three hits concern AGT_AUDIT_SECRET_KEY lookup/docs; none scrub a tool or subprocess environment) (verified)
  - *To reach the next level:* Unwrapped tools, extensions and sub-agents are not routed through the same authorization layer.
- **D L1:** Defaults are agent_id='*' and whatever credentials the host process holds; the safe Policy default_action=deny is overridden to allow in the README's lead example. — [agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py:247](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py#L247); [README.md:100](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/README.md#L100) (verified)
  - *To reach the next level:* No near-minimal default identity; least privilege depends entirely on developer-written policy and credentials.
- **B L1:** If the gate is bypassed or misconfigured, wrapped tools act with whatever credentials the host application holds, which the framework does not bound. — [agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py:505](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py#L505) (inferred)
  - *To reach the next level:* Nothing in the framework limits wrapped tools to one project or to read-mostly credentials.
- **Cap:** none

### C2 Approval gates — 0.28 (high)

Human approval only happens when a developer writes a policy rule with action require_approval; nothing is gated by default. When it does fire, the safe fallback is to auto-reject, and an optional coordinator binds the approval to a digest of the exact call. But the built-in console and webhook approvers only show the rule, agent and action label, not the actual arguments. The README's lead policy example sets default_action: allow, so everything not explicitly listed runs without a human.

- **S L2:** Approval is per call and falls back to AutoRejectApproval, and the coordinator path revalidates the action digest before execution, but the shipped approvers display only rule/policy/agent/action label, not arguments. — [agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py:736](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py#L736); [agent-governance-python/agent-mesh/src/agentmesh/governance/approval.py:211](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/approval.py#L211); [agent-governance-python/agent-mesh/src/agentmesh/governance/approval.py:279](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/approval.py#L279); [agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py:837](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py#L837) (verified)
  - *To reach the next level:* Approvers are not shown the exact arguments of the call they approve.
- **C L1:** Only calls matching a developer-written require_approval rule are gated, and the action label that rules match on comes from caller/model-supplied arguments. — [agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py:953](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py#L953); [agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py:434](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py#L434) (verified)
  - *To reach the next level:* No risk tiers derived from the tool itself; consequential tools without a matching rule are never gated.
- **D L0:** Approval is opt-in: no require_approval rule exists unless the developer writes one, and the README's lead policy defaults to allow. — [README.md:100](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/README.md#L100); [agent-governance-python/agent-mesh/src/agentmesh/governance/policy.py:451](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/policy.py#L451) (verified)
  - *To reach the next level:* Approval is not on by default for any class of action.
- **B L1:** The framework offers no checkpoint, rollback or dry-run for wrapped tools; a wrongly approved call executes directly. — [agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py:505](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py#L505) (verified)
  - *To reach the next level:* No undo/checkpoint or preview for consequential actions.
- **Cap:** G1 — Human approval only applies when the developer authors require_approval rules; nothing is gated in the default configuration.

### C3 Tool & action scoping — 0.45 (high)

The policy engine is a single central layer that every govern()-wrapped call passes through, and it fails closed: unmatched calls are denied by default, and malformed or unevaluable deny rules count as matches. Its conditions are simple field-versus-literal string and number comparisons, with no resolved-path containment or URL/host checks, and the scalar arguments it sees are whatever the model passed. Tool names never enter the policy context. The README leads with a default-allow policy, which turns the deny-by-default into a denylist.

- **S L2:** Conditions support ==, in, contains, startswith, endswith and numeric comparisons on caller-supplied fields; prefix and substring checks on raw strings are escapable (no realpath or URL parsing), though errors fail closed for deny rules. — [agent-governance-python/agent-mesh/src/agentmesh/governance/policy.py:307](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/policy.py#L307); [agent-governance-python/agent-mesh/src/agentmesh/governance/policy.py:289](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/policy.py#L289); [agent-governance-python/agent-mesh/src/agentmesh/governance/policy.py:389](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/policy.py#L389) (verified)
  - *To reach the next level:* No resolved-path containment, URL/host allowlists or internal-address blocking in the policy primitives.
- **C L2:** Every govern()-wrapped callable shares one PolicyEngine, but tools must be wrapped individually and the tool name is not part of the evaluated context. — [agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py:430](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py#L430); [agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py:961](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py#L961) (verified)
  - *To reach the next level:* New tools do not inherit the policy layer automatically, and policies cannot key on the tool identity.
- **D L2:** Policy default_action is deny and an empty policy set denies, but the README's lead example and about half of the repo's YAML examples set default_action: allow. — [agent-governance-python/agent-mesh/src/agentmesh/governance/policy.py:451](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/policy.py#L451); [agent-governance-python/agent-mesh/src/agentmesh/governance/policy.py:1305](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/policy.py#L1305); [README.md:100](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/README.md#L100) (verified)
  - *To reach the next level:* Official examples routinely override the safe default (D lowered one level); no per-task tool allowlist.
- **B L1:** A misused allowed tool reaches whatever the wrapped function can do; rate limits exist only when a rule declares a limit. — [agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py:505](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py#L505); [agent-governance-python/agent-mesh/src/agentmesh/governance/policy.py:1163](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/policy.py#L1163) (verified)
  - *To reach the next level:* No default quantity bounds or workspace scoping on wrapped tools.
- **Cap:** none

### C4 Code-execution isolation — 0.50 (high)

govern() runs wrapped tools directly in the host process, and the core package's code-execution sandbox is an in-process restricted exec that its own authors call a soft sandbox, not a boundary. The separate agent-sandbox package ships a properly hardened Docker provider: non-root, all capabilities dropped, no-new-privileges, seccomp, read-only root filesystem and no network by default. But it is opt-in, covers only code sent to it, and falls back to a stock image with a warning when the hardened image is missing.

- **default configuration** (default; raw 0.07, cap G1 → 0.07)
  - **S L1:** The core ExecutionSandbox is an in-process restricted exec documented by its authors as a soft sandbox that a determined attacker can escape. — [agent-governance-python/agent-os/src/agent_os/sandbox.py:10](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-os/src/agent_os/sandbox.py#L10); [agent-governance-python/agent-os/src/agent_os/sandbox.py:887](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-os/src/agent_os/sandbox.py#L887) (verified)
    - *To reach the next level:* No OS-level separation in the default code path.
  - **C L0:** govern()-wrapped tools (shell, code runners) execute in-process on the host with no isolation; the only isolation primitive is a separate, explicitly-invoked API. — [agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py:505](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py#L505); searched `rg -n -i 'sandbox|subprocess\.|isolat'` in `agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py` → 2 hits (both hits are comments; govern() never routes execution through a sandbox) (verified)
    - *To reach the next level:* The main execution path for governed tools is not sandboxed.
  - **D L0:** No isolation is applied unless the developer explicitly uses a sandbox API. — [agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py:505](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py#L505) (verified)
    - *To reach the next level:* Isolation is not on by default.
  - **B L0:** An escape from the in-process sandbox, or any governed tool, lands in the host process with its credentials, network and filesystem. — [agent-governance-python/agent-os/src/agent_os/sandbox.py:887](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-os/src/agent_os/sandbox.py#L887) (verified)
    - *To reach the next level:* Escape reaches the full host process, including credentials in its environment.
- **opt-in agent-sandbox DockerSandboxProvider** (alt; raw 0.55, cap G1 → 0.50) ← counted
  - **S L3:** Containers run as 65534, cap_drop ALL, no-new-privileges, seccomp, AppArmor, pids limit, read-only rootfs and network disabled by default. — [agent-governance-python/agent-sandbox/src/agent_sandbox/docker_provider/provider.py:1146](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-sandbox/src/agent_sandbox/docker_provider/provider.py#L1146); [agent-governance-python/agent-sandbox/src/agent_sandbox/docker_provider/provider.py:1147](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-sandbox/src/agent_sandbox/docker_provider/provider.py#L1147); [agent-governance-python/agent-sandbox/src/agent_sandbox/docker_provider/provider.py:1142](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-sandbox/src/agent_sandbox/docker_provider/provider.py#L1142); [agent-governance-python/agent-sandbox/src/agent_sandbox/docker_provider/provider.py:1134](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-sandbox/src/agent_sandbox/docker_provider/provider.py#L1134); [agent-governance-python/agent-sandbox/src/agent_sandbox/sandbox_provider.py:82](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-sandbox/src/agent_sandbox/sandbox_provider.py#L82) (verified)
    - *To reach the next level:* Not kernel-separated by default (runc unless Kata/gVisor is detected).
  - **C L1:** Only code passed to the provider's execute_code is containerised; governed tools and other paths still run on the host. — [agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py:505](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py#L505) (verified)
    - *To reach the next level:* Other model-reachable paths (wrapped tools, MCP servers) are not routed through the sandbox.
  - **D L2:** Hardened image and AppArmor profile silently degrade (with a log warning) to python:3.11-slim / docker-default unless require_* flags are set. — [agent-governance-python/agent-sandbox/src/agent_sandbox/docker_provider/provider.py:320](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-sandbox/src/agent_sandbox/docker_provider/provider.py#L320) (verified)
    - *To reach the next level:* Fallback to the legacy image is not fail-closed by default.
  - **B L3:** Network off by default, tmpfs workspace, sanitized env vars, memory/CPU/PID limits. — [agent-governance-python/agent-sandbox/src/agent_sandbox/sandbox_provider.py:81](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-sandbox/src/agent_sandbox/sandbox_provider.py#L81); [agent-governance-python/agent-sandbox/src/agent_sandbox/docker_provider/provider.py:1152](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-sandbox/src/agent_sandbox/docker_provider/provider.py#L1152); [agent-governance-python/agent-sandbox/src/agent_sandbox/docker_provider/provider.py:1159](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-sandbox/src/agent_sandbox/docker_provider/provider.py#L1159) (verified)
    - *To reach the next level:* Sessions persist across calls rather than being ephemeral per call.
- **Cap:** G1 — Opt-in mechanism: off in the scored default configuration.

### C5 Untrusted input blast radius — 0.15 (high)

Nothing in the default govern() path distinguishes untrusted content: tool results go straight back to the caller, and no taint tracking or Rule-of-Two enforcement exists. The toolkit ships pattern-based prompt-injection and MCP-response scanners, but they are detection-only and must be called separately. Because governed tools can read untrusted content, touch private data and send data out in the same session, a hijacked agent's worst case is limited only by the developer's own policy rules.

- **S L1:** PromptInjectionDetector and MCPResponseScanner are pattern/heuristic detectors, i.e. detection only. — [agent-governance-python/agent-os/src/agent_os/prompt_injection.py:457](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-os/src/agent_os/prompt_injection.py#L457) (verified)
  - *To reach the next level:* No code-enforced restriction on egress/state change once untrusted content is read.
- **C L1:** Detectors cover inputs and MCP responses only when invoked; govern() treats every tool result identically. — [agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py:505](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py#L505); searched `rg -n -i 'prompt_injection|injection|untrusted|taint'` in `agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py` → 0 hits (verified)
  - *To reach the next level:* Tool results, sub-agent messages and other sources are not distinguished in the governed path.
- **D L0:** No untrusted-input handling is wired into govern() by default. — searched `rg -n -i 'prompt_injection|injection|untrusted|taint'` in `agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py` → 0 hits (verified)
  - *To reach the next level:* Detection is not on by default.
- **B L0:** Per framework rule: docs show governed tools that read data, query databases and send email in one session, and nothing in the framework breaks that combination. — [README.md:100](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/README.md#L100); [agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py:505](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py#L505) (verified)
  - *To reach the next level:* A hijacked session can still exfiltrate and take irreversible actions unless the developer writes rules preventing it.
- **Cap:** C5-WORSTCASE — B is L0: default configuration permits leak plus irreversible action without a human.

### C6 Memory, context & configuration integrity — 0.55 (medium)

The toolkit does not own an agent memory store, and govern() loads policy only from a path or object the developer passes in, with nothing auto-loaded from the working directory. A MemoryGuard helper can screen memory writes, but it is pattern-based and opt-in. One utility, discover_agents(), reads AGENTS.md and security.md from a repository and maps them to kernel policies, so a developer who points it at an untrusted repo lets that repo define its own security settings.

- **S L2:** Policy comes only from an explicit developer-supplied path/object; however agents_compat maps repo-controlled AGENTS.md/security.md into policies when called. — [agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py:337](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py#L337); [agent-governance-python/agent-os/src/agent_os/agents_compat.py:89](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-os/src/agent_os/agents_compat.py#L89) (verified)
  - *To reach the next level:* Security config derived from repo files has no workspace-trust decision; memory writes are only pattern-screened.
- **C L2:** MemoryGuard screens writes for stores that call it; the AGENTS.md path and any host retrieval stores are uncontrolled. — [agent-governance-python/agent-os/src/agent_os/memory_guard.py:243](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-os/src/agent_os/memory_guard.py#L243) (verified)
  - *To reach the next level:* Not every memory/config path is controlled.
- **D L2:** No shared memory store exists in the default path; in-memory audit/policy state is per GovernedCallable/process. — [agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py:331](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py#L331) (inferred)
  - *To reach the next level:* No per-tenant separation or retention limits are provided for host memory.
- **B L3:** Nothing the model reads is persisted back into context by govern(); policy files are developer-supplied. — searched `rg -n -i 'memory|persist|cwd'` in `agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py` → 5 hits (all hits concern the audit log's in-memory/file storage, not model context) (verified)
  - *To reach the next level:* No review/rollback mechanism if a developer feeds repo files into policy.
- **Cap:** none

### C7 Third-party extensions — 0.38 (high)

govern() itself loads no third-party code. The plugin marketplace installer requires an Ed25519 signature from a trusted author by default and runs plugins in a subprocess with a minimal environment. But the signature covers the manifest rather than a code digest, the MCP proxy launches whatever server command the user gives it with the full environment, and installed provider packages are auto-loaded in-process through entry points.

- **S L2:** Marketplace install verifies an Ed25519 signature from a trusted author by default; the signed manifest carries no code digest. — [agent-governance-python/agent-mesh/src/agentmesh/marketplace/installer.py:92](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/marketplace/installer.py#L92); [agent-governance-python/agent-mesh/src/agentmesh/marketplace/installer.py:123](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/marketplace/installer.py#L123) (verified)
  - *To reach the next level:* Integrity of plugin code itself (hash) is not checked, and there is no re-approval on change.
- **C L1:** Only marketplace plugins are verified; MCP servers launched by the proxy and entry-point providers are not. — [agent-governance-python/agent-mesh/src/agentmesh/cli/proxy.py:188](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/cli/proxy.py#L188); [agent-governance-python/agent-mesh/src/agentmesh/providers.py:51](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/providers.py#L51) (verified)
  - *To reach the next level:* MCP servers and providers are not verified.
- **D L2:** Plugins require explicit install; entry-point providers load automatically from installed packages. — [agent-governance-python/agent-mesh/src/agentmesh/providers.py:51](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/providers.py#L51) (verified)
  - *To reach the next level:* Install does not show what will run, and providers auto-load without consent.
- **B L1:** Plugin sandbox uses a subprocess with a minimal env, but MCP servers launched by the proxy inherit the full environment and providers run in-process. — [agent-governance-python/agent-mesh/src/agentmesh/marketplace/sandbox.py:228](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/marketplace/sandbox.py#L228); [agent-governance-python/agent-mesh/src/agentmesh/cli/proxy.py:188](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/cli/proxy.py#L188) (verified)
  - *To reach the next level:* MCP servers and providers do not get a scrubbed environment.
- **Cap:** none

### C8 Secrets & sensitive-data protection — 0.30 (medium)

govern() keeps logs lean: its audit entries record the action label, outcome and matching rule, not the call arguments. But it does no redaction of its own and nothing keeps secrets out of wrapped tools, their subprocesses, or custom approval handlers (which receive the full call context). A credential redactor exists and is applied in the MCP gateway's audit path. OpenTelemetry export is opt-in via bootstrap_otel().

- **S L1:** Secrets (e.g. AGT_AUDIT_SECRET_KEY) come from env vars; CredentialRedactor masking is applied in the MCP gateway path only. — [agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py:111](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py#L111); [agent-governance-python/agent-os/src/agent_os/mcp_gateway.py:235](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-os/src/agent_os/mcp_gateway.py#L235) (verified)
  - *To reach the next level:* No type-level masking or redaction in the govern() path.
- **C L1:** Only the MCP gateway audit path redacts; govern(), approval handlers and subprocess environments do not. — searched `rg -n -i 'redact|secret_pattern|mask'` in `agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py` → 0 hits (verified)
  - *To reach the next level:* Logs, approval payloads and subprocess environments are not covered.
- **D L2:** Telemetry is opt-in through bootstrap_otel(); govern()'s audit records omit arguments. — [agent-governance-python/agent-mesh/src/agentmesh/telemetry.py:32](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/telemetry.py#L32); [agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py:446](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py#L446) (verified)
  - *To reach the next level:* Redaction is not always on.
- **B L1:** The framework does not keep long-lived host credentials away from wrapped tools or subprocesses. — [agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py:505](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py#L505) (inferred)
  - *To reach the next level:* No scoped or short-lived credentials for tools.
- **Cap:** none

### C9 Audit & traceability — 0.38 (high)

Audit is on by default: every governed call writes a policy decision (and every approval or denial) into a Merkle hash-chained log before the tool runs, and a failed audit write stops the call. But the default log lives only in process memory and is lost on exit, and entries record the action label, outcome and matching rule, not the arguments or the tool's result. An HMAC-signed append-only file sink is available if the developer configures a path and key.

- **S L1:** Entries are structured and hash-chained but carry no call arguments and no execution result. — [agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py:446](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py#L446); [agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py:439](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py#L439) (verified)
  - *To reach the next level:* Tool-call arguments and result status are not recorded.
- **C L2:** Every governed call, approval decision and advisory flag is logged; unwrapped tools are not. — [agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py:751](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py#L751) (verified)
  - *To reach the next level:* Unwrapped tools and sub-agents are missing; configuration changes aren't logged.
- **D L2:** audit=True by default, but the default sink is in-process memory the agent's process could alter. — [agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py:248](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py#L248); [agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py:249](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py#L249) (verified)
  - *To reach the next level:* Default log is not written by a component outside the agent process.
- **B L1:** Audit write precedes execution and errors propagate, but the default in-memory log is lost on process exit. — [agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py:1074](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py#L1074) (verified)
  - *To reach the next level:* Records are not durable per action by default.
- **Cap:** none

### C10 Limits & kill switch — 0.30 (high)

govern() has no step, time or cost limits. The only built-in bound on the governed path is an optional per-rule rate limit declared in policy YAML. The toolkit separately ships a token and cost BudgetTracker and a callback-based KillSwitch, but the developer has to wire both into their own loop, and the kill switch relies on the agent's registered callback to actually stop work.

- **default configuration** (default; raw 0.15, cap G1 → 0.15)
  - **S L1:** Rate limits are enforced in code only for rules that declare a limit; no step, wall-clock or cost cap exists in govern(). — [agent-governance-python/agent-mesh/src/agentmesh/governance/policy.py:1163](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/policy.py#L1163); searched `rg -n -i 'budget|max_steps|max_iterations|deadline|kill'` in `agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py` → 2 hits (both hits are comments about the ring breach-detector violation budget) (verified)
    - *To reach the next level:* No iteration, wall-clock or token/cost cap on the governed path.
  - **C L1:** Rate limits apply only to rules with a limit field. — [agent-governance-python/agent-mesh/src/agentmesh/governance/policy.py:1163](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/policy.py#L1163) (verified)
    - *To reach the next level:* No tool timeouts or shared budgets.
  - **D L0:** No limits are active unless the developer adds them to policy. — [agent-governance-python/agent-mesh/src/agentmesh/governance/policy.py:1163](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/policy.py#L1163) (verified)
    - *To reach the next level:* No default limits.
  - **B L0:** A runaway loop calling governed tools has no ceiling by default. — [agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py:505](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/govern.py#L505) (verified)
    - *To reach the next level:* No default ceiling on spend, time or calls.
- **opt-in BudgetTracker + KillSwitch** (alt; raw 0.30, cap G1 → 0.30) ← counted
  - **S L1:** BudgetTracker returns allow/deny for token/cost only when the caller invokes check_budget; KillSwitch invokes a registered callback. — [agent-governance-python/agent-mesh/src/agentmesh/governance/budget.py:35](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/budget.py#L35); [agent-governance-python/agent-hypervisor/src/hypervisor/security/kill_switch.py:75](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-hypervisor/src/hypervisor/security/kill_switch.py#L75) (verified)
    - *To reach the next level:* Limits aren't enforced automatically in the governed call path.
  - **C L1:** Applies only where the developer wires the tracker in. — [agent-governance-python/agent-mesh/src/agentmesh/governance/budget.py:35](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/budget.py#L35) (verified)
    - *To reach the next level:* No coverage of tools or sub-agents automatically.
  - **D L2:** Defaults of 100k tokens / $10 per hour are sensible. — [agent-governance-python/agent-mesh/src/agentmesh/governance/budget.py:35](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-mesh/src/agentmesh/governance/budget.py#L35) (verified)
    - *To reach the next level:* No hard ceiling configuration can't exceed.
  - **B L1:** Kill relies on the agent's own callback; in-flight work isn't guaranteed to stop. — [agent-governance-python/agent-hypervisor/src/hypervisor/security/kill_switch.py:75](https://github.com/microsoft/agent-governance-toolkit/blob/c3e8229dfb19c697468cfb790495d7174ef8bc45/agent-governance-python/agent-hypervisor/src/hypervisor/security/kill_switch.py#L75) (verified)
    - *To reach the next level:* Stop does not cancel in-flight calls or kill process groups.
- **Cap:** G1 — Opt-in mechanism: off in the scored default configuration.

## Rule-of-Two check
[A] untrusted input: Governed tool results return unfiltered to the host agent (govern.py:505). · [B] sensitive data/systems: Wrapped tools use the host's credentials; govern() does not scope them (govern.py:247). · [C] state change / egress: README examples govern send_email and database tools under default_action: allow (README.md:100). · Same default session? Yes

## Highest-impact improvements
1. Ship default-deny policy examples (change README and examples from default_action: allow) and include the tool name in the policy context. — C3 D L2→L3, +0.050 before caps (Playbook 3)
2. Record call arguments (or a redacted digest) and the result status in every policy_evaluation audit entry. — C9 S L1→L2, +0.075 before caps (Playbook 1 step 3)
3. Default the audit log to a durable file/OTel sink outside the process instead of in-memory only. — C9 B L1→L3, +0.100 before caps (Playbook 1 step 3)
4. Show exact call arguments in ConsoleApproval and WebhookApproval payloads. — C2 S L2→L3, +0.075 before caps (Playbook 5)
5. Wire BudgetTracker and a per-call timeout into GovernedCallable with sensible defaults. — C10 D L0→L2, +0.100 before caps (Playbook 3 step 3)

## Re-audit log
- No changes.

## Limitations
- Static source review of the pinned commit only; nothing was executed, installed, or probed.
- Scope is the Python core govern() path (agent-governance-python/agent-mesh and agent-os, plus agent-sandbox/marketplace where cited); the TypeScript, .NET, Go and Rust SDKs and the Claude Code/Codex/Copilot/OpenCode/Antigravity CLI plugins were not scored.
- The repository is ~150k lines of Python; MCP gateway, hypervisor, SRE and compliance modules were sampled, not exhaustively reviewed.
- No text aimed at AI reviewers was found; 'ignore previous instructions' strings in docs are injection test examples.
