Back to skill

Security audit

Stirling Pdf

Security checks for vulnerabilities and agentic risk

Overview

This PDF skill mostly matches a self-hosted Stirling-PDF helper, but it includes unsafe helper scripts that can send sensitive PDFs and API keys to an undocumented network address and expose users to command injection.

Review carefully before installing. Only use this with a Stirling-PDF instance you control, prefer HTTPS for any non-local endpoint, avoid sensitive PDFs until the fixed 192.168.31.158 default and STIRLING_BASE_URL mismatch are removed, and do not use the merge helpers with untrusted filenames. Pin the Docker image to a reviewed digest and avoid passing document passwords as command-line arguments.

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

other

Error
Location
scripts/stirling-pdf.sh:6
Finding

Undocumented Transmission of PDFs and API Credentials to a Fixed Network Host

Content
View full analysis

Vulnerability Details

File Location: scripts/stirling-pdf.sh:6-7, with sensitive transmission occurring at scripts/stirling-pdf.sh:24-27 and equivalent API operations
Vulnerability Type: Undisclosed data transmission to a fixed network endpoint
Risk Level: Critical

Vulnerable Code

bash
BASE_URL="${STIRLING_API_URL:-http://192.168.31.158:3998}"
API_KEY="${STIRLING_API_KEY:-}"
bash
ocr)
  lang="${1:-chi_sim+eng}"
  curl -s -X POST "$BASE_URL/api/v1/misc/ocr-pdf" \
    $(auth_header) \
    -F "fileInput=@${2?}" \
    -F "languages=$lang" \
    -o "${2%.pdf}_ocr.pdf"

The same fixed destination is also used by the merge, compression, blank-page removal, and health-check operations.

Technical Analysis

The documentation instructs users to configure STIRLING_BASE_URL and states that the service defaults to localhost. This script instead reads the differently named STIRLING_API_URL and defaults to the fixed LAN address http://192.168.31.158:3998.

Therefore, setting STIRLING_BASE_URL as documented has no effect on this script. Unless the undocumented STIRLING_API_URL variable is explicitly set, PDF contents and the STIRLING_API_KEY credential are transmitted to the fixed LAN host.

The destination uses unencrypted HTTP. Any attacker capable of observing or modifying traffic on the local network could intercept the API key, capture sensitive documents, or tamper with responses.

Attack Path

  1. A user follows SKILL.md and sets STIRLING_BASE_URL and STIRLING_API_KEY.
  2. The user invokes scripts/stirling-pdf.sh to process a sensitive PDF.
  3. The script ignores the documented STIRLING_BASE_URL variable.
  4. It defaults to http://192.168.31.158:3998.
  5. The script sends the API key in the X-API-KEY header and uploads the PDF to that host over plaintext HTTP.
  6. The operator of that host, or an attacker able to intercept LAN traffic, obtains the ...[truncated 653 chars]
Remediation
View remediation

Remediation Suggestions

  • Remove the fixed 192.168.31.158 destination.

  • Use the documented variable consistently:

    bash
    BASE_URL="${STIRLING_BASE_URL:-http://localhost:3998}"
    
  • Reject non-loopback destinations by default or require explicit user confirmation before uploading documents to them.

  • Require HTTPS for every non-loopback endpoint.

  • Validate the destination against an administrator-controlled allowlist where possible.

  • Clearly display the effective destination before transmitting sensitive files.

  • Rotate any API key that may have been transmitted to the fixed endpoint.

  • Consolidate the two helper scripts so they cannot implement conflicting environment-variable and endpoint behavior.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/stirling-pdf.sh:33
Finding

Arbitrary Shell Command Execution Through Unsafe eval in Merge Operation

Content
View full analysis

Vulnerability Details

File Location: scripts/stirling-pdf.sh:33-39
Vulnerability Type: Shell command injection
Risk Level: Critical

Vulnerable Code

bash
cmd="curl -s -X POST $BASE_URL/api/v1/general/merge-pdfs $(auth_header)"
for f in "${files[@]}"; do
  cmd="$cmd -F fileInput[]=@$f"
done
cmd="$cmd -F sortType=orderProvided -o merged.pdf"
eval "$cmd"
echo "Saved: merged.pdf"

Technical Analysis

The merge operation constructs a shell command as a string and executes it with eval. The command incorporates PDF filenames, BASE_URL, and output generated from the API key without safe shell quoting.

eval causes shell metacharacters and command substitutions embedded in those values to be interpreted as executable shell syntax. A filename containing characters such as semicolons, redirections, backticks, or $() can therefore escape the intended curl argument and execute arbitrary commands.

Environment values are also injection sources. An attacker who can influence STIRLING_API_URL or STIRLING_API_KEY can insert shell syntax that is evaluated during the merge operation.

Attack Path

  1. An attacker supplies or causes the user to obtain a PDF with a malicious filename containing shell syntax, or influences one of the relevant environment variables.

  2. The user passes the crafted filename to:

    bash
    ./scripts/stirling-pdf.sh merge safe.pdf 'crafted-name.pdf'
    
  3. The script concatenates the untrusted filename into cmd without shell-safe encoding.

  4. eval "$cmd" reparses the resulting string as shell code.

  5. The injected command executes with the privileges of the user running the Skill.

Impact Assessment

Successful exploitation provides arbitrary command execution under the invoking user's account. The attacker could read or modify files accessible to that user, steal environment secrets and API keys, upload additional local data, alter ...[truncated 268 chars]

Remediation
View remediation

Remediation Suggestions

  • Remove eval entirely.

  • Construct curl arguments using a Bash array so each filename remains one argument:

    bash
    args=(
      -s
      -X POST
      -H "X-API-KEY: $API_KEY"
      "$BASE_URL/api/v1/general/merge-pdfs"
    )
    
    for f in "${files[@]}"; do
      args+=(-F "fileInput[]=@$f")
    done
    
    args+=(-F "sortType=orderProvided" -o "merged.pdf")
    curl "${args[@]}"
    
  • Validate that each input exists, is a regular file, and has an expected extension before invoking curl.

  • Pass headers directly as quoted arguments rather than generating shell fragments through auth_header.

  • Treat environment variables as untrusted input and validate the URL scheme and destination.

  • Add regression tests using filenames containing spaces, semicolons, command substitutions, leading hyphens, and newline characters.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/pdf-toolkit.sh:34
Finding

Curl Argument Injection Through Unquoted Merge Command Expansion

Content
View full analysis

Vulnerability Details

File Location: scripts/pdf-toolkit.sh:34-41
Vulnerability Type: Argument injection and unintended file upload
Risk Level: High

Vulnerable Code

bash
files=("$@")
if [ ${#files[@]} -lt 2 ]; then echo "Need 2+ files"; exit 1; fi
for f in "${files[@]}"; do
  cmd="$cmd -F fileInput[]=@$f"
done
do_curl -X POST "$BASE_URL/api/v1/general/merge-pdfs" \
  -F sortType=orderProvided $cmd \
  -o merged.pdf
echo "Saved: merged.pdf"

Technical Analysis

Although this implementation does not use eval, it concatenates filenames into the scalar variable cmd and later expands $cmd without quotes. Bash performs word splitting and pathname expansion on that value.

Filenames containing whitespace are broken into multiple curl arguments. Crafted names can introduce additional -F fields or other curl options, manipulate the request, select unintended local files for upload, or alter curl behavior. This also causes ordinary filenames containing spaces to be handled incorrectly.

The risk is amplified because curl form syntax uses @path to read and upload local files. Injected form arguments can consequently cause files other than the PDFs selected by the user to be sent to the configured endpoint.

Attack Path

  1. An attacker supplies a PDF with a filename crafted to produce additional tokens after unquoted expansion.
  2. The user invokes the merge command with that file and another PDF.
  3. The script appends the filename to the scalar cmd.
  4. The unquoted $cmd expansion is split into multiple command-line arguments.
  5. Curl interprets attacker-controlled tokens as separate options or form fields.
  6. Curl may upload unintended local files, manipulate the HTTP request, or write output in an unintended manner, depending on the injected arguments and available files.

Impact Assessment

Exploitation can compromise the confidentiality of files readable by t ...[truncated 442 chars]

Remediation
View remediation

Remediation Suggestions

  • Replace the scalar command fragment with a Bash array:

    bash
    curl_args=(
      -X POST
      "$BASE_URL/api/v1/general/merge-pdfs"
      -F "sortType=orderProvided"
    )
    
    for f in "${files[@]}"; do
      curl_args+=(-F "fileInput[]=@$f")
    done
    
    curl_args+=(-o "merged.pdf")
    do_curl "${curl_args[@]}"
    
  • Quote every variable expansion used as a command argument.

  • Validate each path with checks such as [[ -f "$f" ]].

  • Consider resolving inputs to canonical paths after validation.

  • Use -- where supported to terminate option parsing, while retaining arrays as the primary defense.

  • Add tests for filenames containing spaces, glob characters, newlines, and option-like substrings.

T08 · Insecure Dependencies

Warning
Location
SKILL.md:35
Finding

Unpinned Mutable Docker Image Used in Deployment Instructions

Content
View full analysis

Vulnerability Details

File Location: SKILL.md:35-49
Vulnerability Type: Insecure and non-reproducible dependency acquisition
Risk Level: Medium

Vulnerable Code

bash
docker run -d \
  --name stirling-pdf \
  -p 3998:8080 \
  -v /opt/stirling-pdf/data:/data \
  -v /opt/stirling-pdf/logs:/logs \
  -e DOCKER_ENABLE=true \
  -e PUID=1000 \
  -e PGID=1000 \
  -e SECURITY_INITIALLOGIN_USERNAME=admin \
  -e SECURITY_INITIALLOGIN_PASSWORD=your-password \
  --restart unless-stopped \
  frooodle/s-pdf:latest

Technical Analysis

The deployment instructions use the mutable latest tag without pinning a reviewed release or immutable image digest. The content referenced by this tag can change after the Skill has been audited, making deployments non-reproducible.

If the upstream account, registry, build process, or tag is compromised, following the documented command can execute altered container code without any corresponding change to this repository. The container receives persistent volume mounts, published network access, environment configuration, and an automatic restart policy.

No evidence in the audited project proves that the referenced image is currently malicious. The confirmed issue is the unsafe dependency-pinning practice and resulting supply-chain exposure.

Attack Path

  1. The upstream latest tag is updated, compromised, or unintentionally points to a vulnerable build.
  2. A user follows the deployment instructions or pulls the image during a later deployment.
  3. Docker retrieves the changed image without verifying it against a project-pinned digest.
  4. The altered code starts with access to the mounted data and log directories and the published service port.
  5. The container can access or modify mounted data and communicate over networks according to the Docker host's configuration.

Impact Assessment

A compromised image could process and disclose sensit ...[truncated 530 chars]

Remediation
View remediation

Remediation Suggestions

  • Pin Stirling-PDF to a reviewed release and immutable SHA-256 digest:

    bash
    stirlingtools/stirling-pdf:REVIEWED_VERSION@sha256:VERIFIED_DIGEST
    
  • Verify the current official image repository and avoid obsolete or unofficial image names.

  • Document a controlled upgrade process that includes release-note review, vulnerability scanning, and digest updates.

  • Use signature verification or provenance validation where supported.

  • Run the container as a non-root user and apply read-only mounts where write access is unnecessary.

  • Restrict container capabilities, network access, and filesystem access to the minimum required.

  • Avoid embedding real initial passwords directly in shell history; use Docker secrets or an appropriately protected environment file.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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 (14)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The declared description significantly overstates the functionality present in the supplied code. The script does use the Stirling-PDF API and does perform some PDF-related operations consistent with the description (OCR, merge, compress), but it only exposes a small subset of the claimed toolkit features. There is no implementation of split, watermark, convert, or anything close to 30+ operations in this chunk. Additionally, the code depends on making HTTP requests to a Stirling-PDF server at a configurable URL, so its behavior is more accurately described as an API wrapper/helper script than a complete local PDF toolkit. No unrelated malicious behavior is evident, but the description does not accurately represent the actual scope and nature of the code.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill documents shell-capable setup and operational commands but does not declare any tool scope or allowed-tools restrictions. In an agent environment, that omission weakens least-privilege boundaries and can allow broader shell use than users expect, increasing the chance of unintended command execution or environment access.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
75% confidence
Finding

Docker image references without a specific tag (:latest is implicit) or digest (@sha256:...) can be silently replaced by a malicious image.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill is presented as a local PDF toolkit, but it actually sends files to an HTTP API and requires an API key from the environment. That creates an unexpected trust boundary: sensitive documents and credentials are exposed to a separate service, which is especially risky because the default endpoint uses plaintext HTTP on localhost and could be reconfigured to a remote host.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The script transmits PDF contents over HTTP requests without any warning to the user, and the default base URL uses plaintext HTTP. If the endpoint is remote or traffic leaves the host, documents and API credentials could be intercepted or exposed, which is dangerous for potentially sensitive PDFs.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The encrypt/decrypt commands accept passwords as positional shell arguments and then transmit them to the API service. Positional passwords may be exposed through shell history or process listings, and sending them to the service without explicit warning compounds the risk by disclosing document secrets to another component over the network.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The script requires network access and an API key despite being described as a local toolkit, which indicates hidden external dependencies and a misleading security model. Users may unknowingly expose documents and credentials to another service because the operational behavior is not aligned with the advertised context.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill is presented as a 'local PDF toolkit', but the implementation sends documents to an HTTP API service instead of processing them locally. This is dangerous because users may provide sensitive PDFs under a false assumption of local-only handling, creating an undisclosed confidentiality and trust boundary violation.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The OCR command uploads the provided PDF to the API service without any user-facing warning, confirmation, or disclosure. For potentially sensitive PDFs, silent transmission can cause unintended data exposure, especially since the default endpoint is an HTTP address on a private network rather than an explicitly local in-process tool.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The merge command transmits multiple PDFs to the API service without disclosure, increasing the volume and sensitivity of exposed data. Because several files may be combined in a single operation, this can amplify confidentiality risk and make accidental bulk leakage more severe.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The compression command sends PDF contents to the API service without making that network transfer obvious to the user. This is a confidentiality issue because users may assume compression is a local operation and submit sensitive files that are actually transmitted elsewhere.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The remove-blanks command uploads the PDF to the API service without warning or informed consent. Even though the operation seems innocuous, it still transfers full document contents and can expose private information to another host or service operator.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

The comments describe this as a CLI helper for a local PDF toolkit, but the implementation uploads user documents to an API endpoint for processing. This mismatch can mislead users into handling sensitive PDFs under false assumptions about locality and privacy.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The OCR command defaults to a specific language set, "chi_sim+eng", which imposes a locale/language choice when the user does not provide one. This is a natural-language policy concern because the script does not ask for or document user opt-in for that default language behavior.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.