Back to skill

Security audit

Kb Collector

Security checks for vulnerabilities and agentic risk

Overview

This skill mostly matches a knowledge-base collection workflow, but it can email local note metadata to a hard-coded external recipient and lacks enough user-controlled scoping for that sensitive action.

Review this before installing. Do not use the email features until the recipient is changed to an address you control and you have previewed what will be sent. Avoid running it on private vaults or with untrusted URLs unless URL validation, private temp files, and dependency pinning are added. Treat the cron example as high impact because it can repeatedly perform searches and send email in the background.

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)

other

Error
Location
scripts/digest.sh:6
Finding
Hard-Coded External Recipient Can Receive Private Vault Metadata<![CDATA[ ## Vulnerability Details **File Location**: `scripts/digest.sh:6-7, 41-74, 122-127` **Vulnerability Type**: Undisclosed data exfiltration through a hard-coded email recipient **Risk Level**: High ### Vulnerable Code ```bash TYPE="${1:-weekly}" VAULT="/Users/george/Documents/Georges/Knowledge" RECIPIENT="george@precaster.com.tw" ``` ```bash for file in "$VAULT"/*.md; do [ -f "$file" ] || continue CREATED=$(grep -m1 "^created:" "$file" 2>/dev/null | sed 's/created: *//' | cut -d'T' -f1) # Also check for date: field as fallback if [ -z "$CREATED" ]; then CREATED=$(grep -m1 "^date:" "$file" 2>/dev/null | sed 's/date: *//' | cut -d'T' -f1) fi [ -z "$CREATED" ] && continue if [[ "$CREATED" < "$SINCE" ]]; then continue fi TITLE=$(grep -m1 "^#" "$file" 2>/dev/null | sed 's/^#* *//') [ -z "$TITLE" ] && TITLE=$(grep -m1 "^# " "$file" 2>/dev/null | sed 's/^# //') [ -z "$TITLE" ] && TITLE="${file##*/}" TAGS=$(grep -m1 "^tags:" "$file" 2>/dev/null | sed 's/.*tags: *\[//' | sed 's/\]//' | tr ',' '\n' | tr -d ' ' | grep -v '^$') if [ -n "$TAGS" ]; then for tag in $TAGS; do echo "$tag|$TITLE|${file##*/}" done else echo "無標籤|$TITLE|${file##*/}" fi echo "$file" >> /tmp/digest_files.txt done | sort >> /tmp/digest_tags.txt ``` ```bash if [ -n "$SEND_EMAIL" ]; then echo "" echo "Sending email to $RECIPIENT via gog..." gog gmail send \ --to "$RECIPIENT" \ --subject "$SUBJECT" \ --body-file /tmp/digest_content.txt echo "Email sent!" fi ``` ### Technical Analysis The digest script scans the configured Obsidian vault and extracts note creation dates, titles, tags, filenames, note counts, and topic statistics. When the user supplies `--send`, the generated digest is transmitted through `gog gmail send` to the fixed address `george@precaster.com.tw`. The recipient is embedded directly in t ...[truncated 1511 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove the hard-coded recipient address. - Require the user to provide the destination through an explicit command-line option or a mandatory `RECIPIENT` environment variable. - Fail closed when no recipient has been configured. - Display the exact destination and data scope before transmission. - Require interactive confirmation unless a clearly documented non-interactive mode is enabled. - Minimize digest contents and allow users to exclude titles, tags, or filenames. - Document all outbound data transfers and recipient configuration in `SKILL.md`. - Consider generating the digest locally first and requiring a separate explicit action to send it. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/collect.py:75
Finding
Arbitrary URL Fetching Permits Server-Side Request Forgery<![CDATA[ ## Vulnerability Details **File Location**: `scripts/collect.py:75-81, 158-164` **Vulnerability Type**: Server-Side Request Forgery through unrestricted URL fetching **Risk Level**: Medium ### Vulnerable Code ```python def fetch_url(url): """Fetch URL content""" try: import requests from bs4 import BeautifulSoup headers = {'User-Agent': 'Mozilla/5.0'} r = requests.get(url, headers=headers, timeout=30) soup = BeautifulSoup(r.text, 'html.parser') ``` ```python elif cmd == "url": print("=== URL Mode ===") url = content title = url.split('/')[-1][:50] or "web-note" print(f"Fetching {url}...") text = fetch_url(url) ``` ### Technical Analysis The URL supplied on the command line is passed directly to `requests.get()` without validating the URL scheme, resolving and checking the destination address, or restricting access to internal networks. Python Requests follows redirects by default, but redirect destinations are not revalidated. Consequently, a caller can direct the Skill to loopback, private, link-local, or otherwise sensitive network addresses. Examples include local administrative interfaces, intranet services, and cloud instance metadata endpoints. If such a service returns textual data, the Skill parses it and stores up to the first 100 non-empty lines in an Obsidian note. The current implementation also lacks a response-size limit before parsing the response body, increasing resource-exhaustion exposure when a remote server returns a very large response. ### Attack Path 1. An attacker or untrusted caller supplies a URL resolving to an internal address, loopback service, or cloud metadata endpoint. 2. The user or automation runs `collect.py url <attacker-controlled-url>`. 3. `requests.get()` sends the request from the host running the Skill. 4. The target can return data that is inaccessible from the attacker's external network position. 5. The response is con ...[truncated 779 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Parse URLs with `urllib.parse` and allow only explicitly supported `http` and `https` schemes. - Resolve the hostname before connecting and reject loopback, private, link-local, multicast, unspecified, and reserved IP ranges. - Disable automatic redirects or revalidate the resolved destination after every redirect. - Protect against DNS rebinding by ensuring the validated address is the address used for the connection. - Consider an allowlist of approved domains for automated deployments. - Set strict connection and read timeouts. - Stream responses and enforce a maximum response size before parsing. - Avoid returning detailed network errors that could assist internal-network enumeration. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/digest.sh:41
Finding
Predictable Shared Temporary Files Enable Symlink Attacks and Cross-Run Data Collisions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/collect.py:35-43`; `scripts/collect.sh:43-47, 74`; `scripts/digest.sh:41-42, 76-116, 126, 130`; `scripts/nightly-research.sh:32, 46-58, 72, 87, 96, 101` **Vulnerability Type**: Unsafe use of predictable files in a shared temporary directory **Risk Level**: Medium ### Vulnerable Code ```python def download_youtube_audio(url): """Download YouTube audio""" output = "/tmp/youtube_audio" subprocess.run( ['yt-dlp', '-f', 'bestaudio[ext=m4a]', '--extract-audio', '--audio-format', 'm4a', '-o', f'{output}.%(ext)s', url], capture_output=True, timeout=120 ) return f"{output}.m4a" ``` ```bash yt-dlp -f "bestaudio[ext=m4a]" --extract-audio --audio-format m4a -o "/tmp/youtube_audio.%(ext)s" "$URL" echo "Transcribing with Whisper..." whisper /tmp/youtube_audio.m4a --model tiny --output_format txt --output_dir /tmp --language Chinese 2>/dev/null ``` ```bash rm -f /tmp/youtube_audio.m4a /tmp/youtube_audio.txt ``` ```bash # Extract tags and titles from markdown files > /tmp/digest_tags.txt > /tmp/digest_files.txt ``` ```bash echo "=== $TYPE Digest ===" > /tmp/digest_content.txt ``` ```bash gog gmail send \ --to "$RECIPIENT" \ --subject "$SUBJECT" \ --body-file /tmp/digest_content.txt ``` ```bash rm -f /tmp/digest_tags.txt /tmp/digest_content.txt /tmp/digest_files.txt ``` ```bash # Search each topic using Tavily > /tmp/research_results.txt ``` ```bash echo "### $topic" >> /tmp/research_results.txt echo "" >> /tmp/research_results.txt ``` ```bash $(cat /tmp/research_results.txt) ``` ```bash gog gmail send \ --to "$RECIPIENT" \ --subject "📡 AI 趨勢追蹤 $TODAY" \ --body-file /tmp/research_results.txt ``` ```bash rm -f /tmp/research_results.txt ``` ### Technical Analysis Several scripts use fixed names directly under the globally shared `/tmp` directory. Shell truncation redirections such as `> /tmp/digest_content.txt` and `> /tmp/research_r ...[truncated 1871 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - In shell scripts, set a restrictive file-creation mask with `umask 077`. - Create a private temporary directory using `mktemp -d`. - Store every temporary artifact under that unique directory. - Register an `EXIT`, `INT`, and `TERM` trap to remove the temporary directory safely. - Quote every temporary path. - Do not reuse static paths across processes or users. - In Python, use `tempfile.TemporaryDirectory()` or `tempfile.NamedTemporaryFile()`. - Pass unique output templates to `yt-dlp` and explicit unique output directories to transcription tools. - Avoid deleting shared fixed paths during cleanup. - Where possible, keep digest content in memory and pass it securely to downstream commands instead of storing it in a globally visible location. ]]>

T08 · Insecure Dependencies

Note
Location
SKILL.md:20
Finding
Unpinned and Incomplete Third-Party Dependency Configuration<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:20-25`; `scripts/collect.py:10` **Vulnerability Type**: Unpinned dependencies and an undeclared runtime dependency **Risk Level**: Low ### Vulnerable Code ```bash # Install dependencies pip install yt-dlp faster-whisper requests beautifulsoup4 # For AI summarization (optional) pip install openai anthropic ``` ```python import yfinance as yf ``` ### Technical Analysis The documented installation commands install mutable latest versions of third-party packages without version constraints, integrity hashes, or a locked Python dependency manifest. A future installation can therefore resolve materially different code from the versions originally reviewed. The executable also imports `yfinance`, although this package is absent from the installation instructions and is unused elsewhere in the script. Because imports execute before argument handling, the missing package can prevent the collector from starting. Users may respond by installing an arbitrary current package version outside a controlled dependency process. The empty Node.js `package-lock.json` does not lock any of the Python dependencies used by the project. No specific malicious package or compromised version was identified during this audit; the finding concerns preventable supply-chain exposure and non-reproducible installation. ### Attack Path 1. A user follows the documented `pip install` commands. 2. The package index resolves the latest available versions at installation time. 3. A future compromised release, unsafe transitive dependency, or unexpected incompatible update is downloaded. 4. Package code can execute during installation, import, or normal Skill operation with the user's privileges. 5. Separately, the undocumented `yfinance` import causes startup failure and may prompt an uncontrolled ad hoc installation. ### Impact Assessment Third-party Python packages execute with the privileges of the user running the Skill a ...[truncated 361 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove the unused `yfinance` import from `scripts/collect.py`. - Create a dedicated Python dependency manifest such as `requirements.txt` or `pyproject.toml`. - Pin direct dependencies to reviewed versions. - Use a lock file generated by a tool such as `pip-tools`, Poetry, or uv. - Include cryptographic hashes for packages where practical, for example with `pip install --require-hashes`. - Review and pin transitive dependencies. - Use a trusted package index and avoid uncontrolled extra indexes. - Add automated dependency vulnerability scanning and controlled update reviews. - Keep optional dependencies in a separate, clearly documented dependency group. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Output HandlingUnvalidated Output Injection, Cross-Context Output, Unbounded Output
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (27)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The documented behavior suggests outbound searches and email delivery that are not clearly disclosed in permissions, while also shifting from user-supplied collection to automated topic-based research. That is dangerous because it expands network and exfiltration behavior beyond the core user expectation of saving provided content, creating privacy and trust risks.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The documented behavior suggests outbound searches and email delivery that are not clearly disclosed in permissions, while also shifting from user-supplied collection to automated topic-based research. That is dangerous because it expands network and exfiltration behavior beyond the core user expectation of saving provided content, creating privacy and trust risks.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The documented behavior suggests outbound searches and email delivery that are not clearly disclosed in permissions, while also shifting from user-supplied collection to automated topic-based research. That is dangerous because it expands network and exfiltration behavior beyond the core user expectation of saving provided content, creating privacy and trust risks.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The documented behavior suggests outbound searches and email delivery that are not clearly disclosed in permissions, while also shifting from user-supplied collection to automated topic-based research. That is dangerous because it expands network and exfiltration behavior beyond the core user expectation of saving provided content, creating privacy and trust risks.

Unvalidated Output Injection

High
Category
Output Handling
Content
def download_youtube_audio(url):
    """Download YouTube audio"""
    output = "/tmp/youtube_audio"
    subprocess.run(
        ['yt-dlp', '-f', 'bestaudio[ext=m4a]', '--extract-audio', 
         '--audio-format', 'm4a', '-o', f'{output}.%(ext)s', url],
        capture_output=True, timeout=120
Confidence
95% confidence
Finding
The skill accepts an arbitrary user-supplied URL and passes it to yt-dlp, causing the host environment to perform outbound requests and process remote content. In an agent/skill setting this can be abused for SSRF-style internal network access, fetching from sensitive endpoints, or forced interaction with attacker-controlled content, especially because no domain allowlist or scheme validation is applied.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
exit 1
        fi
        
        rm -f /tmp/youtube_audio.m4a /tmp/youtube_audio.txt
        ;;
        
    url)
Confidence
95% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
echo "Email sent!"
fi

rm -f /tmp/digest_tags.txt /tmp/digest_content.txt /tmp/digest_files.txt
echo ""
echo "Done!"
Confidence
95% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
echo "Email sent!"
fi

rm -f /tmp/research_results.txt
echo ""
echo "Done!"
Confidence
95% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill advertises and demonstrates capabilities that imply filesystem access, shell execution, and network use, but it declares no permissions or allowed-tools scope. That omission is dangerous because users and host platforms cannot accurately constrain or review what the skill may access, increasing the chance of over-privileged execution and unintended data exposure.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
Advertising digest emails and research sends without warning that collected content may be transmitted externally creates a privacy vulnerability. Users may provide notes, URLs, or transcripts expecting local storage only, while the feature set implies possible sharing of sensitive content through third-party email or network services.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The nightly research section describes automated collection from multiple online sources and optional email sending, but provides no warning about privacy, network usage, rate limits, or system impact from scheduled execution. Because this can run unattended via cron, the lack of disclosure increases the chance of unexpected external communication and background resource consumption.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def get_video_title(url):
    """Get YouTube video title"""
    try:
        result = subprocess.run(
            ['yt-dlp', '--get-title', url],
            capture_output=True, text=True, timeout=30
        )
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def download_youtube_audio(url):
    """Download YouTube audio"""
    output = "/tmp/youtube_audio"
    subprocess.run(
        ['yt-dlp', '-f', 'bestaudio[ext=m4a]', '--extract-audio', 
         '--audio-format', 'm4a', '-o', f'{output}.%(ext)s', url],
        capture_output=True, timeout=120
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Natural-Language Policy Violations

Medium
Confidence
98% confidence
Finding
The code hard-codes Chinese transcription in both paths: `language="zh"` for faster-whisper and `--language Chinese` for the shell fallback. This imposes a specific language/locale behavior on all users without opt-in or any documented region-specific reason, which matches the language-policy violation criteria.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
return transcript
    except:
        # Fallback to shell whisper
        result = subprocess.run(
            ['whisper', audio_path, '--model', 'tiny', '--output_format', 'txt', 
             '--output_dir', '/tmp', '--language', 'Chinese'],
            capture_output=True, timeout=300
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Intent-Code Divergence

Medium
Confidence
97% confidence
Finding
The docstring says the function performs 'AI Summarize text using MiniMax', but the actual code simply returns the first `max_length` characters with an ellipsis. This is an active contradiction between documented intent and real behavior, not just missing detail.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
In YouTube mode, the script sends the provided URL to yt-dlp and then passes downloaded audio to Whisper for transcription. In URL mode, it fetches remote page content with web_fetch. Although there are progress messages, there is no user-facing warning that external network requests and content processing will occur, which matters because supplied content may contain private or sensitive material.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
The Whisper command hard-codes `--language Chinese`, which imposes a specific language/locale behavior regardless of the user's input or preferences. This is a natural-language policy issue because the skill does not offer opt-in, selection, or a documented reason for restricting transcription to Chinese.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
When run with --send, the script transmits a digest of locally collected knowledge notes to an external email address without any confirmation prompt, preview gate, or explicit data-sensitivity warning at the point of exfiltration. In a knowledge-base collection skill, the digest may contain private research topics, note titles, or sensitive metadata, so silent sending increases the risk of unintended disclosure.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
The code defaults the transcription language to "zh" and the main entrypoint calls the function without offering the user any language choice. This imposes a specific locale behavior across all runs, which matches the policy concern for forced language or locale without opt-in.

External Transmission

Medium
Category
Data Exfiltration
Content
echo "Searching: $topic..."
    
    # Call Tavily API
    result=$(curl -s "https://api.tavily.com/search" \
        -H "Content-Type: application/json" \
        -d "{
            \"api_key\": \"$TAVILY_API_KEY\",
Confidence
70% 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
echo "Searching: $topic..."
    
    # Call Tavily API
    result=$(curl -s "https://api.tavily.com/search" \
        -H "Content-Type: application/json" \
        -d "{
            \"api_key\": \"$TAVILY_API_KEY\",
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The script emits user-facing text in Chinese, including the no-results message, note title, and email subject/body context, but provides no opt-in or locale selection. This can violate language/locale policy because the skill forces a specific language for generated content rather than adapting to user preference or documenting a justified locale constraint.

Context-Inappropriate Capability

Medium
Confidence
82% confidence
Finding
The file header describes the script as "Nightly Research" and says it uses Tavily API for searching AI/LLM/tech trends, which suggests gathering research results. The additional capability to send outbound email via Gmail is a separate communication action and is not described in this file's own documentation, making it broader than the immediate research-collection purpose.

Context-Inappropriate Capability

Low
Confidence
94% confidence
Finding
The skill is described as collecting YouTube, URLs, and text into Obsidian with summarization/transcription. Importing `yfinance` introduces a finance-data capability that is not used anywhere in this file and is not justified by the stated knowledge-base collection purpose.

Static analysis

No suspicious patterns detected.