Back to skill

Security audit

Obsidian Sync KB

Security checks for vulnerabilities and agentic risk

Overview

This appears to be a real Obsidian knowledge-base tool, but it needs review because it sends note-derived data to the web by default and can fetch arbitrary URLs found in notes.

Review this skill before installing if your Obsidian vault contains private, client, work, or internal research notes. Use --disable-network or set research.enable_network to false unless you intentionally want the tool to contact note-linked sites and DuckDuckGo, and avoid running network enrichment on untrusted or sensitive synced notes.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/kb_tool.py:1460
Finding

Server-Side Request Forgery Through Unrestricted Note-Controlled URL Fetching

Content
View full analysis
Dict[str, Any]: if not config.enable_network: return {"status": "failed", "url": url, "text": "", "error": "network disabled"} request = urllib.request.Request( url, headers={ "User-Agent": DEFAULT_USER_AGENT, "Accept": "text/html,application/xhtml+xml,text/plain,application/json;q=0.8,*/*;q=0.5", }, ) try: with urllib.request.urlopen(request, timeout=config.network_timeout) as response: raw = response.read(250000) ``` HTTP URLs extracted from notes become source candidates without destination validation: ```python def source_candidates_for_note(note: NormalizedNote, vault_root: pathlib.Path) -> List[Dict[str, str]]: candidates: List[Dict[str, str]] = [] seen = set() for link in dedupe_keep_order(([note.original_url] if note.original_url else []) + note.source_links): if not link or link in seen: continue seen.add(link) if link.startswith("obsidian://"): resolved = resolve_obsidian_uri_to_path(link, vault_root) if resolved: candidates.append({"kind": "obsidian_note", "value": str(resolved.resolve()), "raw": link}) else: candidates.append({"kind": "obsidian_note", "value": "", "raw": link}) elif link.startswith("http"): kind = "source_url" if link == note.original_url else "embedded_url" candidates.append({"kind": kind, "value": link, "raw": link}) return candidates ``` The resulting candidate is fetched ...[truncated 3881 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/kb_tool.py:1548
Finding

Private Note Content Can Be Disclosed to a Public Search Provider by Default

Content
View full analysis
List[str]: if not config.enable_network or not query.strip(): return [] final_query = normalize_whitespace(query) if preferred_domain: final_query = f"{final_query} site:{preferred_domain}" url = "https://duckduckgo.com/html/?" + urllib.parse.urlencode({"q": final_query}) result = fetch_web_document(url, config) ``` The search query may be derived directly from the note title, summary, or body: ```python def build_search_query(note: NormalizedNote) -> str: title = normalize_whitespace(strip_markdown_links(note.title)) normalized_title = re.sub(r"[^a-zA-Z0-9\u4e00-\u9fff]+", "", title).lower() generic = {"docs", "docs2", "消息", "内容创作", "message", "opencla", "claudecod"} if normalized_title and normalized_title not in generic and len(normalized_title) >= 5: return title if len(note.summary) >= 10: return note.summary[:80] if len(note.clean_text) >= 20: return build_summary(note.clean_text, max_chars=80) return "" ``` The query is sent when enrichment has insufficient local text: ```python combined_text = combine_texts_for_summary(note, evidence) if config.enable_network and len(combined_text) < config.research_min_chars: search_query = build_search_query(note) if search_query: for url in search_public_sources(search_query, note.source_domain, config): ``` Network access is enabled by default in the distributed configur ...[truncated 3573 chars]
Remediation
View remediation
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (19)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 25)May include surrounding context.

md
python3 scripts/kb_tool.py build-index

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 26)May include surrounding context.

md
python3 scripts/kb_tool.py build-index

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 27)May include surrounding context.

md
python3 scripts/kb_tool.py build-index

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 33)May include surrounding context.

md
python3 scripts/kb_tool.py build-index

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 34)May include surrounding context.

md
python3 scripts/kb_tool.py build-index

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 40)May include surrounding context.

md
python3 scripts/kb_tool.py build-index

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 41)May include surrounding context.

md
python3 scripts/kb_tool.py build-index

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 42)May include surrounding context.

md
python3 scripts/kb_tool.py build-index

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill is described as a local Obsidian knowledge-base builder, but the code performs outbound HTTP fetching and search-engine based enrichment. That expands trust boundaries significantly: local note titles, summaries, URLs, and derived query text can be transmitted to third parties, which can leak sensitive research content and violate user expectations about a purely local indexing tool.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill invokes local scripts that read environment variables, read/write files, and may use network access, but it does not declare any explicit tool scope or permission boundaries. This can lead to overbroad agent execution where the agent may access vault contents or external resources without clear least-privilege constraints, increasing the risk of unintended data exposure or unsafe operations.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The behavior section mandates that responses return 简要结论, 相关文章, 关键摘录, and 引用来源, which imposes a specific language/locale format. The file does not indicate this is optional, user-selected, or justified as a region-specific requirement, so it conflicts with the language/locale policy criteria.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The natural-language default prompt includes Chinese text ('笔记同步助手') as part of the required instruction, which imposes a specific language/locale behavior. The file does not indicate user opt-in, alternative language support, or a documented region-specific reason for this constraint.

Content

No source excerpt is available for this finding.

Context Window Stuffing

Medium
Category
Memory Poisoning
Confidence
80% confidence
Finding

Skill attempts to fill the context window with filler content, displacing legitimate instructions and safety constraints. This can degrade agent performance or bypass safety boundaries.

Content

Scanner excerpt · scripts/kb_tool.py (reported line 1256)May include surrounding context.

python
clean_text, image_refs_json, source_links_json, auto_topics_json, summary, quality_score,
                theme_candidate, change_summary, content_hash, last_seen_at, last_enriched_at, retry_after,
                obsidian_path, raw_text_length
            ) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
            """,
            [
                (

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The code submits derived search queries to DuckDuckGo for public source discovery, even though the stated purpose is structuring synced Obsidian notes. Those queries are built from note titles, summaries, or cleaned text, so confidential note content may be exposed to an external search provider without an obvious necessity or informed consent.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

Network fetching and public search use note-derived content to form outbound requests, but this file provides no user-facing warning, consent flow, or audit disclosure. In a knowledge-base tool processing personal or organizational notes, silent transmission of derived content to external services creates a privacy and data-governance risk even if the request body is only a search query.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

CLI descriptions, generated markdown sections, status messages, and topic labels are consistently emitted in Chinese throughout the file. Because the skill does not provide language or locale selection, this is a natural-language policy concern under the locale-choice rule.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
86% confidence
Finding

The configuration uses Chinese directory names in multiple path values, which imposes a specific language/locale choice in a natural-language-facing part of the skill. There is no indication in this file that the locale is optional, user-selectable, or justified as region-specific.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
75% confidence
Finding

The comment says fetch_web_document strips HTML tags, so a separate raw download is needed, but immediately above the code stores result.get("text", "") into raw_html, which is not raw HTML at all. This is an intent/documentation contradiction that can mislead maintainers about what data is available at this point and why the second network request exists.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The script hard-codes Chinese default folder names in user-facing CLI arguments, which imposes a specific language/locale convention on all users. There is no natural-language option or opt-in that lets users choose localized defaults, so this may violate language/locale policy requirements.

Content

No source excerpt is available for this finding.