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.
