Back to skill

Security audit

Universal Watermarker

Security checks for vulnerabilities and agentic risk

Overview

This watermarking skill appears purpose-built, but it needs review because it automatically fetches an unchecked font from GitHub and uses non-reproducible dependencies.

Review before installing. Use this only in an isolated environment where outbound access to GitHub and unpinned Python packages are acceptable, and avoid running it in directories containing important existing wm_* files. The publisher should bundle or hash-pin the font, pin dependencies, document network access, and add overwrite protection.

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

T08 · Insecure Dependencies

Warning
Location
main.py:17
Finding
Unverified Download of a Mutable Font Asset## Vulnerability Details **File Location**: `main.py`, lines 17-39 **Vulnerability Type**: Unverified third-party component retrieval **Risk Level**: Medium ```python font_dir = "./fonts" font_name = "AlibabaPuHuiTi-3-65-Medium.ttf" font_path = os.path.join(font_dir, font_name) # 必须使用 raw.githubusercontent.com 获取真实二进制文件 raw_url = "https://raw.githubusercontent.com/cribug/universal-watermarker/main/fonts/AlibabaPuHuiTi-3-65-Medium.ttf" if not os.path.exists(font_path): print(f"⏳ 检测到首次运行,正在自动拉取核心字体: {font_name} ...") # 自动创建 fonts 文件夹 os.makedirs(font_dir, exist_ok=True) try: # 伪装 User-Agent,防止 GitHub API 拦截爬虫 req = urllib.request.Request( raw_url, headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'} ) with urllib.request.urlopen(req) as response, open(font_path, 'wb') as out_file: # 将流写入本地文件 out_file.write(response.read()) print("✅ 字体文件下载并部署成功!环境已就绪。") except Exception as e: print(f"❌ 字体下载失败,请检查网络或 URL: {e}") # 如果下载失败,抛出异常阻断后续运行,符合我们“宁可报错不可乱码”的原则 raise RuntimeError("初始化环境失败,无法获取字体文件。") ``` ### Technical Analysis On first execution, the Skill retrieves a TTF file from the mutable `main` branch of an external GitHub repository. Although HTTPS provides transport protection, the downloaded content is not pinned to an immutable commit and is not verified using a cryptographic digest or digital signature. No response-size limit or explicit font-format validation is applied before the response is written to disk. The downloaded file is subsequently parsed by Pillow and ReportLab during watermark generation. Fonts are complex binary inputs, and a malicious or malformed font could exercise vulnerabilities in those libraries or their underlying font-processing components. Bundling a reviewed font would eliminat ...[truncated 1479 chars]
Remediation
## Remediation Suggestions 1. Bundle a reviewed and properly licensed font inside the Skill package so runtime network access is unnecessary. 2. If downloading remains necessary, reference an immutable repository commit rather than the mutable `main` branch. 3. Store the expected SHA-256 digest in source code and verify the complete download before moving it into the fonts directory. 4. Download into a securely created temporary file, validate its size and format, and atomically rename it only after successful verification. 5. Apply a strict response-size limit and reject redirects to unapproved hosts. 6. Delete partial or invalid files after any error. 7. Document the network requirement and destination domain clearly, and require explicit approval where the execution environment supports permission prompts.

T08 · Insecure Dependencies

Note
Location
requirements.txt:1
Finding
Non-Reproducible Dependencies Specified Without Upper Bounds or Hashes## Vulnerability Details **File Location**: `requirements.txt`, lines 1-3 **Vulnerability Type**: Insufficient dependency pinning and integrity control **Risk Level**: Low ```text pypdf>=3.0.0 reportlab>=4.0.0 Pillow>=10.0.0 ``` ### Technical Analysis All dependencies use lower-bound-only version constraints. A new installation can therefore resolve to any later release available from the configured package index. This makes installations non-reproducible and allows code that was not part of the audited dependency set to enter the runtime automatically. No package hashes are supplied, so installation integrity depends entirely on the package index and transport configuration. This is not evidence that any listed package is malicious; the weakness is the absence of controls that ensure deployment uses the exact versions and artifacts reviewed by the project. ### Attack Path 1. A future dependency release is compromised, malicious, or introduces a security regression. 2. A user or deployment system installs the Skill in a fresh environment. 3. The package resolver selects the affected version because it satisfies the unrestricted lower-bound constraint. 4. The package is imported by `main.py` or invoked while processing attacker-controlled PDF, image, or font content. 5. The affected dependency executes malicious behavior or exposes the process to exploitation. ### Impact Assessment A compromised Python package executes during installation or import with the permissions of the installing or running user. Depending on those permissions, this could expose files, network credentials, input documents, or other resources available to the process. No privilege escalation beyond the package manager or Skill process's existing authority is demonstrated by the project itself.
Remediation
## Remediation Suggestions 1. Pin each dependency to a reviewed exact version, such as `package==x.y.z`. 2. Generate and commit a lock file appropriate to the deployment workflow. 3. Require hashes for all packages and transitive dependencies, for example through `pip install --require-hashes`. 4. Test and review dependency upgrades before changing the lock file. 5. Use automated vulnerability and provenance scanning for direct and transitive dependencies. 6. Install packages from an explicitly configured trusted index in an isolated virtual environment under a non-privileged account.

T09 · Insecure Skill Coding Practices

Note
Location
main.py:305
Finding
Predictable Output Names Permit Silent Overwriting of Existing Files## Vulnerability Details **File Location**: `main.py`, lines 169-172, 264-265, and 305-316 **Vulnerability Type**: Unsafe output-file handling **Risk Level**: Low ```python # Image output is saved directly to the selected destination. combined = Image.alpha_composite(base, txt_layer) if output_path.lower().endswith(('.jpg', '.jpeg')): combined.convert("RGB").save(output_path, "JPEG", quality=90) else: combined.save(output_path) ``` ```python # PDF output is opened in truncating write mode. with open(output_path, "wb") as f: writer.write(f) ``` ```python ext = os.path.splitext(f)[1].lower() output_name = os.path.join(os.path.dirname(f), f"wm_{os.path.basename(f)}") try: if ext == '.pdf': add_pdf_watermark(f, output_name, text, opacity, scale, mode, angle, auto_adjust, color, font_path) elif ext in ['.jpg', '.jpeg', '.png', '.bmp']: add_image_watermark(f, output_name, text, opacity, scale, mode, angle, auto_adjust, color, font_path) results.append(output_name) print(f"成功处理: {output_name}") except Exception as e: print(f"处理 {f} 失败: {str(e)}") ``` ### Technical Analysis The output path is deterministically derived by adding `wm_` to the source basename. The code does not check whether that path already exists and does not request overwrite approval. PDF output uses `open(..., "wb")`, which truncates an existing file. Pillow's save operation similarly replaces an existing destination. Because output is written alongside the user-selected source, this behavior remains within the declared storage location. However, it can destroy an earlier watermarked file or another same-name file without warning. The implementation also lacks atomic output replacement, so an interrupted operation can leave a partial or corrupt result. ### Attack Path 1. A file named `wm_<source-name>` already exists beside the selected source file. 2. The user invokes ...[truncated 601 chars]
Remediation
## Remediation Suggestions 1. Check whether the destination exists before processing and reject the operation unless the caller explicitly enables overwriting. 2. Generate a unique output name when overwrite permission has not been provided. 3. Write results to a temporary file in the destination directory and atomically rename it after successful completion. 4. Where practical, use exclusive file creation to reduce time-of-check/time-of-use races. 5. Return a clear structured error when an output collision occurs. 6. Avoid adding a path to `results` for unsupported extensions or operations that did not produce an output file.
Vulnerability Patterns
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (12)

Lp3

Medium
Category
MCP Least Privilege
Confidence
79% confidence
Finding
The skill does not declare any explicit tool scope or permissions, yet static analysis detected network-capable behavior. Undeclared network capability is dangerous because it can enable unexpected outbound communication, dependency fetching, telemetry, or exfiltration without user awareness, especially in a file-processing skill that handles potentially sensitive documents.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
SQP-3 covers natural-language policy violations such as forcing a specific language without user opt-in. The user-facing description and instructions are presented in Chinese only, with no indication that other languages are supported or that the user can choose their preferred language.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
The file's natural-language instructions and user-facing messaging are written exclusively in Chinese, including the environment setup description. Under the stated policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale constraint is justified, which is not documented here.

Context-Inappropriate Capability

Medium
Confidence
94% confidence
Finding
The skill automatically downloads a font from the network at runtime and writes it to the local filesystem without integrity verification, pinning by cryptographic hash, or any trust control beyond HTTPS. That creates a supply-chain risk: if the remote repository, DNS/TLS path, or dependency source is compromised, the skill will consume attacker-controlled content in an environment processing local files.

Missing User Warnings

Low
Confidence
82% confidence
Finding
For markdown files, SQP-2 applies when the description omits warnings about behaviors that affect user data or the filesystem. Lines L38-L40 describe automatic output file creation in the source file directory, but there is no caution that running the skill will write new files there.

Intent-Code Divergence

Low
Confidence
88% confidence
Finding
The inline comment says the request disguises the client to prevent GitHub API anti-crawler blocking, but the URL being fetched is a raw content URL, not a GitHub API endpoint. This is an active mismatch between documentation intent and the actual request target/behavior.

Unpinned Dependencies

Low
Category
Supply Chain
Content
pypdf>=3.0.0
reportlab>=4.0.0
Pillow>=10.0.0
Confidence
95% confidence
Finding
The dependency is specified with a lower bound only, which allows installation of any newer release, including unreviewed major versions or versions later found to be vulnerable. In a PDF-processing skill, dependency behavior and security posture matter because these libraries may parse attacker-controlled documents, increasing supply-chain and exposure risk.

Unverifiable Dependency: pypdf has 16 known advisory(ies) (CVE-2026-84310 (pypdf: Possible long runtimes/large memory usage when retrieving outlines); CVE-2026-48156 (pypdf: Possible long runtimes for zero-only width values in cross-reference stre); CVE-2026-24688 (pypdf has possible Infinite Loop when processing outlines/bookmarks) +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
92% confidence
Finding
The manifest does not pin pypdf, so it is impossible to verify whether the installed version is affected by known advisories. Since pypdf parses PDF structures and this skill likely handles externally supplied PDFs, vulnerable versions could enable denial of service through excessive CPU or memory consumption during parsing.

Unpinned Dependencies

Low
Category
Supply Chain
Content
pypdf>=3.0.0
reportlab>=4.0.0
Pillow>=10.0.0
Confidence
96% confidence
Finding
reportlab is unpinned, so builds may resolve to different versions over time, including versions with severe known issues. This is especially risky for a document-generation library because historical advisories include high-impact flaws such as code execution and SSRF in document rendering contexts.

Unverifiable Dependency: reportlab has 8 known advisory(ies) (CVE-2023-33733 (Reportlab vulnerable to remote code execution); CVE-2020-28463 (Server-side Request Forgery (SSRF) via img tags in reportlab); CVE-2019-19450 (ReportLab vulnerable to remote code execution via paraparser) +5 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
97% confidence
Finding
Because reportlab is not pinned, there is no assurance that installation will avoid versions with known serious advisories. In the context of document generation, historical reportlab issues include remote code execution and SSRF, so leaving the version unverifiable materially raises risk if any user-controlled content reaches rendering features.

Unpinned Dependencies

Low
Category
Supply Chain
Content
pypdf>=3.0.0
reportlab>=4.0.0
Pillow>=10.0.0
Confidence
95% confidence
Finding
Pillow is unpinned, allowing arbitrary newer versions to be installed and making the environment non-reproducible and harder to audit. Because image libraries often process untrusted input and have a history of memory corruption and resource-consumption bugs, this increases the chance of pulling in an exploitable or regressive release.

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
94% confidence
Finding
An unpinned Pillow dependency means the actual installed version cannot be validated against known advisories. Given Pillow's role in decoding image data and its history of code execution and resource-consumption issues, this is risky when processing potentially untrusted images for watermarking.

Static analysis

No suspicious patterns detected.