Back to skill

Security audit

pexels-image-downloader

Security checks for vulnerabilities and agentic risk

Overview

This is a straightforward Pexels image downloader with disclosed network, API-key, and local file-writing behavior, though it has some hardening and documentation gaps.

Install only if you are comfortable giving the tool a Pexels API key and letting it download remote images into a chosen local directory. Prefer running it in a virtual environment, use a dedicated output folder, keep dependencies updated or pinned, and avoid running it from a directory where untrusted files can be added.

Vulnerability Patterns
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • 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 (3)

T09 · Insecure Skill Coding Practices

Warning
Location
download_pexels.py:132
Finding

Unbounded Download and Processing of Remote Image Data

Content
View full analysis

Vulnerability Details

File Location: download_pexels.py, lines 132-143
Vulnerability Type: Unrestricted resource consumption
Risk Level: Medium

Vulnerable Code

python
# 下载图片
img_response = requests.get(photo_url, timeout=15)
if img_response.status_code != 200:
    print(f"❌ 下载失败: HTTP {img_response.status_code}")
    return None

# 打开图片
img = Image.open(io.BytesIO(img_response.content))
original_size = img.size

# 确定目标尺寸
if target_size is None:

Technical Analysis

The application retrieves a remotely supplied image URL with requests.get() and accesses img_response.content, causing the entire response body to be buffered in memory. It does not enforce a maximum response size, inspect Content-Length, validate the media type, or limit the image's decoded dimensions before processing it with Pillow.

A compressed image can be relatively small on the network while requiring substantial memory and CPU when decoded or resized. Although Pillow includes decompression-bomb safeguards, relying only on library defaults does not address unrestricted HTTP response buffering and may not prevent all resource-exhaustion conditions.

The URL originates from the Pexels API response rather than direct command-line input, which reduces exposure under normal operation. Exploitation would require a malicious or compromised upstream response, unsafe direct use of download_photo() with attacker-controlled metadata, or redirection to hostile image content.

Attack Path

  1. An attacker causes photo_info["src"]["large"] to reference an attacker-controlled resource, such as through a compromised upstream response or an application that invokes download_photo() using untrusted metadata.
  2. The hostile server returns an extremely large response body or a compressed image with excessive decoded dimensions.
  3. requests.get() buffers the unrestricted response in img_response.content.
  4. Pillo ...[truncated 622 chars]
Remediation
View remediation

Remediation Suggestions

  • Use stream=True and read the response in bounded chunks.
  • Reject responses whose declared Content-Length exceeds a configured maximum.
  • Stop streaming and delete partial data if the actual byte count exceeds that maximum.
  • Require an approved image Content-Type, while also validating the file signature because headers can be forged.
  • Set explicit maximum width, height, and pixel-count limits before fully decoding or resizing an image.
  • Convert PIL.Image.DecompressionBombWarning into an exception and handle it explicitly.
  • Apply memory, CPU, and execution-time limits when processing remote media.
  • Disable redirects or validate every redirect destination if images are not strictly limited to trusted HTTPS hosts.

T09 · Insecure Skill Coding Practices

Warning
Location
download_pexels.py:109
Finding

Unbounded Recursive Retry on HTTP 429 Responses

Content
View full analysis

Vulnerability Details

File Location: download_pexels.py, lines 109-112
Vulnerability Type: Uncontrolled recursion and retry exhaustion
Risk Level: Medium

Vulnerable Code

python
elif response.status_code == 429:
    print(f"⚠️  请求过多,等待后重试...")
    time.sleep(2)
    return self.search_photos(query, per_page, page, orientation, size)

Technical Analysis

Every HTTP 429 response recursively invokes search_photos() without a maximum retry count. Each retry adds another Python stack frame, while the fixed two-second delay can keep the process occupied indefinitely. A sufficiently long sequence of rate-limit responses eventually reaches Python's recursion limit and raises RecursionError.

The implementation also ignores the server's Retry-After header and does not use exponential backoff or jitter. Because the broad exception handler in the calling frame catches the resulting exception, the operation may fail only after a prolonged delay and substantial unnecessary request activity.

Attack Path

  1. The Pexels endpoint continuously returns HTTP 429, whether due to exhausted API quota, aggressive rate limiting, or compromised network/upstream behavior.
  2. The application sleeps for two seconds and recursively calls search_photos().
  3. Each subsequent 429 response creates another active stack frame.
  4. The process remains occupied for an extended period and continues issuing requests.
  5. Once the recursion limit is reached, the operation fails with a recursion-related exception, producing a denial of service for the current job.

Impact Assessment

The issue can delay or terminate download jobs and consume a worker or automation slot for a prolonged period. It does not provide elevated privileges or direct data access. Its scope is primarily availability, although repeated retries may also aggravate rate limiting or consume permitted API requests depending on upstream accounting.

Remediation
View remediation

Remediation Suggestions

  • Replace recursion with an iterative retry loop.
  • Enforce a small, configurable maximum number of retries.
  • Honor a valid Retry-After response header, subject to a safe upper bound.
  • Use exponential backoff with random jitter.
  • Return a clear rate-limit error after exhausting retries.
  • Add an overall operation deadline so a download job cannot wait indefinitely.
  • Record retry counts and terminal failures for monitoring and diagnosis.

Example hardening pattern:

python
for attempt in range(max_retries + 1):
    response = requests.get(...)
    if response.status_code != 429:
        break
    if attempt == max_retries:
        return []
    delay = min(max_delay, base_delay * (2 ** attempt))
    time.sleep(delay)

T07 · Tool Hijacking and Spoofing

Note
Location
download_pexels.py:13
Finding

Import-Path Precedence Permits Local Dependency Shadowing

Content
View full analysis

Vulnerability Details

File Location: download_pexels.py, lines 13-18
Vulnerability Type: Python module search-path hijacking
Risk Level: Low

Vulnerable Code

python
# 添加当前目录到Python路径
sys.path.insert(0, str(Path(__file__).parent))

try:
    import requests
    from PIL import Image

Technical Analysis

The script explicitly places its own directory at the beginning of Python's module search path before importing third-party dependencies. Consequently, a local requests.py, requests package, or PIL package can take precedence over the expected installed dependency and execute arbitrary top-level code during import.

When the file is run directly, Python commonly already places the script directory on sys.path, so the explicit insertion is redundant and reinforces an unsafe reliance on a writable project directory. Exploitation requires the attacker to place a file or directory in the skill folder or otherwise influence its contents. An attacker who can already modify download_pexels.py has equivalent execution ability; the more relevant scenario is one where auxiliary files can be introduced but reviewed application files or installed packages cannot be modified.

Attack Path

  1. An attacker obtains the ability to add files to the skill directory without directly modifying the audited script.
  2. The attacker creates a malicious requests.py, requests/__init__.py, or PIL/__init__.py.
  3. A user or automation process starts download_pexels.py.
  4. The prepended project directory is searched before the legitimate dependency location.
  5. Python imports the attacker's module and executes its top-level code with the privileges and environment of the downloader process.

Impact Assessment

Successful exploitation permits arbitrary Python code execution with the same operating-system privileges as the user or service running the downloader. This could expose environment vari ...[truncated 267 chars]

Remediation
View remediation

Remediation Suggestions

  • Remove the explicit sys.path.insert(0, ...) statement because the script does not import any project-local module.
  • Install and execute the application from a read-only, trusted directory.
  • Run the downloader using a dedicated virtual environment with tightly controlled dependencies.
  • Prevent untrusted users and processes from writing into the application directory.
  • Package project-local modules under a unique package namespace and use explicit package imports.
  • Consider isolated Python execution where operationally appropriate, while verifying that required site packages remain available.
  • Add deployment checks that reject unexpected files named after dependencies, including requests.py, requests/, and PIL/.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • 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 (10)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill documents use of environment variables, network access to the Pexels API, and writing downloaded images and metadata to disk, but the front matter does not declare any explicit tool scope or permissions. This weakens reviewability and consent boundaries because an agent may perform network and filesystem actions without a clear, user-visible capability declaration.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The natural-language description and user instructions are presented in Chinese, which effectively imposes a specific language on users without opt-in or justification. The policy requires flagging language or locale constraints unless the skill offers a choice or clearly documents a justified region-specific limitation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The manifest description is written entirely in Chinese, which indicates a language-specific presentation without any accompanying option for other languages or documentation that this skill is intentionally region- or locale-specific. Under the policy, forcing a specific language without user opt-in or clear justification is a natural-language policy concern.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · download_pexels.py (reported line 91)May include surrounding context.

python
try:
            response = requests.get(
                'https://api.pexels.com/v1/search',
                headers=self.headers,
                params=params,
                timeout=15

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The quick-start commands instruct users to run download and batch operations, but they do not clearly warn that the skill will create local directories and write downloaded images, metadata, and generated content files. This can lead to unexpected filesystem modifications, especially in automated agent contexts where users may assume preview-only behavior.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

The package description is written entirely in Chinese, which imposes a specific language/locale in the skill's natural-language metadata without indicating that users can choose another language. The policy requires either user opt-in or a documented justification for locale constraints, neither of which appears in this file.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
97% confidence
Finding

The dependency is specified with a lower-bound range (requests>=2.31.0) rather than an exact pinned version, which makes builds non-reproducible and can silently introduce vulnerable or breaking releases during installation. In a security-sensitive skill that performs network operations, this increases supply-chain uncertainty and makes it impossible to verify which version is actually deployed.

Content

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

text
requests>=2.31.0
pillow>=10.0.0

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
86% confidence
Finding

Requests has multiple known advisories, and because the manifest does not pin the package version, there is no reliable way to determine whether the deployed environment is using a patched or affected release. For a skill that searches and downloads remote content over HTTP, ambiguity around the exact requests version increases the risk of inheriting network-layer security flaws.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
97% confidence
Finding

The dependency is not pinned to an exact version (pillow>=10.0.0), so installations may resolve to different releases over time, including versions with newly disclosed flaws or incompatible behavior. Because this skill processes downloaded images, an unsafe or unexpected Pillow release could increase exposure to parser-related vulnerabilities.

Content

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

text
requests>=2.31.0
pillow>=10.0.0

Unverifiable Dependency: pillow has 16 known advisory(ies) (CVE-2016-2533 (Pillow buffer overflow in ImagingPcdDecode); CVE-2023-50447 (Arbitrary Code Execution in Pillow); CVE-2021-27922 (Pillow Uncontrolled Resource Consumption) +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
87% confidence
Finding

Pillow has a history of vulnerabilities affecting image parsing and resource handling, and the unpinned requirement prevents verification that a safe release will be installed. In this skill's context—downloading and processing images from external sources—using an unknown Pillow version is more concerning because image decoders are a common attack surface for denial-of-service and memory-corruption issues.

Content

No source excerpt is available for this finding.