T09 · Insecure Skill Coding Practices
- Location
scripts/douyin_daily_increment.py:173- Finding
Untrusted API Response Fields Are Rendered as Unsanitized Markdown
- Content
View full analysis
Vulnerability Details
File Location:
scripts/douyin_daily_increment.py, lines 173–191
Vulnerability Type: Improper output neutralization and insufficient URL validation
Risk Level: MediumVulnerable Code
python for idx, item in enumerate(items[:limit], start=1): raw_title = (item.get("aweme_desc") or "-").replace("|", "|").replace("[", "【").replace("]", "】").replace("\n", " ").replace("\r", " ") work_url = item.get("share_url", "") if work_url: title = f"[{raw_title}]({work_url})" else: title = raw_title author = item.get("user_nickname", "-") cat = item.get("category") or "-" collect = format_number(item.get("add_collect_count")) comment = format_number(item.get("add_comment_count")) share = format_number(item.get("add_share_count")) like = f"**{format_number(item.get('add_digg_count'))}**" pub_time = format_time(item.get("create_time_str")) if is_all: print(f"| {idx} | {title} | {author} | {cat} | {collect} | {comment} | {share} | {like} | {pub_time} |") else: print(f"| {idx} | {title} | {author} | {collect} | {comment} | {share} | {like} | {pub_time} |")Technical Analysis
The script treats fields returned by the remote API as trusted presentation data. In particular,
share_urlis inserted directly into a Markdown hyperlink without parsing the URL, enforcing HTTPS, or restricting the destination to expected Douyin domains.The
user_nicknameandcategoryfields are also inserted directly into Markdown table rows without escaping pipe characters, carriage returns, line feeds, or Markdown metacharacters. Althoughaweme_descreceives limited replacement, that protection is not consistently applied to all remote fields.If the API, its upstream data source, or the network trust boundary is compromised, an attacker can return:
- An HTTPS link to an attacker-controlled phishing d ...[truncated 1738 chars]
- Remediation
View remediation
Remediation Suggestions
- Parse
share_urlwithurllib.parse.urlparsebefore rendering it. - Require the
httpsscheme and reject URLs containing credentials, control characters, malformed hosts, or unexpected ports. - Restrict links to an explicit allowlist of verified Douyin domains and carefully validate subdomain boundaries. For example, a hostname should either equal an allowed domain or end with
"." + allowed_domain; a simple substring check is insufficient. - If URL validation fails, render the title as plain text and omit the hyperlink.
- Apply one centralized Markdown-table escaping function to every remote string field, including the title, author, category, timestamp fallback, and URL-derived display values.
- Replace or escape pipe characters, carriage returns, line feeds, backslashes, and relevant Markdown metacharacters before interpolation.
- Consider returning structured data separately from presentation formatting so the consuming Agent can safely render remote values.
- Add tests using malicious URLs, embedded newlines, pipe characters, link syntax, and Unicode control characters.
Example hardening pattern:
python from urllib.parse import urlparse ALLOWED_HOSTS = { "douyin.com", "iesdouyin.com", } def escape_markdown_cell(value): text = str(value or "-") return ( text.replace("\\", "\\\\") .replace("|", "\\|") .replace("\r", " ") .replace("\n", " ") .replace("[", "\\[") .replace("]", "\\]") ) def validate_work_url(value): try: parsed = urlparse(str(value)) host = (parsed.hostname or "").lower().rstrip(".") allowed = any( host == domain or host.endswith("." + domain) for domain in ALLOWED_HOSTS ) if parsed.scheme != "https" or not allowed: return None if parsed.username or parsed. ...[truncated 168 chars]- Parse
