Back to skill

Security audit

tavily-search

Security checks for vulnerabilities and agentic risk

Overview

The skill coherently provides Tavily web search, with ordinary privacy caveats because queries go to Tavily and one helper can cache results locally.

Install this only if you are comfortable with Tavily receiving your search queries and API usage. Do not put secrets, private customer data, or confidential internal text into searches; use --no-cache for sensitive searches with the fast helper, and consider deleting ~/.cache/tavily_search if sensitive results may have been cached. Prefer installing tavily-python in a virtual environment and pinning a reviewed version.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/tavily_search_fast.py:15
Finding

Search Queries and Results Persisted in an Unprotected Plaintext Cache

Content
View full analysis

Vulnerability Details

File Location: scripts/tavily_search_fast.py, lines 15-50
Vulnerability Type: Plaintext storage of potentially sensitive search data
Risk Level: Medium

Vulnerable Code

python
# Cache configuration
CACHE_DIR = Path.home() / ".cache" / "tavily_search"
CACHE_DURATION = 300  # 5 minutes cache

def get_cache_key(query: str, **kwargs) -> str:
    """Generate cache key from query and parameters."""
    cache_data = f"{query}:{json.dumps(kwargs, sort_keys=True)}"
    return hashlib.md5(cache_data.encode()).hexdigest()

def get_cached_result(cache_key: str) -> Optional[Dict]:
    """Get cached result if valid."""
    cache_file = CACHE_DIR / f"{cache_key}.json"
    if not cache_file.exists():
        return None
    
    try:
        with open(cache_file, 'r') as f:
            cached = json.load(f)
        
        # Check if cache is still valid
        if time.time() - cached.get('timestamp', 0) < CACHE_DURATION:
            return cached['data']
    except Exception:
        pass
    
    return None

def cache_result(cache_key: str, data: Dict) -> None:
    """Cache search result."""
    try:
        CACHE_DIR.mkdir(parents=True, exist_ok=True)
        cache_file = CACHE_DIR / f"{cache_key}.json"
        
        with open(cache_file, 'w') as f:
            json.dump({
                'timestamp': time.time(),
                'data': data
            }, f)
    except Exception:
        pass  # Cache failures shouldn't break search

Technical Analysis

The optimized search script writes Tavily responses to JSON files beneath ~/.cache/tavily_search. These responses can contain the original search query, result titles, URLs, and retrieved content.

The implementation does not explicitly restrict the cache directory to mode 0700 or individual cache files to mode 0600. Effective permissions therefore depend on the runtime environment and process umask. In permissively configured or shared environments, ano ...[truncated 1726 chars]

Remediation
View remediation

Remediation Suggestions

  1. Disable persistent caching by default and require an explicit --cache option to enable it.
  2. Clearly document that enabling the cache stores search queries and responses locally.
  3. Create the cache directory with owner-only permissions:
    python
    CACHE_DIR.mkdir(parents=True, exist_ok=True, mode=0o700)
    os.chmod(CACHE_DIR, 0o700)
    
  4. Create cache files atomically with mode 0600, rather than relying on the process umask.
  5. Delete expired entries when they are encountered and add a startup cleanup routine for stale files.
  6. Provide a --clear-cache option and an option to set a shorter retention period.
  7. Avoid caching queries or responses identified as sensitive. If persistent storage is necessary, consider authenticated encryption backed by an operating-system credential facility.
  8. Do not silently suppress every cache exception; report permission and cleanup failures without exposing cached content.

T08 · Insecure Dependencies

Note
Location
SKILL.md:202
Finding

Third-Party Dependency Installation Is Recommended Without Version or Integrity Pinning

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, line 202
Vulnerability Type: Unpinned third-party dependency
Risk Level: Low

Vulnerable Code

markdown
- `tavily-python` package installed (`pip install tavily-python`)

The same unpinned installation instruction is also presented when the import fails in scripts/tavily_search.py:

python
except ImportError:
    print("Error: tavily-python package not installed.", file=sys.stderr)
    print("Install with: pip install tavily-python", file=sys.stderr)
    sys.exit(1)

Technical Analysis

The project instructs users to install tavily-python without specifying a reviewed version, lockfile, or package hash. As a result, installation resolves whichever release is current at that time. This makes deployments non-reproducible and means the effective dependency code can change without a corresponding change to the audited skill.

No evidence was found that tavily-python is currently malicious, typosquatted, or retrieved from an unauthorized package source. The risk arises from unsafe dependency management: a future compromised upstream release, package-index compromise, or unexpectedly incompatible update could be installed automatically when the documented command is followed.

Because Python packages may execute code during installation or when imported, a compromised package could execute with the privileges of the user performing the installation or running the search scripts.

Attack Path

  1. An attacker compromises the upstream package, a maintainer account, or the package-distribution channel and publishes a malicious release under the expected package name.
  2. A user follows the documented pip install tavily-python instruction.
  3. Because no version or hash is pinned, the package manager resolves the malicious current release.
  4. Malicious code executes during package installation or when the scripts import TavilyClient.
  5. The code acts with the permissions of t ...[truncated 722 chars]
Remediation
View remediation

Remediation Suggestions

  1. Pin tavily-python to a specifically reviewed version.
  2. Maintain a lockfile or hashed requirements file, for example:
    text
    tavily-python==REVIEWED_VERSION --hash=sha256:REVIEWED_PACKAGE_HASH
    
  3. Install with hash verification, such as pip install --require-hashes -r requirements.txt.
  4. Periodically review and deliberately update the pinned dependency rather than resolving the latest release at installation time.
  5. Install dependencies inside an isolated virtual environment under a non-privileged account.
  6. Document the expected package index and avoid untrusted extra indexes.
  7. Add automated dependency and vulnerability scanning to the release process.
  8. Update the import-error message to reference the project’s pinned requirements file instead of recommending an unconstrained package installation.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (6)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
83% confidence
Finding

The skill declares an environment requirement and includes example code patterns that can read secrets from the environment and interact with external services, but it does not declare an explicit tool scope or permissions boundary. In an agent ecosystem, missing scope metadata can cause the skill to be invoked with broader capabilities than users expect, increasing the chance of unintended file or secret access.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The description uses broad activation wording like 'use when you need to search the web for current information' and 'any topic,' which can make the skill match a very wide range of requests. Over-broad matching raises the risk of unnecessary invocation, causing user prompts or sensitive context to be sent to an external search provider when a narrower or local capability would have been more appropriate.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The documentation instructs users to send queries and, in some cases, large blocks of retrieved context to Tavily, but it does not clearly warn that this data is transmitted to a third-party service. This creates a privacy and data-handling risk because users or downstream agents may include sensitive prompts, internal questions, or proprietary context in requests without informed consent.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% 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 · references/TAVILY_API.md (reported line 11)May include surrounding context.

md
### 1. Search API

**Endpoint**: `POST https://api.tavily.com/search`

Search the web for any query.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The script persists raw search results under the user's home directory cache without notifying the caller or offering any privacy controls beyond a cache bypass flag. Search queries and returned content can contain sensitive research topics, internal URLs, or user-supplied data, and storing them on disk creates a local confidentiality risk if the host is shared, monitored, or later compromised.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The script sends the user-provided search query to Tavily, an external third-party service, without any notice, consent mechanism, or filtering. In an agent context, queries may contain sensitive user data, internal prompts, secrets, or proprietary information, so this creates a real privacy and data-exfiltration risk even though the code’s purpose is to perform web search.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.