Back to skill

Security audit

Zettel Link

Security checks for vulnerabilities and agentic risk

Overview

This note-search skill mostly does what it says, but it needs Review because remote provider mode can send private note text and keys off-device without strong disclosure or HTTPS enforcement.

Install only if you are comfortable with the skill indexing the selected Markdown directory and storing embeddings, previews, search results, and links under .embeddings. Prefer the default local Ollama provider for sensitive notes. If using OpenAI or Gemini, assume note text and queries are sent to that provider, keep keys limited, and verify provider URLs use HTTPS. Avoid the README's mutable install command unless you can pin the installer and source revision.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/embed.py:67
Finding

Remote embedding endpoints do not enforce HTTPS

Content
View full analysis
None: """Ensure the URL matches the expected official endpoint for the provider.""" parsed = urlparse(url) domain = parsed.netloc.split(':')[0] allowed = ALLOWED_DOMAINS.get(provider_name, []) if allowed and domain not in allowed: # If it's a known provider but unknown domain, check if it's explicitly allowed in config # For now, we raise a security warning and exit unless it's a local/custom case. print(f"⚠️ Security Warning: Provider '{provider_name}' is using an unofficial endpoint: {domain}") print(f" Official domains: {', '.join(allowed)}") print(" To bypass this, use a custom provider name in config or verify the URL.") sys.exit(1) ``` OpenAI subsequently sends the note text and API key to the validated URL: ```python url = provider["url"].rstrip("/") + "/embeddings" validate_url(url, "openai") payload = json.dumps({"model": model, "input": text}).encode() req = urllib.request.Request( url, data=payload, headers={ "Content-Type": "application/json", "Authorization": f"Bearer {api_key}", }, method="POST", ) with urllib.request.urlopen(req, timeout=60) as resp: data = json.loads(resp.read()) return data["data"][0]["embedding"] ``` Gemini uses the same validation mechanism before transmitting note text and its API key: ```python base_url = provider["url"].rstrip("/") url = f"{base_url}/v1beta/models/{model}:embedContent" validate_url(url, "gemini") payload = json.dumps({ "content": {"parts": [{"text": text}]}, }).encode() req = urllib.request.Request( url, data=payload, headers={ "Content-Type": "application/json", "x-goog-api-key": api_key, } ...[truncated 2512 chars]
Remediation
View remediation
None: parsed = urlparse(url) hostname = parsed.hostname if not hostname: raise ValueError("Provider URL must contain a valid hostname") if provider_name in {"openai", "gemini"} and parsed.scheme != "https": raise ValueError(f"{provider_name} endpoints must use HTTPS") if provider_name == "ollama": if parsed.scheme not in {"http", "https"}: raise ValueError("Ollama endpoint must use HTTP or HTTPS") if hostname not in {"localhost", "127.0.0.1", "::1"}: raise ValueError("Ollama endpoint must resolve to loopback") allowed = ALLOWED_DOMAINS.get(provider_name, []) if allowed and hostname not in allowed: raise ValueError("Provider hostname is not allowlisted") ``` 2. Reject URLs containing embedded credentials, unexpected fragments, or unsupported schemes. 3. Consider disabling automatic cross-origin redirects or revalidating every redirect target before sending sensitive headers and request bodies. 4. Keep plaintext HTTP limited to verified loopback Ollama endpoints. 5. Explicitly warn users before enabling a remote provider that note content and search queries will be disclosed to that provider. 6. Add automated tests covering HTTP rejection, malformed URLs, user-information syntax, IPv6 loopback, ports, and redirect behavior. ]]>

T08 · Insecure Dependencies

Warning
Location
README.md:17
Finding

Installation instructions execute an unpinned package and use a mutable source reference

Content
View full analysis
Remediation
View remediation
install ``` 2. Replace the mutable `main` reference with an immutable Git commit, signed release tag, or versioned release archive. 3. Publish and verify a cryptographic checksum for the installed artifact. 4. Prefer a package-lock or equivalent integrity-controlled dependency mechanism where supported. 5. Document the exact package version, repository commit, and expected checksum used for each Skill release. 6. Use signed releases and protected repository branches to reduce the risk of unauthorized upstream modifications. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (18)

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

The declared description focuses on maintaining and searching note embeddings, but the documented behavior also includes configuration management, environment inspection, and remote provider setup. This mismatch can mislead users or orchestrating agents into invoking the skill without realizing it may alter local config or prepare outbound data flows, which is especially risky for a notes vault that may contain sensitive content.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

The documentation recommends OpenAI and Gemini remote providers but does not clearly warn that note contents and user queries may be sent off-device to third-party services for embedding. In the context of a Zettelkasten or Obsidian vault, notes often contain private research, credentials, health information, or proprietary material, so silent transmission to external APIs creates a significant confidentiality risk.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/embed.py (reported line 109)May include surrounding context.

python
def get_api_key(env_var_name: str) -> str:
    """Try to get API key from environment, then from .env file."""
    # 1. Try environment
    key = os.environ.get(env_var_name, "")
    if key:

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/embed.py (reported line 115)May include surrounding context.

python
def get_api_key(env_var_name: str) -> str:
    """Try to get API key from environment, then from .env file."""
    # 1. Try environment
    key = os.environ.get(env_var_name, "")
    if key:

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/embed.py (reported line 109)May include surrounding context.

python
def get_api_key(env_var_name: str) -> str:
    """Try to get API key from environment, then from .env file."""
    # 1. Try environment
    key = os.environ.get(env_var_name, "")
    if key:

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/embed.py (reported line 118)May include surrounding context.

python
# 2. Try .env file in the skill root
    script_dir = Path(__file__).parent
    skill_root = script_dir.parent
    env_file = skill_root / ".env"
    
    if env_file.exists():
        try:

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding

npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding

npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill processes note contents with OpenAI and Gemini embedding providers, but the README does not clearly warn that note text may be transmitted to third-party services when those providers are selected. In the context of a Zettelkasten/Obsidian note corpus, this can expose highly sensitive personal, work, or credential-adjacent information through an easy-to-miss privacy boundary.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding

The skill describes and enables capabilities that read and write local files, inspect environment configuration, and optionally contact networked embedding providers, but it does not declare any explicit tool scope or permissions boundaries. In an agent setting, this increases the chance of unintended filesystem or network access because the agent has no machine-readable limits on what resources the skill is allowed to use.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The trigger condition says the skill should be used when the user wants to search, retrieve, or discover connections between notes, which is broad enough to match many ordinary note-related requests. Overbroad activation boundaries can cause an agent to run embedding or configuration workflows unnecessarily, leading to unexpected file writes, cache generation, or remote embedding requests.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The TODO explicitly plans support for remote embedding providers such as OpenAI and Gemini, which would require sending note contents or queries over the network. In a note-management skill handling potentially sensitive personal knowledge-base data, failing to specify user-facing disclosure, consent, and data-handling controls creates a real privacy and data-exfiltration risk.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/config.py (reported line 34)May include surrounding context.

python
},
    "openai": {
        "name": "openai",
        "url": "https://api.openai.com/v1",
        "api_key_env": "OPENAI_API_KEY",
        "default_model": "text-embedding-3-small",
    },

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The script sends note contents to remote embedding providers (OpenAI and Gemini) by design, but it does so without any explicit user-facing consent or warning at the point of transmission. In a note-taking/Zettelkasten context, notes often contain sensitive personal, business, or secret material, so silent upload to third-party APIs creates a meaningful privacy and data-exposure risk even if the provider endpoints are validated.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The module docstring and CLI description present this script as performing semantic search over embedded notes, which implies reading embeddings, computing similarity, and returning results. The code also persists query results to search_results.json, a state-modifying behavior not indicated by the description and not obviously required for note retrieval/search.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

The TODO explicitly calls for creating config/config.json, and later sections describe reading and writing embedding cache and link.json outputs. Because this markdown file documents behavior that changes files on disk, it should disclose that the skill creates or updates local files so users are not surprised by data modifications.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

The comment on L105 states that the results are output as JSON to stdout for programmatic use, but the implementation below constructs the JSON object and writes it to search_results.json instead. This is an active contradiction between the inline documentation and actual behavior.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
92% confidence
Finding

This code saves search output to search_results.json, which is a file write affecting user data on disk. While there is a post-action print statement confirming the save, there is no prior warning in the module docstring or CLI help that running the search will persist results to a file, so users may reasonably expect a read-only search operation.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.