Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 23 additions & 6 deletions autogen/agent_safety.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,13 @@ rules:
value: NEVER
- agent_kwarg_present:
- code_execution_config
- not:
agent_kwarg_value:
kwarg: code_execution_config
value: "False"
explanation: >
This agent enables code execution (code_execution_config is set) while
This agent enables code execution (code_execution_config is set, and not
to the disabling False) while
human_input_mode="NEVER" removes the human-in-the-loop step. The combination
makes the agent fully autonomous: every code block the model emits runs with
zero review, so a prompt injection or a confused model executes attacker-chosen
Expand Down Expand Up @@ -102,12 +107,19 @@ rules:
- autogen_assistant_agent
scope: agent
match:
agent_kwarg_present:
- code_execution_config
all:
- agent_kwarg_present:
- code_execution_config
- not:
agent_kwarg_value:
kwarg: code_execution_config
value: "False"
explanation: >
This AssistantAgent sets code_execution_config, enabling code execution on the
This AssistantAgent sets code_execution_config to an enabling value,
turning on code execution on the
LLM-facing agent. AutoGen's recommended pattern splits responsibilities: the
AssistantAgent generates code with code_execution_config=False, and a separate
AssistantAgent generates code with code_execution_config=False (the safe,
explicit-disable form, which this rule does not flag), and a separate
UserProxyAgent (the executor) runs it. Collapsing both roles into one agent
removes that review boundary — the same agent that the model fully controls
also executes whatever it produces, so a prompt injection has a direct path
Expand All @@ -131,10 +143,15 @@ rules:
all:
- agent_kwarg_present:
- code_execution_config
- not:
agent_kwarg_value:
kwarg: code_execution_config
value: "False"
- agent_kwarg_missing:
- max_consecutive_auto_reply
explanation: >
This executor enables code execution (code_execution_config is set) but does
This executor enables code execution (code_execution_config is set, and
not to the disabling False) but does
not set max_consecutive_auto_reply, so it falls back to AutoGen's class
default of 100 (MAX_CONSECUTIVE_AUTO_REPLY). That is a cap, but a very loose
one for an agent that auto-executes model-emitted code: up to 100 consecutive
Expand Down
8 changes: 8 additions & 0 deletions autogen/network.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ rules:
- httpx.patch
- httpx.head
- httpx.request
- requests.Session.get
- requests.Session.post
- urllib.request.urlopen
# Bare `urlopen` covers the from-import form.
- urlopen
# Aliased aiohttp sessions canonicalize to aiohttp.ClientSession.<m>.
- aiohttp.ClientSession.get
- aiohttp.ClientSession.post
missing: timeout
explanation: >
This AutoGen tool makes a network request without a timeout, so it will hang
Expand Down
106 changes: 106 additions & 0 deletions claude_sdk/agent_safety.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,112 @@ rules:
restrict the tool surface, and prefer a safe default permission mode
("default" or "acceptEdits") scoped to a minimal tool set.

- id: CSDK-121
title: TypeScript AgentDefinition is granted the Bash tool
severity: high
confidence: 0.8
language: typescript
applies_to:
- claude_agent_definition
scope: agent
match:
agent_grants_builtin_tool:
- Bash
explanation: >
This TypeScript AgentDefinition (a typed const or an inline entry in
options.agents) lists "Bash" in its tools array, giving the subagent
arbitrary shell-command execution. Subagents are dispatched autonomously
by a lead agent in response to model-generated task descriptions, so a
prompt-injected or mis-scoped task can reach the shell with no human in
the loop. This is acute when the surrounding options set
permissionMode: "bypassPermissions", which removes the SDK's interactive
approval gate entirely.
fix: >
Remove "Bash" from this agent definition's tools array unless shell
execution is essential to its job. If it is essential, gate it with a
PreToolUse hook that allowlists the specific commands the subagent may
run, and do not pair it with permissionMode: "bypassPermissions".

- id: CSDK-122
title: TypeScript AgentDefinition is granted the WebSearch tool
severity: medium
confidence: 0.8
language: typescript
applies_to:
- claude_agent_definition
scope: agent
match:
agent_grants_builtin_tool:
- WebSearch
explanation: >
This TypeScript AgentDefinition lists "WebSearch" in its tools array,
giving the subagent access to live internet content. Subagents are
dispatched autonomously by a lead agent; a prompt-injected or mis-scoped
task can direct the subagent to retrieve attacker-controlled pages, whose
content can then inject further instructions into the agentic loop.
WebSearch results are untrusted external data with no SDK-level
filtering — the subagent will process whatever the search returns.
fix: >
Remove "WebSearch" from this agent definition's tools array unless
internet access is essential to its job. If it is essential, gate inbound
queries with a PreToolUse hook that validates the search query before it
executes, and ensure the lead agent that dispatches this subagent
validates task descriptions before passing them down.

- id: CSDK-123
title: TypeScript AgentDefinition is granted filesystem-write built-ins
severity: high
confidence: 0.8
language: typescript
applies_to:
- claude_agent_definition
scope: agent
match:
agent_grants_builtin_tool:
- Write
- Edit
- MultiEdit
- NotebookEdit
explanation: >
This TypeScript AgentDefinition lists a filesystem-write built-in
(Write, Edit, MultiEdit, or NotebookEdit) in its tools array, letting
the subagent modify files on disk. Subagents are dispatched
autonomously; a prompt-injected or mis-scoped task can drive the
subagent to rewrite source, configuration, or even the
.claude/settings.json that governs the agent's own permissions. Write
access is comparable blast radius to shell execution (CSDK-121) and is
not gated by any guardrail mechanism on AgentDefinition itself.
fix: >
Remove the write built-ins from this agent definition's tools array
unless file mutation is essential to its job. If it is essential, scope
the writable area, gate writes with a PreToolUse hook, and never pair
broad write access with permissionMode: "bypassPermissions".

- id: CSDK-124
title: TypeScript AgentDefinition is granted the WebFetch tool
severity: high
confidence: 0.75
language: typescript
applies_to:
- claude_agent_definition
scope: agent
match:
agent_grants_builtin_tool:
- WebFetch
explanation: >
This TypeScript AgentDefinition lists "WebFetch" in its tools array.
WebFetch retrieves a model-chosen URL and feeds its content back into
the agent loop. Unlike WebSearch (CSDK-122), WebFetch takes a direct
URL, so it is both a prompt-injection vector (attacker-controlled page
content re-enters the loop) and a server-side request forgery surface
(the model can point it at internal addresses). Fetched content is
untrusted external data with no SDK-level filtering.
fix: >
Remove "WebFetch" from this agent definition's tools array unless
fetching is essential. If it is essential, gate it with a PreToolUse
hook that allowlists permitted hosts/URLs and blocks internal address
ranges, and validate the task descriptions the lead agent passes down.

- id: CSDK-130
title: TypeScript query() main agent is granted the Bash tool
severity: high
Expand Down
15 changes: 7 additions & 8 deletions claude_sdk/network.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,13 @@ rules:
- httpx.head
- httpx.request
- urllib.request.urlopen
# NOTE: these aiohttp.ClientSession.* entries match only DIRECT
# calls of that literal text. The engine's alias resolver
# canonicalizes a reused session (s = aiohttp.ClientSession();
# s.get(...)) to `aiohttp.get` / `aiohttp.post`, which are NOT in
# this list — so aliased aiohttp sessions are not yet caught. To
# cover them, add `aiohttp.get` / `aiohttp.post` callees here.
# (requests/httpx aliasing already works: their callee lists use the
# `requests.get` / `httpx.post` form the resolver produces.)
# Bare `urlopen` covers the from-import form
# (`from urllib.request import urlopen`).
- urlopen
# The engine's alias resolver canonicalizes a reused session
# (s = aiohttp.ClientSession(); s.get(...)) to
# `aiohttp.ClientSession.get` / `.post`, so these entries match
# both the aliased and the direct form.
- aiohttp.ClientSession.get
- aiohttp.ClientSession.post
missing: timeout
Expand Down
1 change: 0 additions & 1 deletion claude_sdk/repo_hygiene.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ rules:
title: Repo ships Claude Agent SDK code without an agent-guidance doc (AGENTS.md/CLAUDE.md)
severity: low
confidence: 0.9
language: python
applies_to:
- claude_sdk
scope: repo
Expand Down
27 changes: 18 additions & 9 deletions claude_skill/skill_safety.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ policy:
rules:
- id: CSKILL-001
title: Skill auto-approves unrestricted shell
severity: critical
confidence: 0.95
severity: high
confidence: 0.9
applies_to:
- claude_skill
scope: skill
Expand All @@ -21,14 +21,23 @@ rules:
explanation: >
This skill's allowed-tools pre-approves unrestricted shell — a bare `Bash`
grant or `Bash(*)`. In Claude Code allowed-tools is an auto-approval list,
not a sandbox: while the skill is active Claude runs any shell command it
lists WITHOUT prompting you. Combined with a skill's ability to run bundled
scripts and dynamic-context commands, this is effectively arbitrary local
code execution on activation.
not a sandbox: while the skill is active, any shell command Claude issues
runs without a per-command approval prompt. The grant is bounded — it
applies only while this skill is active, only after the workspace or
plugin is trusted, and a skill cannot change permission modes — but
activation is not only manual: unless disable-model-invocation is set,
Claude can load the skill itself when it judges it relevant. During that
window, a prompt-injected instruction (from a file the skill reads, a
tool result, or fetched content) can run arbitrary commands with no
approval gate, and a bare grant auto-approves far more than the commands
the skill actually needs.
fix: >
Replace the wildcard with the specific commands the skill needs, e.g.
`allowed-tools: Bash(git status *) Bash(git diff *)`. Never grant `Bash(*)`
or a bare `Bash` in a skill checked into a shared repository.
Replace the wildcard with the specific command prefixes the skill needs,
e.g. `allowed-tools: Bash(git status *) Bash(git diff *)` — the skill
keeps working, but only its own commands are pre-approved. For
side-effecting workflows, also set `disable-model-invocation: true` so
only the user can activate the skill (see CSKILL-050). Never grant
`Bash(*)` or a bare `Bash` in a skill checked into a shared repository.

- id: CSKILL-002
title: Skill runs shell during load (dynamic-context execution)
Expand Down
37 changes: 22 additions & 15 deletions google_adk/agent_safety.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ rules:
it as documentation for the parent agent's model.

- id: ADK-102
title: Agent with BashTool has no before_tool_callback
title: Agent with the bash tool has no before_tool_callback
severity: high
confidence: 0.85
language: python
Expand All @@ -44,17 +44,19 @@ rules:
match:
all:
- agent_class: [LlmAgent]
- agent_uses_hosted_tool_class: [BashTool]
- agent_uses_hosted_tool_class: [BashTool, ExecuteBashTool]
- agent_kwarg_missing: [before_tool_callback]
explanation: >
An LlmAgent with `BashTool` in its tools= list can execute
An LlmAgent with the bash tool (`ExecuteBashTool` in current google-adk;
`BashTool` in earlier releases) in its tools= list can execute
arbitrary shell commands chosen by the model. ADK's
`before_tool_callback` is the only synchronous interception point
where you can inspect a tool call before it executes and
short-circuit it — the moral analog of an OpenAI agent's
`input_guardrails`. An agent with BashTool and no
`before_tool_callback` has no opportunity to deny a shell command
the model invents.
`input_guardrails`. An agent with the bash tool and no
`before_tool_callback` has no policy of its own over which shell
commands the model invents — only the tool's generic built-in
confirmation prompt stands between the model and execution.
fix: >
Add a `before_tool_callback=` that inspects the tool name and
arguments, allow-lists known-safe commands (or denies known-bad
Expand All @@ -63,7 +65,7 @@ rules:
tool execution.

- id: ADK-103
title: Sub-agent is granted BashTool
title: Sub-agent is granted the bash tool
severity: high
confidence: 0.9
language: python
Expand All @@ -73,17 +75,18 @@ rules:
match:
all:
- agent_is_subagent_of_any: true
- agent_uses_hosted_tool_class: [BashTool]
- agent_uses_hosted_tool_class: [BashTool, ExecuteBashTool]
explanation: >
This LlmAgent appears in another agent's `sub_agents=[...]` tree
AND is granted `BashTool`. A parent agent can delegate to a
child without the child's tool calls being mediated by the
parent's `before_tool_callback`. Granting shell access to a
AND is granted the bash tool (`ExecuteBashTool` in current
google-adk; `BashTool` in earlier releases). A parent agent can
delegate to a child without the child's tool calls being mediated
by the parent's `before_tool_callback`. Granting shell access to a
delegated sub-agent makes the parent's policy worthless: the
model can route around any safety hook by handing off to the
sub-agent and asking it to run the same command.
fix: >
Either remove `BashTool` from this sub-agent's tools= list, or
Either remove the bash tool from this sub-agent's tools= list, or
restructure so the shell-capable agent is the orchestrator at
the top of the tree (so its `before_tool_callback` actually
mediates every shell call).
Expand Down Expand Up @@ -236,11 +239,15 @@ rules:
match:
all:
- agent_class: [LlmAgent]
- agent_uses_hosted_tool_class: [UrlContextTool, LoadWebPage]
- any:
- agent_uses_hosted_tool_class: [UrlContextTool, LoadWebPage]
- agent_grants_builtin_tool: [url_context, load_web_page]
- agent_kwarg_missing: [before_tool_callback]
explanation: >
This LlmAgent is wired with a URL/page-fetch built-in (UrlContextTool or
LoadWebPage) but has no before_tool_callback. These tools retrieve
This LlmAgent is wired with a URL/page-fetch built-in — the idiomatic
url_context instance or the load_web_page function reference, or a
directly-constructed UrlContextTool — but has no before_tool_callback.
These tools retrieve
model-chosen URLs whose content re-enters the agent loop — a prompt
injection vector (attacker-controlled page content) and an SSRF surface
(the model can point the fetch at internal addresses). Without a callback
Expand Down
Loading
Loading