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## 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
## 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## 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
## 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## 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
## 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## 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
## 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## 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
## 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)

Lp3

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.

Missing User Warnings

Medium
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.

Natural-Language Policy Violations

Low
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.

Static analysis

No suspicious patterns detected.