T09 · Insecure Skill Coding Practices
- Location
scripts/setup.py:213- Finding
API Credentials Are Duplicated into an Unprotected Plaintext Configuration File
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill mostly does what it claims, but it needs review because it copies existing API credentials into a plaintext local file and can post LLM-generated content to multiple messaging channels.
Review before installing. Use dedicated low-scope API keys with spending limits, avoid copying credentials from existing OpenClaw auth where possible, protect or remove scripts/.env after setup, and configure only the exact delivery channel and target you intend. Treat generated digests as untrusted because monitored tweets can influence the LLM output and links posted by the bot.
scripts/setup.py:213API Credentials Are Duplicated into an Unprotected Plaintext Configuration File
scripts/monitor.py:161Untrusted Tweet Content Can Inject Instructions into LLM-Generated Digests
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
if not key:
return None
try:
r = requests.post(
"https://api.anthropic.com/v1/messages",
headers={"x-api-key": key, "anthropic-version": "2023-06-01",
"content-type": "application/json"},
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
if not key:
return None
try:
r = requests.post(
"https://api.openai.com/v1/chat/completions",
headers={"Authorization": f"Bearer {key}",
"Content-Type": "application/json"},
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
if not key:
return None
try:
r = requests.post(
f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key={key}",
headers={"Content-Type": "application/json"},
json={"contents": [{"parts": [{"text": prompt}]}]},
The declared purpose emphasizes a Twitter-to-Discord digest, but the documentation shows broader behavior including multi-channel outbound messaging and use of third-party services not clearly disclosed in the metadata. This mismatch can mislead users and reviewers about where data is sent and what external systems are involved, increasing the risk of unauthorized exfiltration or policy bypass.
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
AI Twitter Digest — OpenClaw Skill
Fetches tweets from AI/tech influencers, summarizes with available LLM, posts to a chat channel.
Configuration (environment variables or .env file next to this script):
Required:
AISA_API_KEY - AISA API key (aisa.one)
DELIVERY_CHANNEL - Channel to post to: discord | whatsapp | telegram | slack | ...
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
AI Twitter Digest — OpenClaw Skill
Fetches tweets from AI/tech influencers, summarizes with available LLM, posts to a chat channel.
Configuration (environment variables or .env file next to this script):
Required:
AISA_API_KEY - AISA API key (aisa.one)
DELIVERY_CHANNEL - Channel to post to: discord | whatsapp | telegram | slack | ...
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
AI Twitter Digest — OpenClaw Skill
Fetches tweets from AI/tech influencers, summarizes with available LLM, posts to a chat channel.
Configuration (environment variables or .env file next to this script):
Required:
AISA_API_KEY - AISA API key (aisa.one)
DELIVERY_CHANNEL - Channel to post to: discord | whatsapp | telegram | slack | ...
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
AI Twitter Digest — OpenClaw Skill
Fetches tweets from AI/tech influencers, summarizes with available LLM, posts to a chat channel.
Configuration (environment variables or .env file next to this script):
Required:
AISA_API_KEY - AISA API key (aisa.one)
DELIVERY_CHANNEL - Channel to post to: discord | whatsapp | telegram | slack | ...
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
AI Twitter Digest — OpenClaw Skill
Fetches tweets from AI/tech influencers, summarizes with available LLM, posts to a chat channel.
Configuration (environment variables or .env file next to this script):
Required:
AISA_API_KEY - AISA API key (aisa.one)
DELIVERY_CHANNEL - Channel to post to: discord | whatsapp | telegram | slack | ...
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
AI Twitter Digest — OpenClaw Skill
Fetches tweets from AI/tech influencers, summarizes with available LLM, posts to a chat channel.
Configuration (environment variables or .env file next to this script):
Required:
AISA_API_KEY - AISA API key (aisa.one)
DELIVERY_CHANNEL - Channel to post to: discord | whatsapp | telegram | slack | ...
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
from pathlib import Path
SKILL_DIR = Path(__file__).parent.parent
ENV_FILE = Path(__file__).parent / ".env"
def banner(text):
print(f"\n{'─' * 50}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
from pathlib import Path
SKILL_DIR = Path(__file__).parent.parent
ENV_FILE = Path(__file__).parent / ".env"
def banner(text):
print(f"\n{'─' * 50}")
The skill documentation describes capabilities that require environment access, filesystem reads/writes, network access, and shell execution, but it does not declare any explicit tool scope or permission boundaries. This increases the chance that an agent runtime grants broader access than users expect, enabling unintended data exposure or side effects during setup, scheduling, and delivery.
The manifest presents the skill as Discord-focused, while the body documents configurable delivery to WhatsApp, Telegram, Slack, and Signal. In security terms, this is a disclosure/integrity issue because users may authorize the skill under the assumption of a single destination when it can send content to multiple external channels.
The skill explicitly persists API keys and operational state to local files, including a .env file. Storing credentials on disk creates a durable target for theft by other local users, backup systems, logs, or later-compromised processes, especially if file permissions and storage location are not tightly controlled.
- Auto-detect API keys from your environment and OpenClaw config
- Prompt for any missing keys (AISA, LLM, Discord channel)
- Test connectivity to AISA and your chosen LLM provider
- Write a `.env` file — no manual editing needed
> If you prefer manual setup, create `scripts/.env` with the following content:
>
The manifest says the skill summarizes Twitter/X posts and delivers the digest to a Discord channel, suggesting Discord-scoped delivery behavior. The code and its documentation explicitly support multiple messaging platforms via DELIVERY_CHANNEL and DELIVERY_TARGET, materially broadening the delivery behavior beyond the declared description.
The configuration text sets SUMMARY_LANGUAGE to Chinese by default, which imposes a specific language on users unless they discover and override the setting. The policy for this audit flags language or locale constraints when they are forced without explicit user choice or opt-in.
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
def save_sent_tweets(ids):
ids_list = sorted(ids)[-MAX_STORED_IDS:]
with open(STATE_FILE, "w") as f:
json.dump({"tweet_ids": ids_list,
"updated": datetime.now(timezone.utc).isoformat()}, f)
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
def fetch_tweets(username, limit=20):
import requests
headers = {"Authorization": f"Bearer {AISA_API_KEY}"}
base = "https://api.aisa.one/apis/v1/twitter/tweet/advanced_search"
for query in [
f"from:{username} AI OR LLM OR GPT OR model OR agent",
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
def fetch_tweets(username, limit=20):
import requests
headers = {"Authorization": f"Bearer {AISA_API_KEY}"}
base = "https://api.aisa.one/apis/v1/twitter/tweet/advanced_search"
for query in [
f"from:{username} AI OR LLM OR GPT OR model OR agent",
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
if not key:
return None
try:
r = requests.post(
"https://api.anthropic.com/v1/messages",
headers={"x-api-key": key, "anthropic-version": "2023-06-01",
"content-type": "application/json"},
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
if not key:
return None
try:
r = requests.post(
"https://api.openai.com/v1/chat/completions",
headers={"Authorization": f"Bearer {key}",
"Content-Type": "application/json"},
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
if not key:
return None
try:
r = requests.post(
f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key={key}",
headers={"Content-Type": "application/json"},
json={"contents": [{"parts": [{"text": prompt}]}]},
The manifest frames the skill as delivering a formatted digest to a Discord channel, but the code delegates delivery to a generic openclaw message send command with a configurable channel type. This makes the actual behavior broader than a Discord-specific integration and changes the operational model described to users.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def send_message(message):
"""Send a message via OpenClaw to the configured channel and target."""
subprocess.run([
"openclaw", "message", "send",
"--channel", DELIVERY_CHANNEL,
"--target", DELIVERY_TARGET,
No suspicious patterns detected.