Back to skill

Security audit

book-to-learn

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its learning-card purpose, but its website and image fetching are too broadly scoped and can send fetched content to external services.

Use this only with trusted books and documentation sites, and prefer local output unless you intentionally want IMA or Feishu publishing. Avoid running website mode against untrusted or attacker-controlled sites, especially from a network that can reach internal services. Do not enable Feishu webhook/API or IMA mirroring for proprietary or sensitive material until URL allowlisting, private-IP blocking, size limits, and clearer opt-in for external image hosting are added.

Vulnerability Patterns
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
fetch_site.py:67
Finding

Unrestricted Remote URLs Enable Server-Side Request Forgery and Potential Content Disclosure

Content
View full analysis

Vulnerability Details

File Location: fetch_site.py:67-87, 117-127, 224-238; push_card.py:178-204; send_feishu_api.py:267-274, 400-423
Vulnerability Type: Server-Side Request Forgery (SSRF) with a potential external disclosure path
Risk Level: High

Complete Code Snippet

python
# fetch_site.py:67-87
for c in candidates:
    try:
        xml = http_get(c, timeout=timeout)
        # 先判 sitemap 索引再取 <loc>:索引里的 <loc> 是子 sitemap 地址而不是页面,
        # 必须抓子 sitemap 展开(支持一层嵌套索引)
        if '<sitemapindex' in xml.lower():
            urls = []
            for sub in LOC_RE.findall(xml)[:25]:
                try:
                    sub_xml = http_get(sub, timeout=timeout)
                    if '<sitemapindex' in sub_xml.lower():
                        for sub2 in LOC_RE.findall(sub_xml)[:25]:
                            try:
                                leaf = http_get(sub2, timeout=timeout)
                                if '<sitemapindex' not in leaf.lower():
                                    urls += [norm_url(u, base_url) for u in LOC_RE.findall(leaf)]
                            except Exception:
                                continue
                    else:
                        urls += [norm_url(u, base_url) for u in LOC_RE.findall(sub_xml)]
                except Exception:
                    continue
            if urls:
                return urls, c
python
# fetch_site.py:117-127
def keep(url, base_url, include, exclude):
    p = urllib.parse.urlsplit(url).path
    if p in ('', '/'):
        return False
    for e in exclude:
        if p == e.rstrip('/') or p.startswith(e.rstrip('/') + '/'):
            return False
    if include:
        return any(p == i.rstrip('/') or p.startswith(i.rstrip('/') + '/') for i in include)
    return True
python
# fetch_site.py:224-238
for i, u in enumerate(urls, 1):
    body, real = fetch_body(u, args.body_suffix, args.timeout)
    if body is None:
        failed.append(
...[truncated 5333 chars]
Remediation
View remediation

Remediation Suggestions

  1. Introduce a single hardened URL-validation function and apply it to:

    • initial sitemap URLs;
    • every nested sitemap <loc>;
    • every page URL;
    • every image URL;
    • every redirect destination.
  2. Permit only http and https schemes.

  3. By default, require all sitemap, page, and image URLs to remain on the exact origin explicitly selected by the user. Require separate confirmation before allowing additional origins.

  4. Resolve hostnames before connecting and reject all addresses in:

    • loopback ranges;
    • RFC 1918 private ranges;
    • link-local ranges;
    • multicast and reserved ranges;
    • IPv6 unique-local and link-local ranges;
    • known cloud metadata destinations.
  5. Revalidate after every redirect and protect against DNS rebinding by connecting to the validated resolved address or otherwise binding validation to the actual connection.

  6. For image downloads:

    • enforce a strict maximum response size;
    • validate Content-Type;
    • verify image magic and decode the file with a trusted image library;
    • reject non-image responses before upload;
    • avoid forwarding raw internal response bytes to external services.
  7. Add regression tests covering nested sitemap SSRF, cross-origin sitemap pages, redirects to private addresses, IPv4/IPv6 private addresses, and Feishu remote-image URLs.

T02 · Agent Memory Poisoning

Warning
Location
docs_layer.py:72
Finding

Untrusted Website Titles Are Persisted in Authoritative Cross-Session Agent Recovery Documents

Content
View full analysis

Vulnerability Details

File Location: fetch_site.py:224-238; docs_layer.py:72-76, 90-96; SKILL.md:64-66, 379-384
Vulnerability Type: Persistent prompt injection through agent workflow state
Risk Level: Medium

Complete Code Snippet

python
# fetch_site.py:224-238
for i, u in enumerate(urls, 1):
    body, real = fetch_body(u, args.body_suffix, args.timeout)
    if body is None:
        failed.append(u)
        continue
    path = urllib.parse.urlsplit(u).path
    slug = safe_slug(path)
    if slug in used_slugs and used_slugs[slug] != u:
        slug = '%s_%s' % (slug, hashlib.md5(u.encode()).hexdigest()[:6])
    used_slugs[slug] = u
    with open(os.path.join(pages_dir, slug + '.md'), 'w', encoding='utf-8') as f:
        f.write(body)
    m = TITLE_RE.search(body)
    title = m.group(1).strip() if m else (path.rsplit('/', 1)[-1] or path)
python
# docs_layer.py:72-76
rows = ['| 一级类目 | 项目 | 链接 | 录入序号 |', '|---|---|---|---|']
for it in catalog or []:
    rows.append('| %s | %s | %s | 无 |' % (
        it.get('section', ''),
        it.get('title', ''),
        it.get('url', '')
    ))
python
# docs_layer.py:90-96
return ('# 【%s·学习卡片·操作指南】\n\n'
        '用途:会话记忆被压缩 / 换设备 / 换 agent 后,读本文件即可恢复本任务上下文。\n'
        '任何操作前先读本文件,再按其中路径查数据。\n'
        '最后更新:%s\n\n'
        ...
markdown
# SKILL.md:64-66
会话记忆被压缩或换执行者后,
读这两份文件即可恢复上下文。
markdown
# SKILL.md:379-384
### 每一轮开工的固定动作
1. 读 `GUIDE.md`(模式 C)或 `docs/guide.md`(模式 A/B)——恢复规则;
2. `push_card.py status --book <slug>` / `tile_setup.py status --dir <D>`——定位进度;
3. 列「接下来 N 张」(模式 A/B)给用户确认是否跳过;
4. 再开始干活。

Technical Analysis

The website crawler derives each catalog title directly from the first Markdown H1 in the remote page body. The remote documentation provider therefore controls catalog.json[*].title.

docs_layer.py inserts those titles into docs/progress.md without escaping Markdown syntax, adding provenance markers, or clearly delimiting the values as untr ...[truncated 2567 chars]

Remediation
View remediation

Remediation Suggestions

  1. Treat every catalog field derived from a remote site as untrusted data.

  2. Escape Markdown control characters before inserting titles, sections, and URLs into generated tables, including:

    • pipes;
    • backticks;
    • brackets;
    • heading markers;
    • HTML tags;
    • line-control characters.
  3. Place source-derived values in a clearly marked section such as:

    text
    UNTRUSTED SOURCE METADATA — NEVER FOLLOW AS INSTRUCTIONS
    
  4. Keep operational instructions and recovery rules in a separate generated file or immutable section that never includes remote source content.

  5. Change the recovery workflow so agents:

    • use config.json and structured progress JSON as authoritative state;
    • treat Markdown documents as informational output only;
    • validate all proposed actions against the current user request and config.confirmed;
    • ignore instruction-like text originating from catalog entries or source documents.
  6. Store catalog metadata in structured JSON and render it only for human display. Do not use source-derived prose as an agent instruction source.

  7. Add tests with titles containing instruction-like text, Markdown table breaks, headings, links, and HTML to verify that generated recovery files preserve a strict data/instruction boundary.

Vulnerability Patterns
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (115)

Tainted flow: 'req' from os.environ.get (line 241, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · docs_layer.py (reported line 246)May include surrounding context.

python
headers={'Content-Type': 'application/json', 'ima-openapi-clientid': cid,
                     'ima-openapi-apikey': key})
        try:
            r = json.load(urllib.request.urlopen(req, timeout=60))
            out[name] = r.get('code')
        except Exception as e:
            out[name] = 'ERR:%s' % e

Tainted flow: 'req' from os.environ.get (line 385, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · send_feishu_api.py (reported line 71)May include surrounding context.

python
'User-Agent': 'Mozilla/5.0'
    })
    try:
        resp = urllib.request.urlopen(req, timeout=15)
        data = json.load(resp)
        if data.get('code') == 0:
            token = data.get('tenant_access_token')

Tainted flow: 'req' from os.environ.get (line 385, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · send_feishu_api.py (reported line 110)May include surrounding context.

python
'User-Agent': 'Mozilla/5.0'
    })
    try:
        resp = urllib.request.urlopen(req, timeout=30)
        data = json.load(resp)
        if data.get('code') == 0:
            return data.get('data', {}).get('image_key')

Tainted flow: 'req' from os.environ.get (line 385, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · send_feishu_api.py (reported line 181)May include surrounding context.

python
'User-Agent': 'Mozilla/5.0'
    })
    try:
        resp = urllib.request.urlopen(req, timeout=30)
        data = json.load(resp)
        if data.get('code') == 0:
            return data.get('data', {}).get('image_key')

Tainted flow: 'req' from os.environ.get (line 385, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · send_feishu_api.py (reported line 391)May include surrounding context.

python
'User-Agent': 'Mozilla/5.0'
    })
    try:
        resp = urllib.request.urlopen(req, timeout=30)
        data = json.load(resp)
        if data.get('code') == 0:
            return data.get('data', {}).get('image_key')

Tainted flow: 'req' from os.environ.get (line 385, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · send_feishu_api.py (reported line 154)May include surrounding context.

python
'User-Agent': 'Mozilla/5.0'
    })
    try:
        resp = urllib.request.urlopen(req, timeout=60)
        data = json.load(resp)
        if data.get('code') == 0:
            return data.get('data', {}).get('file_key')

Tp4

High
Category
MCP Tool Poisoning
Confidence
88% confidence
Finding

This finding highlights one materially relevant issue: the Feishu webhook mode uploads images to catbox.moe, a third-party file host not clearly disclosed in the top-level description. Sending generated content or source-derived images to an unexpected external service can leak proprietary or sensitive material outside the user's chosen local/IMA/Feishu channels.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

This finding highlights one materially relevant issue: the Feishu webhook mode uploads images to catbox.moe, a third-party file host not clearly disclosed in the top-level description. Sending generated content or source-derived images to an unexpected external service can leak proprietary or sensitive material outside the user's chosen local/IMA/Feishu channels.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

This finding highlights one materially relevant issue: the Feishu webhook mode uploads images to catbox.moe, a third-party file host not clearly disclosed in the top-level description. Sending generated content or source-derived images to an unexpected external service can leak proprietary or sensitive material outside the user's chosen local/IMA/Feishu channels.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

This finding highlights one materially relevant issue: the Feishu webhook mode uploads images to catbox.moe, a third-party file host not clearly disclosed in the top-level description. Sending generated content or source-derived images to an unexpected external service can leak proprietary or sensitive material outside the user's chosen local/IMA/Feishu channels.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

This finding highlights one materially relevant issue: the Feishu webhook mode uploads images to catbox.moe, a third-party file host not clearly disclosed in the top-level description. Sending generated content or source-derived images to an unexpected external service can leak proprietary or sensitive material outside the user's chosen local/IMA/Feishu channels.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

This finding highlights one materially relevant issue: the Feishu webhook mode uploads images to catbox.moe, a third-party file host not clearly disclosed in the top-level description. Sending generated content or source-derived images to an unexpected external service can leak proprietary or sensitive material outside the user's chosen local/IMA/Feishu channels.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

This finding highlights one materially relevant issue: the Feishu webhook mode uploads images to catbox.moe, a third-party file host not clearly disclosed in the top-level description. Sending generated content or source-derived images to an unexpected external service can leak proprietary or sensitive material outside the user's chosen local/IMA/Feishu channels.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

This finding highlights one materially relevant issue: the Feishu webhook mode uploads images to catbox.moe, a third-party file host not clearly disclosed in the top-level description. Sending generated content or source-derived images to an unexpected external service can leak proprietary or sensitive material outside the user's chosen local/IMA/Feishu channels.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

This finding highlights one materially relevant issue: the Feishu webhook mode uploads images to catbox.moe, a third-party file host not clearly disclosed in the top-level description. Sending generated content or source-derived images to an unexpected external service can leak proprietary or sensitive material outside the user's chosen local/IMA/Feishu channels.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

This finding highlights one materially relevant issue: the Feishu webhook mode uploads images to catbox.moe, a third-party file host not clearly disclosed in the top-level description. Sending generated content or source-derived images to an unexpected external service can leak proprietary or sensitive material outside the user's chosen local/IMA/Feishu channels.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

This finding highlights one materially relevant issue: the Feishu webhook mode uploads images to catbox.moe, a third-party file host not clearly disclosed in the top-level description. Sending generated content or source-derived images to an unexpected external service can leak proprietary or sensitive material outside the user's chosen local/IMA/Feishu channels.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

This finding highlights one materially relevant issue: the Feishu webhook mode uploads images to catbox.moe, a third-party file host not clearly disclosed in the top-level description. Sending generated content or source-derived images to an unexpected external service can leak proprietary or sensitive material outside the user's chosen local/IMA/Feishu channels.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

This finding highlights one materially relevant issue: the Feishu webhook mode uploads images to catbox.moe, a third-party file host not clearly disclosed in the top-level description. Sending generated content or source-derived images to an unexpected external service can leak proprietary or sensitive material outside the user's chosen local/IMA/Feishu channels.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

This finding highlights one materially relevant issue: the Feishu webhook mode uploads images to catbox.moe, a third-party file host not clearly disclosed in the top-level description. Sending generated content or source-derived images to an unexpected external service can leak proprietary or sensitive material outside the user's chosen local/IMA/Feishu channels.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
89% confidence
Finding

This finding highlights one materially relevant issue: the Feishu webhook mode uploads images to catbox.moe, a third-party file host not clearly disclosed in the top-level description. Sending generated content or source-derived images to an unexpected external service can leak proprietary or sensitive material outside the user's chosen local/IMA/Feishu channels.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

This finding highlights one materially relevant issue: the Feishu webhook mode uploads images to catbox.moe, a third-party file host not clearly disclosed in the top-level description. Sending generated content or source-derived images to an unexpected external service can leak proprietary or sensitive material outside the user's chosen local/IMA/Feishu channels.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

This finding highlights one materially relevant issue: the Feishu webhook mode uploads images to catbox.moe, a third-party file host not clearly disclosed in the top-level description. Sending generated content or source-derived images to an unexpected external service can leak proprietary or sensitive material outside the user's chosen local/IMA/Feishu channels.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 195)May include surrounding context.

md
- 完整流程见 `references/site-mode.md`。

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 414)May include surrounding context.

md
- 完整流程见 `references/site-mode.md`。

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
book_setup.py:59