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

Content
View full analysis

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

Content

No source excerpt is available for this finding.

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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
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.

Content

Scanner excerpt · README.md (reported line 20)May include surrounding context.

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

External Transmission

Medium
Category
Data Exfiltration
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.

Content

Scanner excerpt · README.md (reported line 22)May include surrounding context.

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

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
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.

Content

Scanner excerpt · SKILL.md (reported line 43)May include surrounding context.

md
## API endpoints
Base URL:
- `https://api.xss-cross-service-solutions.com/solutions/solutions`

Create watermark job:
- `POST /api/61`

External Transmission

Medium
Category
Data Exfiltration
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.

Content

Scanner excerpt · scripts/add-watermark-to-pdf.py (reported line 30)May include surrounding context.

python
import requests


DEFAULT_BASE_URL = "https://api.xss-cross-service-solutions.com/solutions/solutions"
CREATE_PATH = "/api/61"

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
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.

Content

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

text
requests>=2.32.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
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.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.