Back to skill

Security audit

Add watermark to PDF

Security checks for vulnerabilities and agentic risk

Overview

This PDF watermarking skill is not covertly malicious, but it should be reviewed because it can send PDFs, watermark text, and the API key to an arbitrary configured URL.

Install only if you trust the Solutions API and are comfortable uploading the selected PDFs and watermark text to that third-party service. Do not process confidential, regulated, or customer documents unless the provider's retention, access, and deletion practices are acceptable. Avoid using --base-url or SOLUTIONS_BASE_URL unless it is locked to a trusted HTTPS endpoint, because a malicious or mistaken override could disclose the PDFs and API key.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/add-watermark-to-pdf.py:217
Finding
Unrestricted API Base URL Allows Credential and PDF Exfiltration## Vulnerability Details **File Location**: `scripts/add-watermark-to-pdf.py:217-220`, with the sensitive-data transmission occurring at `scripts/add-watermark-to-pdf.py:52-67` and the unvalidated value reaching that operation at `scripts/add-watermark-to-pdf.py:271-276` **Vulnerability Type**: Unvalidated destination for sensitive network transmission **Risk Level**: High ### Vulnerable Code ```python def create_job( base_url: str, api_key: str, pdf_paths: List[str], text: str, timeout_s: int = 180, ) -> Dict[str, Any]: url = base_url.rstrip("/") + CREATE_PATH headers = make_headers(api_key) files: List[Tuple[str, Tuple[str, Any, str]]] = [] opened = [] try: for p in pdf_paths: f = open(p, "rb") opened.append(f) files.append(("files", (os.path.basename(p), f, "application/pdf"))) data = {"text": text} resp = requests.post(url, headers=headers, files=files, data=data, timeout=timeout_s) ``` ```python ap.add_argument( "--base-url", default=os.getenv("SOLUTIONS_BASE_URL", DEFAULT_BASE_URL), help="Base URL override", ) ``` ```python created = create_job( base_url=args.base_url, api_key=args.api_key, pdf_paths=pdf_paths, text=text, ) ``` ### Technical Analysis The script permits the API destination to be replaced through either the `--base-url` command-line argument or the `SOLUTIONS_BASE_URL` environment variable. It performs no validation of the URL scheme, hostname, port, embedded credentials, or relationship to the documented Solutions API domain. The supplied value flows directly into `create_job()`, which appends `/api/61` and sends an HTTP request containing: - The Solutions API key in the `Authorization: Bearer` header - The complete contents of every selected PDF - The supplied watermark text - The input file base names Cons ...[truncated 1850 chars]
Remediation
## Remediation Suggestions 1. Remove `--base-url` and `SOLUTIONS_BASE_URL` support if alternate service deployments are not a documented requirement. 2. If an override is required, parse it with a standard URL parser and enforce an explicit allowlist of trusted hostnames. 3. Require the `https` scheme and reject plaintext HTTP, embedded user information, fragments, unexpected ports, and malformed URLs. 4. Canonicalize and validate the hostname before appending endpoint paths. Do not use substring or suffix checks that permit deceptive domains. 5. Avoid forwarding the Authorization header across redirects. Disable redirects for sensitive API requests or validate every redirect destination against the same allowlist. 6. Prefer an administrator-controlled configuration file over process-environment or agent-generated destination overrides. 7. Clearly notify users that their PDFs and watermark text are uploaded to the named third-party service, and obtain appropriate consent for confidential documents. 8. Add automated tests confirming rejection of `http://` URLs, untrusted domains, embedded credentials, deceptive subdomains, unexpected ports, and redirects to untrusted hosts.
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 (11)

Lp1

High
Category
MCP Least Privilege
Confidence
97% confidence
Finding
The script makes outbound HTTPS requests to a remote service using requests.post and requests.get, but network capability is not declared. Undeclared network access is risky because it allows local files and secrets to be transmitted off-host without transparent permissioning, and in this skill that network access is core to the PDF upload workflow.

Lp1

High
Category
MCP Least Privilege
Confidence
97% confidence
Finding
The script makes outbound HTTPS requests to a remote service using requests.post and requests.get, but network capability is not declared. Undeclared network access is risky because it allows local files and secrets to be transmitted off-host without transparent permissioning, and in this skill that network access is core to the PDF upload workflow.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The README explicitly instructs users to upload PDF documents and watermark text to a third-party API, but it does not warn that document contents will leave the local environment or discuss privacy, confidentiality, retention, or compliance implications. Because PDFs often contain sensitive business or personal data, omission of this disclosure can lead users to transmit regulated or confidential material without informed consent.

External Transmission

Medium
Category
Data Exfiltration
Content
## How it works
1) Upload PDFs + watermark text to:
   `POST https://api.xss-cross-service-solutions.com/solutions/solutions/api/61`
2) Poll:
   `GET  https://api.xss-cross-service-solutions.com/solutions/solutions/api/<job_id>`
3) Return `output.files[].path` as download URL(s)
Confidence
89% confidence
Finding
This line documents transmission of user-supplied PDFs and watermark text to an external domain, which creates a real data exfiltration/privacy risk if users process sensitive files. In this skill's context, the external transfer is core functionality rather than covert behavior, but it is still security-relevant because users may not expect confidential documents to be uploaded off-host.

External Transmission

Medium
Category
Data Exfiltration
Content
1) Upload PDFs + watermark text to:
   `POST https://api.xss-cross-service-solutions.com/solutions/solutions/api/61`
2) Poll:
   `GET  https://api.xss-cross-service-solutions.com/solutions/solutions/api/<job_id>`
3) Return `output.files[].path` as download URL(s)

## Script (CLI)
Confidence
82% confidence
Finding
The polling endpoint confirms continued interaction with the same external service and may expose job metadata or facilitate retrieval of processed files from a third party. While expected for asynchronous processing, it reinforces that the workflow depends on off-platform handling of user documents and should therefore be treated as an external transmission risk.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill processes user-supplied PDFs and watermark text by uploading them to an external third-party API, but the user-facing description does not clearly warn about that data transfer at the point of use. This creates a privacy and data-handling risk because users may provide sensitive documents or confidential watermark text without informed consent.

External Transmission

Medium
Category
Data Exfiltration
Content
## API endpoints
Base URL:
- `https://api.xss-cross-service-solutions.com/solutions/solutions`

Create watermark job:
- `POST /api/61`
Confidence
87% confidence
Finding
The skill is explicitly designed to transmit uploaded PDFs and watermark text to an external service endpoint, which is a real external data exfiltration path from the agent environment. While this may be functionally necessary, it is still security-relevant because sensitive files, watermark contents, and possibly metadata leave the local trust boundary and are exposed to third-party handling and downloadable result URLs.

External Transmission

Medium
Category
Data Exfiltration
Content
import requests


DEFAULT_BASE_URL = "https://api.xss-cross-service-solutions.com/solutions/solutions"
CREATE_PATH = "/api/61"
Confidence
91% confidence
Finding
The hardcoded external endpoint shows the skill is designed to transmit data to a third-party service. While external communication is not inherently malicious, it becomes security-relevant here because the transmitted content includes user-supplied PDFs and text, and the unusual domain naming increases the need for scrutiny and explicit trust validation.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The code uploads the provided PDF files and watermark text to a remote API, yet there is no explicit warning in the execution flow that user documents and entered text leave the local environment. This is a privacy and data-governance issue because users may reasonably assume a watermarking utility operates locally, while sensitive PDFs could be disclosed to an external service.

Unpinned Dependencies

Low
Category
Supply Chain
Content
requests>=2.32.0
Confidence
95% confidence
Finding
The dependency is specified as `requests>=2.32.0`, which allows future unreviewed versions to be installed and makes builds non-reproducible. In a skill that uploads files to an external API and likely handles URLs and HTTP responses, dependency drift can unexpectedly introduce vulnerable or breaking releases into the execution environment.

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
88% confidence
Finding
Because `requests` is not pinned, it is impossible to verify whether the installed version is affected by known advisories, including issues involving credential leakage and TLS/request verification behavior. Given this skill’s purpose of uploading PDFs to a remote API and returning download URLs, use of an affected HTTP client version could expose sensitive file contents, credentials, or request integrity.

Static analysis

No suspicious patterns detected.