SkillTotal

Is crewai safe?

No malicious indicators - review capabilities before installing
Notable — review in context (capabilities are not malware):
  • Python shell/command execution
  • Python dynamic code execution
  • Unsafe deserialization

crewai is an AI python_package analyzed by SkillTotal's deterministic static scanner. The scan found no malicious indicators, though 7 risky constructs are reported for review. It can: dynamic code execution, 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 20/100 (low).

crewai 1.15.1

python_package · pypi:crewai
LOW
20
/ 100 risk score
Snapshot · scanned Jul 7, 2026 · crewai@1.15.1 · engine 0.34.2 / ruleset 34

Automated static-analysis result. It can contain false positives and false negatives, and is not a claim about the intent of crewai's authors. Report a false positive.

Capabilities — what this component can do (not a risk score):
dynamic code executionfilesystem readfilesystem writemcp tools detectednetwork egressshell execution

Findings (7)

HIGHUnsafe deserializationST-DESERIALIZE-PY

It loads data with a format that can rebuild arbitrary objects (e.g. pickle, or unsafe YAML).

return pickle.load(file)  # noqa: S301

Why it matters: Feeding such a loader untrusted data can execute code hidden inside that data.

Fix: Deserialize untrusted data with a safe format/loader: JSON, or yaml.safe_load / Loader=SafeLoader. Reserve pickle/marshal for data you fully control.

HIGHPython dynamic code executionST-DYN-PY

The code turns strings into live code at runtime (eval / new Function / exec).

exec(compile(module, filename, "exec"), namespace)  # nosec B102 # noqa: S102

Why it matters: If those strings aren't fixed and trusted, they become a way to run arbitrary code.

Fix: Avoid evaluating dynamically constructed code; if unavoidable, ensure the input is a trusted constant and never derived from external data.

HIGHPython shell/command executionST-SHELL-PY

The component can run operating-system commands or spawn processes.

res = subprocess.run(
                    ["/usr/sbin/system_profiler", "SPHardwareDataType"],
                    capture_output=True,
                    text=True,
                    timeout=2,
                )
res = subprocess.run(
                    [
                        "C:\\Windows\\System32\\wbem\\wmic.exe",
                        "csproduct",
                        "get",
                        "UUID",
                    ], …

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.

MEDIUMPython filesystem readST-FS-PY-READ

The component reads files from disk.

root_certs = Path(self.ca_cert_path).read_bytes()
private_key = Path(self.client_key_path).read_bytes()
certificate_chain = Path(self.client_cert_path).read_bytes()
return Path(self.private_key_path).read_text()
results.append(json.loads(meta_file.read_text()))
contents = source_path.expanduser().read_text(encoding="utf-8")
css_content = css_file.read_text(encoding="utf-8")
js_content = js_file.read_text(encoding="utf-8")
with open(file_path, "r", encoding="utf-8") as csvfile:
with open(path, "r", encoding="utf-8") as json_file:
with open(config_path, encoding="utf-8") as file:
return parse_jsonc(path.read_text(encoding="utf-8"), source=path)
content = path.read_text(encoding="utf-8")
with open(file_path, "rb") as f:
with open(self._path, encoding="utf-8") as read_file:
with open(self.file_path, "rb") as file:
with open(self.prompt_file, encoding="utf-8") as f:
with open(prompts_path, encoding="utf-8") as f:

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.

MEDIUMPython filesystem write/deleteST-FS-PY-WRITE

The component writes or deletes files on disk.

p.write_text(json.dumps(data, indent=2))
p.write_text(json.dumps(data, indent=2))
(skill_dir / _META_FILENAME).write_text(json.dumps(meta, indent=2))
css_output_path.write_text(css_content, encoding="utf-8")
js_output_path.write_text(js_content, encoding="utf-8")
output_path.write_text(html_content, encoding="utf-8")
shutil.rmtree(shard_path, ignore_errors=True)
shutil.rmtree(self._local_path, ignore_errors=True)
shutil.rmtree(self._local_path, ignore_errors=True)
shutil.rmtree(entry, ignore_errors=True)
shutil.rmtree(entry, ignore_errors=True)
with open(self._path, "w", encoding="utf-8") as write_file:
with open(self._path, "a", encoding="utf-8") as file:
with open(self.file_path, "wb") as f:

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.

MEDIUMPython network egressST-NET-PY

The component makes outbound network requests.

client.auth = DigestAuth(self.username, self.password)
auth_url = f"{self.authorization_url}?{urllib.parse.urlencode(params)}"
async with httpx.AsyncClient() as client:
from httpx import AsyncClient, Response
async with httpx.AsyncClient(
            timeout=timeout, verify=verify
        ) as temp_auth_client:
async with httpx.AsyncClient(
        timeout=timeout, headers=headers, verify=verify
    ) as temp_client:
async with httpx.AsyncClient(
        timeout=timeout,
        headers=headers,
        verify=verify,
    ) as httpx_client:
from urllib.parse import urlparse
dl_response = httpx.get(download_url, follow_redirects=True)
from httpx import (
    AsyncHTTPTransport as _AsyncHTTPTransport,
    HTTPTransport as _HTTPTransport,
)
from httpx import Limits, Request, Response
from httpx._types import CertTypes, ProxyTypes
async_client_params["http_client"] = httpx.AsyncClient(
                transport=async_transport
            )

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.

LOWMCP tool surface detectedST-MCP-DETECTED

An MCP tool surface (manifest or tool definitions) was found.

"tools": "\nYou ONLY have access to the following tools, and should NEVER make up tools that are not listed here:\n\n{tools}\n\nIMPORTANT: Use the following format in your response:\n\n```\nThought: you should always think about what to do\ …
1. @tool - decorator without arguments, uses function name
2. @tool("name") - decorator with custom name
3. @tool(result_as_answer=True) - decorator with options
Only includes classes that inherit from BaseTool or functions decorated with @tool.
"Please ensure your project contains valid tools (classes inheriting from BaseTool or functions with @tool decorator)."
"or functions decorated with [bold]@tool[/bold]."

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 component

Or get notified if this component's risk changes:

How we determine this: deterministic static analysis (regex + AST), evidence-anchored, no code execution. Methodology →