Back to skill

Security audit

Auto Research

Security checks for vulnerabilities and agentic risk

Overview

This research skill is mostly purpose-aligned, but it has under-disclosed credential use, external data transfer, and unsafe helper execution behavior that users should review before installing.

Review and modify this skill before installation. Remove the built-in Brave token, require explicit per-skill API keys, disable the main-agent auth-profile fallback, avoid running a mutable helper outside the skill directory, use private cache paths, and require TLS/authenticated endpoints for Redis and Qdrant. Do not use it for confidential research topics unless these data-handling paths are understood and controlled.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (7)

T09 · Insecure Skill Coding Practices

Error
Location
research.sh:12
Finding
Hardcoded Brave Search API Credential<![CDATA[ ## Vulnerability Details **File Location**: `research.sh:12` **Vulnerability Type**: Hardcoded API credential **Risk Level**: High ### Vulnerable Code ```bash BRAVE_API_KEY="${BRAVE_API_KEY:-BSAfZrm_28TmR5FM9FhMCrTA1A3zS2n}" ``` ### Technical Analysis The script embeds a plausible Brave Search subscription token and uses it whenever `BRAVE_API_KEY` is not explicitly supplied. Secrets committed to source code must be considered compromised because every recipient of the package can extract and reuse them. The environment-variable override does not mitigate the exposure: the embedded token remains present in every distributed copy and in source-control history. ### Attack Path 1. An attacker obtains or downloads the Skill package. 2. The attacker reads `research.sh` without needing to execute it. 3. The attacker extracts the embedded subscription token. 4. The attacker submits requests directly to the Brave Search API using that token. 5. Requests consume the credential owner's quota and may create billing or service-availability consequences. ### Impact Assessment An attacker can obtain unauthorized access to the Brave Search subscription represented by the credential. The practical scope includes unauthorized API calls, quota exhaustion, billing impact, rate-limit exhaustion, and possible suspension of the associated service account. This finding does not establish access to the host system itself. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions 1. Revoke and rotate the exposed Brave API token immediately. 2. Remove the default credential from the source code and repository history. 3. Require `BRAVE_API_KEY` to be supplied through an approved secret manager or protected environment injection. 4. Fail closed with a clear error if the credential is absent: ```bash : "${BRAVE_API_KEY:?BRAVE_API_KEY must be supplied through secure configuration}" ``` 5. Add automated secret scanning to pre-commit hooks and CI. 6. Restrict replacement credentials by quota, permitted API, and environment where the provider supports those controls. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
vectorize.sh:31
Finding
Undisclosed Access to the Main Agent Authentication Profile<![CDATA[ ## Vulnerability Details **File Location**: `vectorize.sh:31-42` **Vulnerability Type**: Automatic extraction and export of a credential from another Agent profile **Risk Level**: High ### Vulnerable Code ```bash OPENAI_API_KEY="${OPENAI_API_KEY:-}" if [[ -z "$OPENAI_API_KEY" ]]; then # Try to load from auth profiles AUTH_FILE="$HOME/.openclaw/agents/main/agent/auth-profiles.json" if [[ -f "$AUTH_FILE" ]]; then OPENAI_API_KEY=$(jq -r '.["openai:default"].apiKey // empty' "$AUTH_FILE" 2>/dev/null || echo "") fi fi if [[ -z "$OPENAI_API_KEY" ]]; then echo "Warning: OPENAI_API_KEY not found. Vectorization requires API key." >&2 exit 1 fi export OPENAI_API_KEY ``` ### Technical Analysis When no explicit key is supplied, the Skill reads `$HOME/.openclaw/agents/main/agent/auth-profiles.json` and extracts the main Agent's OpenAI API key. This crosses a credential boundary beyond the Skill's explicit inputs and is not disclosed in the user-facing documentation. The extracted secret is exported into the environment and inherited by the subsequently executed Python process. This increases exposure because the child program is located at a mutable external path rather than in the reviewed package. ### Attack Path 1. A user invokes vectorization without defining `OPENAI_API_KEY`. 2. The script locates the main Agent authentication profile. 3. It extracts the `openai:default` API key with `jq`. 4. It exports the key into the process environment. 5. The script executes the ingestion helper, which inherits the credential. 6. If that external helper has been replaced or modified, it can read and disclose the inherited key. ### Impact Assessment The Skill gains use of the main Agent's OpenAI credential without explicit per-run authorization. A malicious or compromised child process could obtain the key and exercise the API permissions and quota associated with it. Consequences may include unauthorized API usage, billing impa ...[truncated 161 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove all automatic access to another Agent's authentication profile. 2. Require the caller to inject a dedicated, least-privileged `OPENAI_API_KEY`. 3. Obtain explicit user consent before using any credential that was not directly supplied for this Skill. 4. Use a provider key dedicated to embeddings, with restrictive quota and project permissions. 5. Avoid globally exporting the secret where possible; pass it only to a verified child process. 6. Ensure child code is bundled with the Skill and integrity-checked before execution. 7. Document the credential requirements and external service usage in `SKILL.md`. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
vectorize.sh:14
Finding
Creation and Execution of a Mutable Helper Outside the Skill Directory<![CDATA[ ## Vulnerability Details **File Location**: `vectorize.sh:14-15, 45-49, 235-240, 288` **Vulnerability Type**: Unsafe persistent helper installation and execution **Risk Level**: High ### Vulnerable Code ```bash QDRANT_URL="${QDRANT_URL:-http://10.0.0.120:6333}" COLLECTION="web_research" INGEST_TOOL="/Users/gregborden/.openclaw/workspace/tools/research-ingest.py" ``` ```bash # Check if research-ingest.py exists, create if needed if [[ ! -f "$INGEST_TOOL" ]]; then mkdir -p "$(dirname "$INGEST_TOOL")" cat > "$INGEST_TOOL" << 'INGEST_EOF' ``` ```bash if __name__ == "__main__": main() INGEST_EOF chmod +x "$INGEST_TOOL" fi ``` ```bash if python3 "$INGEST_TOOL" "$RESEARCH_FILE" "$TOPIC" "$SOURCES"; then ``` ### Technical Analysis The Skill creates an executable Python helper at a hardcoded path outside the audited project and leaves it in place after execution. On later runs, the script only checks whether a regular file exists; it does not verify ownership, permissions, origin, content hash, or signature. Because the helper is skipped when it already exists, any code pre-positioned at that location is trusted and executed. The process also inherits the exported OpenAI API key. This creates a local code-execution and credential-disclosure boundary around an unauthenticated external file. Although the helper persists on disk, the reviewed code does not register a startup service, scheduled task, login hook, or automatic cross-session execution. The primary classification is therefore insecure coding rather than system persistence. ### Attack Path 1. An attacker with write access to the fixed workspace path creates or replaces `research-ingest.py`. 2. The user invokes `vectorize.sh`. 3. The script finds that the helper already exists and does not recreate or validate it. 4. The script obtains and exports an OpenAI API key. 5. `python3` executes the attacker-controlled helper with the user's privileges. 6. The helper can read t ...[truncated 702 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Bundle `research-ingest.py` inside the reviewed Skill package. 2. Resolve the helper relative to `SCRIPT_DIR`, not a hardcoded user-specific path. 3. Refuse to execute helpers that are writable by untrusted users. 4. Verify the helper against a pinned cryptographic digest or package signature before execution. 5. Do not silently reuse an existing external executable. 6. Avoid exporting credentials to mutable child processes. 7. If runtime generation is unavoidable, create the file in a private directory with mode `0700`, create it atomically, verify ownership, and remove it after use. 8. Document all filesystem modifications and obtain consent before writing outside the Skill directory. ]]>

other

Warning
Location
vectorize.sh:66
Finding
Undisclosed Transmission of Research Content to OpenAI<![CDATA[ ## Vulnerability Details **File Location**: `vectorize.sh:66-72, 165-178` **Vulnerability Type**: Undisclosed external data transmission **Risk Level**: Medium ### Vulnerable Code The following generated Python code submits document chunks to OpenAI: ```python def get_embedding(text: str, client: OpenAI) -> List[float]: """Generate embedding using OpenAI API.""" response = client.embeddings.create( model="text-embedding-3-small", input=text[:8000] # Limit input size ) return response.data[0].embedding ``` ```python chunks = chunk_text(clean_content) print(f"Processing {len(chunks)} chunks for topic: {topic}") points = [] for i, chunk in enumerate(chunks): if len(chunk.strip()) < 50: continue chunk_id = f"{doc_id}-{i:04d}" embedding = get_embedding(chunk, client) ``` ### Technical Analysis The vectorization process reads the generated research document, splits it into chunks, and submits each qualifying chunk to the OpenAI embeddings API. User-facing documentation describes vector storage in Qdrant but does not clearly disclose that the briefing content is first transferred to OpenAI. Research documents can contain sensitive topics, user-entered queries, summaries, citations, internal context, or other generated content. Sending those fields to an external processor without clear disclosure prevents users from making an informed data-handling decision. ### Attack Path 1. A user researches a confidential or sensitive topic. 2. `research.sh` creates a Markdown briefing in the configured vault. 3. `vectorize.sh` reads the complete briefing. 4. The generated ingestion program splits the content into chunks. 5. Every chunk of at least 50 characters is sent to OpenAI for embedding. 6. The user may be unaware that document text leaves the local environment. ### Impact Assessment The externally transmitted scope consists of up to 8,000 characters from each research chunk sent for embed ...[truncated 271 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Clearly disclose in `SKILL.md` that document content is sent to OpenAI. 2. Identify the exact transmitted fields, purpose, retention implications, and destination. 3. Require explicit opt-in before enabling external embedding. 4. Provide a local embedding backend for confidential deployments. 5. Add configurable redaction for secrets, personal data, internal identifiers, and sensitive topics. 6. Minimize submitted text to the content strictly necessary for embedding. 7. Provide a mode that writes the briefing without vectorizing it. 8. Align processing with organizational data-classification and third-party processing policies. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
search-cache.sh:22
Finding
Predictable Shared Temporary Cache Permits Disclosure and Symlink Attacks<![CDATA[ ## Vulnerability Details **File Location**: `search-cache.sh:22-24, 49-53, 68-75` **Vulnerability Type**: Unsafe temporary-file handling **Risk Level**: High ### Vulnerable Code ```bash # File cache fallback FILE_CACHE_DIR="/tmp/research-cache" mkdir -p "$FILE_CACHE_DIR" ``` ```bash # Get cache file path for key get_cache_file() { local key="$1" local hash=$(hash_key "$key") echo "$FILE_CACHE_DIR/$hash.json" } ``` ```bash # Fall back to file cache local cache_file=$(get_cache_file "$key") local expiry=$(($(date +%s) + ttl)) printf '{"expiry":%d,"data":%s}\n' "$expiry" "$(echo "$value" | jq -Rs '.')" > "$cache_file" return 0 ``` ### Technical Analysis The fallback cache uses a fixed, shared directory under `/tmp`. The code neither applies restrictive permissions nor verifies directory ownership. Cache filenames are deterministic SHA-256 hashes of cache keys, and files are opened using ordinary shell redirection without protection against symbolic links. On a multi-user host, another local user may pre-create the directory or a predicted cache path. If a cache path is a symlink, shell redirection follows it and writes with the victim user's privileges. Depending on host settings and target permissions, this can overwrite an accessible file. Weak directory permissions can also expose complete cached search responses or allow cache manipulation. ### Attack Path 1. A local attacker predicts or observes the research topic and depth. 2. The attacker reconstructs the deterministic cache key and SHA-256 filename. 3. Before the victim writes the fallback cache, the attacker creates `/tmp/research-cache` or places a symlink at the expected JSON path. 4. Redis is unavailable, causing the script to use the file fallback. 5. The victim executes a research request. 6. Shell redirection follows the attacker-created symlink and writes the search response to the linked target. For disclosure or cache poisoning, the attacker instead reads or modi ...[truncated 394 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create a per-user private cache directory with `mktemp -d` or use `${XDG_CACHE_HOME}`. 2. Set a restrictive umask before creating cache content: ```bash umask 077 ``` 3. Require directory ownership by the current user and mode `0700`. 4. Create cache files atomically and reject symbolic links. 5. Write to a securely created temporary file, validate it, then atomically rename it. 6. Do not use a globally shared fixed path for sensitive cache data. 7. Validate cache structure and provenance before consuming it. 8. Consider encrypting cached responses if they may contain sensitive queries or results. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
search-cache.sh:13
Finding
Redis Credentials and Qdrant Research Data Use Insecure Transport<![CDATA[ ## Vulnerability Details **File Location**: `search-cache.sh:13-20, 26-34`; `vectorize.sh:14, 120-134, 199-203` **Vulnerability Type**: Plaintext sensitive transport and command-line credential exposure **Risk Level**: High ### Vulnerable Code ```bash REDIS_HOST="${REDIS_HOST:-10.0.0.120}" REDIS_PORT="${REDIS_PORT:-6379}" REDIS_PASSWORD="${REDIS_PASSWORD:-$(python3 "$(dirname "$0")/../../tools/secrets.py" get REDIS_PASSWORD 2>/dev/null)}" REDIS_DB="${REDIS_DB:-0}" CACHE_PREFIX="research:" DEFAULT_TTL=86400 # 24 hours ``` ```bash # Build Redis connection string REDIS_CONN="-h $REDIS_HOST -p $REDIS_PORT -n $REDIS_DB" if [[ -n "$REDIS_PASSWORD" ]]; then REDIS_CONN="$REDIS_CONN -a $REDIS_PASSWORD" fi # Check if Redis is available redis_available() { redis-cli $REDIS_CONN ping >/dev/null 2>&1 } ``` ```bash QDRANT_URL="${QDRANT_URL:-http://10.0.0.120:6333}" ``` The generated Python helper communicates with Qdrant over the configured URL: ```python def ensure_collection(): """Ensure web_research collection exists.""" try: # Check if collection exists resp = requests.get(f"{QDRANT_URL}/collections/{COLLECTION}") if resp.status_code == 200: return # Create collection resp = requests.put( f"{QDRANT_URL}/collections/{COLLECTION}", json={ "vectors": { "size": 1536, "distance": "Cosine" } } ) ``` ```python resp = requests.put( f"{QDRANT_URL}/collections/{COLLECTION}/points?wait=true", json={"points": batch} ) ``` ### Technical Analysis Redis is configured as a host and port without TLS, while the password is passed through the `redis-cli -a` command-line argument. Plaintext Redis traffic can expose credentials and cached research results to an attacker capable of observing the network. Command-line credentials may also be observable through process inspec ...[truncated 1395 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require TLS-protected Redis connections, such as `rediss://`, with certificate validation. 2. Require HTTPS for Qdrant and reject plaintext URLs unless an explicit development-only override is enabled. 3. Configure Qdrant authentication and send credentials through protected headers. 4. Do not pass Redis passwords in command-line arguments. 5. Use a protected Redis configuration file, file descriptor, environment mechanism appropriate to the deployment, or a secret manager integration. 6. Apply least-privileged Redis ACLs and dedicated Qdrant API credentials. 7. Rotate the Redis password if it has been exposed through process listings or plaintext transport. 8. Add connection timeouts, hostname verification, and trusted CA configuration. 9. Document all service endpoints and the data sent to each service. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
vectorize.sh:74
Finding
Vectorization Chunking Algorithm Can Loop Indefinitely<![CDATA[ ## Vulnerability Details **File Location**: `vectorize.sh:74-96` **Vulnerability Type**: Denial of service caused by non-progressing loop **Risk Level**: Medium ### Vulnerable Code ```python def chunk_text(text: str, size: int = CHUNK_SIZE, overlap: int = CHUNK_OVERLAP) -> List[str]: """Split text into overlapping chunks.""" chunks = [] start = 0 text_len = len(text) while start < text_len: end = min(start + size, text_len) # Try to break at sentence or word boundary if end < text_len: # Look for sentence ending sentence_break = text.rfind('. ', start, end) if sentence_break > start + size // 2: end = sentence_break + 1 else: # Look for word boundary word_break = text.rfind(' ', start, end) if word_break > start: end = word_break chunks.append(text[start:end].strip()) start = end - overlap return chunks ``` ### Technical Analysis When processing the final chunk, `end` becomes `text_len`. The code then sets `start` to `text_len - overlap`. On the next iteration, `end` again becomes `text_len`, and `start` is assigned the same value. The loop therefore stops making progress and repeatedly appends the same final chunk. For the configured positive overlap of 200 characters, ordinary sufficiently long documents can trigger this behavior. Repeated appends cause unbounded memory consumption and prevent vectorization from completing. ### Attack Path 1. A user or attacker causes the Skill to process a research document longer than the configured overlap. 2. `chunk_text` reaches the final segment. 3. `end` is set to the document length. 4. `start` is reset to `text_len - 200`. 5. Every subsequent iteration uses the same `start` and `end`. 6. The process repeatedly appends the final chunk until it is terminated or exhausts available memory. # ...[truncated 350 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Terminate the loop immediately after appending the final chunk: ```python chunks.append(text[start:end].strip()) if end >= text_len: break next_start = end - overlap if next_start <= start: raise ValueError("Chunking configuration does not make progress") start = next_start ``` 2. Validate that `size > 0`, `overlap >= 0`, and `overlap < size`. 3. Add a progress invariant ensuring every iteration increases `start`. 4. Add an upper bound on chunk count based on document size. 5. Add unit tests for empty text, short text, exact-size text, long text, boundary breaks, and invalid overlap settings. 6. Apply process-level time and memory limits as defense in depth. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (21)

Ae1

High
Category
analysis-evasion
Content
2. Calls `research.sh` with appropriate parameters
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Tool Parameter Abuse

High
Category
Tool Misuse
Content
fi
    
    # Clear file cache
    rm -rf "$FILE_CACHE_DIR"/*
    mkdir -p "$FILE_CACHE_DIR"
    
    echo "Cache cleared"
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).

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill documentation describes autonomous research, storage in an Obsidian vault, and vectorization into Qdrant, but it does not prominently warn users that their prompts, derived content, and metadata may be written locally and transmitted to external services. This can lead users to submit sensitive topics or proprietary data without informed consent, causing unintended disclosure or retention across multiple systems.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The configuration section exposes that the skill communicates with Brave Search, Qdrant, and Redis, including default network endpoints, but it does not clearly warn that using the skill triggers outbound requests to third-party or self-hosted services. Users may assume research is local-only and unknowingly transmit sensitive queries, topics, or generated content over the network to services with different trust boundaries and retention policies.

Skill Enumeration

Medium
Category
Agent Snooping
Content
---

*Generated by OpenClaw Auto-Research Agent v1.0*  
*For questions or improvements, see: `clawhub-skills/auto-research/SKILL.md`*
Confidence
80% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
The script contains a hard-coded Brave API key and automatically transmits user-supplied research topics to Brave Search without an explicit warning or consent prompt. Research queries can contain proprietary, strategic, or personal information, so silent third-party transmission creates a real confidentiality and privacy risk, amplified by embedding a credential directly in the skill.

External Transmission

Medium
Category
Data Exfiltration
Content
API_RESPONSE=$(curl -s --max-time 30 \
        -H "Accept: application/json" \
        -H "X-Subscription-Token: $BRAVE_API_KEY" \
        "https://api.search.brave.com/res/v1/web/search?q=$(printf '%s' "$SEARCH_QUERY" | jq -sRr @uri)&count=$SOURCE_COUNT&freshness=py&result_filter=web") || {
        echo -e "${RED}✗ Search API call failed${NC}"
        exit 1
    }
Confidence
97% confidence
Finding
This curl call sends the user-provided search query to an external service, which is an actual data egress event. In the context of a research assistant, users may input confidential project names, incident details, or internal topics; transmitting them off-host without prominent disclosure can leak sensitive information to a third party.

Context-Inappropriate Capability

Medium
Confidence
89% confidence
Finding
The file is documented and structured as a research orchestrator that searches for sources and compiles a briefing, but these lines additionally invoke a separate vectorization pipeline and update the output file to reflect ingestion status. Writing research notes into a Qdrant-backed vector store is a distinct capability not justified by the visible purpose of producing a research briefing when no manifest scope declares it.

Context-Inappropriate Capability

Medium
Confidence
89% confidence
Finding
With no manifest available, the code's own description frames this as a cache helper for storing and retrieving research results. Line L017 adds an additional capability by invoking a separate secrets-retrieval tool to obtain REDIS_PASSWORD, which goes beyond basic cache manipulation and introduces credential access behavior not implied by the script's stated purpose.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The clear_cache function deletes all matching Redis cache keys and removes all files under the cache directory, which is a destructive operation. While the script comments and usage text describe the command, there is no confirmation prompt or stronger user-facing warning at the point of execution before data is removed.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
The script silently falls back to reading an OpenAI API key from a local auth-profiles file instead of requiring the caller to provide credentials explicitly. That behavior expands the script's access to local secrets and can cause unintended credential use or disclosure, especially for a utility whose purpose is file vectorization rather than credential management.

Context-Inappropriate Capability

Medium
Confidence
86% confidence
Finding
The script dynamically writes an executable Python program into the user's workspace and then later executes it. Self-installing code increases the attack surface, creates persistence on disk, and makes review harder because the behavior is split between the shell script and generated code.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The generated ingestion tool sends document content to the OpenAI embeddings API, which is an external service, but this file does not provide a clear user-facing disclosure or consent step. Research documents may contain confidential or proprietary information, so silent transmission materially increases data exposure risk.

External Transmission

Medium
Category
Data Exfiltration
Content
return
        
        # Create collection
        resp = requests.put(
            f"{QDRANT_URL}/collections/{COLLECTION}",
            json={
                "vectors": {
Confidence
80% 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
for i in range(0, len(points), batch_size):
        batch = points[i:i+batch_size]
        try:
            resp = requests.put(
                f"{QDRANT_URL}/collections/{COLLECTION}/points?wait=true",
                json={"points": batch}
            )
Confidence
96% confidence
Finding
This request sends vector payloads containing chunked research text and metadata to Qdrant over HTTP to a configured host. Because the default URL is unencrypted HTTP to a private IP and the payload includes user content and file metadata, interception, unintended retention, or internal data leakage are credible risks.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The tool uploads chunked document text and metadata into Qdrant without an explicit disclosure in the script's interface. Even if Qdrant is internal, this is still a data write to a network service and may store sensitive content, source URLs, file paths, and topic metadata beyond the user's expectations.

Missing User Warnings

Low
Confidence
84% confidence
Finding
The script creates an output directory and writes a markdown research report into the user's Obsidian vault, then later modifies that file again to update vectorization status. Although there are status messages after the fact, there is no clear up-front disclosure in the usage/help text or comments warning users that running the skill will create and edit files in a local notes repository.

Intent-Code Divergence

Low
Confidence
77% confidence
Finding
The comment says the file is updated "using a safe replacement," but the implementation performs in-place or temporary-file mutation with chained fallbacks and suppressed errors. That is not a direct contradiction about core purpose, but it overstates the safety of the operation compared with the actual best-effort editing behavior.

Missing User Warnings

Low
Confidence
80% confidence
Finding
The script reads a Redis password from the environment or a secrets helper, which is access to sensitive credentials. Although this is functionally necessary, the file does not include any explicit user-facing notice that credentials will be accessed when connecting to Redis.

Missing User Warnings

Low
Confidence
84% confidence
Finding
Reading API credentials from a local auth profile without prominently disclosing that behavior can violate user expectations and lead to accidental use of secrets. In this context it is less severe than direct exfiltration, but it still reflects hidden secret access by the skill.

Missing User Warnings

Low
Confidence
85% confidence
Finding
Creating and later executing a helper file on disk without explicit warning hides material behavior from the user and can leave behind executable artifacts. In an agent skill context, undisclosed code generation is more dangerous because users may assume they are only running the visible script.