Is strands-agents safe?
- Python shell/command execution
- Python network egress
- Python filesystem read
strands-agents is an AI python_package analyzed by SkillTotal's deterministic static scanner. The scan found no malicious indicators, though 6 risky constructs are reported for review. It can: filesystem read, filesystem write, mcp tools detected, network egress and shell execution — capabilities are what the code can do, not a verdict on intent. Risk score 10/100 (low).
strands-agents 1.45.0
Automated static-analysis result. It can contain false positives and false negatives, and is not a claim about the intent of strands-agents's authors. Report a false positive.
Findings (6)
The component can run operating-system commands or spawn processes.
proc = await asyncio.create_subprocess_exec(
program,
*args,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
start_new_session=_USE_PROCESS_GROUP,
)Why it matters: Powerful and often legitimate — confirm the commands aren't built from untrusted input.
Fix: Confirm the command and its arguments are fully controlled and not derived from untrusted input; avoid shell=True.
A server is bound to all network interfaces (0.0.0.0), not just your own machine.
llama-server -m model.gguf --host 0.0.0.0 --port 8080
host: The host address to bind the server to. Defaults to "0.0.0.0".
Why it matters: Without authentication, other hosts on the network can reach it.
Fix: Bind to 127.0.0.1 for local-only use, or require authentication and restrict access if remote exposure is intended.
The component reads files from disk.
with open(config_path) as f:
return self._resolve_path(path).read_bytes()
with open(path, encoding="utf-8") as f:
return path.read_text(encoding="utf-8")
parsed = json.loads(path.read_text(encoding="utf-8"))
return path.read_text(encoding="utf-8")
return file_path.read_bytes(), content_type
raw = await self._sandbox.read_text(self._artifact_path(self._METADATA_FILE))
result: dict[str, str] = json.loads(metadata_path.read_text(encoding="utf-8"))
skill = Skill.from_content(await sandbox.read_text(md_path), strict=self._strict)
content = skill_md_path.read_text(encoding="utf-8")
file_content = await sandbox.read_text(file_path)
file_content = await sandbox.read_text(file_path)
file_text = await sandbox.read_text(file_path)
Why it matters: Usually legitimate, but worth confirming it can't be steered into reading sensitive files.
Fix: Confirm which files are read and that paths cannot be influenced by untrusted input to reach sensitive locations.
The component writes or deletes files on disk.
full_path.write_bytes(content)
with open(tmp, "w", encoding="utf-8", newline="\n") as f:
shutil.rmtree(session_dir)
await self._sandbox.write_text(self._artifact_path(self._METADATA_FILE), json.dumps(self._content_types))
host_path.write_bytes(content)
metadata_path.write_text(json.dumps(self._content_types), encoding="utf-8")
await sandbox.write_text(file_path, file_text)
await sandbox.write_text(file_path, new_content)
await sandbox.write_text(file_path, new_content)
Why it matters: Usually legitimate, but worth confirming the paths can't be controlled by untrusted input.
Fix: Confirm which files are written/deleted and that paths cannot be influenced by untrusted input.
The component makes outbound network requests.
import httpx
async with httpx.AsyncClient(timeout=self.timeout) as client:
async with httpx.AsyncClient(timeout=self.timeout) as httpx_client:
import httpx
timeout_obj = httpx.Timeout(
connect=timeout[0] if len(timeout) > 0 else None,
read=timeout[1] if len(timeout) > 1 else None,
write=timeout[2] if len(timeout) > 2 else None,
po …timeout_obj = httpx.Timeout(timeout or 30.0)
self.client = httpx.AsyncClient(
base_url=self.base_url,
timeout=timeout_obj,
)from urllib.parse import urlparse
parsed = urlparse(url.rstrip("/"))import urllib.error
import urllib.request
req = urllib.request.Request(url, headers={"User-Agent": "strands-agents-sdk"}) # noqa: S310with urllib.request.urlopen(req, timeout=30) as response: # noqa: S310
Why it matters: Usually legitimate, but confirm the destinations are expected and no sensitive data leaves.
Fix: Confirm the destination hosts are expected and that no sensitive data is sent off-host.
An MCP tool surface (manifest or tool definitions) was found.
- Python-Based Tools: Simple `@tool` decorator with hot reloading
@tool(context=True)
@tool(context=True)
@tool(context=True)
"Python module names, or @tool annotated functions in files.",
or @tool annotated functions). For tools requiring code-based instantiation with constructor
# @tool validation does not enforce ``minItems``, so guard here.
to scan for @hook (and optionally @tool) decorated methods, and shared registry
"""Scan an instance's class hierarchy for @tool decorated methods.
Unlike agent-level Plugin, MultiAgentPlugin does not support @tool decorated methods
with @hook and @tool decorators.
tools: Tools attached to the agent, auto-discovered from @tool decorated methods during __init__
Scans the class for methods decorated with @hook and @tool and stores
"""List of tools the plugin provides, auto-discovered from @tool decorated methods."""
3. Auto-registering discovered @tool decorated methods with the agent
4. Auto-registers all discovered @tool methods with the agent's tool registry
@tool(name="noop", description="This is a fake tool that MUST be completely ignored.")
This module provides the @tool decorator that transforms Python functions into SDK Agent tools with automatic metadata
The @tool decorator performs several functions:
Why it matters: Just context — review which tools it offers and their permissions.
Fix: Review the declared MCP tools and their permissions.
Check your own component
Run the same evidence-backed scan on any MCP server, agent skill, or package.
Scan your own componentOr get notified if this component's risk changes:
How we determine this: deterministic static analysis (regex + AST), evidence-anchored, no code execution. Methodology →