Skip to content

OWASP Default Template

What you'll learn: The 8 OWASP security policies in the owasp_agentic_default manifest, what each one does, which OWASP risks they cover, and how to customize them.


Overview

The OWASP Top 10 for Agentic Applications (2026) manifest provides baseline security coverage for any Kyvvu agent. It is applied to every agent at registration as part of the bundled baseline (Claude Code agents get Claude Code Safety in its place); assigning it again is refused with 409. The copy in the Kyvvu/manifests repository is the starting point for a customised version.

Design principles: - Immediate value — every policy works on install with zero configuration. - Structural over content-based — catches missing gates, runaway loops, and undeclared tools. Does not require environment-specific patterns (regex, domains, budgets). - Demonstrates path-dependence — policies 7 and 8 read task history, showcasing the "policies on paths" model.

The 8 policies

Registration policies (3)

# Name Rule Severity OWASP risks
1 Agent must declare a substantive purpose field_matches_regex high ASI01, ASI10
2 Agent must declare a tool allowlist field_not_empty high ASI02, ASI04
3 Agent must declare a valid risk classification field_in_list critical (enables classification-aware policies)

Policy 1 requires the purpose field to be at least 30 characters (^.{30,}$). Prevents placeholder values like "chat" or "assistant". Without an explicit purpose, goal drift cannot be detected and hijacking has no baseline to violate.

Policy 2 requires the declared_tools field to be non-empty. This is the precondition for runtime policy 4 (tool allowlist enforcement). Together they implement least-agency.

Policy 3 requires risk_classification to be one of high, limited, or minimal. Bridges to EU AI Act Article 6 for installations that add compliance templates.

Runtime policies (5)

# Name Rule Severity OWASP risks
4 Tool calls must be in the declared allowlist step_name_in_allowlist critical ASI02, ASI04
5 Code execution requires a preceding gate step_requires_gate critical ASI05
6 Destructive resource operations require a preceding gate step_requires_gate critical ASI09
7 External content taint: high-impact actions require a fresh gate not(all_of(...)) critical ASI01, ASI06, ASI09
8 Bound resource calls per task to prevent runaway loops execution_max_steps high ASI08, ASI02

Policy 4 blocks tool calls whose step_name is not in the agent's declared_tools allowlist. Catches typosquatting, supply-chain swap-ins, and silent capability creep.

Policy 5 requires any step.exec to be preceded by a step.gate (human approval, static check, or sandbox dry-run) that records output.decision: "pass" — a bare step.gate, or one whose decision is anything else, does not clear the block. Direct mitigation for OWASP ASI05 (Unexpected Code Execution).

Policy 6 requires step.resource with verb DELETE to be preceded by a step.gate. Narrows to DELETE only so development iteration isn't constantly blocked. Production deployments typically widen to include POST and PATCH.

Policy 7 is a compound path-dependent policy — the headline demonstration of "policies on paths". See below.

Policy 8 blocks tasks that issue more than 50 step.resource calls. Catches runaway loops, the most common dev-time issue.

Policy 7: the taint policy explained

This policy enforces: if the agent has fetched external content earlier in this task, any subsequent high-impact action must be immediately preceded by a step.gate.

It addresses the indirect prompt injection pattern (EchoLeak): malicious instructions hidden in retrieved content silently redirect the agent's plan.

The logic uses not(all_of(...)):

not(
  all_of(
    # Current step IS a high-impact action
    any_of(
      current_is(step.exec),
      current_is(step.message, POST),
      current_is(step.resource, POST),
      current_is(step.resource, PATCH),
      current_is(step.resource, DELETE)
    ),
    # External content HAS been fetched earlier
    history_contains(step.resource, GET, target.trust="external"),
    # Current step is NOT directly preceded by a gate
    not(step_directly_preceded_by(step.gate))
  )
)

Why not(all_of(...)) instead of just all_of(...)? Rule functions return True to pass and False to block. all_of returns True when all conditions match. If all three danger conditions are present, that means the situation is dangerous — but all_of would return True (pass). The not wrapper inverts it to False (block). See Compound Policies for a full explanation of this pattern.

Key property convention: step.resource GETs must set target.trust = "external" when fetching from outside an internal allowlist. No built-in template sets this property. Set it in a template rule, in @kv.step(properties=...), or in the tool's metadata["kyvvu"]; until it is set, policy 7 does not fire.

OWASP coverage summary

OWASP Risk Coverage Policies
ASI01 Agent Goal Hijack Partial 1, 7
ASI02 Tool Misuse Strong 2, 4, 6, 8
ASI03 Identity & Privilege Abuse Gap (add credential-taint policy when deploying step.credential events)
ASI04 Agentic Supply Chain Partial 2, 4
ASI05 Unexpected Code Execution Strong 5
ASI06 Memory & Context Poisoning Partial 7
ASI07 Insecure Inter-Agent Comms Out of scope (transport-layer concern, not runtime policy)
ASI08 Cascading Failures Moderate 8
ASI09 Human-Agent Trust Exploit Partial 5, 6, 7
ASI10 Rogue Agents Partial 1

Policy 7 requires target.trust to be set (see above).

What the demo agent demonstrates

The demo agent from kyvvu init runs four steps: 1. step.resource (GET) — resource read. Passes: reading is allowed. 2. step.model (no verb) — model call. Passes: no policies restrict model calls in the default template, and the EU AI Act baseline's content-marking policy applies to delivering model output, not to the call. 3. step.message (POST) — the reply is delivered. Nothing in this template objects; the EU AI Act baseline warns (content marking, Art. 50(2), and chatbot disclosure, Art. 50(1)) because no gate follows the model call. A compliance finding: the step still runs. 4. step.exec — code execution. Blocked: no passing step.gate precedes it (policy 5; a gate counts only when it records output.decision: "pass"). The security stop.

This demonstrates that the same agent code is allowed or blocked depending on the task's history and the policies in effect.

Customizing

Disable a policy

curl -X PUT https://platform.kyvvu.com/api/v1/policies/{id} \
  -H "Authorization: Bearer <JWT>" \
  -H "Content-Type: application/json" \
  -d '{"enabled": false}'

Change severity

curl -X PUT https://platform.kyvvu.com/api/v1/policies/{id} \
  -H "Authorization: Bearer <JWT>" \
  -H "Content-Type: application/json" \
  -d '{"severity": "high"}'

Expansion path

The template deliberately excludes rules that require environment-specific configuration. The recommended additions, in order of coverage value:

Priority Add Covers When
1 Credential read taint policy (tainted_path_block with taint_kind: step.credential and taint_verb: GET) ASI03 Your deployment instruments step.credential events
2 Sensitive data read gate (step_requires_gate) ASI06, EU AI Act Art. 10 A review gate is recorded before external calls
3 Domain allowlist for resource reads (domain_allowlist, with allowed_domains and target_kinds: [step.resource]) Closes "what counts as external" for policy 7 Internal/external domain split is known. A listed step with no target.host is a violation, so declare a host on every step.resource
4 Cost ceiling (usage_budget with kind: step.model over usage.cost_usd) ASI08 economic DoS Cost properties are emitted on step.model behaviours; the budget applies to that action kind only

Next steps