T09 · Insecure Skill Coding Practices
- Location
src/intelligence_desk_brief/retrieval.py:157- Finding
Apify API token is embedded in URLs and can leak into generated brief output
- Content
View full analysis
Vulnerability Details
File Location:
src/intelligence_desk_brief/retrieval.py:157-169, 181-205;src/intelligence_desk_brief/synthesis.py:338-348;src/intelligence_desk_brief/renderer.py:88-96
Vulnerability Type: Credential exposure through query strings and error output
Risk Level: HighVulnerable Code
python query = urlencode( { "token": self._config.apify_api_token or "", "timeout": str(self._config.apify_timeout_seconds), "format": "json", "clean": "1", "limit": str(self._config.apify_max_items), } ) base_url = self._config.apify_base_url.rstrip("/") url = f"{base_url}/actor-tasks/{task_id}/run-sync-get-dataset-items?{query}"On failure or an empty response, the credential-bearing URL is retained in a notice:
python notice = self._build_notice( lane=lane, url=url, fact=f"{lane} retrieval failed after {self._config.apify_retry_attempts} attempts: {last_error}", raw_reference={"error": str(last_error), "task_input": task_input}, )python notice = self._build_notice( lane=lane, url=url, fact=f"{lane} retrieval returned no usable results.", raw_reference={"task_input": task_input, "raw_payload": items[:3]}, retrieval_status="empty", )The URL is subsequently included in synthesized evidence:
python def _build_evidence_and_sources(evidence: list[dict[str, Any]]) -> list[dict[str, Any]]: ordered = sorted(evidence, key=lambda item: item["ranking"]["score"], reverse=True) return [ { "evidence_id": item["id"], "source_title": item["source"]["title"], "url_or_publisher": item["source"]["url"], "timestamp_or_recency_note": item["source"]["timestamp"] or item["source"]["recency_note"], "why_it_mattered": f"[{item['source_lane']}] {item['why_it_matters']}", "fact_excerpt": item["fact"], "interpretation_excerpt": item["interpretation"], ...[truncated 2653 chars]- Remediation
View remediation
Remediation Suggestions
-
Send the Apify token only through an authorization header:
python request = Request( url_without_token, data=body, headers={ "Authorization": f"Bearer {token}", "Content-Type": "application/json", }, method="POST", ) -
Never store a credential-bearing request URL in evidence, notices, exceptions, logs, or handoff payloads.
-
Build a sanitized display URL containing only the provider origin and non-sensitive task reference.
-
Introduce a centralized redaction function that removes known secrets and sensitive query parameters from error messages before logging or returning them.
-
Avoid placing raw upstream error bodies or request details into user-facing artifacts unless they have been sanitized.
-
Add regression tests that use a distinctive fake token and assert it does not appear in:
- Raw retrieval results
- Failure and empty-result notices
- Normalized evidence
- JSON responses
- Rendered Markdown
- Structured logs
- Delivery handoff payloads
-
Rotate any real token that may already have appeared in generated output or retained infrastructure logs.
-
