T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/fetch_edinet.py:45
- Finding
- EDINET API key may be disclosed through request URLs and exception logs## Vulnerability Details **File Location**: `scripts/fetch_edinet.py:45-46`, `scripts/fetch_edinet.py:70-71`, `scripts/fetch_edinet.py:138-140`, and `scripts/fetch_edinet.py:157-159` **Vulnerability Type**: Credential exposure through URL query parameters and unsanitized error logging **Risk Level**: Medium ### Vulnerable Code ```python def search_documents(api_key: str, target_date: str) -> list[dict]: """指定日の書類一覧を取得.""" url = f"{EDINET_BASE}/documents.json" params = {"date": target_date, "type": 2, "Subscription-Key": api_key} resp = httpx.get(url, params=params, timeout=30) resp.raise_for_status() data = resp.json() return data.get("results", []) ``` ```python def download_pdf(api_key: str, doc_id: str, output_dir: Path) -> Path | None: """EDINET から書類 PDF をダウンロード (ZIP展開).""" url = f"{EDINET_BASE}/documents/{doc_id}" params = {"type": 2, "Subscription-Key": api_key} # type=2: PDF resp = httpx.get(url, params=params, timeout=60) resp.raise_for_status() ``` ```python try: results = search_documents(api_key, check_date) except httpx.HTTPError as e: print(f" API エラー: {e}", file=sys.stderr) continue ``` ```python try: path = download_pdf(api_key, doc_id, output_dir) if path: downloaded.append(str(path)) print(f" ✓ 保存: {path}", file=sys.stderr) except httpx.HTTPError as e: print(f" ダウンロードエラー: {e}", file=sys.stderr) ``` ### Technical Analysis The `EDINET_API_KEY` secret is inserted into the URL query string as `Subscription-Key`. HTTP client exceptions may include the complete request URL in their string representation. The code writes raw `httpx.HTTPError` objects to standard error without redaction, creating a potential path for the API key to enter terminal histories, Agent logs, CI logs, centralized logging systems, or diagnostic records. Query-string credentials can also be retained ...[truncated 1767 chars]
- Remediation
- ## Remediation Suggestions 1. Do not print raw exception objects for authenticated HTTP requests. Log a sanitized error type and status code instead: ```python except httpx.HTTPStatusError as e: print( f"EDINET API error: HTTP {e.response.status_code}", file=sys.stderr, ) except httpx.RequestError: print("EDINET API request failed", file=sys.stderr) ``` 2. If the EDINET API supports authentication through an HTTP header, place the subscription key in that header rather than the query string: ```python headers = {"Subscription-Key": api_key} params = {"date": target_date, "type": 2} resp = httpx.get(url, params=params, headers=headers, timeout=30) ``` 3. If EDINET requires the key as a query parameter, introduce explicit URL sanitization before any request-related information is logged. Replace the value of `Subscription-Key` with `[REDACTED]`. 4. Configure CI, Agent, and centralized logging systems to redact known credential parameter names, including `Subscription-Key`. 5. Rotate the current API key if existing logs may contain failed EDINET request URLs. Restrict access to historical logs and remove exposed copies where feasible. 6. Add automated tests that induce request and HTTP status failures, capture standard error, and assert that the configured API key never appears in output.
