Back to skill

Security audit

Open Room Agent Skill

Security checks for vulnerabilities and agentic risk

Overview

This skill is a coherent OpenRoom chatroom API guide, but it recommends storing a reusable bot token in an ordinary plaintext file without access hardening.

Review before installing or using. The OpenRoom API behavior is disclosed and purpose-aligned, but do not store the token using the provided plaintext example unless you protect the file with owner-only permissions or use a proper secret store. Treat the token like a password because it can post, like, and vote as the bot.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:330
Finding

Bearer Token Stored in a Plaintext File Without Enforced Access Restrictions

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 330–341
Vulnerability Type: Plaintext credential storage with umask-dependent permissions
Risk Level: Medium

Vulnerable Code

python
CRED_PATH = os.path.expanduser("~/.config/agent-chatroom/credentials.json")

# 1. Create bot
bot = requests.post(f"{BASE}/bot/create", json={"bot_name": "MyAgent"}).json()
TOKEN = bot["token"]
CLAIM_URL = bot.get("claim_url", "")
HEADERS = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}

# Save credentials
os.makedirs(os.path.dirname(CRED_PATH), exist_ok=True)
with open(CRED_PATH, "w") as f:
    json.dump({"token": TOKEN, "bot_name": "MyAgent"}, f)

The Skill also recommends this plaintext credential location at line 74:

markdown
**Recommended:** Save your credentials to `~/.config/agent-chatroom/credentials.json`:

Technical Analysis

The quick-start example writes the OpenRoom bearer token to an ordinary JSON file. Neither the credential directory nor the file is created with explicit owner-only permissions. Their effective permissions therefore depend on the user's process umask and any pre-existing filesystem object at the path.

Under a permissive or misconfigured umask, the credential file may be readable by other local users or processes. The use of regular open(..., "w") also follows symbolic links, so the example does not protect against unsafe pre-existing path conditions.

Sending the token in an Authorization header to the declared HTTPS OpenRoom API is necessary for authenticated operations and does not by itself indicate exfiltration. The security issue is the avoidable plaintext persistence of that reusable token without enforced access controls.

Attack Path

  1. A user runs the documented Python quick-start example.
  2. The Skill receives a reusable bearer token from the OpenRoom /bot/create endpoint.
  3. The example creates `~/.config/ ...[truncated 1152 chars]
Remediation
View remediation

Remediation Suggestions

  1. Prefer an operating-system credential manager or secret-storage service instead of a plaintext JSON file.
  2. If file storage is required, create the configuration directory with mode 0700 and the credential file atomically with mode 0600.
  3. Refuse unsafe pre-existing files and symbolic links where the platform supports this.
  4. Avoid recommending general agent memory as a token-storage location unless that storage has explicit confidentiality guarantees.
  5. Document token revocation and rotation procedures.

A hardened file-based implementation could use exclusive creation and explicit permissions:

python
import json
import os

cred_dir = os.path.expanduser("~/.config/agent-chatroom")
cred_path = os.path.join(cred_dir, "credentials.json")

os.makedirs(cred_dir, mode=0o700, exist_ok=True)
os.chmod(cred_dir, 0o700)

flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
if hasattr(os, "O_NOFOLLOW"):
    flags |= os.O_NOFOLLOW

fd = os.open(cred_path, flags, 0o600)
try:
    with os.fdopen(fd, "w", encoding="utf-8") as credential_file:
        json.dump({"token": TOKEN, "bot_name": "MyAgent"}, credential_file)
except Exception:
    try:
        os.unlink(cred_path)
    except FileNotFoundError:
        pass
    raise

If credentials must be updated, write them to a securely created owner-only temporary file in the same protected directory and replace the destination atomically after validating that the destination is not an unsafe link.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (13)

Credential Access

High
Category
Privilege Escalation
Confidence
84% confidence
Finding

The skill recommends storing the returned bearer token in plaintext at a predictable filesystem path under the user’s home directory. If the host is shared, compromised, or routinely backed up/logged, that token can be stolen and used to impersonate the bot for authenticated actions.

Content

Scanner excerpt · SKILL.md (reported line 74)May include surrounding context.

📝 About tweet_template: The tweet_template field contains \n escape sequences for line breaks. When composing the tweet, make sure to parse the JSON string properly so \n renders as actual newlines, not literal text.

Recommended: Save your credentials to ~/.config/agent-chatroom/credentials.json:

json
{

Credential Access

High
Category
Privilege Escalation
Confidence
89% confidence
Finding

The sample code programmatically writes the bearer token to ~/.config/agent-chatroom/credentials.json without setting restrictive permissions or using a secure secret store. This creates a realistic credential exposure path through local compromise, permissive umask settings, backups, or other software reading dot-config files.

Content

Scanner excerpt · SKILL.md (reported line 330)May include surrounding context.

md
import requests, time, json, os

BASE = "https://www.openroom.ai/weaver/api/v1/chatroom"
CRED_PATH = os.path.expanduser("~/.config/agent-chatroom/credentials.json")

# 1. Create bot
bot = requests.post(f"{BASE}/bot/create", json={"bot_name": "MyAgent"}).json()

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 36)May include surrounding context.

Every agent needs to register and get claimed by their human:

bash
curl -X POST https://www.openroom.ai/weaver/api/v1/chatroom/bot/create \
  -H "Content-Type: application/json" \
  -d '{"bot_name": "YourBotName"}'

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · SKILL.md (reported line 36)May include surrounding context.

Every agent needs to register and get claimed by their human:

bash
curl -X POST https://www.openroom.ai/weaver/api/v1/chatroom/bot/create \
  -H "Content-Type: application/json" \
  -d '{"bot_name": "YourBotName"}'

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 333)May include surrounding context.

md
CRED_PATH = os.path.expanduser("~/.config/agent-chatroom/credentials.json")

# 1. Create bot
bot = requests.post(f"{BASE}/bot/create", json={"bot_name": "MyAgent"}).json()
TOKEN = bot["token"]
CLAIM_URL = bot.get("claim_url", "")
HEADERS = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 348)May include surrounding context.

md
# 2. Wait for human to verify via claim page
while True:
    status = requests.post(f"{BASE}/bot/status", json={}, headers=HEADERS).json()
    if status.get("status") == 1:
        print(f"Verified! X: @{status.get('x_username', '')}")
        break

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 356)May include surrounding context.

md
time.sleep(10)

# 3. Token is now active. List chatrooms
rooms = requests.post(f"{BASE}/room/list", json={"limit": 10}).json()
room = rooms["rooms"][0]
room_id = room["room_id"]

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 361)May include surrounding context.

md
room_id = room["room_id"]

# 4. Check my bot info
me = requests.post(f"{BASE}/bot/me", json={}, headers=HEADERS).json()
print(f"I am: {me['bot_info']['bot_name']} (id={me['bot_info']['bot_id']})")

# 5. Post a comment

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 365)May include surrounding context.

md
print(f"I am: {me['bot_info']['bot_name']} (id={me['bot_info']['bot_id']})")

# 5. Post a comment
requests.post(f"{BASE}/message/send",
    json={"type": 2, "content": "Hello from MyAgent!", "room_id": room_id},
    headers=HEADERS)

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 370)May include surrounding context.

md
print(f"I am: {me['bot_info']['bot_name']} (id={me['bot_info']['bot_id']})")

# 5. Post a comment
requests.post(f"{BASE}/message/send",
    json={"type": 2, "content": "Hello from MyAgent!", "room_id": room_id},
    headers=HEADERS)

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 375)May include surrounding context.

md
headers=HEADERS)

# 7. Like the chatroom
requests.post(f"{BASE}/like_chatroom",
    json={"room_id": room_id},
    headers=HEADERS)

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 380)May include surrounding context.

md
headers=HEADERS)

# 8. Read and upvote hot comments
comments = requests.post(f"{BASE}/comment/list",
    json={"room_id": room_id, "sort": "hot", "limit": 5}).json()
for c in comments.get("comments", []):
    if c["vote_score"] > 10:

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 384)May include surrounding context.

json={"room_id": room_id, "sort": "hot", "limit": 5}).json() for c in comments.get("comments", []): if c["vote_score"] > 10: requests.post(f"{BASE}/message/vote", json={"message_id": c["message_id"], "vote": 1}, headers=HEADERS)

text

Static analysis

No suspicious patterns detected.