T09 · Insecure Skill Coding Practices
Warning
- Location
- src/sparki_cli/config.py:45
- Finding
- API Key Stored in Plaintext Without Restrictive File Permissions<![CDATA[ ## Vulnerability Details **File Location**: `src/sparki_cli/config.py:45-55` **Vulnerability Type**: Plaintext credential storage and insecure file permissions **Risk Level**: Medium ### Vulnerable Code ```python def save(self, api_key: str | None = None, base_url: str | None = None, default_output_dir: str | None = None) -> None: self.config_dir.mkdir(parents=True, exist_ok=True) if api_key is not None: self._data["api_key"] = api_key if base_url is not None: self._data["base_url"] = base_url elif "base_url" not in self._data: self._data["base_url"] = DEFAULT_BASE_URL if default_output_dir is not None: self._data["default_output_dir"] = default_output_dir self.config_file.write_text(json.dumps(self._data, indent=2)) ``` ### Technical Analysis The `save` method stores the Sparki API key directly in the JSON configuration file at `~/.openclaw/config/sparki.json`. The API key is not encrypted or protected through an operating-system credential store. The code also does not explicitly enforce owner-only permissions on either the configuration directory or the configuration file. `Path.mkdir()` and `Path.write_text()` rely on the process umask and any pre-existing filesystem permissions. In an environment with a permissive umask or an improperly permissioned pre-existing configuration directory, another local account may be able to read the credential. The method also does not verify whether `sparki.json` is a symbolic link before writing it, which weakens the integrity guarantees around credential storage. ### Attack Path 1. The user runs `sparki setup --api-key <key>`. 2. The `Config.save()` method serializes the API key into `~/.openclaw/config/sparki.json`. 3. The file is created using permissions derived from the current process umask rather than an explicitly enforced `0600` mode. 4. Another local user or process with filesystem access reads the configuration file. 5. The attacker extra ...[truncated 635 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Prefer an operating-system credential manager, such as Keychain, Secret Service, or another platform-specific secret store. 2. If file-based storage is required: - Create the configuration directory with mode `0700`. - Create the configuration file atomically with mode `0600`. - Explicitly verify and correct permissions after writing. 3. Refuse to read from or write to symbolic links. 4. Write to a securely created temporary file in the same directory, set its permissions, and atomically replace the destination. 5. Consider storing only non-sensitive settings in `sparki.json` and requiring the API key through `SPARKI_API_KEY`. 6. Document the credential-storage behavior and provide a command to remove or rotate stored credentials. ]]>
