T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/forecast.py:93
- Finding
- Plaintext User Identifiers Stored Without Restrictive File Permissions## Vulnerability Details **File Location**: `scripts/forecast.py:93-151` **Vulnerability Type**: Plaintext sensitive-data storage and insecure file permissions **Risk Level**: Medium ### Vulnerable Code ```python def __init__(self, skill_name: str): self.skill_name = skill_name self.trial_dir = os.path.expanduser("~/.openclaw/skill_trial") self.trial_file = os.path.join(self.trial_dir, f"{skill_name}.json") self.max_free_calls = 10 # Ensure trial directory exists os.makedirs(self.trial_dir, exist_ok=True) def _save_trial_data(self, data: Dict[str, Any]): """Save trial data to file.""" try: with open(self.trial_file, 'w', encoding='utf-8') as f: json.dump(data, f, ensure_ascii=False, indent=2) except IOError as e: print(f"Warning: Could not save trial data: {e}", file=sys.stderr) def use_trial(self, user_id: str) -> bool: """Record a free trial usage for a user.""" if not user_id: return False data = self._load_trial_data() if user_id not in data: data[user_id] = {'used_calls': 0, 'first_use': datetime.now().isoformat()} data[user_id]['used_calls'] += 1 data[user_id]['last_use'] = datetime.now().isoformat() self._save_trial_data(data) return True ``` ### Technical Analysis Caller-supplied user identifiers are used directly as JSON keys and stored with usage counts and timestamps. Contrary to the claims in `SECURITY.md` and `FAQ.md`, the identifiers are not hashed. The directory and file are created without explicit restrictive modes. Their effective permissions depend on the process umask and any pre-existing directory permissions. The implementation also performs an unlocked read-modify-write sequence, so concurrent processes can overwrite each other's updates or leave inconsistent state. The path is predictable: ```text ~/.openclaw/skill_trial/agricultural-output-f ...[truncated 1127 chars]
- Remediation
- ## Remediation Suggestions - Do not store raw external identifiers. Derive storage keys using a keyed HMAC with a locally protected secret. - Create `~/.openclaw/skill_trial` with mode `0700`. - Create the state file with mode `0600`, using `os.open` with explicit flags and permissions. - Write to a private temporary file, flush and synchronize it, and atomically replace the destination with `os.replace`. - Add inter-process locking around the complete read-modify-write operation. - Reject malformed or excessively long identifiers before persistence. - Document a retention period and provide a supported deletion mechanism. - Correct the security documentation so it accurately describes whether identifiers are hashed, encoded, or stored in plaintext.
