Back to skill

Security audit

Obsidian Wiki

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly aligned with maintaining an Obsidian wiki, but it has review-worthy risks around unpinned runtime package execution and imperfect vault path containment.

Review before installing. Use an explicit vault path, keep the vault under version control, avoid --fix until you have backups, and pin or preinstall markdownlint-cli2 instead of allowing automatic npx downloads. Do not pass untrusted file paths to wiki-manifest.sh mark until canonical path containment is fixed.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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)

T08 · Insecure Dependencies

Warning
Location
scripts/wiki-lint.sh:307
Finding

Unpinned Package Download and Execution Through npx

Content
View full analysis
/dev/null 2>&1; then NPX_BIN="npx" elif [ -x "${VOLTA_HOME:-$HOME/.volta}/bin/npx" ]; then NPX_BIN="${VOLTA_HOME:-$HOME/.volta}/bin/npx" elif [ -x "${NVM_DIR:-$HOME/.nvm}/current/bin/npx" ]; then NPX_BIN="${NVM_DIR:-$HOME/.nvm}/current/bin/npx" elif [ -d "${NVM_DIR:-$HOME/.nvm}/versions/node" ]; then _nvm_latest=$(/bin/ls -1d "${NVM_DIR:-$HOME/.nvm}/versions/node/"v* 2>/dev/null | sed 's/.*\/v//' | sort -t. -k1,1n -k2,2n -k3,3n | tail -1) _nvm_latest="${NVM_DIR:-$HOME/.nvm}/versions/node/v$_nvm_latest" [ -x "$_nvm_latest/bin/npx" ] && NPX_BIN="$_nvm_latest/bin/npx" elif [ -x "$HOME/.nix-profile/bin/npx" ]; then NPX_BIN="$HOME/.nix-profile/bin/npx" fi if [ -n "$NPX_BIN" ] && [ -f "$MDL_CONFIG" ]; then _mdl_opts=() $FIX_MODE && _mdl_opts+=(--fix) _mdl_tmp=$(mktemp) find "$WIKI" -type f -name '*.md' ! -path "$WIKI/index.md" ! -path "$WIKI/log.md" -print0 2>/dev/null | xargs -0 "$NPX_BIN" --yes markdownlint-cli2 "${_mdl_opts[@]}" --config "$MDL_CONFIG" > "$_mdl_tmp" 2>&1 ``` ### Technical Analysis The lint workflow invokes `npx --yes markdownlint-cli2` without specifying an exact package version, using a committed lockfile, or verifying package integrity. If the package is not already available locally, `npx` can retrieve the current registry version and immediately execute it without interactive confirmation. Consequently, the code that executes during linting is not fully represented by the audited project. Its effective behavior may change when the registry package is updated. A compromised package release, registry account, package resolution path, or upstream dependency could therefore introduce arbitrary code ...[truncated 1283 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
``` 2. The attacker selects an existing readable file with a supported extension outside the vault, such as `/tmp/secret.md`. 3. The attacker supplies a traversal path such as: ```text raw/../../../tmp/secret.md ``` with the necessary number of parent traversals for the vault location. 4. `[ -f "$VAULT/$ARG" ]` follows the traversal and confirms that the external file exists. 5. The unnormalized string still begins with `raw/`, so the lexical containment check accepts it. 6. `file_hash` and `wc` ...[truncated 1028 chars]:125
Finding

Path Traversal in Manifest Mark Command Permits Out-of-Scope File Reads

Content
View full analysis
mark " >&2; exit 2; } # Canonicalize path if [ -f "$VAULT/$ARG" ]; then abs_file="$VAULT/$ARG" elif [ -f "$ARG" ]; then # Ensure it's absolute case "$ARG" in /*) abs_file="$ARG" ;; *) abs_file="$(cd "$(dirname "$ARG")" && pwd)/$(basename "$ARG")" ;; esac else echo "Error: file not found: $ARG" >&2; exit 1 fi # Reject symlinks (find_raw uses -type f which excludes them) if [ -L "$abs_file" ]; then echo "Error: symlinks are not supported (find_raw excludes them): $ARG" >&2; exit 1 fi relpath="${abs_file#"$VAULT"/}" # Enforce that file is under raw/ case "$relpath" in raw/*) ;; *) echo "Error: file must be under raw/: $relpath" >&2; exit 1 ;; esac # Enforce supported file type (case-insensitive, must match find_raw extensions) _ext_lower=$(printf '%s' "${relpath##*.}" | tr '[:upper:]' '[:lower:]') case "$_ext_lower" in md|pdf|txt|epub|html) ;; *) echo "Error: unsupported file type .${relpath##*.} (supported: .md, .pdf, .txt, .epub, .html, case-insensitive)" >&2; exit 1 ;; esac current_hash=$(file_hash "$abs_file") file_size=$(wc -c < "$abs_file" | tr -d ' ') ``` ### Technical Analysis The code comments claim to canonicalize the supplied file path, but the resulting `abs_file` is not normalized with `realpath`, `readlink -f`, or an equivalent canonical-path operation. The containment check is lexical: ```bash relpath="${abs_file#"$VAULT"/}" case "$relpath" in raw/*) ;; ``` A supplied path such as `raw/../../../tmp/secret.md` retains the `raw/` prefix and therefore passes this check, even though filesystem re ...[truncated 2293 chars]
Remediation
View remediation
Vulnerability Patterns
  • 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
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (15)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description centers on maintaining a compiled wiki inside an Obsidian vault. This script does not interact with an Obsidian vault structure (raw/, wiki/), compile wiki pages, generate indexes, lint links, maintain schema files, or answer questions. Its primary purpose is materially different: extracting the first 12 pages of PDFs as plain text files. While PDF extraction could conceivably support a source-ingestion pipeline, this code chunk by itself only performs raw text extraction and does not implement the described wiki-management behavior. Therefore the description does not accurately represent the supplied code chunk.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- `bash` — required by all scripts; `python3` — required by `wiki-lint.sh`, `wiki-manifest.sh`, and all Python scripts

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- `bash` — required by all scripts; `python3` — required by `wiki-lint.sh`, `wiki-manifest.sh`, and all Python scripts

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- `bash` — required by all scripts; `python3` — required by `wiki-lint.sh`, `wiki-manifest.sh`, and all Python scripts

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- `bash` — required by all scripts; `python3` — required by `wiki-lint.sh`, `wiki-manifest.sh`, and all Python scripts

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- `bash` — required by all scripts; `python3` — required by `wiki-lint.sh`, `wiki-manifest.sh`, and all Python scripts

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
`wiki-lint-links.py`, `wiki-crosslink.py`, and `wiki-graph.py`.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
`wiki-lint-links.py`, `wiki-crosslink.py`, and `wiki-graph.py`.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding

The skill instructs the agent to read and write extensively within a user-resolved Obsidian vault, but it does not declare any explicit tool scope, permissions, or path constraints. That makes the effective trust boundary ambiguous: an agent may infer broad filesystem authority and perform writes to whatever path is placed in $VAULT, increasing the chance of unintended data modification or exfiltration from local files referenced during ingest.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

When --fix is supplied, the script enables in-place rewrites through wiki-lint-links.py without any interactive confirmation, dry-run safeguard, or backup. In a workflow where the vault path may point to valuable notes or where an agent invokes the script automatically, this can cause unintended bulk content modification and integrity loss.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

In fix mode the script runs markdownlint-cli2 --fix across all markdown files under the wiki, performing bulk in-place rewrites without confirmation. Because this skill operates on a persistent knowledge base, accidental invocation or over-broad targeting can silently alter many files and create hard-to-review content changes.

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.

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
89% confidence
Finding

With --fix, the script passes --fix to wiki-crosslink.py, which can auto-link mentions by editing wiki pages in place with no confirmation step. In this skill context, automated semantic edits are especially risky because incorrect links can propagate misinformation or damage the structure of the compiled wiki at scale.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
79% confidence
Finding

This code rewrites markdown files by creating a temporary file and replacing the original when --fix is enabled. Although the module docstring and help text mention 'Apply format fixes', there is no direct confirmation prompt or user-facing warning immediately before the destructive file-write operation, which reduces visibility for a safety-relevant action.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.