Back to skill

Security audit

ArXiv Search Collector

Security checks for vulnerabilities and agentic risk

Overview

The skill’s arXiv collection workflow is coherent, but unsafe path handling can let crafted inputs read or write files outside the intended collection folder.

Review before installing or running. Use only trusted run names, query labels, selection JSON, and cached query-result files; keep output roots in a dedicated disposable directory; avoid running it where overwriteable important JSON or Markdown files are nearby. The package does not show credential theft or deceptive behavior, but the path traversal bugs should be fixed before use in shared or untrusted workflows.

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 (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/init_collection_run.py:193
Finding

Output-root escape through an unsanitized run name

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/fetch_query_metadata.py:523
Finding

Query-label path traversal permits writes outside the query-results directory

Content
View full analysis
Remediation
View remediation
str: if not LABEL_PATTERN.fullmatch(label): raise ValueError("Invalid query label") return label ``` - Apply identical validation in both `fetch_queries_batch.py` and `fetch_query_metadata.py`. - Do not silently normalize a malicious explicit label if callers rely on stable names; reject it with a clear error. - Resolve each destination and verify that it remains under `query_results`: ```python json_path = (out_dir / f"{label}.json").resolve() md_path = (out_dir / f"{label}.md").resolve() if not json_path.is_relative_to(out_dir.resolve()): raise ValueError("JSON output escapes query_results") if not md_path.is_relative_to(out_dir.resolve()): raise ValueError("Markdown output escapes query_results") ``` - Use atomic writes to reduce partial-file and concurrent-write risks. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/merge_selected_papers.py:391
Finding

Selection labels allow unintended JSON reads outside query-results

Content
View full analysis
.json`. 4. `load_query_result()` reads and parses the external file. 5. Selected records from that file are incorporated into generated manifests and paper metadata. 6. A malicious paper identifier in the external record can be combined with the paper-directory traversal vulnerability to obtain an out-of-run write. ### Impact Assessment The direct impact is unauthorized reading and processing of JSON files accessible to the Skill account. File contents may be exposed indirectly through generated metadata, manifests, ...[truncated 185 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/merge_selected_papers.py:454
Finding

Insufficient arXiv ID validation allows paper-directory traversal

Content
View full analysis
str: raw = str(raw or "").strip() if not raw: return "" m = re.search(r"/(?:abs|pdf)/([^/?#]+)", raw) if m: raw = m.group(1) raw = raw.split("/")[-1] return re.sub(r"v\d+$", "", raw) ``` The normalized value is then used directly as a directory name: ```python # Remove stale paper directories from previous merges. old_paper_dirs = load_existing_paper_dirs(run_dir) new_paper_dirs = {run_dir / paper["base_id"] for paper in merged_list} removed_paper_dirs = remove_stale_paper_dirs(old_paper_dirs, new_paper_dirs) # Write final per-paper metadata directories. paper_index = [] for paper in merged_list: paper_dir = run_dir / paper["base_id"] paper_dir.mkdir(parents=True, exist_ok=True) metadata_json_path = paper_dir / "metadata.json" metadata_md_path = paper_dir / "metadata.md" metadata_json_path.write_text(json.dumps(paper, indent=2, ensure_ascii=False) + "\n") write_paper_metadata_md(paper, metadata_md_path, lang_code) ``` ### Technical Analysis `normalize_arxiv_id()` removes slash-delimited prefixes but accepts values such as `.` and `..`. For example, a crafted query-result record with `"base_id": ".."` remains `".."` after normalization. Joining that value to `run_dir` selects the parent directory. The normal arXiv API is expected to return legitimate identifiers, but query-result JSON is stored locally and later trusted without integrity protection or strict schema validation. The preceding selection-label traversal issue can also direct the merge script to an attacker-crafted JSON document. ### Attack Path 1. The ...[truncated 866 chars]
Remediation
View remediation
str: value = normalize_arxiv_id(raw) if value in {".", ".."}: raise ValueError("Invalid arXiv ID") if not (MODERN_ID.fullmatch(value) or LEGACY_ID.fullmatch(value)): raise ValueError("Invalid arXiv ID") return value ``` - If legacy identifiers containing `/` must be represented as one directory name, map them to a safe canonical encoding rather than using raw path separators. - Resolve every generated paper directory and enforce containment: ```python paper_dir = (run_dir / safe_directory_name).resolve() if not paper_dir.is_relative_to(run_dir.resolve()): raise ValueError("Paper directory escapes run directory") ``` - Validate every loaded paper record before adding it to `merged_list`, even when it originated from a local cache or the arXiv API. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (8)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill instructs the agent to run local Python scripts, read and write files under arbitrary paths, and make network calls to the arXiv API, but it declares no explicit tool scope or permission boundaries. Without an allowlist such as permissions or allowed-tools, an integrating agent may grant broader shell, filesystem, and network access than necessary, increasing the blast radius if the skill is misused, modified, or prompt-injected through surrounding workflow context.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This markdown file documents that repeated merge runs will remove previously generated paper directories when they are no longer selected. Because this is a destructive file operation affecting stored outputs, the skill description should clearly warn users that rerunning or changing selections can delete prior artifacts.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The code sets default_language to params.language if present, otherwise falls back to the literal value "English". This is a natural-language locale policy issue because it forces a specific language by default rather than prompting for or preserving user choice.

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

python
if args.print_commands:
            print(f"[CMD {idx}/{query_count}] " + " ".join(shlex.quote(x) for x in cmd))

        proc = subprocess.run(cmd, text=True, capture_output=True)
        parsed = parse_last_json_block(proc.stdout)

        entry: dict[str, Any] = {

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The normalize_language function maps a small set of Chinese identifiers to "zh" and returns "en" for all other values. This creates a forced language policy in which unsupported or unexpected user locale inputs are silently coerced to English without explicit user opt-in or choice.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The language normalization logic maps only a small set of Chinese identifiers to 'zh' and defaults every other value to 'en'. This means a user-supplied language such as French, Spanish, or another locale is silently overridden to English, which is a natural-language policy concern because it imposes a language choice without explicit opt-in.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The language normalization logic maps several Chinese variants to 'zh' and defaults every other input to 'en'. This enforces an English output locale for unspecified or unsupported languages rather than preserving user choice or clearly documenting that only English and Chinese are supported.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

The script captures full stdout/stderr from each subprocess and embeds them in the final JSON summary, which may disclose sensitive query terms, local paths, API/debug output, or downstream script data to logs or calling systems. In an agent workflow that may persist or surface run artifacts automatically, this creates an information exposure risk even if the data is not secret by design.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.