Back to skill

Security audit

Repo Explainer

Security checks for vulnerabilities and agentic risk

Overview

The skill’s repo-explainer purpose is mostly coherent, but local scans and generated reports can expose more source data than users intend and the HTML report has unsafe script-rendering behavior.

Review generated reports before sharing them, especially for private or local workspaces. Avoid pointing this skill at broad home/work directories or sensitive monorepos until subpath enforcement and symlink handling are fixed, and treat generated HTML as untrusted content unless Mermaid is escaped/bundled safely.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
Findings (4)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/scan_structure.py:65
Finding

Repository Scanning Follows Symbolic Links Outside the Authorized Root

Content
View full analysis
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/fetch_repo.py:202
Finding

The Declared Subpath Restriction Is Recorded but Never Enforced

Content
View full analysis
__/ ``` ### Technical Analysis The command-line parser accepts `--subpath`, and the value is copied into the output metadata. However, it is never validated, resolved, or applied to `local_path`. Neither local preparation nor remote cloning changes the directory that downstream stages scan. Consequently, an explicit request to focus on a monorepo component does not create an access boundary. The scanner still processes the full repository root. This also undermines the documented use of `--subpath` as a response to the 500 MB limit: remote repositories are cloned in full, and the size check is performed only after cloning. The def ...[truncated 1381 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/render_html.py:392
Finding

Unescaped Mermaid Content Enables Script Injection in Generated HTML Reports

Content
View full analysis
text
{architecture_mermaid}
{architecture_legend} ``` ```html
text
{sequence_mermaid}
``` ```javascript // scripts/render_html.py:451-462 mermaid.initialize({ startOnLoad: false, theme: t.mermaid.theme, themeVariables: t.mermaid.themeVariables, securityLevel: 'loose', flowchart: { curve: 'basis', padding: 24, nodeSpacing: 60, rankSpacing: 80, htmlLabels: true, useMaxWidth: true, diagramPadding: 16 }, sequence: { actorMargin: 70, boxMargin: 14, messageMargin: 50, boxTextMargin: 8, noteMargin: 12, mirrorActors: false, bottomMarginAdj: 12, useMaxWidth: true, rightAngles: false, showSequenceNumbers: true, wrap: true } }); // reset rendered mermaid blocks document.querySelectorAll('.mermaid').forEach((el, i) => { if (!el.dataset.source) el.dataset.source = el.textContent; el.removeAttribute('data-processed'); el.innerHTML = el.dataset.source; }); mermaid.run(); ``` ```python # scripts/render_html.py:922-943 out = HTML_TPL.format( title=esc(summary.get("project_name", repo or "Local Project")), project_name=esc(summary.get("project_name", repo or "Local Project")), one_liner=esc(summary.get("one_liner", gh_meta.get("description", ""))), repo_link=repo_link_html, badges=badges_html, footer_meta=footer_meta, who_for=esc(summary.get("who_for", "")), problem_solved=esc(summary.get("problem_solved", "")), core_value=esc(summary.get("core_value", "")), tech_stack_table=render_tech_stack(summary.get("tech_stack", [])), architecture_mermaid=arch_src_enriched, architecture_le ...[truncated 2669 chars]
Remediation
View remediation
`, `

T08 · Insecure Dependencies

Warning
Location
scripts/render_html.py:33
Finding

Generated Reports Execute Mutable Third-Party JavaScript Without Integrity Verification

Content
View full analysis
``` ### Technical Analysis Each generated HTML report loads Mermaid JavaScript from jsDelivr at viewing time. The dependency is pinned only to the major release line `10`, so the effective executable content can change after the Skill and generated report have been reviewed. No Subresource Integrity hash is supplied. A compromise of the package distribution path, CDN, or a later incompatible release could cause attacker-controlled JavaScript to execute with the report page's browser privileges. This also conflicts with the report's description as self-contained. The external Google Fonts stylesheet is not executable JavaScript by itself, but it causes an additional third-party request. Opening a report therefore discloses network metadata such as IP address, user agent, and timing information to external providers. ### Attack Path 1. The Skill generates an HTML report containing the remote Mermaid script reference. 2. A user opens the report while network access is available. 3. The browser requests the current `mermaid@10` asset from jsDelivr. 4. If the mutable package path or delivery infrastructure serves compromised content, the browser executes it automatically. 5. The malicious script operates in the generated report's browser context and can manipulate content or initiate further network requests. No compromise of the audited Skill package is required after generation because the effective script payload is retrieved when the report is viewed. ### Impact As ...[truncated 477 chars]
Remediation
View remediation
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
Findings (38)

Instruction Override

High
Category
Prompt Injection
Confidence
90% confidence
Finding

This pattern attempts to override system instructions or ignore safety constraints. Without LLM analysis, manual review is recommended.

Content

Scanner excerpt · README.md (reported line 49)May include surrounding context.

md
- **Key Models table.** Extracts the project's core domain types (structs / classes / protobuf) before diving into module internals — readers learn the "what" before the "how".
- **Honest limitations.** Every report includes 4–8 limitation cards tagged with severity (high / medium / low) and category. Generic platitudes like "needs more tests" are forbidden in the prompt.
- **Single-file HTML.** Inline CSS, Mermaid via CDN, print-friendly (`@media print`), no build server required. A small JS click-handler guarantees every code-path link opens in a new tab even inside strict sandboxed iframes (Mira / Lark / Notion preview), without ever stealing the current window.
- **Theme override system.** Drop in a JSON file with just the variables you want to change; it is deep-merged on top of the base theme.

---

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The description promises an end-to-end repository explainer that supports GitHub URLs and local directories, scans source code, and emits polished HTML and Markdown reports with rich analysis artifacts and line-referenced evidence. This code chunk only implements an intermediate structural scan stage for a local directory. It collects shallow metadata: file counts, language LOC by extension, manifest detection, simple entry-point filename matches, README excerpt extraction, and lightweight dependency parsing for a few manifest types. That is consistent with a supporting component of such a system, but not with the declared skill behavior as presented. Because the actual code's primary behavior is much narrower than the declared purpose and omits most headline capabilities, this is a description-behavior mismatch.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The README explicitly supports analyzing local directories and generating shareable HTML/Markdown reports with path:line evidence, but it does not warn that these outputs may expose proprietary source structure, filenames, code snippets, git remotes, or commit metadata. In an agent-skill context, this increases the risk of users unintentionally processing sensitive local workspaces and then sharing the generated artifacts externally.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · README.md (reported line 111)May include surrounding context.

Claude Code auto-discovers any skill folder under ~/.claude/skills/. Unpack the zip there and the skill is live on the next session:

bash
mkdir -p ~/.claude/skills
unzip -d ~/.claude/skills/ repo-explainer.zip
# verify the layout
ls ~/.claude/skills/repo-explainer/          # → SKILL.md  scripts/  templates/

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

This line explicitly states that the prompt and schema fields favor Chinese output, which is a natural-language policy concern because it imposes a language preference by default. The README does not clearly frame this as user-configurable or opt-in, so it may violate language/locale neutrality expectations.

Content

No source excerpt is available for this finding.

Skill Enumeration

Medium
Category
Agent Snooping
Confidence
85% confidence
Finding

Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Content

Scanner excerpt · README.md (reported line 111)May include surrounding context.

Claude Code 会自动发现 ~/.claude/skills/ 下的任何 skill 文件夹。解压到那里,下次会话就生效:

bash
mkdir -p ~/.claude/skills
unzip -d ~/.claude/skills/ repo-explainer.zip
# 验证一下目录结构
ls ~/.claude/skills/repo-explainer/          # → SKILL.md  scripts/  templates/

Skill Enumeration

Medium
Category
Agent Snooping
Confidence
85% confidence
Finding

Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Content

Scanner excerpt · README.zh-CN.md (reported line 111)May include surrounding context.

Claude Code 会自动发现 ~/.claude/skills/ 下的任何 skill 文件夹。解压到那里,下次会话就生效:

bash
mkdir -p ~/.claude/skills
unzip -d ~/.claude/skills/ repo-explainer.zip
# 验证一下目录结构
ls ~/.claude/skills/repo-explainer/          # → SKILL.md  scripts/  templates/

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · README.zh-CN.md (reported line 111)May include surrounding context.

Claude Code 会自动发现 ~/.claude/skills/ 下的任何 skill 文件夹。解压到那里,下次会话就生效:

bash
mkdir -p ~/.claude/skills
unzip -d ~/.claude/skills/ repo-explainer.zip
# 验证一下目录结构
ls ~/.claude/skills/repo-explainer/          # → SKILL.md  scripts/  templates/

Skill Enumeration

Medium
Category
Agent Snooping
Confidence
85% confidence
Finding

Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Content

Scanner excerpt · README.md (reported line 114)May include surrounding context.

mkdir -p ~/.claude/skills unzip -d ~/.claude/skills/ repo-explainer.zip

验证一下目录结构

ls ~/.claude/skills/repo-explainer/ # → SKILL.md scripts/ templates/

text

然后开一个 Claude Code 会话触发:

Skill Enumeration

Medium
Category
Agent Snooping
Confidence
85% confidence
Finding

Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Content

Scanner excerpt · README.zh-CN.md (reported line 114)May include surrounding context.

mkdir -p ~/.claude/skills unzip -d ~/.claude/skills/ repo-explainer.zip

验证一下目录结构

ls ~/.claude/skills/repo-explainer/ # → SKILL.md scripts/ templates/

text

然后开一个 Claude Code 会话触发:

Skill Enumeration

Medium
Category
Agent Snooping
Confidence
85% confidence
Finding

Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Content

Scanner excerpt · README.md (reported line 129)May include surrounding context.

Codex 遵循同样的磁盘约定,解压到 Codex 的 skills 目录:

bash
mkdir -p ~/.codex/skills
unzip -d ~/.codex/skills/ repo-explainer.zip
# (或者 repo 内:<project>/.codex/skills/repo-explainer/)

Skill Enumeration

Medium
Category
Agent Snooping
Confidence
85% confidence
Finding

Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Content

Scanner excerpt · README.zh-CN.md (reported line 129)May include surrounding context.

Codex 遵循同样的磁盘约定,解压到 Codex 的 skills 目录:

bash
mkdir -p ~/.codex/skills
unzip -d ~/.codex/skills/ repo-explainer.zip
# (或者 repo 内:<project>/.codex/skills/repo-explainer/)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill requests or implies broad capabilities across environment access, file reading, network, and shell execution without declaring any explicit tool scope or least-privilege boundary. Because it can operate on arbitrary local directories and clone remote repositories, the absence of restrictions materially increases the blast radius if the skill is misused, triggered unexpectedly, or extended with unsafe prompts.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill explicitly supports in-place analysis of local directories, including work-in-progress checkouts, but does not warn that source trees may contain secrets, private code, config files, or metadata that will be scanned and synthesized. This raises confidentiality risk, especially if results are uploaded externally or if users point it at overly broad paths.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

Several required output fields are explicitly specified in Chinese, such as '≤30 字白话定位', Chinese tech-stack labels, and Chinese glossary/plain-language expectations. This imposes a language policy on outputs without user opt-in, which matches the locale policy violation criteria.

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/fetch_repo.py (reported line 54)May include surrounding context.

python
# ---------------------------------------------------------------------------

def fetch_meta(owner: str, repo: str) -> dict:
    api = f"https://api.github.com/repos/{owner}/{repo}"
    req = urllib.request.Request(api, headers={"User-Agent": "repo-explainer"})
    token = os.environ.get("GH_TOKEN") or os.environ.get("GITHUB_TOKEN")
    if token:

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
88% confidence
Finding

The code recursively deletes an existing target directory under the configured workdir before cloning, with no safety guard beyond path construction and no confirmation. In a skill that may process user-supplied repos and configurable workdirs, this can cause unintended destructive deletion if the target path collides with valuable data or if workdir is pointed at a sensitive location.

Content

Scanner excerpt · scripts/fetch_repo.py (reported line 80)May include surrounding context.

python
def clone(owner: str, repo: str, branch: str | None, workdir: Path):
    target = workdir / f"{owner}__{repo}"
    if target.exists():
        subprocess.run(["rm", "-rf", str(target)], check=True)
    cmd = ["git", "clone", "--depth=1"]
    if branch:
        cmd += ["--branch", branch]

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script deletes an existing directory non-interactively using rm -rf and provides no warning or confirmation to the user. Given this skill supports local directories and a user-controlled --workdir, the destructive behavior is more dangerous because it operates on the local filesystem in an analysis workflow where users may not expect data deletion.

Content

No source excerpt is available for this finding.

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_repo.py (reported line 85)May include surrounding context.

python
if branch:
        cmd += ["--branch", branch]
    cmd += [f"https://github.com/{owner}/{repo}.git", str(target)]
    r = subprocess.run(cmd, capture_output=True, text=True)
    if r.returncode != 0:
        sys.exit(f"ERROR: git clone failed: {r.stderr}")
    du = subprocess.run(["du", "-sm", str(target)], capture_output=True, text=True)

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_repo.py (reported line 88)May include surrounding context.

python
r = subprocess.run(cmd, capture_output=True, text=True)
    if r.returncode != 0:
        sys.exit(f"ERROR: git clone failed: {r.stderr}")
    du = subprocess.run(["du", "-sm", str(target)], capture_output=True, text=True)
    size_mb = int(du.stdout.split()[0])
    if size_mb > MAX_SIZE_MB:
        sys.exit(

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_repo.py (reported line 95)May include surrounding context.

python
f"ERROR: repo size {size_mb}MB exceeds limit {MAX_SIZE_MB}MB. "
            "Re-run with --subpath to focus on a sub-directory."
        )
    sha = subprocess.run(
        ["git", "-C", str(target), "rev-parse", "HEAD"],
        capture_output=True, text=True
    ).stdout.strip()

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_repo.py (reported line 108)May include surrounding context.

python
def _git(path: Path, *args: str) -> str | None:
    """Run a git command inside *path*; return stdout (stripped) or None."""
    r = subprocess.run(
        ["git", "-C", str(path), *args],
        capture_output=True, text=True,
    )

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_repo.py (reported line 166)May include surrounding context.

python
if not path.is_dir():
        sys.exit(f"ERROR: not a directory: {path}")

    du = subprocess.run(
        ["du", "-sm", "--exclude=.git", "--exclude=node_modules",
         "--exclude=venv", "--exclude=.venv", "--exclude=__pycache__",
         "--exclude=dist", "--exclude=build", "--exclude=target",

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The generated HTML declares lang="zh-CN", and the template throughout uses fixed Chinese UI text, making the output language effectively forced to Simplified Chinese. This is a natural-language/locale policy issue because the file does not offer any user choice or documented opt-in for language selection.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The generated HTML unconditionally loads executable third-party browser resources from jsDelivr and Google Fonts. Because the skill operates on local directories and external repositories, opening the report causes network egress to third parties and trusts remotely served JavaScript, creating supply-chain and privacy risk outside the core rendering task.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.