Back to skill

Security audit

GitHub 项目分析助手

Security checks for vulnerabilities and agentic risk

Overview

This skill performs the advertised GitHub search, analysis, and optional download workflow, with some privacy and robustness caveats but no hidden or destructive behavior found.

Install only if you are comfortable sending search terms, repository names, and any configured GITHUB_TOKEN to GitHub. Use a minimally scoped token or unset GITHUB_TOKEN for unauthenticated use, treat README-derived analysis as untrusted, and avoid downloading very large or unknown repositories until size limits are added.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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
Findings (2)

T01 · Skill Instruction Hijacking

Error
Location
scripts/analyze_repo.py:34
Finding

Untrusted Repository README Content Creates an Indirect Prompt-Injection Channel

Content
View full analysis

Vulnerability Details

File Location: scripts/analyze_repo.py:34-38 and scripts/analyze_repo.py:111; related workflow in SKILL.md:31-34
Vulnerability Type: Indirect prompt injection through attacker-controlled repository content
Risk Level: High

Vulnerable Code

python
content = data.get("content", "")
try:
    decoded = base64.b64decode(content).decode("utf-8", errors="ignore")
    # Only take the first 3000 characters
    return decoded[:3000]
except Exception:
    return ""

The decoded content is then placed directly into the analysis result:

python
"readme_excerpt": readme,

The documented workflow states that repository details and README content are retrieved and then analyzed by the AI.

Technical Analysis

Repository README content is fully controlled by the repository owner. The implementation decodes up to 3,000 characters of this content and returns it as readme_excerpt without sanitization, structural isolation, or a trust-boundary marker.

Because the Skill workflow subsequently provides this content to an AI agent for report generation, a malicious README can contain text framed as agent instructions. For example, it could instruct the agent to ignore the requested analysis, misrepresent repository quality, expose unrelated context, or request additional tool actions.

Limiting the README to 3,000 characters reduces volume but does not mitigate prompt injection because a functional injected instruction can be very short. The code also does not distinguish descriptive repository content from imperative instructions.

This is an indirect prompt-injection weakness rather than direct native-code execution. Whether an injected instruction can cause tool use or sensitive disclosure depends on the permissions, surrounding system prompt, and safety controls of the host agent.

Attack Path

  1. An attacker creates or modifies a public GitHub repository.

...[truncated 1196 chars]

Remediation
View remediation

Remediation Suggestions

  1. Treat every README, description, topic, and other repository-provided field as untrusted data.
  2. Add explicit agent instructions stating that repository content must only be summarized and that instructions embedded in it must never be followed.
  3. Place README text inside strongly delimited data blocks and label it as untrusted external content.
  4. Separate untrusted content from system and workflow instructions rather than concatenating both into the same instruction context.
  5. Prefer structured, non-agent extraction for objective metadata such as language, license, activity, and repository statistics.
  6. Apply content screening for common prompt-injection patterns, while recognizing that pattern filtering is only defense in depth.
  7. Require fresh user confirmation before any tool action suggested by repository content.
  8. Ensure the host agent uses least-privilege tools and cannot expose secrets or perform unrelated actions during repository analysis.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/download_repos.py:37
Finding

Unbounded Repository Archive Download Can Exhaust Memory and Disk Resources

Content
View full analysis

Vulnerability Details

File Location: scripts/download_repos.py:37-39
Vulnerability Type: Unbounded remote response buffering and storage
Risk Level: Medium

Vulnerable Code

python
with urllib.request.urlopen(req, timeout=60) as r:
    with open(out_path, "wb") as f:
        f.write(r.read())

Technical Analysis

The call to r.read() reads the complete repository archive into process memory before writing it to disk. The implementation does not:

  • Enforce a maximum archive size.
  • Validate the Content-Length response header.
  • Stream data using bounded chunks.
  • Track cumulative downloaded bytes.
  • Check available disk capacity.
  • Remove partially downloaded files after a failure or size-limit violation.

A network timeout limits how long an individual operation may wait, but it does not constrain the total response size. A sufficiently large GitHub repository archive can therefore cause high memory consumption when buffered and substantial disk consumption when saved.

The archive is not extracted or executed by this script, so archive path traversal and direct remote-code execution are not present in the reviewed implementation.

Attack Path

  1. An attacker creates or identifies a repository whose default-branch archive is exceptionally large.
  2. The repository is supplied directly or selected following the Skill's analysis and ranking process.
  3. The user confirms the repository download.
  4. download_repos.py requests the default-branch ZIP from GitHub.
  5. r.read() attempts to buffer the entire archive in process memory.
  6. The response is then written without a configured disk-size limit.
  7. The process or host may experience memory exhaustion, disk exhaustion, termination, or severe performance degradation.

Impact Assessment

Exploitation primarily affects availability. It can terminate the downloader or agent process, degrade other workloads on the ...[truncated 365 chars]

Remediation
View remediation

Remediation Suggestions

  1. Stream responses in fixed-size chunks instead of using an unbounded r.read() call.
  2. Define a configurable maximum archive size and abort when cumulative downloaded bytes exceed it.
  3. Inspect Content-Length before downloading and reject responses larger than the configured limit. Continue enforcing the limit while streaming because the header may be absent or inaccurate.
  4. Write to a temporary partial file and atomically rename it after successful completion.
  5. Delete partial files when downloads fail or exceed the size limit.
  6. Check available disk capacity before and during large downloads.
  7. Require explicit user confirmation when an archive exceeds a conservative warning threshold.
  8. A hardened implementation should resemble:
python
max_bytes = 500 * 1024 * 1024
downloaded = 0
chunk_size = 1024 * 1024
partial_path = out_path + ".part"

try:
    with urllib.request.urlopen(req, timeout=60) as response:
        declared_size = response.headers.get("Content-Length")
        if declared_size and int(declared_size) > max_bytes:
            raise ValueError("Repository archive exceeds the download limit")

        with open(partial_path, "wb") as output:
            while True:
                chunk = response.read(chunk_size)
                if not chunk:
                    break
                downloaded += len(chunk)
                if downloaded > max_bytes:
                    raise ValueError("Repository archive exceeds the download limit")
                output.write(chunk)

    os.replace(partial_path, out_path)
except Exception:
    if os.path.exists(partial_path):
        os.remove(partial_path)
    raise
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (25)

Tainted flow: 'req' from os.environ.get (line 50, 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 · scripts/analyze_repo.py (reported line 22)May include surrounding context.

python
def gh_get(url):
    req = urllib.request.Request(url, headers=HEADERS)
    try:
        with urllib.request.urlopen(req, timeout=15) as r:
            return json.loads(r.read())
    except urllib.error.HTTPError as e:
        return {"error": str(e), "url": url}

Tainted flow: 'req' from os.environ.get (line 50, 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 · scripts/analyze_repo.py (reported line 52)May include surrounding context.

python
url = f"https://api.github.com/repos/{owner}/{repo}/contributors?per_page=1&anon=true"
    req = urllib.request.Request(url, headers=HEADERS)
    try:
        with urllib.request.urlopen(req, timeout=10) as r:
            # 从 Link header 获取总页数
            link = r.getheader("Link", "")
            if 'rel="last"' in link:

Tainted flow: 'req' from os.environ.get (line 35, 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 · scripts/download_repos.py (reported line 27)May include surrounding context.

python
f"https://api.github.com/repos/{owner}/{repo}",
        headers=headers
    )
    with urllib.request.urlopen(req, timeout=15) as r:
        repo_data = json.loads(r.read())
    branch = repo_data.get("default_branch", "main")

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

The script reuses the same headers, including the GitHub token, when requesting the repository ZIP from github.com. This causes a sensitive credential to be transmitted to an endpoint that does not need it, increasing unnecessary exposure in transit, logs, intermediaries, or future redirect edge cases. In a tool that downloads arbitrary user-specified repositories, over-sharing credentials is riskier because attacker-chosen inputs drive outbound requests.

Content

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

python
print(f"⬇️  下载 {full_name} ...")
    req = urllib.request.Request(zip_url, headers=headers)
    with urllib.request.urlopen(req, timeout=60) as r:
        with open(out_path, "wb") as f:
            f.write(r.read())

Tainted flow: 'req' from os.environ.get (line 21, 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 · scripts/search_github.py (reported line 22)May include surrounding context.

python
def gh_get(url):
    req = urllib.request.Request(url, headers=HEADERS)
    with urllib.request.urlopen(req, timeout=15) as r:
        return json.loads(r.read())

def search_repos(query: str, limit: int = 10):

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

该代码的核心行为是调用 GitHub API 分析指定仓库,并输出结构化仓库元数据。它与声明中的“直链分析”部分部分吻合,但声明的主要卖点——基于项目想法搜索相关项目、比较分析、优缺点评价、评分、选出前 3 名并下载代码包——在代码中均未出现。虽然代码会生成 zip_url 字段,但这只是构造下载链接,并没有执行下载,也没有任何 top-3 排序逻辑。因此描述明显夸大了实际能力,存在实质性不匹配。

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

声明描述的是一个“搜索 + 分析 + 排名 + 下载”的完整 GitHub 项目分析器;但实际代码只是一个下载脚本。它接受 owner/repo 或 GitHub URL,查询默认分支并下载 zip 包到本地。虽然这与声明中“可下载代码包”这一子功能部分相关,但缺少核心能力:项目搜索、分析报告生成、评分与排序、Top 3 选择,以及意图搜索模式。因此该代码块的实际主要用途与声明存在明显不一致。代码也未表现出额外的高风险越权行为;问题在于声明显著夸大了已实现功能。

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The code chunk's behavior is substantially narrower than the declared description. It searches GitHub repositories using a text query, filters out forks/archived/low-star repos, and returns metadata such as stars, forks, language, topics, and license. While this partially aligns with the 'search related open-source projects' portion, it does not implement the main advertised features: analyzing a provided GitHub link, producing structured evaluation reports with pros/cons and scores, ranking by generated scores, or downloading top repositories. This is a material description-to-behavior mismatch.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding

The skill documents execution of Python scripts that use network access and may read environment variables, but it does not declare any explicit tool scope such as allowed-tools or permissions. Missing scope boundaries weakens reviewability and can allow broader-than-expected capability use, especially since the skill also references automatic use of GITHUB_TOKEN from the environment.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The skill sends user-provided search queries and repository URLs to GitHub APIs but does not warn users that their inputs will be transmitted to a third party. This can expose sensitive project ideas, internal repository references, or confidential research topics, especially in enterprise contexts where even metadata disclosure matters.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

Documenting automatic use of GITHUB_TOKEN without a user-facing warning obscures that the skill may consume credentials from the environment and perform authenticated requests on the user's behalf. This can surprise users, complicate auditing, and increase risk if tokens with broader-than-necessary scopes are present in shared or CI environments.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The module docstring and command-line usage text are written in Chinese, which imposes a specific language for user-facing interaction. There is no indication that users can choose another language or that the locale restriction is intentional and documented for a region-specific tool.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The returned error message and printed usage text are hard-coded in Chinese, creating a language policy issue for general users. The file does not offer locale selection, fallback messaging, or documentation limiting the tool to a Chinese-language audience.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/analyze_repo.py (reported line 28)May include surrounding context.

python
"User-Agent": "github-analyzer/1.0"
    }
    req = urllib.request.Request(
        f"https://api.github.com/repos/{owner}/{repo}",
        headers=headers
    )
    with urllib.request.urlopen(req, timeout=15) as r:

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/analyze_repo.py (reported line 42)May include surrounding context.

python
"User-Agent": "github-analyzer/1.0"
    }
    req = urllib.request.Request(
        f"https://api.github.com/repos/{owner}/{repo}",
        headers=headers
    )
    with urllib.request.urlopen(req, timeout=15) as r:

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/analyze_repo.py (reported line 49)May include surrounding context.

python
"User-Agent": "github-analyzer/1.0"
    }
    req = urllib.request.Request(
        f"https://api.github.com/repos/{owner}/{repo}",
        headers=headers
    )
    with urllib.request.urlopen(req, timeout=15) as r:

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/analyze_repo.py (reported line 69)May include surrounding context.

python
"User-Agent": "github-analyzer/1.0"
    }
    req = urllib.request.Request(
        f"https://api.github.com/repos/{owner}/{repo}",
        headers=headers
    )
    with urllib.request.urlopen(req, timeout=15) as r:

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/download_repos.py (reported line 24)May include surrounding context.

python
"User-Agent": "github-analyzer/1.0"
    }
    req = urllib.request.Request(
        f"https://api.github.com/repos/{owner}/{repo}",
        headers=headers
    )
    with urllib.request.urlopen(req, timeout=15) as r:

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/search_github.py (reported line 27)May include surrounding context.

python
"User-Agent": "github-analyzer/1.0"
    }
    req = urllib.request.Request(
        f"https://api.github.com/repos/{owner}/{repo}",
        headers=headers
    )
    with urllib.request.urlopen(req, timeout=15) as r:

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

This code reads a sensitive credential from the GITHUB_TOKEN environment variable and sends it in HTTP requests to api.github.com. The script has no confirmation prompt, visible warning, logging, or explanatory comment/docstring telling the user that their token and search query will be transmitted over the network.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
80% confidence
Finding

The README presents the skill name, usage instructions, and example invocations entirely in Chinese, with no indication that users may choose another language. This can be a natural-language policy issue when a skill appears to force a specific language without user opt-in.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
85% confidence
Finding

This markdown file advertises a one-click code download feature, which implies network access and local file creation, but it does not include any warning or disclosure about those effects. Under the markdown-file criteria for SQP-2, user-facing descriptions should warn when behavior may affect user data, privacy, or system integrity.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

This code performs outbound HTTP requests to the GitHub API and includes an Authorization header derived from the GITHUB_TOKEN environment variable. While network access is central to the script's purpose, there is no docstring, comment, or user-facing disclosure near the request path explaining that repository identifiers and authentication will be sent to GitHub.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
98% confidence
Finding

This is a code file, so natural-language policy checks apply to docstrings and printed strings. The script forces a specific language for usage/help text and status messages, which can violate language/locale policy when no user choice or justification is provided.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The only natural-language instructions in the file are written in Chinese, including the title and usage text, with no indication that this locale is optional or intentionally region-specific. Under the stated policy, forcing a specific language without user opt-in is a language/locale policy concern.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.