Back to skill

Security audit

ArXiv Paper Processor

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it says, but its source downloader extracts downloaded arXiv archives with insufficient containment, so a malicious paper source bundle could write files outside the intended folder.

Review this skill before installing if it will process untrusted or arbitrary arXiv IDs. Run it in an isolated workspace, avoid giving it access to sensitive files, and patch the source extraction routine to use path-aware containment and reject links/special tar members before using batch downloads broadly.

Vulnerability Patterns
  • 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
  • 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 (1)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/download_arxiv_source.py:286
Finding

Unsafe Tar Archive Extraction Allows Writes Outside the Destination Directory

Content
View full analysis

Vulnerability Details

File Location: scripts/download_arxiv_source.py, lines 286–292
Vulnerability Type: Unsafe archive extraction and incomplete path-containment validation
Risk Level: Medium

Vulnerable Code

python
def extract_tar_safely(archive: tarfile.TarFile, dest_dir: Path) -> None:
    root = dest_dir.resolve()
    for member in archive.getmembers():
        target = (dest_dir / member.name).resolve()
        if not str(target).startswith(str(root)):
            raise ValueError(f"Unsafe path in tar: {member.name}")
    archive.extractall(dest_dir)

Technical Analysis

The function attempts to prevent tar path traversal by resolving each archive member's destination and comparing path strings with startswith(). String-prefix comparison does not establish that one filesystem path is a descendant of another.

For example, if root is /run/source_extract, a malicious member resolving to /run/source_extract_backup/payload passes the check because that path begins with the string /run/source_extract, even though it is outside the intended directory.

The validation also does not reject symbolic links, hard links, device files, or FIFOs, nor does it validate archive link targets. Consequently, an archive may use special member types or link-based paths to redirect subsequent extraction writes outside the destination. Calling archive.extractall(dest_dir) after this incomplete validation exposes the filesystem to attacker-controlled archive semantics.

Attack Path

  1. An attacker causes the processor to download or reuse a crafted source bundle for a paper.
  2. The bundle contains a tar member whose resolved path has a prefix collision with the extraction root, or contains a symbolic or hard link targeting a location outside that root.
  3. extract_from_blob() opens the untrusted payload as a tar archive and calls extract_tar_safely().
  4. The string-prefix check accepts the prefix-collision path, or the function ...[truncated 950 chars]
Remediation
View remediation

Remediation Suggestions

  1. Replace string-prefix comparison with a path-aware containment test:
python
root = dest_dir.resolve()
target = (root / member.name).resolve()
if not target.is_relative_to(root):
    raise ValueError(f"Unsafe path in tar: {member.name}")

For Python versions without Path.is_relative_to(), use target.relative_to(root) and reject members when it raises ValueError.

  1. Reject unsafe member types unless they are explicitly required:
python
if member.issym() or member.islnk() or member.isdev() or member.isfifo():
    raise ValueError(f"Unsupported tar member type: {member.name}")
  1. On supported Python versions, use the standard data extraction filter:
python
archive.extractall(dest_dir, filter="data")

This should supplement, rather than replace, explicit application-specific validation.

  1. Extract into a newly created temporary directory on the same filesystem. Validate all extracted paths and object types before atomically moving the directory into its final location.

  2. Avoid extracting over an existing directory. Remove or quarantine stale extraction output first so preexisting symbolic links cannot influence writes.

  3. Add regression tests covering:

    • ../ traversal entries;
    • absolute paths;
    • prefix-collision paths such as ../source_extract_backup/file;
    • symbolic links pointing outside the extraction root;
    • hard links targeting outside paths;
    • device files and FIFOs;
    • nested link chains.
Vulnerability Patterns
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (6)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill describes and encourages use of shell commands, network downloads, and file read/write behavior, but it does not declare any explicit tool scope such as allowed-tools or permissions. That creates an authorization gap: an agent or reviewer cannot easily verify whether the skill is intended to have these capabilities, increasing the risk of over-privileged execution or accidental tool misuse.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The markdown explicitly instructs the operator to choose a workflow language such as English or Chinese and requires that every summary be written in that selected language. This imposes a language constraint in the skill behavior, but the file does not indicate user opt-in, user preference collection, or a justified region-specific limitation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

This markdown file presents the entire skill content in Chinese with no indication that the language was user-selected or that the skill is intentionally limited to a Chinese-speaking audience. Under the language/locale policy, forcing a specific language without opt-in is a natural-language policy violation.

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

python
def run_cmd(cmd: list[str], *, print_command: bool) -> dict[str, Any]:
    if print_command:
        print("[CMD] " + " ".join(shlex.quote(part) for part in cmd))
    proc = subprocess.run(cmd, text=True, capture_output=True)
    parsed = parse_last_json_block(proc.stdout)
    return {
        "command": cmd,

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

The language normalization logic maps a small set of Chinese identifiers to 'zh' and converts every other input to 'en'. This effectively forces English as the default locale behavior without offering a user choice beyond the raw argument value, which matches the policy category for language or locale constraints without opt-in.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
91% confidence
Finding

The code sets --language to English by default, which imposes a specific language choice unless the user explicitly overrides it. This is a natural-language policy concern because the skill does not prompt for or otherwise obtain language preference before selecting a locale.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.