T05 · Unauthorized Access and Privilege Escalation
- Location
scripts/scan_structure.py:65- Finding
Repository Scanning Follows Symbolic Links Outside the Authorized Root
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
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.
scripts/scan_structure.py:65Repository Scanning Follows Symbolic Links Outside the Authorized Root
scripts/fetch_repo.py:202The Declared Subpath Restriction Is Recorded but Never Enforced
scripts/render_html.py:392Unescaped Mermaid Content Enables Script Injection in Generated HTML Reports
{architecture_mermaid}{sequence_mermaid}scripts/render_html.py:33Generated Reports Execute Mutable Third-Party JavaScript Without Integrity Verification
This pattern attempts to override system instructions or ignore safety constraints. Without LLM analysis, manual review is recommended.
- **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.
---
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.
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.
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.
Claude Code auto-discovers any skill folder under ~/.claude/skills/. Unpack the zip there and the skill is live on the next session:
mkdir -p ~/.claude/skills
unzip -d ~/.claude/skills/ repo-explainer.zip
# verify the layout
ls ~/.claude/skills/repo-explainer/ # → SKILL.md scripts/ templates/
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.
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.
Claude Code 会自动发现 ~/.claude/skills/ 下的任何 skill 文件夹。解压到那里,下次会话就生效:
mkdir -p ~/.claude/skills
unzip -d ~/.claude/skills/ repo-explainer.zip
# 验证一下目录结构
ls ~/.claude/skills/repo-explainer/ # → SKILL.md scripts/ templates/
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.
Claude Code 会自动发现 ~/.claude/skills/ 下的任何 skill 文件夹。解压到那里,下次会话就生效:
mkdir -p ~/.claude/skills
unzip -d ~/.claude/skills/ repo-explainer.zip
# 验证一下目录结构
ls ~/.claude/skills/repo-explainer/ # → SKILL.md scripts/ templates/
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.
Claude Code 会自动发现 ~/.claude/skills/ 下的任何 skill 文件夹。解压到那里,下次会话就生效:
mkdir -p ~/.claude/skills
unzip -d ~/.claude/skills/ repo-explainer.zip
# 验证一下目录结构
ls ~/.claude/skills/repo-explainer/ # → SKILL.md scripts/ templates/
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.
mkdir -p ~/.claude/skills unzip -d ~/.claude/skills/ repo-explainer.zip
ls ~/.claude/skills/repo-explainer/ # → SKILL.md scripts/ templates/
然后开一个 Claude Code 会话触发:
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.
mkdir -p ~/.claude/skills unzip -d ~/.claude/skills/ repo-explainer.zip
ls ~/.claude/skills/repo-explainer/ # → SKILL.md scripts/ templates/
然后开一个 Claude Code 会话触发:
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.
Codex 遵循同样的磁盘约定,解压到 Codex 的 skills 目录:
mkdir -p ~/.codex/skills
unzip -d ~/.codex/skills/ repo-explainer.zip
# (或者 repo 内:<project>/.codex/skills/repo-explainer/)
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.
Codex 遵循同样的磁盘约定,解压到 Codex 的 skills 目录:
mkdir -p ~/.codex/skills
unzip -d ~/.codex/skills/ repo-explainer.zip
# (或者 repo 内:<project>/.codex/skills/repo-explainer/)
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.
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.
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.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
# ---------------------------------------------------------------------------
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:
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.
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]
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.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
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 calls execute external commands. Without careful input validation, this enables command injection.
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 calls execute external commands. Without careful input validation, this enables command injection.
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 calls execute external commands. Without careful input validation, this enables command injection.
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 calls execute external commands. Without careful input validation, this enables command injection.
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",
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.
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.
No suspicious patterns detected.