T09 · Insecure Skill Coding Practices
Warning
- Location
- SKILL.md:599
- Finding
- Scraped Content Is Transmitted to a Third-Party LLM Without Sensitivity Controls<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 599-644 **Vulnerability Type**: Sensitive-data disclosure to a third-party service **Risk Level**: Medium ### Vulnerable Code ```python OPENROUTER_API_KEY = os.environ.get("OPENROUTER_API_KEY", "") OPENROUTER_ENDPOINT = "https://openrouter.ai/api/v1/chat/completions" def extract_entities_llm(text: str, metadata: dict) -> dict: """Extracts entities from a news article using LLM.""" text_sample = text[:4000] if len(text) > 4000 else text prompt = f"""You are a news entity extractor. Analyze the text below and extract: TITLE: {metadata.get('title', 'N/A')} DATE: {metadata.get('date', 'N/A')} TEXT: {text_sample} Respond ONLY with valid JSON, no markdown, in this format: {{ "people": [ {{"name": "Full Name", "role": "Role/Title", "context": "One sentence about their role in the article"}} ], "organizations": [ {{"name": "Org Name", "type": "company|government|ngo|other", "context": "role in article"}} ], "locations": [ {{"name": "Location Name", "type": "city|state|country|address", "context": "mention"}} ], "events": [ {{"name": "Event", "date": "date if available", "description": "brief description"}} ], "relationships": [ {{"subject": "Entity A", "relation": "relation type", "object": "Entity B"}} ] }}""" try: response = req.post( OPENROUTER_ENDPOINT, headers={ "Authorization": f"Bearer {OPENROUTER_API_KEY}", "Content-Type": "application/json", }, json={ "model": "google/gemini-2.5-flash-lite", "messages": [{"role": "user", "content": prompt}], "max_tokens": 2000, "temperature": 0.1, }, timeout=30, ) ``` ### Technical Analysis The optional entity-extraction stage includes the article title, publication date, and up to 4,000 characters of scraped tex ...[truncated 1776 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Require explicit user opt-in immediately before enabling Stage 5 and identify OpenRouter as the recipient. 2. Display which content fields will leave the local environment. 3. Disable external LLM processing by default for authenticated, internal, private, or access-restricted pages. 4. Add configurable redaction for email addresses, phone numbers, account identifiers, addresses, secrets, and other sensitive patterns. 5. Minimize transmitted data by sending only the passages required for the requested extraction. 6. Provide a local entity-extraction alternative for sensitive workloads. 7. Add an allowlist for approved LLM providers and models. 8. Document provider retention, training, residency, and privacy implications. 9. Fail closed when the API key is absent rather than sending an empty bearer token. 10. Record user consent and the destination provider in provenance metadata without logging the API key or sensitive prompt content. ]]>
