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

Content
View full analysis
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
vectorize.sh:31
Finding

Undisclosed Access to the Main Agent Authentication Profile

Content
View full analysis
/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
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
vectorize.sh:14
Finding

Creation and Execution of a Mutable Helper Outside the Skill Directory

Content
View full analysis
"$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
View remediation

other

Warning
Location
vectorize.sh:66
Finding

Undisclosed Transmission of Research Content to OpenAI

Content
View full analysis
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
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
search-cache.sh:22
Finding

Predictable Shared Temporary Cache Permits Disclosure and Symlink Attacks

Content
View full analysis
"$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
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
search-cache.sh:13
Finding

Redis Credentials and Qdrant Research Data Use Insecure Transport

Content
View full analysis
/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
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
vectorize.sh:74
Finding

Vectorization Chunking Algorithm Can Loop Indefinitely

Content
View full analysis
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
View remediation
= 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
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
2. Calls `research.sh` with appropriate parameters

Tool Parameter Abuse

High
Category
Tool Misuse
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).

Content

Scanner excerpt · search-cache.sh (reported line 138)May include surrounding context.

sh
fi
    
    # Clear file cache
    rm -rf "$FILE_CACHE_DIR"/*
    mkdir -p "$FILE_CACHE_DIR"
    
    echo "Cache cleared"

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Skill Enumeration

Medium
Category
Agent Snooping
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.

Content

Scanner excerpt · briefing-template.md (reported line 87)May include surrounding context.

md
---

*Generated by OpenClaw Auto-Research Agent v1.0*  
*For questions or improvements, see: `clawhub-skills/auto-research/SKILL.md`*

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
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.

Content

Scanner excerpt · research.sh (reported line 108)May include surrounding context.

sh
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
    }

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% 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 · vectorize.sh (reported line 156)May include surrounding context.

sh
return
        
        # Create collection
        resp = requests.put(
            f"{QDRANT_URL}/collections/{COLLECTION}",
            json={
                "vectors": {

External Transmission

Medium
Category
Data Exfiltration
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.

Content

Scanner excerpt · vectorize.sh (reported line 226)May include surrounding context.

sh
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}
            )

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.