Back to skill

Security audit

Smart Downloader

Security checks for vulnerabilities and agentic risk

Overview

The skill is a real downloader, but its broad URL, header, proxy, and temporary-file behavior needs review before installation.

Review before installing. Use only trusted public URLs, avoid passing cookies or tokens in headers unless necessary, use a dedicated empty output directory, do not rely on the advertised integrity verification, and install dependencies in an isolated environment with pinned versions.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/smart_download.py:98
Finding

Unrestricted URL Fetching Enables Server-Side Request Forgery

Content
View full analysis

Vulnerability Details

File Location: scripts/smart_download.py, lines 98-104; user-controlled URLs are loaded at lines 254-258
Vulnerability Type: Unrestricted outbound request / SSRF
Risk Level: High

Vulnerable Code

python
async with client.stream(
    "GET",
    url,
    headers=headers,
    timeout=self.timeout,
    follow_redirects=True
) as response:
    response.raise_for_status()

User input reaches this request through:

python
if Path(args.urls).exists():
    urls = load_urls_from_file(args.urls)
    print(f"✓ 从文件加载 {len(urls)} 个 URL")
else:
    urls = [url.strip() for url in args.urls.split(',') if url.strip()]
    print(f"✓ 从命令行加载 {len(urls)} 个 URL")

Technical Analysis

The downloader accepts arbitrary user-supplied URLs and passes them to httpx.AsyncClient.stream() without validating the URL scheme, destination hostname, resolved IP address, or redirect targets. Redirect following is explicitly enabled.

An attacker who can influence the URL list can direct the process toward loopback addresses, private network ranges, link-local services, or cloud instance metadata endpoints. An apparently public URL can also redirect to a prohibited internal address because each redirect destination is not independently validated.

Custom request headers increase the sensitivity of unrestricted requests because user-provided header values are attached to requests made to attacker-selected destinations.

Attack Path

  1. An attacker supplies a URL such as one targeting a loopback, private-network, or link-local service.
  2. Alternatively, the attacker supplies a public URL that redirects to an internal service.
  3. The downloader makes the request and follows redirects without destination validation.
  4. The internal response body is downloaded and saved under the configured output directory.
  5. A user or downstream process may expose or consume the retrieve ...[truncated 408 chars]
Remediation
View remediation

Remediation Suggestions

  • Permit only explicitly supported schemes, normally https and, if necessary, http.
  • Reject URLs containing embedded credentials or malformed host components.
  • Resolve destination hostnames before connecting and reject loopback, private, link-local, multicast, unspecified, and reserved IP ranges.
  • Repeat scheme, hostname, and resolved-address validation for every redirect.
  • Consider enforcing an explicit hostname allowlist when the expected download sources are known.
  • Apply outbound firewall or proxy controls so the process cannot reach metadata services or internal administrative networks.
  • Restrict which custom headers can be supplied and avoid forwarding sensitive headers across origins.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/smart_download.py:109
Finding

Resume Logic Appends Full Responses and Can Produce Corrupted Files

Content
View full analysis

Vulnerability Details

File Location: scripts/smart_download.py, lines 109-129
Vulnerability Type: Improper validation of HTTP range responses
Risk Level: Medium

Vulnerable Code

python
# 获取总大小
total_size = int(response.headers.get("content-length", 0))
if downloaded_size > 0:
    total_size += downloaded_size

# 下载文件
mode = "ab" if downloaded_size > 0 else "wb"
with open(temp_path, mode) as f:
    async for chunk in response.aiter_bytes(chunk_size=self.chunk_size):
        f.write(chunk)
        downloaded_size += len(chunk)
        progress.update(task_id, completed=downloaded_size, total=total_size)

# 下载完成,移动文件
temp_path.rename(output_path)
progress.update(task_id, completed=total_size, total=total_size)
print(f"✓ 下载成功: {filename}")
return True

Technical Analysis

When a temporary file exists, the downloader sends a Range header and opens the file in append mode. It does not verify that the server returned HTTP status 206 Partial Content, nor does it validate that the Content-Range starts at the expected byte offset.

If a server ignores the range request and returns 200 OK with the complete resource, the full response is appended to the existing partial file. The resulting corrupted file is then renamed to the final destination and reported as successful.

The project documentation claims automatic file-integrity verification, but the implementation performs no checksum, signature, expected-size, or equivalent integrity verification.

Attack Path

  1. A partial .tmp file remains from an interrupted download.
  2. A subsequent request includes a Range header based on that file's size.
  3. The remote server ignores the header or intentionally responds with 200 OK and the full resource.
  4. The downloader opens the partial file in append mode.
  5. The complete response is appended after the partial content.
  6. The malformed file is renamed to the fina ...[truncated 359 chars]
Remediation
View remediation

Remediation Suggestions

  • When resuming, require a 206 Partial Content response.
  • Parse and verify Content-Range, including that its starting offset exactly matches the local temporary-file size.
  • If the server returns 200 OK, truncate the temporary file and restart the download from byte zero.
  • Handle 416 Range Not Satisfiable by checking whether the local file is already complete or must be discarded.
  • Use ETag or Last-Modified validators with If-Range to ensure that the remote resource did not change between attempts.
  • Support an expected cryptographic digest such as SHA-256 and verify it before renaming the temporary file.
  • Do not report success until expected length and integrity checks pass.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/smart_download.py:78
Finding

Duplicate URL Basenames Cause Concurrent File Collisions

Content
View full analysis

Vulnerability Details

File Location: scripts/smart_download.py, lines 78-81 and 175-188
Vulnerability Type: Predictable shared temporary file and race condition
Risk Level: Medium

Vulnerable Code

python
parsed = urlparse(url)
filename = Path(parsed.path).name or f"download_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
output_path = self.output_dir / filename
temp_path = self.temp_dir / f"{filename}.tmp"

Concurrent tasks are then created without checking for duplicate destinations:

python
tasks = []
for url in self.urls:
    filename = Path(urlparse(url).path).name or "unknown"
    task_id = progress.add_task(f"[cyan]{filename}", total=None)
    tasks.append((url, task_id))

# 并发下载
semaphore = asyncio.Semaphore(self.max_workers)

async def download_with_semaphore(url: str, task_id: Any):
    async with semaphore:
        return await self.download_file(client, url, progress, task_id)

await asyncio.gather(*[
    download_with_semaphore(url, task_id)
    for url, task_id in tasks
])

Technical Analysis

Destination and temporary paths are derived only from the final URL path component. Two different URLs ending in the same basename therefore resolve to the same temporary and output files.

Because these URLs may be downloaded concurrently, multiple tasks can open and write the same temporary file. Depending on timing, writes can be interleaved, one task can rename the file while another still uses it, or one downloaded resource can be substituted for another. Timestamp-based fallback names can also collide when multiple pathless URLs are processed during the same second.

Attack Path

  1. An attacker or user supplies two different URLs whose paths end in the same filename.
  2. The downloader schedules both URLs concurrently.
  3. Both tasks derive the same .temp/<filename>.tmp and final destination.
  4. Both tasks write to or rename the shared temp ...[truncated 467 chars]
Remediation
View remediation

Remediation Suggestions

  • Detect duplicate destination names before starting any download and reject them or require an explicit conflict policy.
  • Derive temporary names from a collision-resistant identifier, such as a UUID or SHA-256 hash of the normalized URL.
  • Use a unique per-run temporary directory created securely by tempfile.
  • Open new temporary files with exclusive creation semantics where possible.
  • Serialize operations that intentionally target the same final path.
  • Perform an atomic final replacement only after the corresponding unique temporary file passes integrity checks.
  • Replace second-resolution timestamp fallback names with cryptographically random unique names.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/smart_download.py:192
Finding

Temporary Directory Cleanup Deletes Files Not Owned by the Current Run

Content
View full analysis

Vulnerability Details

File Location: scripts/smart_download.py, lines 192-198
Vulnerability Type: Unsafe temporary-file cleanup
Risk Level: Medium

Vulnerable Code

python
# 清理临时目录
if self.temp_dir.exists():
    for file in self.temp_dir.iterdir():
        try:
            file.unlink()
        except Exception:
            pass
    print("\n✓ 临时目录已清理")

Technical Analysis

The downloader uses the fixed directory {output_dir}/.temp and deletes every entry found there after processing. It does not record which files were created by the current invocation and does not establish ownership of an existing .temp directory.

Consequently, files placed in that directory by a user, another downloader process, or another application can be deleted. Exceptions are silently suppressed, and the script prints a successful cleanup message even if cleanup failed.

Attack Path

  1. The selected output directory already contains a .temp directory.
  2. That directory contains files created by another process or retained by the user.
  3. The downloader completes its request processing.
  4. Cleanup iterates over all entries in .temp, regardless of origin.
  5. Every unlinkable file is deleted.

A similar collision can occur when multiple downloader instances use the same output directory: one invocation can remove another invocation's active temporary files.

Impact Assessment

Exploitation or accidental triggering can cause local data loss and denial of service for concurrent downloads. Deletion is limited to unlinkable entries directly inside the selected .temp directory under the privileges of the downloader process. The code does not recursively delete directories or independently escalate privileges.

Remediation
View remediation

Remediation Suggestions

  • Create a unique temporary directory for each invocation using tempfile.TemporaryDirectory or tempfile.mkdtemp.
  • Track every temporary path created by the current run and delete only those paths.
  • Never sweep a pre-existing shared directory.
  • Coordinate concurrent runs through unique run identifiers rather than a common .temp directory.
  • Report cleanup failures instead of suppressing every exception.
  • Remove the unique run directory only after confirming that it belongs to the current invocation and contains no untracked entries.

T08 · Insecure Dependencies

Note
Location
scripts/smart_download.py:27
Finding

Unpinned Dependency Installation Guidance Creates Supply-Chain Exposure

Content
View full analysis

Vulnerability Details

File Location: scripts/smart_download.py, lines 27-32; related version claims appear in SKILL.md, lines 72-77
Vulnerability Type: Unpinned and unnecessary third-party dependencies
Risk Level: Low

Vulnerable Code

python
except ImportError as e:
    print(f"错误: 缺少必要的依赖库")
    print(f"请安装: pip install httpx aiofiles rich")
    sys.exit(1)

The documentation separately lists versions:

markdown
## Tech Stack

- Python 3.11+
- httpx 0.28.1
- aiofiles 25.1.0
- rich 14.3.3

Technical Analysis

The runtime installation instruction asks users to install mutable package names without exact version constraints or package hashes. Although the documentation names particular versions, the actual command does not enforce them. This makes installations non-reproducible and allows pip to resolve different future releases.

The installation instruction also includes aiofiles, although the implementation does not import or use that package. Every unnecessary dependency increases the package-resolution and supply-chain attack surface.

No evidence was found that the named packages are malicious. The risk arises from unsafe dependency-management guidance rather than a confirmed compromised dependency.

Attack Path

  1. A required import fails on a user's system.
  2. The program instructs the user to run pip install httpx aiofiles rich.
  3. Pip resolves whatever package versions are current under the user's configured indexes.
  4. A compromised future release, unsafe package index, or incompatible version may be installed.
  5. Installed package code executes during import or subsequent downloader operation with the user's privileges.

Impact Assessment

If dependency resolution is compromised, third-party code can execute with the same operating-system privileges as the user running the installation or downloader. Under normal trusted package-index condi ...[truncated 181 chars]

Remediation
View remediation

Remediation Suggestions

  • Provide a reviewed lock file or requirements file with exact versions and cryptographic hashes.
  • Install dependencies with hash enforcement, such as pip install --require-hashes -r requirements.txt.
  • Ensure documented versions and enforced versions are identical.
  • Remove aiofiles from installation guidance unless it is actually required.
  • Use a trusted package index and review dependency updates before changing the lock file.
  • Run the downloader in an isolated virtual environment with least-privilege permissions.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (3)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill advertises and instructs use of file reading and outbound network access, but it does not declare any explicit tool scope such as permissions or allowed-tools. This creates a governance and least-privilege gap: an agent or reviewer cannot easily tell what capabilities the skill requires, increasing the chance of unintended file access or external communication when the skill is invoked.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill is explicitly designed to make outbound requests and supports user-supplied headers and proxy settings, yet the description does not clearly warn that URLs, headers, and proxy configuration will be sent to external systems. This omission can lead users to provide sensitive tokens, internal URLs, or proxy endpoints without understanding the privacy and SSRF-style exposure risks.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

The file’s user-facing title, descriptions, help text, and runtime messages are all written in Chinese, which imposes a specific language/locale on users. The policy allows locale constraints only when they are explicitly justified or when the user is offered a language choice, neither of which appears here.

Content

No source excerpt is available for this finding.