T09 · Insecure Skill Coding Practices
- Location
data/file_sorter.py:125- Finding
External 7-Zip and RAR extraction occurs before path validation
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This archive-sorting skill is coherent, but its file-handling flaws could let crafted archives or rules write, move, or delete files outside the folder the user chose.
Install only after review or fixes. Use this skill with trusted archives and trusted classification rules, avoid the 7z/unrar command-line fallback for sensitive or untrusted archives, do not use password-protected archives where process arguments may be visible, and require explicit confirmation before execute or overwrite behavior.
data/file_sorter.py:125External 7-Zip and RAR extraction occurs before path validation
data/file_sorter.py:198Classification rules can move files outside the requested output directory
data/file_sorter.py:94Predictable and reusable temporary directories enable file collisions and unsafe cleanup
data/file_sorter.py:132Archive passwords are exposed through external command-line arguments
SKILL.md:181Dependency installation guidance does not pin versions or package integrity
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
## 安全机制
脚本在解压前对所有归档成员执行路径安全校验:
- 拒绝绝对路径成员(如 `/etc/passwd`)
- 拒绝包含 `..` 的路径遍历成员(如 `../../etc/passwd`)
- 校验解压目标路径确实在指定目录内(resolve 后比较前缀)
- TAR 格式额外跳过符号链接和特殊文件(设备文件等)
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
## 安全机制
脚本在解压前对所有归档成员执行路径安全校验:
- 拒绝绝对路径成员(如 `/etc/passwd`)
- 拒绝包含 `..` 的路径遍历成员(如 `../../etc/passwd`)
- 校验解压目标路径确实在指定目录内(resolve 后比较前缀)
- TAR 格式额外跳过符号链接和特殊文件(设备文件等)
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.
for kw in rule["keywords"]:
kw_lower = kw.lower()
if mode == "contains" and kw_lower in name_lower:
return rule["name"]
elif mode == "startswith" and name_lower.startswith(kw_lower):
return rule["name"]
elif mode == "endswith" and name_lower.endswith(kw_lower):
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.
for kw in rule["keywords"]:
kw_lower = kw.lower()
if mode == "contains" and kw_lower in name_lower:
return rule["name"]
elif mode == "startswith" and name_lower.startswith(kw_lower):
return rule["name"]
elif mode == "endswith" and name_lower.endswith(kw_lower):
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.
for kw in rule["keywords"]:
kw_lower = kw.lower()
if mode == "contains" and kw_lower in name_lower:
return rule["name"]
elif mode == "startswith" and name_lower.startswith(kw_lower):
return rule["name"]
elif mode == "endswith" and name_lower.endswith(kw_lower):
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.
for kw in rule["keywords"]:
kw_lower = kw.lower()
if mode == "contains" and kw_lower in name_lower:
return rule["name"]
elif mode == "startswith" and name_lower.startswith(kw_lower):
return rule["name"]
elif mode == "endswith" and name_lower.endswith(kw_lower):
The skill documents use of Bash, file reads/writes, and archive extraction, but the manifest declares no explicit tool scope or permission boundaries. That increases the chance the agent invokes shell and filesystem capabilities more broadly than intended, especially since the skill persists rules and moves files on disk.
The trigger text says to use this skill whenever the user mentions archive handling terms like '解压', which is broad enough to activate on common benign conversations. Over-broad invocation can cause unintended access to local files, archive contents, or shell-based processing when the user did not clearly request those actions.
The workflow examples include vague prompts like '分类这个文件' or '整理这个压缩包', which do not ensure the file is actually an archive or that the user intended filesystem modification. In an agent setting, ambiguous triggers can lead to accidental execution of extraction and movement operations on sensitive user data.
The file's user-facing docstrings, CLI help text, status messages, and warnings are consistently hardcoded in Chinese, making the skill effectively operate in a single language without user opt-in. The policy allows locale constraints only when documented and justified or when users are given a choice, which is not present here.
The skill's stated purpose is archive parsing and file classification, but this code can invoke general system binaries for extraction when Python modules are unavailable. That broadens capability and trust assumptions: a file-sorting skill now depends on host-installed executables, which can be abused indirectly through malicious archives or unsafe local binary resolution.
The code invokes external archive utilities (7z/unrar) via subprocess.run, expanding the skill's execution surface beyond pure in-process file sorting. Although arguments are passed as a list rather than through a shell, reducing classic command-injection risk, this still executes attacker-influenced inputs (archive path, password, output path) in third-party binaries that may have their own parsing quirks or memory-safety vulnerabilities.
def _run_cmd(cmd):
import subprocess
cmd = [c for c in cmd if c] # 去掉空字符串
r = subprocess.run(cmd, capture_output=True, text=True)
if r.returncode != 0:
raise RuntimeError(f"命令失败: {' '.join(cmd)}\n{r.stderr}")
The value "_未分类" sets a specific language/locale in natural-language content within the configuration. For all file types, forcing a locale-specific label without offering user choice or documenting a region-specific justification can violate language/locale policy expectations.
No suspicious patterns detected.