Write a Policy
Each example below is a complete, deployable Kyverno CEL policy. For how policy anatomy, enforcement modes, and matching work conceptually, see Policies.
General workflow
- Decide what to match. Use
object.mcp.tool != ""to target MCP tool calls orobject.llm.model != ""to target LLM calls — see Policy Context for the full set of available fields. - Write the validation expression. A CEL boolean expression that must evaluate to
truefor the request to be allowed. - Start in
Auditmode. Deploy withvalidationActions: [Audit]to measure false-positive rates against real workload before enforcing. - Graduate to
Warn, thenDeny. Once you have confidence in the pattern, tighten the enforcement mode — or useApprove(warn-approve) to route matching requests to human review instead of blocking them outright.
Restrict premium models by capability
Deny requests for Claude Opus unless the calling agent has the premium-models capability:
apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: model-access-by-capability
spec:
matchConditions:
- expression: 'object.llm.model != ""'
validations:
- expression: |
!object.llm.model.startsWith("claude-opus") ||
"premium-models" in object.agent.capabilities
message: "Claude Opus is restricted to agents with the premium-models capability"
validationActions: [Deny]
Block shell commands containing secret patterns
Match only bash tool calls, then deny any command whose text matches common secret-key prefixes:
apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: no-secrets-in-bash
spec:
matchConditions:
- expression: 'object.mcp.tool == "bash"'
validations:
- expression: |
!object.mcp.arguments.command.matches("(sk-|ghp_|AKIA)[A-Za-z0-9]+")
message: "Possible secret detected in bash command — request blocked"
validationActions: [Deny]
Require a ticket when session budget is half-consumed
Combine a namespace match condition with a session-budget validation to require a ticket ID once spend crosses 50%:
apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: session-budget-gate
spec:
matchConditions:
- expression: 'object.agent.namespace == "production"'
validations:
- expression: |
object.session.percentUsed < 0.5 ||
object.session.ticketId != ""
message: "A ticket ID is required once 50% of session budget is consumed"
validationActions: [Deny]
Route model from Opus to Haiku for non-ML users
Use a MutatingPolicy instead of a ValidatingPolicy to rewrite the request rather than block it — here, redirecting Opus requests to Haiku for anyone outside the ml-team group:
apiVersion: policies.kyverno.io/v1
kind: MutatingPolicy
metadata:
name: route-to-haiku
spec:
matchConditions:
- expression: |
object.llm.model.startsWith("claude-opus") &&
!("ml-team" in object.user.groups)
mutations:
- patchType: ApplyConfiguration
applyConfiguration:
expression: '{"model": "claude-haiku-4-5"}'
Content safety policies
AIControls scans every LLM prompt and tool call inline before forwarding it. The examples below use the Warn enforcement mode where a soft interrupt is preferable to a hard block: the request is allowed through, but a warning is appended to the model's response. The AI tool (e.g. Claude Code) sees the warning in its next context window and surfaces it to the developer, who can then decide whether to continue.
Warn the agent when a prompt contains PII
Detects common PII patterns (email addresses, SSNs, credit card numbers) in the user's prompt and asks the agent to confirm before proceeding. The request is not blocked — the agent receives the response plus the warning and can relay it to the developer.
apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: warn-pii-in-prompt
annotations:
proxy.nirmata.io/enforcement-mode: warn
spec:
matchConditions:
- expression: 'object.llm.model != ""'
- expression: 'object.llm.currentPrompt != ""'
validations:
- expression: |
!object.llm.currentPrompt.matches(
"\\b[A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z]{2,}\\b|" +
"\\b\\d{3}-\\d{2}-\\d{4}\\b|" +
"\\b(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14})\\b"
)
message: |
This prompt appears to contain personal data (email address, SSN, or
credit card number). Confirm with the user that sharing this information
is intentional before continuing.
validationActions: [Warn]
Because the mode is Warn, the LLM receives both the original response and the warning appended to it. Claude Code will show the warning to the developer and pause for confirmation before its next action.
Block prompt injection attempts
Detects common prompt injection patterns — instructions embedded in tool outputs or user messages that attempt to hijack the agent's behavior.
apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: block-prompt-injection
spec:
matchConditions:
- expression: 'object.llm.model != ""'
- expression: 'object.llm.currentPrompt != ""'
validations:
- expression: |
!object.llm.currentPrompt.matches(
"(?i)(ignore (previous|prior|all) instructions|" +
"you are now|disregard your|new persona|" +
"system prompt:|<\\/?(system|instruction)>)"
)
message: "Possible prompt injection detected — request blocked"
validationActions: [Deny]
Warn before reading credential or secret files
Allows the read to proceed but warns the agent — and by extension the developer — that a sensitive file is being accessed. Useful as a first step before committing to a hard block.
apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: warn-credential-file-access
annotations:
proxy.nirmata.io/enforcement-mode: warn
spec:
matchConditions:
- expression: 'object.mcp.tool == "read_file"'
validations:
- expression: |
!has(object.mcp.arguments.path) ||
!object.mcp.arguments.path.matches(
"(?i)(\\.env|\\.pem|\\.key|credentials|secrets|id_rsa|id_ed25519|" +
"\\.aws/credentials|\\.kube/config)"
)
message: |
This file may contain credentials or secrets. Confirm with the user
that reading it is intentional and that the content will not be
logged or forwarded to external systems.
validationActions: [Warn]
Block exfiltration of secrets through bash
Denies bash commands that print or curl known secret file paths or environment variables matching credential name patterns.
apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: block-secret-exfiltration
spec:
matchConditions:
- expression: 'object.mcp.tool == "bash"'
validations:
- expression: |
!object.mcp.arguments.command.matches(
"(?i)(cat|curl|wget|echo).*(\\.(env|pem|key)|" +
"AWS_SECRET|GITHUB_TOKEN|ANTHROPIC_API_KEY|" +
"DATABASE_URL)"
)
message: "Possible secret exfiltration blocked — review the command and try again"
validationActions: [Deny]
Start with Audit mode for all content safety policies to measure false-positive rates against your team's real workload. Graduate to Warn, then Deny, once you have confidence in the patterns.