T09 · Insecure Skill Coding Practices
- Location
helper.py:118- Finding
SenseAudio API keys are persisted in plaintext session-state files
- Content
View full analysis
None: _state_file(session_id).write_text( json.dumps(state, ensure_ascii=False, indent=2), encoding="utf-8", ) ``` Caller-provided API keys are copied into the state configuration: ```python @staticmethod def _merge_after_sales_payload(cfg: dict[str, Any], payload: dict[str, Any]) -> None: for key in ("api_key", "refund_policy", "shipping_fee_by", "audio_output_path"): value = str(payload.get(key, "")).strip() if value: cfg[key] = value ``` Equivalent storage occurs for sales-mode configuration through `_merge_sales_payload`. Repository artifacts confirm that the resulting state schema contains plaintext credentials: ```json "sales": { "api_key": "k", "voice_id": "male_0018_a" } ``` This pattern appears in: - `.session_state/persist-fix.json:13` - `.session_state/persist-fix-2.json:13` - `.session_state/persist-fix-3.json:13` ### Technical Analysis The Skill accepts a SenseAudio API key through its runtime payload, stores it in the session configuration, and serializes the complete configuration directly to a JSON file. There is no credential redaction, encryption, secret-store integration, expiry mechanism, or explicit owner-only file permission enforcement. Although the API key is legitimately required to authenticate TTS requests, persisting it in session state is not necessary for the declared functionality. The key can remain in process memory or be retrieved from the documented `SENSEAUDIO_API_KEY` environment variable when a request is made. The generated files inherit permissions from the process umask. In a shared or incorrectly configured environment, other local users or processes may therefore be able to read the credential ...[truncated 1231 chars]- Remediation
View remediation
