T09 · Insecure Skill Coding Practices
- Location
scripts/archive_wechat_article.py:322- Finding
Unrestricted article asset retrieval permits SSRF and non-HTTP resource access
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill mostly does what it claims, but its image downloader can fetch arbitrary embedded resource URLs into the archive without host, scheme, size, or network-safety limits.
Review before installing or running on untrusted saved HTML. Use it only with WeChat URLs and output folders you choose, consider --skip-images for untrusted content, and prefer running it in a sandboxed environment without access to internal networks or sensitive local files until asset URL and size limits are added.
scripts/archive_wechat_article.py:322Unrestricted article asset retrieval permits SSRF and non-HTTP resource access
scripts/archive_wechat_article.py:322Unbounded image response buffering permits memory and disk exhaustion
requirements.txt:1Python dependency uses an open-ended version range
The declared purpose is a local archival/export tool that saves WeChat articles and image-note pages as Markdown plus local assets in a user-specified folder. This code chunk instead implements an extractor/parser: it accepts a URL or HTML, fetches supported pages over the network, parses the DOM and embedded JS, identifies content type, extracts metadata/content/publish time/images, and optionally extracts links/tags/repost metadata. While this could be a supporting component of a larger archival tool, on its own it does not carry out the core declared behaviors of writing files locally, choosing a destination folder, converting to Markdown, or downloading assets. It also includes undeclared capabilities such as accessing weixin.sogou.com, following redirects/transfer links, and optional extraction of mp links/tags/repost metadata. Therefore the supplied code does not accurately match the declared description.
Referenced artifact was not completely inspected
- Parses the article with the bundled `extract.js`.
The lockfile includes undici 7.28.0, and the advisory set describes request/response parsing and desynchronization flaws that can lead to cross-user information disclosure or crashes. In this skill's context, the package is especially relevant because the tool fetches remote WeChat content and processes untrusted network responses, making HTTP client parsing flaws materially more dangerous than a dormant dependency issue.
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.
process.exit(1);
}});
"""
env = os.environ.copy()
if html_file:
env["WECHAT_HTML_FILE"] = str(html_file)
res = subprocess.run(
The skill instructs the agent to run a local Python script, access environment variables, write files, and fetch remote content, but it declares no explicit tool scope or permissions. This creates an authorization gap where an orchestrator or reviewer cannot easily constrain or audit the skill's shell, network, and filesystem behavior, increasing the chance of overbroad execution.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
env = os.environ.copy()
if html_file:
env["WECHAT_HTML_FILE"] = str(html_file)
res = subprocess.run(
[node, "-e", js],
capture_output=True,
text=True,
The executable path comes from os.environ.get("NODE"), then is passed directly to subprocess.run. In an agent/tooling environment, a caller or surrounding runtime that can influence environment variables could replace NODE with an attacker-controlled binary, causing arbitrary code execution under the agent's privileges.
env = os.environ.copy()
if html_file:
env["WECHAT_HTML_FILE"] = str(html_file)
res = subprocess.run(
[node, "-e", js],
capture_output=True,
text=True,
This code file contains only Chinese-language user-facing error strings, which forces a specific language on users. Under the policy, locale or language constraints should either be user-selectable or clearly documented as intentionally region-specific.
This code performs an HTTP request to a user-provided WeChat URL and sends request metadata including a user-agent and Accept-Language header. There is no confirmation prompt, logging, or comment/docstring here warning that external network access will occur, which fits the missing user warning criterion for code files.
The lockfile pins qs to 6.15.3, and the supplied advisory metadata indicates known denial-of-service and parser-bypass issues in that version. Even though package-lock.json itself is not executable, including a vulnerable dependency is a real supply-chain weakness if the application uses qs on attacker-controlled query strings or form data.
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.
{
"dependencies": {
"cheerio": "^1.2.0",
"dayjs": "^1.11.19",
"lodash.unescape": "^4.0.1",
"qs": "^6.15.0"
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.
{
"dependencies": {
"cheerio": "^1.2.0",
"dayjs": "^1.11.19",
"lodash.unescape": "^4.0.1",
"qs": "^6.15.0"
}
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.
"dependencies": {
"cheerio": "^1.2.0",
"dayjs": "^1.11.19",
"lodash.unescape": "^4.0.1",
"qs": "^6.15.0"
}
}
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.
"cheerio": "^1.2.0",
"dayjs": "^1.11.19",
"lodash.unescape": "^4.0.1",
"qs": "^6.15.0"
}
}
The manifest resolves to a known vulnerable qs version with published advisories, including denial-of-service and parser limit bypass issues. In the context of a skill that archives WeChat articles and may process attacker-influenced URLs, query strings, or imported HTML-derived parameters, malformed input could trigger excessive resource consumption or bypass expected parsing constraints if qs is used on untrusted data.
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.
beautifulsoup4>=4.12
The request hard-codes an Accept-Language header of 'zh-CN,zh;q=0.9,en;q=0.8', which imposes a locale/language preference rather than letting the user choose. This is a natural-language policy concern because it forces a specific language/locale in behavior without documented opt-in or justification.
No suspicious patterns detected.