Repository navigation
MLE-34177 fix multipart header and parameter injection in document write - #150
Open
RitaChen609 wants to merge 2 commits into
Open
RitaChen609 wants to merge 2 commits into
RitaChen609 wants to merge 2 commits into
Conversation
RitaChen609
requested review from
jonmille,
ngodugu-marklogic,
rjdew-progress and
rjrudin
as code owners
November 6, 2026 19:11
There was a problem hiding this comment.
🟡 Changes recommended
Quoted version IDs ending in a backslash can escape the emitted header’s closing quote, allowing URI content to inject separate disposition parameters.
1 open issue
What changed in this PR
This PR hardens Document multipart writes against header and parameter injection while preserving read behavior and support for quoted version IDs.
Changes:
- Removes the raw metadata filename parameter in favor of urllib3’s escaped filename.
- Validates write-path URIs, disposition parameters, and supported
repairvalues. - Adds injection regression tests and integration tests for valid document writes.
| File | Description |
|---|---|
tests/test_write_documents_injection.py |
Tests rejected inputs, multipart rendering, and valid write scenarios. |
marklogic/documents.py |
Adds write-time validation and removes unsafe metadata filename rendering. |
Review effort: Balanced
Comment on lines
+39
to
+40
| if name == "versionId" and len(value) >= 2 and value[0] == value[-1] == '"': | ||
| to_check = value[1:-1] |
jonmille
approved these changes
Nov 6, 2026
rjdew-progress
approved these changes
Nov 6, 2026
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
Documentwrite paths built multipartContent-Dispositionheaders with unescaped f-strings. Caller-supplied values could inject extra disposition parameters, extra header lines, or a blank line that ends the part's headers early. Examples are a URI containing\r\n, or arepairvalue containing;.Security review finding F1. CWE-93, CWE-113, CWE-74.
CVSS 3.1:
AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N= 4.3 (Medium). This assumes an application that passes untrusted input intoDocument.Root cause
to_metadata_request_fieldrenderedfilename={self.uri}raw and unquoted. urllib3 also appends its own escapedfilename="...", so the header carried twofilenameparameters, and the first one was injectable._make_content_dispositionappendedextension,directory,repair,extract,version_idandtemporal_documentas raw;key=value.name/filenameit renders itself.Changes (
marklogic/documents.py)filename={uri}is removed. The disposition is nowattachment; category=metadata, and the filename comes only from urllib3's escapedfilename="...", the same as the content part.ValueErrorif the URI contains control characters (\x00-\x1f,\x7f). Validation happens only on the write path, so reading documents is unchanged.repair: onlyfullornoneare accepted, as already documented.extension,directory,extract,version_id,temporal_document: these raiseValueErrorif they contain control characters,;or".version_idmay be wrapped in one pair of quotes, so a raw ETag such as"12345"still works. The existingtest_optimistic_lockingrelies on this.Behavior change
Values that were previously sent unvalidated now raise
ValueErrorbefore any request is made:repairvalue other thanfullornone;;,"or control characters.Legitimate URIs that contain
;or"still work.