Back to skill

Security audit

Compress PDF

Security checks for vulnerabilities and agentic risk

Overview

This PDF compressor largely does what it says, but its helper script can be redirected to send the PDF and API token to an arbitrary server.

Review this skill before installing. Use it only for PDFs you are comfortable sending to an external service, prefer environment or secret-manager credentials over command-line tokens, and do not allow untrusted users or wrappers to set --base-url or SOLUTIONS_BASE_URL.

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)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/compress-pdf.py:200
Finding
Arbitrary Base URL Allows PDF and API Credential Exfiltration<![CDATA[ ## Vulnerability Details **File Location**: `scripts/compress-pdf.py:34-52, 69-77, 200` **Vulnerability Type**: Unrestricted sensitive-data destination / SSRF-like behavior **Risk Level**: High ### Vulnerable Code ```python def create_job( base_url: str, api_key: str, pdf_path: str, image_quality: int, dpi: int, timeout_s: int = 60, ) -> Dict[str, Any]: url = base_url.rstrip("/") + CREATE_PATH headers = make_headers(api_key) # multipart/form-data: # - file: PDF document # - imageQuality: number # - dpi: number with open(pdf_path, "rb") as f: files = { "file": (os.path.basename(pdf_path), f, "application/pdf"), } data = { "imageQuality": str(image_quality), "dpi": str(dpi), } resp = requests.post(url, headers=headers, files=files, data=data, timeout=timeout_s) ``` ```python def get_job( base_url: str, api_key: str, job_id: Any, timeout_s: int = 30, ) -> Dict[str, Any]: url = base_url.rstrip("/") + f"/api/{job_id}" headers = make_headers(api_key) resp = requests.get(url, headers=headers, timeout=timeout_s) ``` ```python ap.add_argument("--base-url", default=os.getenv("SOLUTIONS_BASE_URL", DEFAULT_BASE_URL), help="Base URL override") ``` ### Technical Analysis Uploading the PDF and authenticating to the documented Cross-Service-Solutions endpoint are necessary for the declared compression functionality. However, the implementation permits the destination to be replaced through either the `--base-url` argument or the `SOLUTIONS_BASE_URL` environment variable. The supplied URL is not restricted to the declared provider, is not required to use HTTPS, and is not checked against a hostname allowlist. The script subsequently attaches the Bearer token and PDF contents to a request sent to that URL. Consequently, control over the process arguments or environment is sufficient to redirect both sensi ...[truncated 1725 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `--base-url` and `SOLUTIONS_BASE_URL` if alternate service deployments are not a required feature. 2. If configuration is required, parse the URL and enforce: - The `https` scheme. - An explicit allowlist of approved hostnames. - An approved port, normally `443`. - No embedded username or password. - No URL fragment. 3. Ensure the `Authorization` header is only attached when the final request origin exactly matches an approved provider origin. 4. Disable redirects for authenticated upload and polling requests, or manually validate every redirect target before following it. 5. Reject loopback, link-local, private, multicast, and otherwise non-public destination addresses unless explicitly required. 6. Separate trusted administrator configuration from user-controlled Skill input. 7. Add automated tests confirming that HTTP URLs, unapproved domains, IP literals, alternate ports, and cross-origin redirects are rejected before any file or credential is transmitted. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/compress-pdf.py:199
Finding
Bearer Token Accepted Through Process Command-Line Arguments<![CDATA[ ## Vulnerability Details **File Location**: `scripts/compress-pdf.py:8-11, 199`; `README.md:26-33` **Vulnerability Type**: Sensitive credential exposure through process arguments **Risk Level**: Medium ### Vulnerable Code ```python Usage: python scripts/compress_pdf.py --pdf "/path/to/file.pdf" --api-key "..." --image-quality 75 --dpi 144 Or: export SOLUTIONS_API_KEY="..." python scripts/compress_pdf.py --pdf "/path/to/file.pdf" ``` ```python ap.add_argument("--api-key", default=os.getenv("SOLUTIONS_API_KEY", ""), help="SOLUTIONS API key (Bearer token)") ``` The README also demonstrates passing the token through an argument: ```bash python scripts/compress_pdf.py \ --pdf "/path/to/file.pdf" \ --api-key "$SOLUTIONS_API_KEY" \ --image-quality 75 \ --dpi 144 ``` ### Technical Analysis The API key is accepted as a normal command-line argument. Command-line arguments may be visible to local process-inspection tools, process-monitoring agents, diagnostic collectors, CI/CD wrappers, and audit logs while the process is running. The README's environment-variable expansion avoids placing the literal key in shell history when copied exactly, but the shell expands the variable before creating the process. The resulting secret can therefore still appear in the Python process's argument vector. Users who replace the placeholder with the actual key may additionally store it in shell history. The implementation provides an environment-variable fallback, but the documented and supported command-line credential path unnecessarily increases local credential exposure. ### Attack Path 1. A user invokes the script with `--api-key`, either by typing the credential directly or expanding an environment variable. 2. The operating system places the expanded credential in the process argument vector. 3. A local user, monitoring utility, CI component, or process-logging system captures the command line while the process is active. 4. The observer recover ...[truncated 785 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the `--api-key` argument from normal usage. 2. Obtain the token from a protected secret manager, narrowly scoped environment variable, or non-echoing interactive prompt such as `getpass.getpass()`. 3. Update the README and script docstring so they do not recommend command-line credential transmission. 4. Prevent the credential from being included in exception messages, debug output, telemetry, or logs. 5. Use short-lived, narrowly scoped API tokens where supported. 6. Ensure CI/CD systems inject the token through their native secret mechanisms and mask it from logs. 7. Document token rotation procedures in case a credential is exposed. ]]>

T08 · Insecure Dependencies

Note
Location
requirements.txt:1
Finding
Unbounded Dependency Version Prevents Reproducible Installation<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt:1` **Vulnerability Type**: Unpinned third-party dependency **Risk Level**: Low ### Vulnerable Code ```text requests>=2.32.0 ``` ### Technical Analysis The project permits installation of any current or future `requests` release at or above version 2.32.0. This means identical project source can resolve to different dependency versions at different times. No malicious, misspelled, or untrusted package was identified in the audited dependency file. Nevertheless, the open-ended constraint expands supply-chain exposure because future releases can be consumed without project-specific review or testing. It also prevents deterministic builds and makes incident reproduction more difficult. ### Attack Path 1. A future dependency release is compromised, contains a relevant vulnerability, or introduces unsafe behavior. 2. A user installs the project after that release becomes the latest compatible version. 3. The package resolver selects the new version because it satisfies `requests>=2.32.0`. 4. The affected dependency executes within the Skill's process when network requests are made. 5. Any resulting impact occurs with the filesystem, network, and process privileges assigned to the Skill. This is a supply-chain hardening issue; the audit found no evidence that the currently named package is malicious. ### Impact Assessment A compromised or vulnerable dependency would execute in the same Python process as the Skill. Its potential scope would include: - Reading the PDF supplied to the process. - Accessing the API key available to the process. - Observing or modifying outbound API requests and responses. - Accessing other files and network resources permitted to the runtime account. Actual impact depends on the behavior of a future selected release and the privileges of the execution environment. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin `requests` to a specific reviewed version rather than using an unbounded lower constraint. 2. Generate and verify cryptographic package hashes, for example through a hashed lock file. 3. Pin transitive dependencies through a reproducible dependency-management workflow. 4. Use an approved package index and prevent unintended fallback to untrusted indexes. 5. Review dependency updates before changing the lock file. 6. Run automated vulnerability and license scanning as part of the update process. 7. Regularly update the pinned versions so reproducibility does not result in stale vulnerable dependencies. ]]>
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 (12)

Lp1

High
Category
MCP Least Privilege
Confidence
75% confidence
Finding
The skill uses 'env' capability that is not listed in its permissions. This may indicate deceptive intent or missing permission declarations.

Lp1

High
Category
MCP Least Privilege
Confidence
75% confidence
Finding
The skill uses 'network' capability that is not listed in its permissions. This may indicate deceptive intent or missing permission declarations.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The README instructs users to upload PDFs and supply a bearer API key to a third-party service, but it does not clearly warn that document contents and credentials are being transmitted off-platform. This creates a real privacy and trust risk because users may unknowingly send sensitive files or mishandle credentials based on incomplete disclosure.

External Transmission

Medium
Category
Data Exfiltration
Content
- Register / get key: https://login.cross-service-solutions.com/register

## How it works
1) Upload PDF to `POST https://api.xss-cross-service-solutions.com/solutions/solutions/api/29`
2) Poll `GET https://api.xss-cross-service-solutions.com/solutions/solutions/api/<job_id>` until done
3) Return `output.files[0].path` as the download URL
Confidence
90% confidence
Finding
This step explicitly sends a user-provided PDF to an external API endpoint, which is a real data egress event. In the context of a file-processing skill, this is expected behavior, but it is still security-relevant because uploaded PDFs may contain confidential content and the README does not pair the instruction with adequate consent or data-handling warnings.

External Transmission

Medium
Category
Data Exfiltration
Content
## How it works
1) Upload PDF to `POST https://api.xss-cross-service-solutions.com/solutions/solutions/api/29`
2) Poll `GET https://api.xss-cross-service-solutions.com/solutions/solutions/api/<job_id>` until done
3) Return `output.files[0].path` as the download URL

## Defaults
Confidence
88% confidence
Finding
Polling the external API is another outbound interaction with the third-party service and may expose job identifiers or metadata associated with the uploaded document. While normal for asynchronous processing, it increases dependence on the external provider and confirms continued transmission to a remote system handling user data.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill explicitly uploads a user-provided PDF to a third-party service, but it does not clearly warn the user at the point of use that their document contents will leave the local environment and be processed by an external provider. This creates a real privacy and data-handling risk, especially for sensitive PDFs, because users may reasonably assume a local compression operation unless clearly told otherwise.

External Transmission

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

Create compression job:
- `POST /api/29`
Confidence
91% confidence
Finding
This skill is designed to send user-supplied files to an external API endpoint, which is an actual external transmission of potentially sensitive document contents. In context, the transmission is intentional and functionally necessary, but it still represents a security and privacy exposure because PDFs may contain confidential data and the skill provides no trust, retention, or processing guarantees about the third-party service.

External Transmission

Medium
Category
Data Exfiltration
Content
import requests


DEFAULT_BASE_URL = "https://api.xss-cross-service-solutions.com/solutions/solutions"
CREATE_PATH = "/api/29"
Confidence
88% confidence
Finding
The skill is designed to transmit documents to an external domain, which materially changes the trust boundary for user data. In this skill context, external transmission is expected, but it remains dangerous if not prominently disclosed because sensitive PDFs may leave the local environment and be processed by a third party.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The code uploads the full user-provided PDF to a third-party service, but the script itself provides no explicit warning, consent prompt, or data-handling disclosure at the point of transmission. This is dangerous because users may unknowingly send sensitive document contents to an external processor, creating confidentiality and compliance risks.

Unpinned Dependencies

Low
Category
Supply Chain
Content
requests>=2.32.0
Confidence
97% confidence
Finding
The dependency is specified as `requests>=2.32.0` without an upper bound or exact pin, so installations may resolve to different versions over time. This weakens build reproducibility and can unexpectedly introduce vulnerable or incompatible releases into a skill that handles user-supplied PDF uploads and external network communication.

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
92% confidence
Finding
Because `requests` is not pinned, it is not possible to verify whether deployed environments will install a release affected by known advisories. In this skill's context, the package is used for outbound HTTP operations involving user-provided documents and remote URLs, so a vulnerable resolver choice could expose network credentials, request integrity, or sensitive file-handling workflows.

Missing User Warnings

Low
Confidence
84% confidence
Finding
The script accesses a sensitive credential from SOLUTIONS_API_KEY and uses it for remote authentication. While this is functionally expected, the code does not include any user-facing disclosure or comment warning that a bearer token will be consumed and sent to an external service.

Static analysis

No suspicious patterns detected.