T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/config.py:50
- Finding
- Transaction-Authorizing API Key and Shipping PII Stored Without Explicit Access Restrictions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/config.py:50-59`; sensitive credential construction occurs at `scripts/get_api_key.py:94-105` **Vulnerability Type**: Plaintext sensitive-data storage with insufficient file-permission enforcement **Risk Level**: Medium ### Vulnerable Code ```python def save_credentials(creds: dict) -> bool: """Save credentials to credentials.json file.""" try: with open(CREDS_FILE, "w") as f: json.dump(creds, f, indent=2) return True except IOError as e: print(f"❌ Failed to save credentials: {e}", file=sys.stderr) return False ``` The data written through this function includes: ```python creds = { "api_key": result["apiKey"], "wallet_address": result["walletAddress"], "shipping_profile": { "email": args.email, "address": args.address, **({"phone": args.phone} if args.phone else {}), }, } save_credentials(creds) ``` ### Technical Analysis The Skill stores a transaction-authorizing Scout API key alongside the user's wallet address, email, physical shipping address, and optional phone number in plaintext. The file is opened using the process's default creation mode, so its effective permissions depend on the ambient `umask`. The implementation does not explicitly enforce owner-only access, check whether the destination is a symbolic link, or use atomic file replacement. The `.gitignore` entry for `credentials.json` reduces the chance of accidental Git commits, but it does not protect the file from other local users, compromised processes, overly broad backups, or filesystem synchronization tools. This is particularly sensitive because `SKILL.md` states that the API key authorizes transactions from the custodial wallet. A permissive `umask` can result in credentials readable by unintended local principals. Rewriting an existing file also preserves potentially unsafe permissions. If an attacker can pre-create or rep ...[truncated 1553 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Create the credential file with explicit owner-only permissions: - Use `os.open()` with `os.O_WRONLY | os.O_CREAT | os.O_TRUNC` and mode `0o600`. - Apply `os.chmod(CREDS_FILE, 0o600)` when migrating or rewriting an existing file. 2. Write credentials atomically: - Create a temporary file in the same directory with mode `0600`. - Flush and `fsync()` it. - Replace the destination using `os.replace()`. 3. Reject unsafe filesystem targets: - Use `O_NOFOLLOW` where supported. - Verify that the destination is not a symbolic link. - Ensure the parent directory is trusted and not writable by unrelated users. 4. Prefer an operating-system keyring or dedicated secret manager for the API key rather than storing it in a JSON file. 5. Minimize retained PII. Store shipping information only when the user explicitly opts in, and provide a command to delete saved credentials and shipping data. 6. Separate the API key from the shipping profile so compromise of one storage mechanism does not automatically disclose both authorization data and personal information. 7. Implement API-side safeguards such as key rotation, revocation, transaction limits, destination restrictions, and user confirmation for value-moving operations. 8. Avoid returning the credential value in command output or logs, and document the local file's sensitivity and required permissions. ]]>
