Back to skill

Security audit

arxiv-zotero-obsidian

Security checks for vulnerabilities and agentic risk

Overview

This skill matches its research-library automation purpose, but it asks for write credentials and can expose them in diagnostic output while also writing directly to Zotero and Obsidian.

Review this before installing. Use a dedicated Zotero API key with the minimum write permissions needed, do not paste the key into prompts or notes, and avoid running scripts/helper.py directly unless the diagnostic config dump is removed or redacted. Confirm the intended Obsidian vault path and folder before enabling any recurring task, because the skill can create or overwrite markdown notes and add records to Zotero.

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/helper.py:151
Finding
Plaintext Disclosure of Zotero API Credentials Through Diagnostic Output## Vulnerability Details **File Location**: `scripts/helper.py`, lines 15–22 and 151–154 **Vulnerability Type**: Sensitive credential exposure through plaintext logging **Risk Level**: Medium ### Vulnerable Code ```python DEFAULT_CONFIG = { "zotero_api_key": os.environ.get("ZOTERO_API_KEY", ""), "zotero_user_id": os.environ.get("ZOTERO_USER_ID", ""), "zotero_collection_key": os.environ.get("ZOTERO_COLLECTION_KEY", "U4PZ3XNP"), "obsidian_vault_path": os.environ.get("OBSIDIAN_VAULT_PATH", ""), "obsidian_folder": os.environ.get("OBSIDIAN_FOLDER", "DailyArXiv"), "arxiv_base_url": "https://arxiv.drqyq.com", "category": "PTA", } ``` ```python if __name__ == "__main__": print("Arxiv-Zotero-Obsidian Helper Script") print("=" * 40) print(f"Default config: {json.dumps(DEFAULT_CONFIG, indent=2)}") ``` ### Technical Analysis `DEFAULT_CONFIG` stores the live value of the `ZOTERO_API_KEY` environment variable alongside the Zotero user ID and local Obsidian vault path. When the helper is executed directly, the entire dictionary is serialized to JSON and printed without redaction. Although reading the API key is necessary for the declared Zotero synchronization function, disclosing it through standard output is not necessary and exceeds minimum information exposure. Standard output may be retained in CI/CD logs, agent transcripts, terminal recordings, monitoring platforms, scheduled-task logs, or diagnostic reports. The audit found that normal API use sends the key in the `Zotero-API-Key` header exclusively over HTTPS to the official `api.zotero.org` endpoint. No evidence was found that the key, vault contents, notes, or environment data are transmitted to an unrelated network destination. The vulnerability is therefore the local plaintext output of the credential rather than unauthorized network exfiltration. ### Attack Path 1. A user configures `ZOTERO_A ...[truncated 1380 chars]
Remediation
## Remediation Suggestions 1. Remove the complete configuration dump from the `__main__` block. 2. If diagnostic output is required, construct an explicit allowlist of non-sensitive fields rather than serializing the full configuration object. 3. Always replace the API key with a fixed marker such as `[REDACTED]`; do not reveal prefixes, suffixes, or key length. 4. Avoid printing local vault paths, user identifiers, and collection keys unless they are essential for a specific diagnostic operation. 5. Use a logging framework with centralized secret-redaction controls and ensure production logging does not run at a level that exposes configuration details. 6. Revoke and rotate any Zotero API key that may already have appeared in logs or transcripts. 7. Review and delete retained logs containing the exposed configuration where operationally possible. 8. Configure the Zotero key with only the permissions needed to create the intended items and attachments, and use a dedicated key for this Skill when possible. A safe diagnostic pattern would be: ```python if __name__ == "__main__": safe_config = { "zotero_api_key": "[REDACTED]" if DEFAULT_CONFIG["zotero_api_key"] else "", "category": DEFAULT_CONFIG["category"], "obsidian_folder": DEFAULT_CONFIG["obsidian_folder"], "arxiv_base_url": DEFAULT_CONFIG["arxiv_base_url"], } print("Arxiv-Zotero-Obsidian Helper Script") print("=" * 40) print(f"Configuration: {json.dumps(safe_config, indent=2)}") ```
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (12)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The declared behavior materially differs from the detected behavior: the skill claims to scan a specific site and use zotero-mcp/obsidian integrations, but the analysis indicates direct API calls, environment-based configuration, and arbitrary local file writes instead. This mismatch is dangerous because users may grant trust and credentials based on the documented workflow while the actual implementation bypasses expected controls and performs different, more sensitive operations.

Lp3

Medium
Category
MCP Least Privilege
Confidence
83% confidence
Finding
The skill advertises automation that can access environment data, perform network requests, and write files, but it does not declare an explicit tool/permission scope. In an agent setting, this reduces transparency and weakens user consent boundaries, making it easier for the skill to overreach into sensitive actions such as credential use and vault/library modification without clear upfront disclosure.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill description promotes automatic synchronization and note generation but does not clearly warn that it will modify the user's Zotero library and Obsidian vault. In a personal knowledge-management context, silent writes can overwrite, pollute, or reorganize user data, especially when run on a schedule.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The skill instructs users to create a Zotero API key with write permissions but does not warn about the sensitivity of that credential or the account-wide consequences of misuse. If exposed or mishandled, the key could be used to modify library contents, causing unauthorized changes or data integrity issues.

Context-Inappropriate Capability

Medium
Confidence
79% confidence
Finding
The manifest describes application behavior around scanning arxiv.drqyq.com, updating Zotero, and creating Obsidian notes, but does not mention credential or environment-variable access as part of the skill’s scope. This file loads a Zotero API key and local vault path directly from process environment, adding a capability to access host-provided secrets and local configuration beyond the user-facing purpose.

Description-Behavior Mismatch

Medium
Confidence
92% confidence
Finding
The manifest says the skill automatically scans papers on arxiv.drqyq.com, which implies that site is the source of paper information. In contrast, this function reaches out to the separate export.arxiv.org API and builds canonical arxiv.org links, expanding behavior beyond the described single-site scanning workflow.

External Transmission

Medium
Category
Data Exfiltration
Content
"""Add paper to Zotero."""
    import requests
    
    url = f"https://api.zotero.org/users/{user_id}/items"
    headers = {
        "Zotero-API-Key": api_key,
        "Content-Type": "application/json"
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
"""Add paper to Zotero."""
    import requests
    
    url = f"https://api.zotero.org/users/{user_id}/items"
    headers = {
        "Zotero-API-Key": api_key,
        "Content-Type": "application/json"
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
}
    
    try:
        response = requests.post(url, headers=headers, json=[item], timeout=30)
        if response.status_code == 200:
            item_data = response.json()
            if 'successful' in item_data:
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
}
    
    try:
        response = requests.post(url, headers=headers, json=[item], timeout=30)
        if response.status_code == 200:
            item_data = response.json()
            if 'successful' in item_data:
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
The script prints DEFAULT_CONFIG, which includes credential-derived fields such as the Zotero API key and user-specific paths. If this helper is run in a shared terminal, CI log, agent trace, or support bundle, those secrets can be exposed and reused to modify the victim’s Zotero library or disclose local environment details.

Natural-Language Policy Violations

Low
Confidence
86% confidence
Finding
All user-facing instructions and examples in the file are presented only in Chinese. Under the policy, forcing a specific language without user opt-in can be a natural-language policy issue when no alternative language option or justification is provided.