diff --git a/CHANGES b/CHANGES index 4d6e8cec..164228ba 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,8 @@ +Unreleased +---------- + - Preserve leading and trailing CR/LF bytes in binary SOAP attachments + (#1249, #1481). + 4.3.3 (2026-06-18) ------------------ - Wire up the ``forbid_external`` setting (previously defined but unused diff --git a/src/zeep/wsdl/attachments.py b/src/zeep/wsdl/attachments.py index 44e09900..6a4737da 100644 --- a/src/zeep/wsdl/attachments.py +++ b/src/zeep/wsdl/attachments.py @@ -74,7 +74,6 @@ def content(self): if encoding == "base64": return base64.b64decode(content) - elif encoding == "binary": - return content.strip(b"\r\n") else: + # MultipartDecoder has already removed the MIME boundary framing. return content diff --git a/tests/test_soap_xop.py b/tests/test_soap_xop.py index 9c010114..c89ec37b 100644 --- a/tests/test_soap_xop.py +++ b/tests/test_soap_xop.py @@ -1,5 +1,6 @@ from io import StringIO +import pytest import requests_mock from lxml import etree from pretend import stub @@ -68,7 +69,7 @@ def test_rebuild_xml(): 5XJ45-3B2 accident - Li4uYmluYXJ5IEpQRyBpbWFnZS4uLg== + Li4uYmluYXJ5IEpQRyBpbWFnZS4uLg0K @@ -76,6 +77,33 @@ def test_rebuild_xml(): assert_nodes_equal(etree.tostring(document), expected) +@pytest.mark.parametrize( + "content", + [ + b"binary\r\n", + b"\r\nbinary", + b"\r\nbinary\r\n", + b"\r\n", + b"\x00\xff\r\n", + b"binary", + b"", + ], +) +def test_binary_attachment_preserves_content(content): + data = ( + b"--MIME_boundary\r\n" + b"Content-Type: application/octet-stream\r\n" + b"Content-Transfer-Encoding: binary\r\n" + b"Content-ID: \r\n\r\n" + + content + + b"\r\n--MIME_boundary--\r\n" + ) + decoder = MultipartDecoder(data, "multipart/related; boundary=MIME_boundary") + assert decoder.parts[0].content == content + message_pack = MessagePack(parts=decoder.parts) + assert message_pack.attachments[0].content == content + + def get_client_service(): wsdl_main = StringIO( """ @@ -249,7 +277,8 @@ def test_xop(): headers={"Content-Type": content_type}, ) result = service.TestOperation2("") - assert result["_value_1"] == b"BINARYDATA" + # The extra CRLF before the MIME boundary belongs to the binary payload. + assert result["_value_1"] == b"BINARYDATA\r\n" m.post( "http://tests.python-zeep.org/test", @@ -302,4 +331,4 @@ def test_xop_cid_encoded(): headers={"Content-Type": content_type}, ) result = service.TestOperation2("") - assert result["_value_1"] == b"BINARYDATA" + assert result["_value_1"] == b"BINARYDATA\r\n"