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## 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
## 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
Content
**📝 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
{
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.

Credential Access

High
Category
Privilege Escalation
Content
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()
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.

External Transmission

Medium
Category
Data Exfiltration
Content
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"}'
```
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Session Persistence

Medium
Category
Rogue Agent
Content
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"}'
```
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.

External Transmission

Medium
Category
Data Exfiltration
Content
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"}
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
# 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
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
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"]
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
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
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
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)
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
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)
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
headers=HEADERS)

# 7. Like the chatroom
requests.post(f"{BASE}/like_chatroom",
    json={"room_id": room_id},
    headers=HEADERS)
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
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:
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
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)
```
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Static analysis

No suspicious patterns detected.