Back to skill

Security audit

company-search

Security checks for vulnerabilities and agentic risk

Overview

This company-research skill is mostly coherent, but it asks for broad shell-backed web fetching that can reach arbitrary URLs without enough containment.

Review before installing in environments with access to private networks, internal URLs, or sensitive research targets. Prefer built-in scoped search/fetch tools, avoid granting bash/shell unless the local fallback is necessary, use direct-only mode for public URLs, and do not use jina/archive routing for confidential or internal targets.

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

Error
Location
search_fetch.py:91
Finding

Unrestricted URL Fetching Enables Server-Side Request Forgery

Content
View full analysis

Vulnerability Details

File Location: search_fetch.py:91-112
Related Locations: search_fetch.py:115-160, search_fetch.py:171-190, search_fetch.py:230-247
Vulnerability Type: Server-Side Request Forgery through unvalidated URLs
Risk Level: High

Vulnerable Code

python
def _fetch_direct(url: str, timeout: int, max_chars: int) -> dict:
    import requests
    from bs4 import BeautifulSoup

    session = requests.Session()
    session.headers.update(_BROWSER_HEADERS)

    # Visit root domain first to pick up cookies (helps with some anti-bot)
    try:
        from urllib.parse import urlparse
        root = f"{urlparse(url).scheme}://{urlparse(url).netloc}/"
        if root != url:
            session.get(root, timeout=timeout, allow_redirects=True)
            time.sleep(0.5)
    except Exception:
        pass

    resp = session.get(url, timeout=timeout, allow_redirects=True)
    resp.raise_for_status()
    resp.encoding = resp.apparent_encoding or "utf-8"

    soup = BeautifulSoup(resp.text, "lxml")
    title = soup.title.string.strip() if soup.title else ""
    text = _extract_text(resp.text, url)
    return _build_result(url, resp.status_code, title, text, "direct", max_chars)

The URL originates directly from a positional command-line argument:

python
fp.add_argument("url")

Third-party strategies also forward the supplied URL externally:

python
def _fetch_jina(url: str, timeout: int, max_chars: int) -> dict:
    """Use r.jina.ai as a reader proxy — free, no API key, returns clean markdown."""
    import requests

    jina_url = f"https://r.jina.ai/{url}"
    headers = {
        "User-Agent": _BROWSER_HEADERS["User-Agent"],
        "Accept": "text/plain, text/markdown, */*",
        "X-Return-Format": "markdown",
    }
    resp = requests.get(jina_url, headers=headers, timeout=timeout)

Technical Analysis

Th ...[truncated 2731 chars]

Remediation
View remediation

Remediation Suggestions

  1. Restrict accepted schemes to https and, only if required, http. Reject malformed URLs, non-network schemes, embedded credentials, and ambiguous host representations.
  2. Resolve the hostname before connecting and reject every IPv4 and IPv6 address classified as loopback, private, link-local, multicast, reserved, unspecified, or otherwise non-public.
  3. Explicitly block cloud metadata destinations, including link-local metadata addresses and platform-specific metadata hostnames.
  4. Disable automatic redirects. Follow redirects manually only after validating the scheme, hostname, port, and resolved addresses of every destination.
  5. Mitigate DNS rebinding by connecting to a validated resolved address while preserving the intended HTTP Host value and TLS hostname verification, or by using an outbound proxy that enforces public-address policy.
  6. Restrict destination ports to a minimal allowlist, normally 80 and 443.
  7. Remove the root-origin preflight request unless it is essential. If retained, apply the same destination checks before issuing it.
  8. Add an optional hostname allowlist for high-assurance use cases such as government, regulatory, court, and company domains.
  9. Require explicit user confirmation before sending a URL to Jina Reader or the Internet Archive. Reject private and sensitive URLs even when confirmation is provided.
  10. Avoid forwarding URLs containing credentials, access tokens, signed query strings, fragments, or other sensitive parameters to third parties.
  11. Add tests covering decimal and hexadecimal IP representations, IPv4-mapped IPv6, redirects to private addresses, DNS rebinding scenarios, credential-bearing URLs, and metadata endpoints.

T08 · Insecure Dependencies

Warning
Location
package.json:19
Finding

Unpinned Python Dependencies Create Supply-Chain Exposure

Content
View full analysis

Vulnerability Details

File Location: package.json:19-24
Related Locations: SKILL.md:9, README.md:25-31
Vulnerability Type: Mutable and unverified third-party dependency installation
Risk Level: Medium

Vulnerable Code

json
"scripts": {
  "setup": "pip install ddgs requests beautifulsoup4 lxml trafilatura"
},
"pythonDependencies": {
  "required": ["ddgs", "requests", "beautifulsoup4", "lxml"],
  "optional": ["trafilatura"]
},
"pythonEntry": "search_fetch.py"

The README recommends the same unpinned installation:

bash
pip install ddgs requests beautifulsoup4 lxml trafilatura

Technical Analysis

The setup command installs packages by name without exact version constraints, a reviewed lock file, or cryptographic hashes. The code installed by the command can therefore change after the Skill has been audited.

Python package installation may execute package build hooks or other installer-controlled logic. Runtime imports also execute package initialization code. If a package publisher account, release, package index, or transitive dependency is compromised, a future installation could execute attacker-controlled code with the privileges of the user running the setup command.

The dependency names appear relevant to the declared search and content-extraction functionality, and no evidence shows that the currently named packages are intentionally malicious. The issue is the mutable and unverified installation mechanism rather than a confirmed malicious package.

Attack Path

  1. A user or deployment system runs npm run setup or copies the documented pip install command.
  2. pip resolves the newest versions permitted by the live package index and their transitive dependencies.
  3. A compromised or unexpectedly changed release is downloaded without comparison against reviewed hashes.
  4. Malicious installation logic may run during package installation, or mali ...[truncated 958 chars]
Remediation
View remediation

Remediation Suggestions

  1. Pin every direct dependency to an exact reviewed version.
  2. Resolve and pin all transitive dependencies in a committed lock file.
  3. Record cryptographic hashes and install with pip --require-hashes.
  4. Use a dedicated requirements file or a lock generator such as pip-tools, and make npm run setup install exclusively from that reviewed artifact.
  5. Configure the expected package index explicitly and prevent unintended fallback to untrusted indexes.
  6. Run dependency vulnerability, provenance, and license checks in continuous integration.
  7. Use an isolated virtual environment and avoid installing packages as root or an administrator.
  8. Review dependency updates before regenerating the lock file, and use automated update tooling that preserves review and test gates.
  9. Consider making the optional extraction dependency a separate, explicitly selected installation profile to reduce the default dependency surface.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (12)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The skill is presented as a constrained company due-diligence workflow, but its instructions permit generic web search, arbitrary URL fetching, shell-based retrieval, and third-party proxy routing via r.jina.ai and archive.org. That mismatch is dangerous because users and orchestrators may trust it as domain-limited while it can exfiltrate queried URLs to third parties, access unrelated content, and operate more like a general-purpose network retrieval utility than a bounded research skill.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The README explicitly instructs agents to use a local Python/bash fallback and browser tooling when built-in web tools are unavailable, which expands the skill from passive research into local code execution. In an agent environment, that creates a real attack surface because untrusted prompts or retrieved content could cause the agent to invoke local scripts, access the network, and process attacker-controlled URLs or queries.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The documented jina and archive fetch fallbacks send requested URLs and potentially sensitive research targets through third-party services, but the README does not disclose this data flow or its privacy implications. For a due-diligence skill, queried company names, target URLs, and investigation context may themselves be sensitive, so silent proxying can leak confidential user intent or internal research activity.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill advertises and enables network-backed search/fetch behavior, including shell-based fallback execution, but does not declare any explicit tool scope or allowed-tools policy. In an agent environment, this increases the chance of unintended network access, tool overreach, or policy bypass because the runtime may grant broader capabilities than the skill actually needs.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The trigger description is broad enough to auto-activate on many ordinary requests about companies, causing the skill to invoke networked research more often than users may expect. In a privacy-sensitive setting, over-triggering can leak user queries or investigated entities to external search providers and increase the attack surface for unnecessary tool execution.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The listed trigger phrases are vague and overlap with everyday speech, which can cause accidental invocation in conversations that merely mention checking or discussing a company. Because this skill can perform network searches and fetching, accidental activation may disclose user interests, create unnecessary external requests, and execute broader tooling than warranted by the prompt.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill declares bash and shell tool access even though its stated purpose is company research, which can be fulfilled with search, fetch, and browser tools. Unnecessary command-execution capability expands the attack surface: prompt injection from searched content or skill logic errors could cause arbitrary local command execution, file access, network pivoting, or environment inspection.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The implementation is a generic search/fetch utility, not a scoped company-research workflow as described in the manifest. This mismatch matters because agent platforms and users may grant or invoke the skill under narrower expectations, while the code actually enables broad web retrieval that can be repurposed for unrelated or risky targets.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The documented jina/archive/auto modes send target URLs and potentially retrieved content through third-party services, but this capability is outside the manifest’s stated company-research scope. In practice, this creates a data exfiltration and privacy risk: an agent may forward sensitive URLs, query intent, or fetched content to external intermediaries without clear user understanding or consent.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The code hard-codes an Accept-Language header preferring zh-CN/zh and en-US/en for direct fetches. This imposes a language/locale preference on fetched content without offering the user a choice or documenting a justified region-specific constraint.

Content

No source excerpt is available for this finding.

Tainted flow: 'archive_url' from requests.get (line 153, network input) → requests.get (network output)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · search_fetch.py (reported line 154)May include surrounding context.

python
raise ValueError("No Wayback Machine snapshot found")

    archive_url = closest["url"]
    resp = requests.get(archive_url, headers=_BROWSER_HEADERS, timeout=timeout, allow_redirects=True)
    resp.raise_for_status()
    resp.encoding = resp.apparent_encoding or "utf-8"

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The CLI exposes arbitrary URL fetching with caller-controlled URLs, which exceeds the declared due-diligence research use case. In an agent setting, this can become an SSRF-style capability or a means to access sensitive/internal endpoints, especially if the agent can be prompted to fetch attacker-specified URLs.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.