Policy Governance
Comprehensive guide to writing and using CEL policies.
What is CEL?
Definition: Common Expression Language. A Google-developed language for defining security rules.
Why CEL?:
- Safe: No loops, no I/O, guaranteed termination.
- Expressive: List operations, string matching, logical operators.
- Regex-safe: Uses RE2, immune to ReDoS.
Resource Consumption (Security)
To prevent Denial of Service (DoS) attacks via complex policy evaluation, mcptrust actively limits CEL execution:
- Cost Limit: Execution is capped at 1,000,000 cost units (strictly enforced).
- Fail-Closed: Exceeding cost or time limits results in a policy failure.
Policy File Structure
name: "My Security Policy"
rules:
- name: "rule_name"
expr: "CEL expression returning boolean"
failure_msg: "Message shown when rule fails"
severity: warn # or error (default: error)The input Object
Available Fields:
| Field | Type | Description |
|---|---|---|
input.tools | list | All discovered tools |
input.tools[].name | string | Tool name |
input.tools[].description | string | Tool description |
input.tools[].risk_level | string | "LOW", "MEDIUM", "HIGH" |
input.tools[].inputSchema | map | JSON Schema |
input.artifact | object | (With --lockfile) Package metadata |
input.provenance | object | (With --lockfile and provenance) SLSA attestation |
Common Policy Patterns
Pattern 1: No High-Risk Tools
- name: "no_high_risk"
expr: '!input.tools.exists(t, t.risk_level == "HIGH")'
failure_msg: "High-risk tools are not allowed"Pattern 2: Tool Denylist
- name: "no_dangerous_names"
expr: '!input.tools.exists(t, t.name in ["exec", "shell", "eval"])'
failure_msg: "Dangerous tool name detected"Pattern 3: Require Namespace Prefix
- name: "namespaced_tools"
expr: 'input.tools.all(t, t.name.startsWith("myorg_"))'
failure_msg: "All tools must be prefixed with 'myorg_'"Pattern 4: Limit Arguments
- name: "max_args"
expr: 'input.tools.all(t, size(t.inputSchema.properties) <= 5)'
failure_msg: "Tools cannot have more than 5 arguments"Pattern 5: Require Provenance (Supply Chain)
- name: "require_slsa"
expr: 'has(input.provenance) && input.provenance.method == "cosign_slsa"'
failure_msg: "Artifact must have verified SLSA provenance"Pattern 6: Trusted Source Only
- name: "trusted_org"
expr: |
has(input.provenance) &&
input.provenance.source_repo.matches("^https://github.com/(myorg|trustedvendor)/.*")
failure_msg: "Artifact must come from trusted GitHub org"Running Policies
# With external policy file
mcptrust policy check --policy ./policy.yaml -- "npx -y @scope/server"
# With preset
mcptrust policy check --preset strict -- "npx -y @scope/server"
# With lockfile (enables supply chain fields)
mcptrust policy check --lockfile mcp-lock.json --policy ./policy.yaml -- "npx -y @scope/server"Presets
| Preset | Description | Severity | Key Rules |
|---|---|---|---|
| baseline | Permissive, warnings only | warn | Tools exist, no obvious bads |
| strict | Fail on any issue | error | Pinning required, no HIGH risk |