Back to skill

Security audit

Multimedia To Obsidian

Security checks for vulnerabilities and agentic risk

Overview

This skill has a plausible Obsidian import purpose, but it uploads document images to external AI services and has unsafe path and cleanup handling that users should review before installing.

Review this skill carefully before installing. Use it only on documents you are comfortable sending to the selected AI provider, avoid confidential or regulated material unless you have approval, keep MINIMAX_API_HOST at a trusted HTTPS provider, and do not pass category values containing slashes, absolute paths, or .. traversal. Prefer running it in an isolated environment with a dedicated test Obsidian vault and limited-scope API keys.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/media_to_obsidian.py:48
Finding

Configurable MiniMax endpoint can receive confidential images and API credentials

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/media_to_obsidian.py:54
Finding

MiniMax API key is exposed through the curl process argument list

Content
View full analysis
`. 4. During the request, a local user or monitoring process with sufficient process-inspection access reads the command line. 5. The observer extracts the bearer token and uses it to make unauthorized MiniMax API requests. ### Impact Assessment Successful exploitation discloses the MiniMax API credential. The attacker can exercise the per ...[truncated 334 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/media_to_obsidian.py:187
Finding

Unvalidated category path permits writes and recursive deletion outside the Obsidian vault

Content
View full analysis
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
SKILL.md:18
Finding

Third-party dependencies are installed without version or integrity controls

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (22)

Tainted flow: 'headers' from os.environ.get (line 101, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
97% confidence
Finding

This function sends base64-encoded image content and user document-derived data to OpenAI's external API, creating a clear confidentiality and data-governance risk. In a knowledge-base import tool, users may process sensitive training materials, notes, or internal documents, so silent off-device transmission is materially dangerous if not explicitly consented to and controlled.

Content

Scanner excerpt · scripts/media_to_obsidian.py (reported line 86)May include surrounding context.

python
"max_tokens": 1000
    }
    
    resp = requests.post("https://api.openai.com/v1/chat/completions", headers=headers, json=payload, timeout=60)
    data = resp.json()
    
    if "choices" in data and data["choices"]:

Tainted flow: 'headers' from os.environ.get (line 101, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
97% confidence
Finding

This transmits image contents to Anthropic's external API, with the same confidentiality and compliance concerns as the OpenAI path. Because the skill is framed as an Obsidian import utility rather than a cloud-upload tool, the hidden transfer of potentially sensitive document content increases risk in context.

Content

Scanner excerpt · scripts/media_to_obsidian.py (reported line 116)May include surrounding context.

python
]}]
    }
    
    resp = requests.post("https://api.anthropic.com/v1/messages", headers=headers, json=payload, timeout=60)
    data = resp.json()
    
    if "content" in data:

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill advertises and operationally requires shell execution, filesystem read/write, environment variable access, and outbound network calls, but it does not declare any tool scope or permission boundaries. This is dangerous because users and execution frameworks cannot accurately assess or constrain the skill’s privileges, increasing the chance of overbroad access to local files, secrets, and external services during import operations.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill states that it will use multimodal models to understand document and image content, but it does not clearly warn users that file contents may be transmitted to third-party AI providers such as MiniMax, OpenAI, or Anthropic. This is dangerous because users may process confidential training materials, notes, or images without informed consent, causing unintended data disclosure to external processors.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill's core behavior includes uploading extracted document/image content to third-party model APIs, but this is not plainly conveyed by the stated purpose of merely importing content into Obsidian. That mismatch can mislead users into exposing confidential material without understanding that cloud services will receive it.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The code transmits image/document content to external model APIs without any obvious user-facing warning, confirmation, or privacy notice. In this context, imported media may contain proprietary, regulated, or personal information, making undisclosed transmission a significant security and trust issue.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The default prompt hard-codes Chinese instructions for the model output, which imposes a specific language without any user opt-in or alternative locale selection. This is a natural-language policy issue because the skill behavior is language-constrained by default rather than user-selectable.

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

python
"-d", json.dumps(payload)
    ]
    
    result = subprocess.run(cmd, capture_output=True, text=True, timeout=35)
    data = json.loads(result.stdout)
    
    if data.get("base_resp", {}).get("status_code") == 0:

Tainted flow: 'cmd' from os.environ.get (line 52, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/media_to_obsidian.py (reported line 60)May include surrounding context.

python
"-d", json.dumps(payload)
    ]
    
    result = subprocess.run(cmd, capture_output=True, text=True, timeout=35)
    data = json.loads(result.stdout)
    
    if data.get("base_resp", {}).get("status_code") == 0:

External Transmission

Medium
Category
Data Exfiltration
Confidence
94% confidence
Finding

The hardcoded OpenAI endpoint confirms outbound transfer to a third-party service. In combination with arbitrary multimedia ingestion, this can leak sensitive images and document content outside the local environment.

Content

Scanner excerpt · scripts/media_to_obsidian.py (reported line 86)May include surrounding context.

python
"max_tokens": 1000
    }
    
    resp = requests.post("https://api.openai.com/v1/chat/completions", headers=headers, json=payload, timeout=60)
    data = resp.json()
    
    if "choices" in data and data["choices"]:

External Transmission

Medium
Category
Data Exfiltration
Confidence
94% confidence
Finding

The hardcoded OpenAI endpoint confirms outbound transfer to a third-party service. In combination with arbitrary multimedia ingestion, this can leak sensitive images and document content outside the local environment.

Content

Scanner excerpt · scripts/media_to_obsidian.py (reported line 86)May include surrounding context.

python
"max_tokens": 1000
    }
    
    resp = requests.post("https://api.openai.com/v1/chat/completions", headers=headers, json=payload, timeout=60)
    data = resp.json()
    
    if "choices" in data and data["choices"]:

External Transmission

Medium
Category
Data Exfiltration
Confidence
94% confidence
Finding

The hardcoded OpenAI endpoint confirms outbound transfer to a third-party service. In combination with arbitrary multimedia ingestion, this can leak sensitive images and document content outside the local environment.

Content

Scanner excerpt · scripts/media_to_obsidian.py (reported line 86)May include surrounding context.

python
"max_tokens": 1000
    }
    
    resp = requests.post("https://api.openai.com/v1/chat/completions", headers=headers, json=payload, timeout=60)
    data = resp.json()
    
    if "choices" in data and data["choices"]:

External Transmission

Medium
Category
Data Exfiltration
Confidence
94% confidence
Finding

The hardcoded Anthropic endpoint likewise confirms third-party transfer of processed media. For a knowledge import tool, such hidden egress is a real security concern, especially in enterprise or regulated environments.

Content

Scanner excerpt · scripts/media_to_obsidian.py (reported line 116)May include surrounding context.

python
]}]
    }
    
    resp = requests.post("https://api.anthropic.com/v1/messages", headers=headers, json=payload, timeout=60)
    data = resp.json()
    
    if "content" in data:

External Transmission

Medium
Category
Data Exfiltration
Confidence
94% confidence
Finding

The hardcoded Anthropic endpoint likewise confirms third-party transfer of processed media. For a knowledge import tool, such hidden egress is a real security concern, especially in enterprise or regulated environments.

Content

Scanner excerpt · scripts/media_to_obsidian.py (reported line 116)May include surrounding context.

python
]}]
    }
    
    resp = requests.post("https://api.anthropic.com/v1/messages", headers=headers, json=payload, timeout=60)
    data = resp.json()
    
    if "content" in data:

External Transmission

Medium
Category
Data Exfiltration
Confidence
94% confidence
Finding

The hardcoded Anthropic endpoint likewise confirms third-party transfer of processed media. For a knowledge import tool, such hidden egress is a real security concern, especially in enterprise or regulated environments.

Content

Scanner excerpt · scripts/media_to_obsidian.py (reported line 116)May include surrounding context.

python
]}]
    }
    
    resp = requests.post("https://api.anthropic.com/v1/messages", headers=headers, json=payload, timeout=60)
    data = resp.json()
    
    if "content" in data:

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

python
def extract_images_from_docx(docx_path, output_dir):
    """从 DOCX 提取图片"""
    os.makedirs(output_dir, exist_ok=True)
    subprocess.run([
        "pandoc", docx_path, "-t", "markdown",
        "--extract-media", output_dir, "-o", "/dev/null"
    ], capture_output=True)

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

python
# 使用 pdftoppm 转换
    base_name = os.path.join(output_dir, "page")
    subprocess.run([
        "pdftoppm", "-png", "-r", "150", pdf_path, base_name
    ], capture_output=True)

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

python
os.makedirs(output_dir, exist_ok=True)
    
    # 使用 pandoc 转换为图片
    subprocess.run([
        "pandoc", ppt_path, "-t", "slide.html", "-o", "/dev/null"
    ], capture_output=True)

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

python
# 简单方案:转换为 PDF 再提取
    pdf_path = os.path.join(output_dir, "temp.pdf")
    subprocess.run([
        "soffice", "--headless", "--convert-to", "pdf", ppt_path,
        "--outdir", output_dir
    ], capture_output=True)

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
81% confidence
Finding

The code recursively deletes a path derived from user-controlled filenames and category/output selections using rm -rf. Even without shell injection, unsafe path construction can cause unintended deletion if temp_dir resolves outside the intended workspace via symlinks, path confusion, or crafted names in a hostile filesystem context.

Content

Scanner excerpt · scripts/media_to_obsidian.py (reported line 245)May include surrounding context.

python
# 清理临时目录
    if os.path.exists(temp_dir):
        subprocess.run(["rm", "-rf", temp_dir], capture_output=True)
    
    print(f"  -> 已保存: {output_md}")
    return len(images)

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
87% confidence
Finding

The script reads API keys from environment variables for multiple providers, which is access to sensitive credentials under this rule. While the code uses the variables as intended, there is no user-facing notice in comments, CLI output, or documentation in this file explaining that credentials are consumed.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The function docstring and inline comment state that PPT pages are extracted as images, and another comment says pandoc is used to convert to images. In reality, the pandoc call writes slide.html to /dev/null, the python-pptx branch does nothing, and the fallback checks for a temp.pdf path that likely does not match LibreOffice's output naming, so the implementation may return no images at all.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.