Back to skill

Security audit

sn-search-image

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does image search, but it can send the Serper API key to an environment-selected server and can write result JSON to arbitrary paths.

Review before installing. Only use this skill where SERPER_BASE_URL cannot be attacker-controlled, prefer removing or allowlisting that override, and avoid --save-json except to a known safe temporary path. The skill does not show destructive intent or persistence, but its credential destination is under-scoped.

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
scripts/serper_image_search.py:145
Finding

Environment-Controlled Endpoint Can Expose API Credentials and Search Queries

Content
View full analysis

Vulnerability Details

File Location: scripts/serper_image_search.py, lines 68-77 and 145-147
Vulnerability Type: Arbitrary credential-bearing network destination
Risk Level: High

Vulnerable Code

python
def _post_json(url: str, payload: dict[str, Any], api_key: str) -> dict[str, Any]:
    body = json.dumps(payload).encode("utf-8")
    req = request.Request(
        url,
        data=body,
        headers={
            "Content-Type": "application/json",
            "X-API-KEY": api_key,
        },
        method="POST",
    )
python
base_url = os.environ.get("SERPER_BASE_URL", DEFAULT_BASE_URL).rstrip("/")
payload = _build_payload(args)
data = _post_json(f"{base_url}/images", payload, api_key)

Technical Analysis

The script permits SERPER_BASE_URL to override the intended Serper endpoint without validating the URL scheme or hostname. The _post_json function then unconditionally includes the value of SERPER_API_KEY in the X-API-KEY request header.

Sending the API key and search query to the default https://google.serper.dev/images endpoint is necessary for the declared image-search functionality. Allowing an arbitrary environment-selected destination to receive the same credential is not necessary and exceeds the minimum network privileges required by the Skill.

An actor who can influence the process environment can configure an attacker-controlled HTTP or HTTPS endpoint. The script will submit both the API credential and user-provided query to that endpoint. Permitting an HTTP URL can additionally expose these values to passive or active network interception.

Attack Path

  1. An attacker or compromised launcher gains the ability to define environment variables for the Skill process.
  2. The attacker sets SERPER_BASE_URL to a server under their control, such as https://attacker.example.
  3. A user or agent invokes the documented image-search com ...[truncated 1130 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove the SERPER_BASE_URL override if custom endpoints are not required.
  2. If configurability is necessary, parse the URL with urllib.parse.urlparse and require:
    • The https scheme.
    • An exact allowlisted hostname, normally google.serper.dev.
    • No embedded username or password.
    • The expected port, or no explicit port.
  3. Construct the endpoint from a trusted constant rather than concatenating credentials with an arbitrary environment-provided origin.
  4. Ensure authorization headers are never forwarded across redirects to a different origin. Prefer rejecting redirects or validating every redirect destination before resending credentials.
  5. Fail closed with a clear error when URL validation fails.
  6. Add tests proving that HTTP URLs, lookalike domains, subdomain tricks, embedded credentials, and non-allowlisted hosts are rejected.

T08 · Insecure Dependencies

Note
Location
requirements.txt:1
Finding

Unused and Unpinned Third-Party Dependency Expands Supply-Chain Exposure

Content
View full analysis

Vulnerability Details

File Location: requirements.txt, line 1
Vulnerability Type: Unnecessary and non-reproducible dependency declaration
Risk Level: Low

Vulnerable Code

text
requests

Technical Analysis

The project declares the third-party requests package without a version constraint or integrity hash. The reviewed script does not import or use this dependency; it performs network operations through Python's standard-library urllib package.

Installing an unused dependency unnecessarily increases the amount of third-party code introduced into the execution environment. The lack of a pinned version also makes installations non-reproducible because the resolved package version and transitive dependency set can change over time.

The package name is the legitimate requests package rather than an evident typosquat, and the reviewed project does not contain evidence that a malicious version is intentionally selected. The risk is avoidable supply-chain exposure rather than a demonstrated malicious dependency.

Attack Path

  1. A user or automated build installs the project's requirements.txt.
  2. The package installer resolves the currently available compatible release of requests and its transitive dependencies.
  3. Third-party code that is unnecessary for the Skill's operation is added to the environment.
  4. If a resolved package version or distribution source is compromised, the unnecessary installation creates an avoidable path for malicious package code to enter the environment.

This is a preventive supply-chain finding; no compromised dependency was identified in the reviewed files.

Impact Assessment

The declaration expands the dependency and update surface without providing functionality used by the Skill. Potential impact depends on a future vulnerable or compromised package release and on how dependencies are installed and executed.

In the worst case, a compromised ...[truncated 191 chars]

Remediation
View remediation

Remediation Suggestions

  1. Remove requirements.txt or remove requests from it because the current implementation uses only the Python standard library.
  2. If requests becomes necessary, pin it to a reviewed version using an exact version constraint.
  3. Use a lock file or hash-checked installation process to make dependency resolution reproducible.
  4. Regularly scan direct and transitive dependencies for known vulnerabilities.
  5. Restrict package installation to trusted indexes and controlled build environments.
Vulnerability Patterns
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (6)

Tainted flow: 'req' from os.environ.get (line 73, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/serper_image_search.py (reported line 84)May include surrounding context.

python
)
    timeout = int(os.environ.get("SERPER_TIMEOUT_SECONDS", str(DEFAULT_TIMEOUT_SECONDS)))
    try:
        with request.urlopen(req, timeout=timeout) as response:
            raw = response.read().decode("utf-8", errors="replace")
    except error.HTTPError as exc:
        details = exc.read().decode("utf-8", errors="replace").strip()

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill invokes a Python script that requires environment access (SERPER_API_KEY), network access to Serper.dev, and optional file-write capability via --save-json, but it does not declare any explicit tool scope such as permissions or allowed-tools. This creates an authorization gap: an agent may execute a networked, credential-using, file-writing skill without clear policy constraints or review boundaries, increasing the chance of unintended data access or misuse.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The --save-json option allows writing raw response data to an arbitrary local path, which exceeds the advertised behavior of simply returning image search results. In agent or automated environments, this can be abused to overwrite files the process can access, creating integrity and potentially availability risks.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

This skill includes a general local file write primitive unrelated to core image discovery functionality. Even though it only writes Serper response JSON, arbitrary path control can still be dangerous in privileged or shared environments because it may clobber application data or operational files.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
98% confidence
Finding

The dependency is specified as just requests with no version pin, which makes builds non-reproducible and can pull in different releases over time, including newly broken or vulnerable ones. In a network-facing skill that performs external HTTP requests for image discovery, leaving a core HTTP client unpinned increases supply-chain and maintenance risk even if it is not an immediate exploit by itself.

Content

Scanner excerpt · requirements.txt (reported line 1)May include surrounding context.

text
requests

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
94% confidence
Finding

Because requests is unpinned, it is impossible to verify whether installation will resolve to a version affected by known advisories, including issues involving credential leakage and request-handling weaknesses. This skill’s purpose is outbound web querying against external services, so using an uncertain version of the primary HTTP library is more concerning than in an offline-only skill because vulnerable behavior could be exposed through attacker-controlled URLs, redirects, or environment-dependent auth handling.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.