Back to skill

Security audit

GitHub Skill 生态拆解

Security checks for vulnerabilities and agentic risk

Overview

This skill is a disclosed GitHub research helper that uses GitHub CLI to collect repository metadata/content and writes local analysis files; I found no artifact-backed deception, persistence, exfiltration, or destructive behavior.

Before installing, be aware that this skill is oriented toward Chinese/China-market GitHub skill research, uses your logged-in GitHub CLI for read-only API calls, and writes local output files. Set SKILL_OUT deliberately if you do not want files created under the current working directory.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • 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
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (31)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

声明描述的是一个较完整的竞品调研/拆解工具:先扫描 GitHub 热门 skill,再对样本逐个分析并输出拆解报告与国内改造建议。而实际代码只完成了前半段中更窄的一部分:基于预设中文/国内生态相关关键词搜索 GitHub 仓库,收集仓库名、星标、描述、更新时间、topic,并写出 cn_skill_pool.json。它没有下载或解析仓库内容、没有分析 SKILL.md、没有做设计手法拆解、没有生成任何“国内改造意见”分档报告。因此其实际行为与声明的核心能力存在明显缺口,且搜索目标也比声明更狭窄。

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

该代码与声明中的前半部分“扫描 GitHub、按星标建立候选池”基本一致:它确实搜索 GitHub 仓库、汇总 stars/forks/issues/语言/描述等元数据,并输出 gh_skill_pool.json。但声明的核心还包括后续的逐个拆解、设计方法分析、以及面向国内场景的三档改造建议与拆解报告,这些能力在代码中完全没有体现。代码没有读取各仓库内容、没有解析 SKILL.md、没有做任何定性分析或生成改造建议。因此,实际行为只覆盖声明的一部分,且缺失了对用户而言很关键的分析能力,属于实质性描述不符。

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

该代码与声明存在明显功能落差。声明描述的是一个面向竞品调研的技能:应扫描 GitHub 上热门 skill、按星标形成候选池、深入拆解设计方法,并输出带有中国本土化改造建议的分析报告。实际代码只是在固定仓库列表上做元数据和文件树探查,统计 SKILL.md、Markdown、脚本数量等基础指标,并输出简单 JSON 与控制台列表。它没有执行“热门发现/排序候选池”的核心逻辑,也没有读取和分析 SKILL.md 内容、总结设计手法、形成三档改造意见。因此其主要目的更像是前期仓库盘点脚本,而不是完整的 skill teardown / 竞品分析能力。

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

声明描述的是一个完整的竞品调研/拆解技能:从 GitHub 收集热门 skill、建立候选池、逐条分析并给出适配国内场景的改造建议。而提供的代码片段只是一个统计脚本,作用是对已有候选池做结构化汇总,为全景分析提供数据支撑。它没有访问 GitHub、没有做样本拆解、没有产出改造建议,也没有生成声明中承诺的主要输出形式。因此这不是单纯的实现细节差异,而是主要功能与实际行为明显不一致。

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 103)May include surrounding context.

md
skills = [f for f in files if f.endswith("SKILL.md")]

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding

The skill instructs the agent to use shell, file output, environment-specific binaries, and GitHub CLI, but it declares no explicit tool scope or permission boundaries. That increases the chance of unintended command execution, filesystem writes, or environment-dependent behavior when the skill is auto-invoked, especially because the workflow includes background jobs and local file generation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The description is framed around '针对国内实际' and outputting domestic adaptation advice, and the rest of the file is written to operate in Chinese by default. There is no indication that the user can choose another language or locale, which creates a language/locale policy concern.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The invocation phrases are very broad and overlap with ordinary discussion about skills, teardown, positioning, or competitor research. That can cause over-triggering of a skill that performs external API access, shell commands, and file generation, increasing the likelihood of unrequested tool use and expanding the attack surface through accidental activation.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding

npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

Line L013 states that Chinese-language adaptation must be redone rather than translated, framing a specific language/locale as the required default. This is a natural-language locale policy constraint and the document does not offer a user choice or opt-in for other languages/locales.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

Lines L093-L097 say visual references 'must' be replaced with domestic norms and list China-specific aesthetics, fonts, and image-generation channels as required defaults. This forces a locale-specific presentation policy without stating that it is optional or only for China-targeted deployments.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

This code persistently writes README and SKILL content from remote repositories into the local filesystem, but the only disclosure is a Chinese module docstring and progress prints rather than a clear user-facing warning about local file creation. Under the rule, file writes in code should have visible disclosure such as an explicit prompt, warning, or clearly documented notice.

Content

No source excerpt is available for this finding.

Tainted flow: 'fn' from os.environ.get (line 80, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
83% confidence
Finding

Like the other write site, this file creation ultimately trusts the environment-derived output root. In hostile or shared automation environments, a manipulated SKILL_OUT can redirect repository content to unintended locations, creating overwrite, clutter, or policy-violation risks.

Content

Scanner excerpt · scripts/fetch_bodies.py (reported line 81)May include surrounding context.

python
t = gh_raw(full, br, readme)
        if t:
            fn = os.path.join(DST, full.replace("/", "__") + "__README.md")
            open(fn, "w", encoding="utf-8").write(t[:MAXLEN])
            entry["taken"].append(("README", readme, len(t), t[:MAXLEN]))

    # SKILL.md:优先取根目录 / skills/ 直接子级,最多 4 个,避开海量目录

Tainted flow: 'fn' from os.environ.get (line 80, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
83% confidence
Finding

This write sink uses a filename rooted in DST, which itself derives from the environment-controlled OUT path. Although the per-file name components are sanitized from the hardcoded repo/path values, the base directory remains attacker-influenced, enabling arbitrary write placement within whatever path SKILL_OUT specifies.

Content

Scanner excerpt · scripts/fetch_bodies.py (reported line 93)May include surrounding context.

python
if not t:
            continue
        fn = os.path.join(DST, full.replace("/", "__") + "__" + s.replace("/", "_"))
        open(fn, "w", encoding="utf-8").write(t[:MAXLEN])
        entry["taken"].append((s, s, len(t), t[:MAXLEN]))

    # 目录骨架(前 120 条)

Tainted flow: 'OUT' from os.environ.get (line 5, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
84% confidence
Finding

The output directory is taken from the SKILL_OUT environment variable and then used directly for file writes. If an attacker can influence the environment in the execution context, they can redirect output to unexpected filesystem locations, potentially overwriting user-accessible files or causing data placement in sensitive directories.

Content

Scanner excerpt · scripts/fetch_bodies.py (reported line 104)May include surrounding context.

python
% (full, entry["stars"], len(files), len(entry["taken"]))
    )

with open(os.path.join(OUT, "gh_skills_raw.json"), "w", encoding="utf-8") as f:
    json.dump(summary, f, ensure_ascii=False, indent=1)
print()
print("saved:", len(summary), "repos ->", DST)

Tainted flow: 'OUT' from os.environ.get (line 5, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
87% confidence
Finding

The output directory is taken directly from the SKILL_OUT environment variable and then used for file creation without validation or restriction. If an attacker can influence the environment in which this script runs, they can redirect writes to unintended filesystem locations, causing file overwrite, data placement in sensitive directories, or abuse of privileged execution contexts.

Content

Scanner excerpt · scripts/scan_cn.py (reported line 54)May include surrounding context.

python
time.sleep(2)

items = sorted(pool.values(), key=lambda r: -r["stars"])
with open(os.path.join(OUT, "cn_skill_pool.json"), "w", encoding="utf-8") as f:
    json.dump(items, f, ensure_ascii=False, indent=1)

print("%-46s %7s %10s %s" % ("repo", "star", "pushed", "desc"))

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/fetch_bodies.py (reported line 32)May include surrounding context.

python
def gh_api(path):
    p = subprocess.run([GH, "api", path], capture_output=True)
    if p.returncode != 0:
        return None
    try:

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/scan_cn.py (reported line 9)May include surrounding context.

python
def gh_api(path):
    p = subprocess.run([GH, "api", path], capture_output=True)
    if p.returncode != 0:
        return None
    try:

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/scan_repos.py (reported line 9)May include surrounding context.

python
def gh_api(path):
    p = subprocess.run([GH, "api", path], capture_output=True)
    if p.returncode != 0:
        return None
    try:

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/scan_tree.py (reported line 36)May include surrounding context.

python
def gh_api(path):
    p = subprocess.run([GH, "api", path], capture_output=True)
    if p.returncode != 0:
        return None
    try:

Tainted flow: 'OUT' from os.environ.get (line 5, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/scan_repos.py (reported line 65)May include surrounding context.

python
time.sleep(2)

items = sorted(pool.values(), key=lambda r: -r["stars"])
with open(os.path.join(OUT, "gh_skill_pool.json"), "w", encoding="utf-8") as f:
    json.dump(items, f, ensure_ascii=False, indent=1)

print()

Tainted flow: 'OUT' from os.environ.get (line 5, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/scan_tree.py (reported line 89)May include surrounding context.

python
% (full, rec["stars"], rec["file_count"], rec["skill_md_count"], rec["md_count"], rec["script_count"], rec["size_kb"])
    )

with open(os.path.join(OUT, "gh_skill_detail.json"), "w", encoding="utf-8") as f:
    json.dump({"rows": rows, "detail": {k: v["skills"] for k, v in detail.items()}}, f, ensure_ascii=False, indent=1)

print()

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The module docstring and all user-visible output strings are in Chinese, which imposes a specific language on users without offering any locale choice. This matches the policy category for language or locale constraints lacking user opt-in.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

The module docstring is written only in Chinese and sets the script description in a single language, with no indication of user language choice or opt-in. This can violate a language/locale policy when a skill implicitly assumes one language for its user-facing instructions.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

The gh_api function runs gh.exe api via subprocess.run at L09, which executes an external program and performs network-backed API access. Aside from the brief top-level Chinese description, there is no explicit user-facing notice, confirmation, or inline explanation that the script will invoke GitHub CLI repeatedly.

Content

No source excerpt is available for this finding.