CVE-2026-105751
Docling: Arbitrary local file read via draw:image xlink:href in the OpenDocument backend
Docling simplifies document processing by parsing diverse formats and providing integrations with the generative AI ecosystem. From 2.107.0 until 2.120.3, docling/backend/opendocument_backend.py uses the xlink:href attribute value of a draw:image element as a filesystem path when the referenced part is not found in the document archive. The _image_ref_from_odf_image function reads that attacker-controlled path without a scheme check, extraction-directory confinement, or the enable_local_fetch setting used by other backends. Readable files that Pillow can decode as images are embedded in converted output, and other existing paths can be distinguished through the attempted read. This issue is fixed in 2.120.3.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- Active
What an attacker can affect
- Confidentiality
- High
- Integrity
- None
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
6.9
Docling: Arbitrary local file read via draw:image xlink:href in the OpenDocument backend
Docling simplifies document processing by parsing diverse formats and providing integrations with the generative AI ecosystem. From 2.107.0 until 2.120.3, docling/backend/opendocument_backend.py uses the xlink:href attribute value of a draw:image element as a filesystem path when the referenced part is not found in the document archive. The _image_ref_from_odf_image function reads that attacker-controlled path without a scheme check, extraction-directory confinement, or the enable_local_fetch setting used by other backends. Readable files that Pillow can decode as images are embedded in converted output, and other existing paths can be distinguished through the attempted read. This issue is fixed in 2.120.3.
- Record
CVE-2026-105751- CVSS 4.0
- 6.9 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:H/VI:N/VA:N/SC:L/SI:N/SA:N - Weakness
- CWE-22
- Updated
- 2026-11-02
EUVDEUVD-2026-92815
6.9
Docling simplifies document processing by parsing diverse formats and providing integrations with the generative AI ecosystem. From 2.107.0 until 2.120.3, docling/backend/opendocument_backend.py uses the xlink:href attribute value of a draw:image element as a filesystem path when the referenced part is not found in the document archive. The _image_ref_from_odf_image function reads that attacker-controlled path without a scheme check, extraction-directory confinement, or the enable_local_fetch setting used by other backends. Readable files that Pillow can decode as images are embedded in converted output, and other existing paths can be distinguished through the attempted read. This issue is fixed in 2.120.3.
- Record
EUVD-2026-92815- CVSS 4.0
- 6.9 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:H/VI:N/VA:N/SC:L/SI:N/SA:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-4xhp-xg4w-8ppm
6.9
Docling: Arbitrary local file read via draw:image xlink:href in the OpenDocument backend
Summary
The ODF backend resolves the xlink:href of a draw:image element as a filesystem path whenever
the referenced part is not found inside the document archive. The value comes straight from
content.xml, so it is document content, and it is used with no scheme check, no confinement to
the extraction directory, and without consulting the enable_local_fetch / enable_remote_fetch
controls that the other backends use for exactly this decision.
Converting a crafted .odt therefore causes Docling to open an attacker-named absolute path on
the converting host. Where the target decodes as an image, its complete contents are embedded in
the resulting DoclingDocument and appear in the HTML, Markdown and JSON exports.
Both the read and the disclosure are confirmed by execution. The trigger is a 452-byte archive containing two entries.
Affected component
docling/backend/opendocument_backend.py, in _image_ref_from_odf_image:
image_url = _odf_image_href(image)
...
if image_data is None and odf_obj is not None and image_url:
try:
image_data = odf_obj.get_part(image_url)
except Exception:
image_data = None
if image_data is None and image_url:
image_path = Path(image_url)
if image_path.is_file():
image_data = image_path.read_bytes()
_odf_image_href returns the xlink:href attribute. The in-archive lookup is attempted first;
when it fails, the same string is handed to Path and read from disk.
The only filter on the way in is _odf_image_can_be_bitmap, which admits any path whose suffix is
one of .bmp, .gif, .jpeg, .jpg, .png, .tif, .tiff, .webp, or empty. The
empty-suffix case is what admits most paths of interest on a Unix host, /etc/passwd among them.
The backend does not use the project's own fetch controls
$ grep -n "enable_local_fetch\|enable_remote_fetch\|image_resource_loader" \
docling/backend/opendocument_backend.py
$
Nothing. The HTML, Markdown, EPUB and XBRL backends all resolve image resources through
docling/backend/utils/image_resource_loader.py, where enable_local_fetch and
enable_remote_fetch both default to False. The ODF backend reimplements image loading and does
not consult either, so the default of not fetching local resources is not applied on this path.
That is the substance of the report: the control exists, it is off by default, and this backend does not reach it.
Affected versions
Introduced by commit e2afe381, "feat: Add OpenDocument backend support and improve ODF
image/table handling" (#3480), 2026-06-24. First released in v2.107.0.
Affected: docling >= 2.107.0, up to and including 2.117.0 and current main
(52d8a6f24de7318a9ad4be2a7361ba93fc81a5c1).
Subsequent commits touching this file — 2c3e55b3 (skip a draw:object with a missing embedded
part), b627ca91 (preserve content inside sections), 2ec33bc7 (guard backend imports) — address
unrelated defects and leave the path resolution unchanged.
Reproduced on two independent machines:
| | |
|---|---|
| Python 3.12.3 | docling-slim 2.117.0, docling-core 2.88.0, odfdo 3.23.1 |
| Python 3.14.4 | same releases, and again against main clones on sys.path |
Proof of concept
Trigger
An .odt needs only mimetype and content.xml. No manifest, no styles, no embedded image part.
import zipfile
CONTENT = (
'<?xml version="1.0"?><office:document-content '
'xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" '
'xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" '
'xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" '
'xmlns:xlink="http://www.w3.org/1999/xlink" office:version="1.2">'
'<office:body><office:text><text:p>'
'<draw:frame><draw:image xlink:href="{href}"/></draw:frame>'
'</text:p></office:text></office:body></office:document-content>'
)
def build(path, href):
with…
- Record
GHSA-4xhp-xg4w-8ppm- CVSS 4.0
- 6.9 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:H/VI:N/VA:N/SC:L/SI:N/SA:N - Weakness
- CWE-22
- Updated
- 2026-11-02
NVD
6.9
Docling simplifies document processing by parsing diverse formats and providing integrations with the generative AI ecosystem. From 2.107.0 until 2.120.3, docling/backend/opendocument_backend.py uses the xlink:href attribute value of a draw:image element as a filesystem path when the referenced part is not found in the document archive. The _image_ref_from_odf_image function reads that attacker-controlled path without a scheme check, extraction-directory confinement, or the enable_local_fetch setting used by other backends. Readable files that Pillow can decode as images are embedded in converted output, and other existing paths can be distinguished through the attempted read. This issue is fixed in 2.120.3.
- Record
CVE-2026-105751- CVSS 4.0
- 6.9 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:H/VI:N/VA:N/SC:L/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-22
- Updated
- 2026-11-02
OSV
6.9
Docling: Arbitrary local file read via draw:image xlink:href in the OpenDocument backend
Docling simplifies document processing by parsing diverse formats and providing integrations with the generative AI ecosystem. From 2.107.0 until 2.120.3, docling/backend/opendocument_backend.py uses the xlink:href attribute value of a draw:image element as a filesystem path when the referenced part is not found in the document archive. The _image_ref_from_odf_image function reads that attacker-controlled path without a scheme check, extraction-directory confinement, or the enable_local_fetch setting used by other backends. Readable files that Pillow can decode as images are embedded in converted output, and other existing paths can be distinguished through the attempted read. This issue is fixed in 2.120.3.
- Record
CVE-2026-105751- CVSS 4.0
- 6.9 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:H/VI:N/VA:N/SC:L/SI:N/SA:N - Weakness
- CWE-22
- Updated
- 2026-11-02
OSVGHSA-4xhp-xg4w-8ppm
6.9
Docling: Arbitrary local file read via draw:image xlink:href in the OpenDocument backend
Summary
The ODF backend resolves the xlink:href of a draw:image element as a filesystem path whenever
the referenced part is not found inside the document archive. The value comes straight from
content.xml, so it is document content, and it is used with no scheme check, no confinement to
the extraction directory, and without consulting the enable_local_fetch / enable_remote_fetch
controls that the other backends use for exactly this decision.
Converting a crafted .odt therefore causes Docling to open an attacker-named absolute path on
the converting host. Where the target decodes as an image, its complete contents are embedded in
the resulting DoclingDocument and appear in the HTML, Markdown and JSON exports.
Both the read and the disclosure are confirmed by execution. The trigger is a 452-byte archive containing two entries.
Affected component
docling/backend/opendocument_backend.py, in _image_ref_from_odf_image:
image_url = _odf_image_href(image)
...
if image_data is None and odf_obj is not None and image_url:
try:
image_data = odf_obj.get_part(image_url)
except Exception:
image_data = None
if image_data is None and image_url:
image_path = Path(image_url)
if image_path.is_file():
image_data = image_path.read_bytes()
_odf_image_href returns the xlink:href attribute. The in-archive lookup is attempted first;
when it fails, the same string is handed to Path and read from disk.
The only filter on the way in is _odf_image_can_be_bitmap, which admits any path whose suffix is
one of .bmp, .gif, .jpeg, .jpg, .png, .tif, .tiff, .webp, or empty. The
empty-suffix case is what admits most paths of interest on a Unix host, /etc/passwd among them.
The backend does not use the project's own fetch controls
$ grep -n "enable_local_fetch\|enable_remote_fetch\|image_resource_loader" \
docling/backend/opendocument_backend.py
$
Nothing. The HTML, Markdown, EPUB and XBRL backends all resolve image resources through
docling/backend/utils/image_resource_loader.py, where enable_local_fetch and
enable_remote_fetch both default to False. The ODF backend reimplements image loading and does
not consult either, so the default of not fetching local resources is not applied on this path.
That is the substance of the report: the control exists, it is off by default, and this backend does not reach it.
Affected versions
Introduced by commit e2afe381, "feat: Add OpenDocument backend support and improve ODF
image/table handling" (#3480), 2026-06-24. First released in v2.107.0.
Affected: docling >= 2.107.0, up to and including 2.117.0 and current main
(52d8a6f24de7318a9ad4be2a7361ba93fc81a5c1).
Subsequent commits touching this file — 2c3e55b3 (skip a draw:object with a missing embedded
part), b627ca91 (preserve content inside sections), 2ec33bc7 (guard backend imports) — address
unrelated defects and leave the path resolution unchanged.
Reproduced on two independent machines:
| | |
|---|---|
| Python 3.12.3 | docling-slim 2.117.0, docling-core 2.88.0, odfdo 3.23.1 |
| Python 3.14.4 | same releases, and again against main clones on sys.path |
Proof of concept
Trigger
An .odt needs only mimetype and content.xml. No manifest, no styles, no embedded image part.
import zipfile
CONTENT = (
'<?xml version="1.0"?><office:document-content '
'xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" '
'xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" '
'xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" '
'xmlns:xlink="http://www.w3.org/1999/xlink" office:version="1.2">'
'<office:body><office:text><text:p>'
'<draw:frame><draw:image xlink:href="{href}"/></draw:frame>'
'</text:p></office:text></office:body></office:document-content>'
)
def build(path, href):
with…
- Record
GHSA-4xhp-xg4w-8ppm- CVSS 4.0
- 6.9 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:H/VI:N/VA:N/SC:L/SI:N/SA:N - Weakness
- CWE-22
- Updated
- 2026-11-02
OSVPYSEC-2026-4207
6.9
Docling: Arbitrary local file read via draw:image xlink:href in the OpenDocument backend
Summary
The ODF backend resolves the xlink:href of a draw:image element as a filesystem path whenever
the referenced part is not found inside the document archive. The value comes straight from
content.xml, so it is document content, and it is used with no scheme check, no confinement to
the extraction directory, and without consulting the enable_local_fetch / enable_remote_fetch
controls that the other backends use for exactly this decision.
Converting a crafted .odt therefore causes Docling to open an attacker-named absolute path on
the converting host. Where the target decodes as an image, its complete contents are embedded in
the resulting DoclingDocument and appear in the HTML, Markdown and JSON exports.
Both the read and the disclosure are confirmed by execution. The trigger is a 452-byte archive containing two entries.
Affected component
docling/backend/opendocument_backend.py, in _image_ref_from_odf_image:
image_url = _odf_image_href(image)
...
if image_data is None and odf_obj is not None and image_url:
try:
image_data = odf_obj.get_part(image_url)
except Exception:
image_data = None
if image_data is None and image_url:
image_path = Path(image_url)
if image_path.is_file():
image_data = image_path.read_bytes()
_odf_image_href returns the xlink:href attribute. The in-archive lookup is attempted first;
when it fails, the same string is handed to Path and read from disk.
The only filter on the way in is _odf_image_can_be_bitmap, which admits any path whose suffix is
one of .bmp, .gif, .jpeg, .jpg, .png, .tif, .tiff, .webp, or empty. The
empty-suffix case is what admits most paths of interest on a Unix host, /etc/passwd among them.
The backend does not use the project's own fetch controls
$ grep -n "enable_local_fetch\|enable_remote_fetch\|image_resource_loader" \
docling/backend/opendocument_backend.py
$
Nothing. The HTML, Markdown, EPUB and XBRL backends all resolve image resources through
docling/backend/utils/image_resource_loader.py, where enable_local_fetch and
enable_remote_fetch both default to False. The ODF backend reimplements image loading and does
not consult either, so the default of not fetching local resources is not applied on this path.
That is the substance of the report: the control exists, it is off by default, and this backend does not reach it.
Affected versions
Introduced by commit e2afe381, "feat: Add OpenDocument backend support and improve ODF
image/table handling" (#3480), 2026-06-24. First released in v2.107.0.
Affected: docling >= 2.107.0, up to and including 2.117.0 and current main
(52d8a6f24de7318a9ad4be2a7361ba93fc81a5c1).
Subsequent commits touching this file — 2c3e55b3 (skip a draw:object with a missing embedded
part), b627ca91 (preserve content inside sections), 2ec33bc7 (guard backend imports) — address
unrelated defects and leave the path resolution unchanged.
Reproduced on two independent machines:
| | |
|---|---|
| Python 3.12.3 | docling-slim 2.117.0, docling-core 2.88.0, odfdo 3.23.1 |
| Python 3.14.4 | same releases, and again against main clones on sys.path |
Proof of concept
Trigger
An .odt needs only mimetype and content.xml. No manifest, no styles, no embedded image part.
import zipfile
CONTENT = (
'<?xml version="1.0"?><office:document-content '
'xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" '
'xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" '
'xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" '
'xmlns:xlink="http://www.w3.org/1999/xlink" office:version="1.2">'
'<office:body><office:text><text:p>'
'<draw:frame><draw:image xlink:href="{href}"/></draw:frame>'
'</text:p></office:text></office:body></office:document-content>'
)
def build(path, href):
with…
- Record
PYSEC-2026-4207- CVSS 4.0
- 6.9 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:H/VI:N/VA:N/SC:L/SI:N/SA:N - Updated
- 2026-11-02
Affected products
Affected packages
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-105751 is a medium vulnerability (CVSS 6.9) in Docling, published October 5, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.42%, the 36th percentile. Fixed in docling 2.120.3.
References
github.com/docling-project/docling/security/advisories/GHSA-4xhp-xg4w-8ppm
github.com/docling-project/docling/pull/4015
github.com/docling-project/docling/commit/6ee9965adfc364b3696a67c825ec2f0d9cee4311
github.com/docling-project/docling/releases/tag/v2.120.3
github.com/advisories/GHSA-4xhp-xg4w-8ppm
github.com/CVEProject/cvelistV5/tree/main/cves/2026/105xxx/CVE-2026-105751.json
nvd.nist.gov/vuln/detail/CVE-2026-105751
github.com/docling-project/docling
pypi.org/project/docling
Left out here: the rest of 3 source descriptions. The JSON record has them.