T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/phase_a_score.py:68
- Finding
- Configurable API endpoint can disclose the DeepSeek credential and article content<![CDATA[ ## Vulnerability Details **File Location**: `scripts/phase_a_score.py:68-105`; duplicated in `app/src/rss_brew/compat/phase_a_score.py:68-105` **Vulnerability Type**: Unvalidated credential-bearing API destination **Risk Level**: Medium ### Vulnerable Code ```python def _load_phase_a_config() -> PhaseAConfig: api_key = os.getenv("DEEPSEEK_API_KEY", "").strip() base_url = os.getenv("DEEPSEEK_BASE_URL", "https://api.deepseek.com/v1").strip() model = os.getenv("DEEPSEEK_MODEL", "deepseek-reasoner").strip() or "deepseek-reasoner" timeout = float(os.getenv("DEEPSEEK_TIMEOUT_SECONDS", "60")) retries = int(os.getenv("DEEPSEEK_RETRY_COUNT", "2")) return PhaseAConfig( api_key=api_key, base_url=base_url, model=model, timeout=timeout, retries=max(0, retries), ) def _build_client(config: PhaseAConfig) -> Any: if not config.get("api_key"): raise RuntimeError("DEEPSEEK_API_KEY is required unless --mock is used") try: from openai import OpenAI except Exception as exc: raise RuntimeError("openai package is required for direct DeepSeek API mode") from exc return OpenAI( api_key=config["api_key"], base_url=config["base_url"], timeout=config["timeout"], ) def _call_deepseek(prompt: str, config: PhaseAConfig, client: Any) -> str: attempts = config["retries"] + 1 last_err: Optional[Exception] = None for attempt in range(1, attempts + 1): try: resp = client.chat.completions.create( model=config["model"], messages=[ {"role": "system", "content": SYSTEM}, {"role": "user", "content": prompt}, ], temperature=0, ) ``` ### Technical Analysis `DEEPSEEK_BASE_URL` is taken directly from the process environment and supplied to the OpenAI-compatible client without validating its scheme or hostname ...[truncated 1732 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Default to the fixed endpoint `https://api.deepseek.com/v1`. 2. Parse the configured URL and reject non-HTTPS schemes, embedded credentials, fragments, and unexpected ports. 3. Maintain an explicit allowlist of trusted provider hostnames. If arbitrary compatible providers are required, use separate provider-specific credential variables rather than automatically forwarding `DEEPSEEK_API_KEY`. 4. Require an explicit opt-in for custom endpoints and display the selected destination before the first credential-bearing request. 5. Prevent redirects to unapproved hosts and revalidate the destination after redirects. 6. Ensure logs never include API keys or authorization headers. 7. Apply the same validation to the compatibility copy under `app/src/rss_brew/compat/`. ]]>
