T09 · Insecure Skill Coding Practices
Warning
- Location
- SKILL.md:29
- Finding
- Plaintext API Key Persistence Without File Permission Protection<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:29-35` **Vulnerability Type**: Plaintext credential storage **Risk Level**: Medium ### Vulnerable Code ```markdown 2. **Config file** `config.json` in the skill root directory (fallback) ```json { "api_key": "hms_live_xxxxxx" } ``` When user provides a Key, write it to `config.json`. New keys may need 3-5 seconds to activate — if first call returns 403, wait 3 seconds and retry (max 2 retries). ``` The corresponding credential-loading logic appears at `scripts/apiclaw.py:78-85`: ```python config_path = os.path.join(skill_dir, "config.json") if os.path.exists(config_path): try: with open(config_path, "r", encoding="utf-8") as f: config = json.load(f) key = config.get("api_key", "").strip() if key: return key ``` `SECURITY.md:31` states that `config.json` is listed in `.gitignore`, but the audited project directory contains no `.gitignore`. ### Technical Analysis The Skill instructs the agent to persist a user-provided API key in a plaintext JSON file in the project root. It does not require explicit confirmation immediately before persistence, create the file with restrictive permissions, verify existing file permissions, or provide secure secret-storage integration. Although the executable only reads this fallback file, an agent following `SKILL.md` is expected to create it. A file created using ordinary defaults may inherit a permissive process umask and become readable by other local users or processes. Storing it in the project root also increases exposure through source-control commits, workspace synchronization, backups, archives, and project sharing. The documented `.gitignore` protection is not present in the audited package, so the stated mitigation does not apply to this artifact. ### Attack Path 1. A user provides an `APICLAW_API_KEY` to the agent. 2. Following `SKILL.md`, the agent writes the key to `config.json` ...[truncated 974 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Remove the instruction to automatically save user-provided keys. 2. Prefer environment-only credential resolution through `APICLAW_API_KEY`. 3. Require explicit user consent immediately before any persistent credential storage. 4. If file-based storage remains necessary: - Create the file atomically with mode `0600`. - Reject or warn about group-readable or world-readable permissions. - Store the credential outside the project tree in a dedicated user configuration directory. - Use an operating-system keychain or secret manager where available. 5. Include a `.gitignore` containing `/config.json` in the distributed package. 6. Add startup checks that warn if `config.json` is tracked by Git or has unsafe permissions. 7. Document credential revocation and rotation procedures. 8. Avoid printing the key in logs, error messages, command histories, or generated reports. ]]>
