T09 · Insecure Skill Coding Practices
- Location
scripts/search.py:96- Finding
Plaintext caching of potentially sensitive API results without explicit restrictive permissions
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This skill is a coherent Tavily API toolkit with disclosed network, API-key, logging, and cache behavior, but users should handle credentials and cached results carefully.
Install only if you are comfortable giving the skill a Tavily API key and letting it send your queries, URLs, and requested research tasks to Tavily. Prefer a protected environment variable over plaintext ~/.openclaw/.env, avoid printing real keys in terminal output, use --no-cache for confidential searches or extracted pages, and review any cron-based update check before enabling it.
scripts/search.py:96Plaintext caching of potentially sensitive API results without explicit restrictive permissions
SKILL.md:58API-key setup and troubleshooting instructions do not enforce secure storage and may expose the secret on screen
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
method="POST",
)
with urllib.request.urlopen(req, timeout=60) as resp:
body = resp.read().decode("utf-8", errors="replace")
try:
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
# Map can take time
timeout = params.get("timeout", 60)
with urllib.request.urlopen(req, timeout=timeout) as resp:
body = resp.read().decode("utf-8", errors="replace")
try:
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
# Map can take time
timeout = params.get("timeout", 60)
with urllib.request.urlopen(req, timeout=timeout) as resp:
body = resp.read().decode("utf-8", errors="replace")
try:
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
# Research can take a long time
timeout = params.get("timeout", 300)
with urllib.request.urlopen(req, timeout=timeout) as resp:
body = resp.read().decode("utf-8", errors="replace")
try:
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
method="GET",
)
with urllib.request.urlopen(req, timeout=60) as resp:
body = resp.read().decode("utf-8", errors="replace")
return json.loads(body)
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
method="POST",
)
with urllib.request.urlopen(req, timeout=60) as resp:
body = resp.read().decode("utf-8", errors="replace")
try:
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
method="GET",
)
with urllib.request.urlopen(req, timeout=10) as resp:
body = resp.read().decode("utf-8", errors="replace")
data = json.loads(body)
The command uses rm -rf with a wildcard to delete files recursively, which is inherently dangerous if copied incorrectly, variables expand unexpectedly, or the path is modified. In this skill context it is limited to a cache directory, so the impact is lower than a generic wipe, but it still exposes users to unintended local deletion.
rm -rf ~/.openclaw/cache/tavily/*
The command uses rm -rf with a wildcard to delete files recursively, which is inherently dangerous if copied incorrectly, variables expand unexpectedly, or the path is modified. In this skill context it is limited to a cache directory, so the impact is lower than a generic wipe, but it still exposes users to unintended local deletion.
rm -rf ~/.openclaw/cache/tavily/*
Loading a secret from a plaintext ~/.openclaw/.env file is a weaker secret-management practice because it encourages long-lived local credential storage outside OS-backed secret stores. On multi-user systems, misconfigured permissions, backups, or other local access paths could expose the API key.
if key:
return key.strip()
env_path = pathlib.Path.home() / ".openclaw" / ".env"
if env_path.exists():
try:
txt = env_path.read_text(encoding="utf-8", errors="ignore")
Loading secrets from a plaintext ~/.openclaw/.env file increases the chance of credential exposure through weak filesystem permissions, backups, or accidental disclosure. While common in developer tooling, it is less secure than dedicated secret stores and the code does not verify permissions before use.
if key:
return key.strip()
env_path = pathlib.Path.home() / ".openclaw" / ".env"
if env_path.exists():
try:
txt = env_path.read_text(encoding="utf-8", errors="ignore")
The script embeds the loaded API key into the outbound request payload to a third-party service. Although this is functionally necessary, it still represents sensitive credential transmission and increases risk if users are not clearly informed, if logs/proxies capture requests, or if the service endpoint trust assumptions are wrong.
if not key:
log("Missing API key", "ERROR")
raise SystemExit(
"Missing TAVILY_API_KEY. Set env var TAVILY_API_KEY or add it to ~/.openclaw/.env"
)
payload = {"api_key": key, **params}
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.
def update_skill(force: bool = False) -> bool:
"""Update skill if needed"""
log("Checking if skill update is needed...")
check_result = check_for_updates()
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# ============================================================================
def load_key() -> str:
"""Load API key from environment or .env file"""
key = os.environ.get("TAVILY_API_KEY")
if key:
return key.strip()
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# ============================================================================
def load_key() -> str:
"""Load API key from environment or .env file"""
key = os.environ.get("TAVILY_API_KEY")
if key:
return key.strip()
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# ============================================================================
def load_key() -> str:
"""Load API key from environment or .env file"""
key = os.environ.get("TAVILY_API_KEY")
if key:
return key.strip()
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# ============================================================================
def load_key() -> str:
"""Load API key from environment or .env file"""
key = os.environ.get("TAVILY_API_KEY")
if key:
return key.strip()
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# ============================================================================
def load_key() -> str:
"""Load API key from environment or .env file"""
key = os.environ.get("TAVILY_API_KEY")
if key:
return key.strip()
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# ============================================================================
def load_key() -> str:
"""Load API key from environment or .env file"""
key = os.environ.get("TAVILY_API_KEY")
if key:
return key.strip()
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# ============================================================================
def load_key() -> str:
"""Load API key from environment or .env file"""
key = os.environ.get("TAVILY_API_KEY")
if key:
return key.strip()
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# ============================================================================
def load_key() -> str:
"""Load API key from environment or .env file"""
key = os.environ.get("TAVILY_API_KEY")
if key:
return key.strip()
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# ============================================================================
def load_key() -> str:
"""Load API key from environment or .env file"""
key = os.environ.get("TAVILY_API_KEY")
if key:
return key.strip()
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# ============================================================================
def load_key() -> str:
"""Load API key from environment or .env file"""
key = os.environ.get("TAVILY_API_KEY")
if key:
return key.strip()
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
if key:
return key.strip()
env_path = pathlib.Path.home() / ".openclaw" / ".env"
if env_path.exists():
try:
txt = env_path.read_text(encoding="utf-8", errors="ignore")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
if key:
return key.strip()
env_path = pathlib.Path.home() / ".openclaw" / ".env"
if env_path.exists():
try:
txt = env_path.read_text(encoding="utf-8", errors="ignore")