T09 · Insecure Skill Coding Practices
- Location
scripts/trends.py:75- Finding
Unescaped Remote Content in Default Markdown Output
- Content
View full analysis
Vulnerability Details
File Location:
scripts/trends.py, lines 75–79
Vulnerability Type: Markdown injection through untrusted remote content
Risk Level: MediumVulnerable Code
python def fmt_line_markdown(rank, kw, title, url): short = (title[:45] + "…") if len(title) > 48 else title if short: return f"{rank}. [{kw}]({url}) — {short}" return f"{rank}. [{kw}]({url})"The affected values originate from remote Daum pages through the following code:
python word = kw.get("keyword", kw.get("text", f"#{i+1}")) url = search_url(word) title = "" try: search_html = fetch(url) title = extract_title(search_html) except Exception: passTechnical Analysis
The default Markdown formatter inserts the remotely sourced
kwandtitlevalues directly into Markdown without escaping Markdown metacharacters or removing control characters.A crafted keyword containing characters such as
],(, or)can terminate or restructure the generated link. A crafted title can introduce links, mentions, formatting, or instruction-like text. The title extractor increases exposure because it heuristically accepts the first anchor text of a qualifying length from up to 200,000 characters of the remote search page rather than restricting extraction to a trusted news-result selector.Although the HTML formatter escapes visible keyword and title text, Markdown is the default output format. The generated output is explicitly intended for automated Telegram or Discord delivery, where Markdown syntax may be rendered and become deceptive or interactive.
Attack Path
- An attacker causes crafted text to appear as a trend keyword or qualifying anchor title on content returned by the fetched Daum pages.
extract_trends()accepts the keyword, orextract_title()selects the crafted anchor text as a representative title.main()passes the remotely controlled value tofmt_line_markdown().- The formatter inte ...[truncated 917 chars]
- Remediation
View remediation
Remediation Suggestions
- Escape all Markdown metacharacters in remotely sourced keywords and titles using the exact escaping rules required by the destination platform, such as Telegram MarkdownV2 or Discord Markdown.
- Remove carriage returns, line feeds, bidirectional-control characters, zero-width characters, and other control characters before formatting.
- Prefer plain-text output as the default when the downstream renderer is unknown.
- Replace the broad anchor-matching heuristic with a structured parser and selectors limited to the intended news-result elements.
- Enforce conservative character and length policies after HTML decoding.
- Keep destination URLs generated exclusively from the fixed HTTPS search base and URL-encoded keyword.
- Add tests covering payloads such as
]([malicious URL]), embedded mentions, nested formatting, newlines, and Unicode direction-control characters.
A platform-specific escaping helper should be applied before interpolation, for example:
python def escape_markdown(text: str) -> str: text = re.sub(r"[\r\n\x00-\x1f\x7f]", " ", text) return re.sub(r"([\\`*_{}\[\]()#+\-.!|>])", r"\\\1", text) def fmt_line_markdown(rank, kw, title, url): kw_safe = escape_markdown(str(kw)) short = (title[:45] + "…") if len(title) > 48 else title short_safe = escape_markdown(short) if short_safe: return f"{rank}. [{kw_safe}]({url}) — {short_safe}" return f"{rank}. [{kw_safe}]({url})"The final implementation must be adjusted to the Markdown dialect used by the delivery API.
