T09 · Insecure Skill Coding Practices
Warning
- Location
- skill.py:46
- Finding
- Spoofable Trusted-Domain Classification<![CDATA[ ## Vulnerability Details **File Location**: `skill.py`, lines 46–52 **Vulnerability Type**: Improper domain validation through substring matching **Risk Level**: Medium ### Vulnerable Code ```python def calc_domain_score(url, engine='bing'): if not url: return 0.5 domain = urlparse(url).netloc.lower() for good, score in HIGH_QUALITY.items(): if good in domain: return score ``` ### Technical Analysis The function classifies a result as high quality when a trusted-domain token occurs anywhere within the parsed network location: ```python if good in domain: ``` This is not an origin or registrable-domain check. An attacker can register or control a hostname containing a trusted token, such as: - `github.com.attacker.example` - `official.attacker.example` - `wiki-malware.example` These hostnames can match entries in `HIGH_QUALITY` even though they are unrelated to the intended trusted services. Generic entries such as `docs`, `official`, and `wiki` make false classification particularly easy. The resulting score is used to sort search results and generate labels such as `【官方】` (“official”) or high-quality indicators. Therefore, attacker-controlled content can be displayed more prominently and with an unjustified trust signal. ### Attack Path 1. An attacker creates a web page on a controlled hostname containing one of the trusted substrings. 2. The attacker causes the page to be indexed by Bing or DuckDuckGo for a query relevant to the victim. 3. A user invokes the skill with that query. 4. The search engine includes the attacker-controlled page in its response. 5. `calc_domain_score()` extracts the hostname and performs substring matching. 6. The crafted hostname matches a trusted token and receives an elevated score. 7. The skill sorts the result above lower-scored results and presents it with an official or high-quality label. 8. A user who relies on that label may visit the attacker-controlled page and ...[truncated 648 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Replace substring matching with exact hostname or DNS subdomain-boundary matching: ```python def hostname_matches(domain, trusted): domain = domain.rstrip(".").lower() trusted = trusted.rstrip(".").lower() return domain == trusted or domain.endswith("." + trusted) ``` 2. Apply the helper only to specific, fully qualified trusted domains: ```python for trusted, score in HIGH_QUALITY.items(): if hostname_matches(domain, trusted): return score ``` 3. Remove ambiguous entries such as `docs`, `official`, and `wiki`. Replace them with explicitly reviewed domains. 4. Normalize hostnames before comparison, including: - Removing a trailing dot. - Converting internationalized domain names to a consistent IDNA representation. - Handling malformed URLs conservatively. - Using `urlparse(url).hostname` instead of `netloc` to avoid ports and user-information components affecting validation. 5. Use a maintained public-suffix-aware library if scoring must operate on registrable domains. 6. Add regression tests proving that deceptive hostnames do not inherit trusted scores, including: ```python assert calc_domain_score("https://github.com.attacker.example") != 3.0 assert calc_domain_score("https://official-malware.example") != 2.5 assert calc_domain_score("https://github.com/example/repository") == 3.0 assert calc_domain_score("https://docs.openclaw.ai/guide") == 3.0 ``` 7. Consider presenting quality labels as heuristic rather than authoritative, especially where a result has not been cryptographically or manually verified. ]]>
