Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-pyasn1 for openSUSE:Factory 
checked in at 2026-11-08 19:21:22
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pyasn1 (Old)
 and      /work/SRC/openSUSE:Factory/.python-pyasn1.new.3759290 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-pyasn1"

Sun Nov  8 19:21:22 2026 rev:51 rq:1395838 version:0.6.5

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-pyasn1/python-pyasn1.changes      
2026-07-20 09:59:46.216998802 +0200
+++ /work/SRC/openSUSE:Factory/.python-pyasn1.new.3759290/python-pyasn1.changes 
2026-11-08 19:21:30.045677145 +0100
@@ -1,0 +2,19 @@
+Fri Oct 30 12:25:15 UTC 2026 - Robert Schweikert <[email protected]>
+
+- Update to 0.6.5 (bsc#1288851, CVE-2026-119194)
+  * GHSA-mm57-4v3p-rchx: Decoded binary REAL mantissas in linear time
+  instead of repeatedly shifting a growing integer and slicing the
+  remaining bytes (thanks for reporting, manus-use, manus-pi, ucsurf,
+  adriank31)
+  * GHSA-2qm8-x737-gmw5: Reassembled constructed BIT STRING fragments
+    in linear time while preserving their decoded bit values, including
+    intermediate fragment padding (thanks for reporting, manus-use)
+  * GHSA-9m75-vc3g-4x39: Reassembled constructed OCTET STRING and
+    character-string fragments and indefinite-length ANY content in
+    linear time. Nested constructed OCTET STRING fragments now contribute
+    their decoded octets (thanks for reporting, alimony, manus-pi, adriank31)
+  * GHSA-fm9c-5rcf-2q5p: Bounded schemaless container type tracking
+    after two distinct tag sets to prevent quadratic CPU use from
+    colliding long tag IDs (thanks for reporting, droideck)
+
+-------------------------------------------------------------------

Old:
----
  pyasn1-0.6.4.tar.gz

New:
----
  pyasn1-0.6.5.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-pyasn1.spec ++++++
--- /var/tmp/diff_new_pack.aiQ98r/_old  2026-11-08 19:21:30.757706600 +0100
+++ /var/tmp/diff_new_pack.aiQ98r/_new  2026-11-08 19:21:30.759706683 +0100
@@ -1,6 +1,7 @@
 #
 # spec file for package python-pyasn1
 #
+# Copyright (c) 2026 SUSE LLC
 # Copyright (c) 2026 SUSE LLC and contributors
 #
 # All modifications and additions to the file contributed by third parties
@@ -18,7 +19,7 @@
 
 %{?sle15_python_module_pythons}
 Name:           python-pyasn1
-Version:        0.6.4
+Version:        0.6.5
 Release:        0
 Summary:        ASN.1 types and codecs
 License:        BSD-2-Clause

++++++ pyasn1-0.6.4.tar.gz -> pyasn1-0.6.5.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyasn1-0.6.4/CHANGES.rst new/pyasn1-0.6.5/CHANGES.rst
--- old/pyasn1-0.6.4/CHANGES.rst        2026-07-09 03:12:14.000000000 +0200
+++ new/pyasn1-0.6.5/CHANGES.rst        2026-10-27 07:32:26.836591500 +0100
@@ -1,3 +1,21 @@
+Revision 0.6.5, released 26-10-2026
+---------------------------------------
+
+- GHSA-mm57-4v3p-rchx: Decoded binary REAL mantissas in linear time
+  instead of repeatedly shifting a growing integer and slicing the
+  remaining bytes (thanks for reporting, manus-use, manus-pi, ucsurf,
+  adriank31)
+- GHSA-2qm8-x737-gmw5: Reassembled constructed BIT STRING fragments
+  in linear time while preserving their decoded bit values, including
+  intermediate fragment padding (thanks for reporting, manus-use)
+- GHSA-9m75-vc3g-4x39: Reassembled constructed OCTET STRING and
+  character-string fragments and indefinite-length ANY content in
+  linear time. Nested constructed OCTET STRING fragments now contribute
+  their decoded octets (thanks for reporting, alimony, manus-pi, adriank31)
+- GHSA-fm9c-5rcf-2q5p: Bounded schemaless container type tracking
+  after two distinct tag sets to prevent quadratic CPU use from
+  colliding long tag IDs (thanks for reporting, droideck)
+
 Revision 0.6.4, released 08-07-2026
 ---------------------------------------
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyasn1-0.6.4/PKG-INFO new/pyasn1-0.6.5/PKG-INFO
--- old/pyasn1-0.6.4/PKG-INFO   2026-07-09 03:12:22.895374300 +0200
+++ new/pyasn1-0.6.5/PKG-INFO   2026-10-27 07:32:38.123992200 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: pyasn1
-Version: 0.6.4
+Version: 0.6.5
 Summary: Pure-Python implementation of ASN.1 types and DER/BER/CER codecs 
(X.208)
 Author-email: Ilya Etingof <[email protected]>
 Maintainer: pyasn1 maintenance organization
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyasn1-0.6.4/pyasn1/__init__.py 
new/pyasn1-0.6.5/pyasn1/__init__.py
--- old/pyasn1-0.6.4/pyasn1/__init__.py 2026-07-09 03:12:14.000000000 +0200
+++ new/pyasn1-0.6.5/pyasn1/__init__.py 2026-10-27 07:32:26.839507800 +0100
@@ -1,2 +1,2 @@
 # https://www.python.org/dev/peps/pep-0396/
-__version__ = '0.6.4'
+__version__ = '0.6.5'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyasn1-0.6.4/pyasn1/codec/ber/decoder.py 
new/pyasn1-0.6.5/pyasn1/codec/ber/decoder.py
--- old/pyasn1-0.6.4/pyasn1/codec/ber/decoder.py        2026-07-09 
03:12:14.000000000 +0200
+++ new/pyasn1-0.6.5/pyasn1/codec/ber/decoder.py        2026-10-27 
07:32:26.839731500 +0100
@@ -174,6 +174,44 @@
     protoComponent = univ.BitString(())
     supportConstructedForm = True
 
+    @staticmethod
+    def _appendFragment(assembled, component, bitLength):
+        trailingBits = component[0]
+        if trailingBits > 7:
+            raise error.PyAsn1Error(
+                'Trailing bits overflow %s' % trailingBits
+            )
+
+        payload = component[1:]
+        if not payload:
+            if trailingBits:
+                raise error.PyAsn1Error('Trailing bits overflow %s' % 
trailingBits)
+            return bitLength
+
+        if not trailingBits and not bitLength % 8:
+            assembled.extend(payload)
+            return bitLength + len(payload) * 8
+
+        for idx, byte in enumerate(payload):
+            significantBits = 8
+            if idx == len(payload) - 1:
+                significantBits -= trailingBits
+                byte >>= trailingBits
+
+            usedBits = bitLength % 8
+            if not usedBits:
+                assembled.append(byte << (8 - significantBits))
+            elif usedBits + significantBits <= 8:
+                assembled[-1] |= byte << (8 - usedBits - significantBits)
+            else:
+                remainingBits = usedBits + significantBits - 8
+                assembled[-1] |= byte >> remainingBits
+                assembled.append((byte << (8 - remainingBits)) & 0xff)
+
+            bitLength += significantBits
+
+        return bitLength
+
     def valueDecoder(self, substrate, asn1Spec,
                      tagSet=None, length=None, state=None,
                      decodeFun=None, substrateFun=None,
@@ -230,7 +268,8 @@
         # All inner fragments are of the same type, treat them as octet string
         substrateFun = self.substrateCollector
 
-        bitString = self.protoComponent.fromOctetString(b'', 
internalFormat=True)
+        assembled = bytearray()
+        bitLength = 0
 
         current_position = substrate.tell()
 
@@ -241,16 +280,11 @@
                 if isinstance(component, SubstrateUnderrunError):
                     yield component
 
-            trailingBits = component[0]
-            if trailingBits > 7:
-                raise error.PyAsn1Error(
-                    'Trailing bits overflow %s' % trailingBits
-                )
+            bitLength = self._appendFragment(assembled, component, bitLength)
 
-            bitString = self.protoComponent.fromOctetString(
-                component[1:], internalFormat=True,
-                prepend=bitString, padding=trailingBits
-            )
+        bitString = self.protoComponent.fromOctetString(
+            assembled, internalFormat=True, padding=(-bitLength) % 8
+        )
 
         yield self._createComponent(asn1Spec, tagSet, bitString, **options)
 
@@ -270,7 +304,8 @@
         # All inner fragments are of the same type, treat them as octet string
         substrateFun = self.substrateCollector
 
-        bitString = self.protoComponent.fromOctetString(b'', 
internalFormat=True)
+        assembled = bytearray()
+        bitLength = 0
 
         while True:  # loop over fragments
 
@@ -287,16 +322,11 @@
             if component is eoo.endOfOctets:
                 break
 
-            trailingBits = component[0]
-            if trailingBits > 7:
-                raise error.PyAsn1Error(
-                    'Trailing bits overflow %s' % trailingBits
-                )
+            bitLength = self._appendFragment(assembled, component, bitLength)
 
-            bitString = self.protoComponent.fromOctetString(
-                component[1:], internalFormat=True,
-                prepend=bitString, padding=trailingBits
-            )
+        bitString = self.protoComponent.fromOctetString(
+            assembled, internalFormat=True, padding=(-bitLength) % 8
+        )
 
         yield self._createComponent(asn1Spec, tagSet, bitString, **options)
 
@@ -332,23 +362,20 @@
         if LOG:
             LOG('assembling constructed serialization')
 
-        # All inner fragments are of the same type, treat them as octet string
-        substrateFun = self.substrateCollector
-
-        header = b''
+        # Assemble the logical value of each fragment, including nested ones.
+        fragments = []
 
         original_position = substrate.tell()
         # head = popSubstream(substrate, length)
         while substrate.tell() - original_position < length:
             for component in decodeFun(
-                    substrate, self.protoComponent, substrateFun=substrateFun,
-                    **options):
+                    substrate, self.protoComponent, **options):
                 if isinstance(component, SubstrateUnderrunError):
                     yield component
 
-            header += component
+            fragments.append(bytes(component))
 
-        yield self._createComponent(asn1Spec, tagSet, header, **options)
+        yield self._createComponent(asn1Spec, tagSet, b''.join(fragments), 
**options)
 
     def indefLenValueDecoder(self, substrate, asn1Spec,
                              tagSet=None, length=None, state=None,
@@ -362,16 +389,13 @@
 
             return
 
-        # All inner fragments are of the same type, treat them as octet string
-        substrateFun = self.substrateCollector
-
-        header = b''
+        # Assemble the logical value of each fragment, including nested ones.
+        fragments = []
 
         while True:  # loop over fragments
 
             for component in decodeFun(
-                    substrate, self.protoComponent, substrateFun=substrateFun,
-                    allowEoo=True, **options):
+                    substrate, self.protoComponent, allowEoo=True, **options):
 
                 if isinstance(component, SubstrateUnderrunError):
                     yield component
@@ -382,9 +406,9 @@
             if component is eoo.endOfOctets:
                 break
 
-            header += component
+            fragments.append(bytes(component))
 
-        yield self._createComponent(asn1Spec, tagSet, header, **options)
+        yield self._createComponent(asn1Spec, tagSet, b''.join(fragments), 
**options)
 
 
 class NullPayloadDecoder(AbstractSimplePayloadDecoder):
@@ -587,12 +611,7 @@
 
             elif b == 2:  # encbase = 16
                 e *= 4
-            p = 0
-
-            while chunk:  # value
-                p <<= 8
-                p |= chunk[0]
-                chunk = chunk[1:]
+            p = int.from_bytes(bytes(chunk), 'big')
 
             if fo & 0x40:  # sign bit
                 p = -p
@@ -676,7 +695,10 @@
                 break
 
             components.append(component)
-            componentTypes.add(component.tagSet)
+            # Two distinct types settle the choice; keep the set bounded
+            # to avoid quadratic comparisons for colliding tag hashes.
+            if len(componentTypes) < 2:
+                componentTypes.add(component.tagSet)
 
             # Now we have to guess is it SEQUENCE/SET or SEQUENCE OF/SET OF
             # The heuristics is:
@@ -1406,6 +1428,8 @@
         # All inner fragments are of the same type, treat them as octet string
         substrateFun = self.substrateCollector
 
+        fragments = [chunk]
+
         while True:  # loop over fragments
 
             for component in decodeFun(
@@ -1421,7 +1445,9 @@
             if component is eoo.endOfOctets:
                 break
 
-            chunk += component
+            fragments.append(component)
+
+        chunk = b''.join(fragments)
 
         if substrateFun:
             yield chunk  # TODO: Weird
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyasn1-0.6.4/pyasn1.egg-info/PKG-INFO 
new/pyasn1-0.6.5/pyasn1.egg-info/PKG-INFO
--- old/pyasn1-0.6.4/pyasn1.egg-info/PKG-INFO   2026-07-09 03:12:22.000000000 
+0200
+++ new/pyasn1-0.6.5/pyasn1.egg-info/PKG-INFO   2026-10-27 07:32:38.096046000 
+0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: pyasn1
-Version: 0.6.4
+Version: 0.6.5
 Summary: Pure-Python implementation of ASN.1 types and DER/BER/CER codecs 
(X.208)
 Author-email: Ilya Etingof <[email protected]>
 Maintainer: pyasn1 maintenance organization
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyasn1-0.6.4/pyasn1.egg-info/SOURCES.txt 
new/pyasn1-0.6.5/pyasn1.egg-info/SOURCES.txt
--- old/pyasn1-0.6.4/pyasn1.egg-info/SOURCES.txt        2026-07-09 
03:12:22.000000000 +0200
+++ new/pyasn1-0.6.5/pyasn1.egg-info/SOURCES.txt        2026-10-27 
07:32:38.107046100 +0100
@@ -124,6 +124,7 @@
 tests/test_debug.py
 tests/codec/__init__.py
 tests/codec/__main__.py
+tests/codec/test_decoder_schemaless.py
 tests/codec/test_streaming.py
 tests/codec/ber/__init__.py
 tests/codec/ber/__main__.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyasn1-0.6.4/tests/codec/__main__.py 
new/pyasn1-0.6.5/tests/codec/__main__.py
--- old/pyasn1-0.6.4/tests/codec/__main__.py    2026-07-09 03:12:14.000000000 
+0200
+++ new/pyasn1-0.6.5/tests/codec/__main__.py    2026-10-27 07:32:26.841827600 
+0100
@@ -8,6 +8,7 @@
 
 suite = unittest.TestLoader().loadTestsFromNames(
     ['tests.codec.test_streaming.suite',
+     'tests.codec.test_decoder_schemaless.suite',
      'tests.codec.ber.__main__.suite',
      'tests.codec.cer.__main__.suite',
      'tests.codec.der.__main__.suite',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyasn1-0.6.4/tests/codec/ber/test_decoder.py 
new/pyasn1-0.6.5/tests/codec/ber/test_decoder.py
--- old/pyasn1-0.6.4/tests/codec/ber/test_decoder.py    2026-07-09 
03:12:14.000000000 +0200
+++ new/pyasn1-0.6.5/tests/codec/ber/test_decoder.py    2026-10-27 
07:32:26.841891300 +0100
@@ -168,6 +168,27 @@
             bytes((35, 128, 3, 2, 0, 169, 3, 2, 1, 138, 0, 0))
         ) == ((1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1), b'')
 
+    def testChunkedWithIntermediatePadding(self):
+        fragments = (b'\x03\x02\x03\x10' + b'\x03\x01\x00' +
+                     b'\x03\x02\x05\xa0' + b'\x03\x02\x01\x81')
+        expected = univ.BitString(binValue='000101011000000')
+
+        for substrate in (b'\x23' + encode_length(len(fragments)) + fragments,
+                          b'\x23\x80' + fragments + b'\x00\x00'):
+            for asn1Spec in (None, univ.BitString()):
+                value, rest = decoder.decode(substrate, asn1Spec=asn1Spec)
+                assert value == expected
+                assert len(value) == len(expected)
+                assert rest == b''
+
+    def testManyDefModeFragments(self):
+        fragments = b'\x03\x02\x00\x81' * 256
+        substrate = b'\x23' + encode_length(len(fragments)) + fragments
+        value, rest = decoder.decode(substrate)
+
+        assert value == univ.BitString.fromOctetString(b'\x81' * 256)
+        assert rest == b''
+
     def testDefModeChunkedSubst(self):
         assert decoder.decode(
             bytes((35, 8, 3, 2, 0, 169, 3, 2, 1, 138)),
@@ -223,6 +244,32 @@
             bytes((36, 128, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 
4, 111, 119, 110, 32, 4, 3, 102, 111, 120, 0, 0))
         ) == (b'Quick brown fox', b'')
 
+    def testManyFragments(self):
+        fragments = b'\x04\x02AB' * 256
+
+        for substrate in (b'\x24' + encode_length(len(fragments)) + fragments,
+                          b'\x24\x80' + fragments + b'\x00\x00'):
+            for asn1Spec in (None, univ.OctetString()):
+                value, rest = decoder.decode(substrate, asn1Spec=asn1Spec)
+                assert value.asOctets() == b'AB' * 256
+                assert rest == b''
+
+    def testNestedIndefModeChunked(self):
+        substrate = bytes.fromhex('24 07 24 80 04 01 41 00 00')
+
+        for asn1Spec in (None, univ.OctetString()):
+            value, rest = decoder.decode(substrate, asn1Spec=asn1Spec)
+            assert value.asOctets() == b'A'
+            assert rest == b''
+
+    def testNestedDefModeChunked(self):
+        substrate = bytes.fromhex('24 05 24 03 04 01 41')
+
+        for asn1Spec in (None, univ.OctetString()):
+            value, rest = decoder.decode(substrate, asn1Spec=asn1Spec)
+            assert value.asOctets() == b'A'
+            assert rest == b''
+
     def testDefModeChunkedSubst(self):
         assert decoder.decode(
             bytes(
@@ -765,6 +812,16 @@
         assert tuple(value) == (-1, 2, 76354972)
         assert rest == b''
 
+    def testLongBinaryMantissa(self):
+        mantissa = b'\x80' + b'\x01' * 511
+        content = b'\x80\x00' + mantissa
+        substrate = b'\x09' + encode_length(len(content)) + content
+
+        for asn1Spec in (None, univ.Real()):
+            value, rest = decoder.decode(substrate, asn1Spec=asn1Spec)
+            assert tuple(value) == (int.from_bytes(mantissa, 'big'), 2, 0)
+            assert rest == b''
+
     def testLongFormBinaryRealExponentLength(self):
         value, rest = decoder.decode(
             bytes((9, 6, 0x83, 3, 0x0f, 0x42, 0x40, 1))
@@ -1845,6 +1902,14 @@
             bytes((4, 3, 102, 111, 120)), asn1Spec=self.s
         ) == (univ.Any('\004\003fox'), b'')
 
+    def testManyIndefiniteLengthComponents(self):
+        fragments = b'\x04\x01A' * 128
+        substrate = b'\x30\x80' + fragments + b'\x00\x00'
+        value, rest = decoder.decode(substrate, asn1Spec=self.s)
+
+        assert value == b'\x30\x80' + fragments
+        assert rest == b''
+
     def testTaggedExIndefMode(self):
         s = 
univ.Any('\004\003fox').subtype(explicitTag=tag.Tag(tag.tagClassContext, 
tag.tagFormatSimple, 4))
         assert decoder.decode(bytes((164, 128, 4, 3, 102, 111, 120, 0, 0)), 
asn1Spec=s) == (s, b'')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyasn1-0.6.4/tests/codec/cer/test_decoder.py 
new/pyasn1-0.6.5/tests/codec/cer/test_decoder.py
--- old/pyasn1-0.6.4/tests/codec/cer/test_decoder.py    2026-07-09 
03:12:14.000000000 +0200
+++ new/pyasn1-0.6.5/tests/codec/cer/test_decoder.py    2026-10-27 
07:32:26.842228200 +0100
@@ -49,6 +49,17 @@
             bytes((3, 127, 6) + (170,) * 125 + (128,))
         ) == (((1, 0) * 501), b'')
 
+    def testChunkedWithIntermediatePadding(self):
+        fragments = b'\x03\x02\x03\x10\x03\x02\x00\xa5'
+        expected = univ.BitString(binValue='0001010100101')
+
+        for substrate in (b'\x23\x08' + fragments,
+                          b'\x23\x80' + fragments + b'\x00\x00'):
+            value, rest = decoder.decode(substrate, asn1Spec=univ.BitString())
+            assert value == expected
+            assert len(value) == len(expected)
+            assert rest == b''
+
     # TODO: test failures on short chunked and long unchunked substrate samples
 
 
@@ -63,6 +74,16 @@
             bytes((36, 128, 4, 130, 3, 232) + (81,) * 1000 + (4, 1, 81, 0, 0))
         ) == (b'Q' * 1001, b'')
 
+    def testManyFragments(self):
+        fragments = b'\x04\x01A' * 64
+        definite = b'\x24\x81\xc0' + fragments
+        indefinite = b'\x24\x80' + fragments + b'\x00\x00'
+
+        for substrate in (definite, indefinite):
+            value, rest = decoder.decode(substrate, 
asn1Spec=univ.OctetString())
+            assert value.asOctets() == b'A' * 64
+            assert rest == b''
+
     # TODO: test failures on short chunked and long unchunked substrate samples
 
 
@@ -89,6 +110,25 @@
         assert tuple(value) == (-1, 2, 76354972)
         assert rest == b''
 
+    def testLongBinaryMantissa(self):
+        mantissa = b'\x80' + b'\x01' * 255
+        content = b'\x80\x00' + mantissa
+        substrate = b'\x09\x82\x01\x02' + content
+        value, rest = decoder.decode(substrate)
+
+        assert tuple(value) == (int.from_bytes(mantissa, 'big'), 2, 0)
+        assert rest == b''
+
+
+class AnyDecoderTestCase(BaseTestCase):
+    def testManyIndefiniteLengthComponents(self):
+        fragments = b'\x04\x01A' * 64
+        substrate = b'\x30\x80' + fragments + b'\x00\x00'
+        value, rest = decoder.decode(substrate, asn1Spec=univ.Any())
+
+        assert value == b'\x30\x80' + fragments
+        assert rest == b''
+
 
 class SequenceDecoderWithUntaggedOpenTypesTestCase(BaseTestCase):
     def setUp(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyasn1-0.6.4/tests/codec/der/test_decoder.py 
new/pyasn1-0.6.5/tests/codec/der/test_decoder.py
--- old/pyasn1-0.6.4/tests/codec/der/test_decoder.py    2026-07-09 
03:12:14.000000000 +0200
+++ new/pyasn1-0.6.5/tests/codec/der/test_decoder.py    2026-10-27 
07:32:26.842596000 +0100
@@ -9,6 +9,7 @@
 
 from tests.base import BaseTestCase
 
+from pyasn1.type import char
 from pyasn1.type import tag
 from pyasn1.type import namedtype
 from pyasn1.type import opentype
@@ -44,6 +45,15 @@
         else:
             assert 0, 'chunked encoding tolerated'
 
+    def testSchemaDrivenChunkedWithIntermediatePadding(self):
+        fragments = b'\x03\x02\x03\x10\x03\x02\x00\xa5'
+        value, rest = decoder.decode(b'\x23\x08' + fragments,
+                                     asn1Spec=univ.BitString())
+
+        assert value == univ.BitString(binValue='0001010100101')
+        assert len(value) == 13
+        assert rest == b''
+
 
 class OctetStringDecoderTestCase(BaseTestCase):
     def testShortMode(self):
@@ -71,6 +81,21 @@
         else:
             assert 0, 'chunked encoding tolerated'
 
+    def testSchemaDrivenManyFragments(self):
+        fragments = b'\x04\x01A' * 64
+        value, rest = decoder.decode(b'\x24\x81\xc0' + fragments,
+                                     asn1Spec=univ.OctetString())
+
+        assert value.asOctets() == b'A' * 64
+        assert rest == b''
+
+    def testConstructedCharacterString(self):
+        fragments = b'\x0c\x01A' * 64
+        value, rest = decoder.decode(b'\x2c\x81\xc0' + fragments)
+
+        assert value == char.UTF8String(b'A' * 64)
+        assert rest == b''
+
 
 class LargeTagDecoderTestCase(BaseTestCase):
     def testExcessiveLongTag(self):
@@ -104,6 +129,15 @@
         assert tuple(value) == (-1, 2, 76354972)
         assert rest == b''
 
+    def testLongBinaryMantissa(self):
+        mantissa = b'\x80' + b'\x01' * 255
+        content = b'\x80\x00' + mantissa
+        substrate = b'\x09\x82\x01\x02' + content
+        value, rest = decoder.decode(substrate, asn1Spec=univ.Real())
+
+        assert tuple(value) == (int.from_bytes(mantissa, 'big'), 2, 0)
+        assert rest == b''
+
 
 class SequenceDecoderWithUntaggedOpenTypesTestCase(BaseTestCase):
     def setUp(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyasn1-0.6.4/tests/codec/test_decoder_schemaless.py 
new/pyasn1-0.6.5/tests/codec/test_decoder_schemaless.py
--- old/pyasn1-0.6.4/tests/codec/test_decoder_schemaless.py     1970-01-01 
01:00:00.000000000 +0100
+++ new/pyasn1-0.6.5/tests/codec/test_decoder_schemaless.py     2026-10-27 
07:32:26.842784400 +0100
@@ -0,0 +1,184 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2026, Simon Pichugin <[email protected]>
+# License: https://pyasn1.readthedocs.io/en/latest/license.html
+#
+import io
+import sys
+import unittest
+from itertools import product
+
+from tests.base import BaseTestCase
+
+from pyasn1.codec.ber import decoder as ber_decoder
+from pyasn1.codec.cer import decoder as cer_decoder
+from pyasn1.codec.der import decoder as der_decoder
+from pyasn1.error import SubstrateUnderrunError
+from pyasn1.type import tag
+from pyasn1.type import univ
+
+
+class SchemalessContainerDecoderTestCase(BaseTestCase):
+    codecs = (
+        ('ber-def', ber_decoder, False),
+        ('ber-indef', ber_decoder, True),
+        ('cer-def', cer_decoder, False),
+        ('cer-indef', cer_decoder, True),
+        ('der', der_decoder, False),
+    )
+    containers = (
+        (0x30, univ.SequenceOf, univ.Sequence),
+        (0x31, univ.SetOf, univ.Set),
+    )
+
+    def _encodeLength(self, length):
+        if length < 128:
+            return bytes([length])
+
+        octets = length.to_bytes((length.bit_length() + 7) // 8, 'big')
+        return bytes([0x80 | len(octets)]) + octets
+
+    def _encodeConstructed(self, identifier, body, indefinite):
+        if indefinite:
+            return identifier + b'\x80' + body + b'\x00\x00'
+
+        return identifier + self._encodeLength(len(body)) + body
+
+    def _substrate(self, containerTag, tagIds, indefinite, definiteTags=False):
+        components = []
+        for idx, tagId in enumerate(tagIds):
+            octets = [tagId & 0x7f]
+            tagId >>= 7
+            while tagId:
+                octets.append((tagId & 0x7f) | 0x80)
+                tagId >>= 7
+
+            # Long-form context tag, explicitly wrapping a one-octet value.
+            identifier = b'\xbf' + bytes(reversed(octets))
+            body = b'\x04\x01' + bytes([idx % 256])
+            components.append(self._encodeConstructed(
+                identifier, body, indefinite and not definiteTags))
+
+        return self._encodeConstructed(
+            bytes([containerTag]), b''.join(components), indefinite)
+
+    def _tagSet(self, tagId):
+        return univ.OctetString.tagSet.tagExplicitly(
+            tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, tagId))
+
+    def _assertValue(self, value, expectedType, tagIds):
+        assert type(value) is expectedType
+        assert len(value) == len(tagIds)
+        for idx, tagId in enumerate(tagIds):
+            component = value.getComponentByPosition(idx)
+            assert type(component) is univ.OctetString
+            assert component.asOctets() == bytes([idx % 256])
+            assert component.tagSet == self._tagSet(tagId)
+
+    def testHomogeneous(self):
+        for codec, container, tagIds in product(
+                self.codecs, self.containers,
+                ((32,), (32, 32, 32), (32 + sys.hash_info.modulus,) * 3)):
+            name, decoder, indefinite = codec
+            containerTag, sequenceType, recordType = container
+            with self.subTest(codec=name, container=sequenceType, tags=tagIds):
+                value, tail = decoder.decode(
+                    self._substrate(containerTag, tagIds, indefinite))
+                self._assertValue(value, sequenceType, tagIds)
+                assert tail == b''
+
+    def testHeterogeneous(self):
+        for codec, container, tagIds in product(
+                self.codecs, self.containers,
+                ((32, 33), (32, 32, 33, 32), (32, 33, 34, 33, 32))):
+            name, decoder, indefinite = codec
+            containerTag, sequenceType, recordType = container
+            with self.subTest(codec=name, container=recordType, tags=tagIds):
+                value, tail = decoder.decode(
+                    self._substrate(containerTag, tagIds, indefinite))
+                self._assertValue(value, recordType, tagIds)
+                assert tail == b''
+
+    def testCollidingTags(self):
+        for codec, container, count in product(
+                self.codecs, self.containers, (16, 32, 64)):
+            name, decoder, indefinite = codec
+            containerTag, sequenceType, recordType = container
+            with self.subTest(codec=name, container=recordType, count=count):
+                # Distinct integer IDs separated by the hash modulus collide
+                # without replacing TagSet's public hash implementation.
+                tagIds = [32 + idx * sys.hash_info.modulus
+                          for idx in range(count)]
+                assert len({hash(self._tagSet(tagId)) for tagId in tagIds}) == 
1
+                substrate = self._substrate(containerTag, tagIds, indefinite)
+                comparisons = 0
+                originalEq = tag.TagSet.__eq__
+
+                def countedEq(left, right):
+                    nonlocal comparisons
+                    # Exclude fixed dispatch-table lookups and count only
+                    # comparisons between explicitly tagged components.
+                    if (isinstance(right, tag.TagSet) and
+                            len(left) == len(right) == 2 and
+                            left[-1].tagClass == tag.tagClassContext and
+                            right[-1].tagClass == tag.tagClassContext):
+                        comparisons += 1
+                    return originalEq(left, right)
+
+                tag.TagSet.__eq__ = countedEq
+                try:
+                    value, tail = decoder.decode(substrate)
+                finally:
+                    tag.TagSet.__eq__ = originalEq
+
+                self._assertValue(value, recordType, tagIds)
+                assert tail == b''
+                assert comparisons <= count, (
+                    '%d tag comparisons for %d components' %
+                    (comparisons, count))
+
+    def testStreaming(self):
+        class NonBlockingStream(io.BytesIO):
+            def read(self, size=-1):
+                chunk = io.BytesIO.read(self, size)
+                if not chunk and size:
+                    return None
+                return chunk
+
+        for codec, container, tagIds in product(
+                self.codecs, self.containers,
+                ((32, 32, 32), (32, 32, 32 + sys.hash_info.modulus, 32))):
+            name, decoder, indefinite = codec
+            containerTag, sequenceType, recordType = container
+            expectedType = sequenceType if len(set(tagIds)) == 1 else 
recordType
+            with self.subTest(codec=name, container=expectedType, tags=tagIds):
+                # Keep explicit wrappers definite so resumption exercises
+                # the container's own indefinite-length path.
+                substrate = self._substrate(
+                    containerTag, tagIds, indefinite, definiteTags=True)
+                stream = NonBlockingStream()
+                iterator = iter(decoder.StreamingDecoder(stream))
+                supplied = 0
+                underruns = 0
+                while True:
+                    value = next(iterator)
+                    if not isinstance(value, SubstrateUnderrunError):
+                        break
+
+                    assert supplied < len(substrate), 'Decoder did not resume'
+                    position = stream.tell()
+                    stream.seek(0, io.SEEK_END)
+                    supplied += stream.write(substrate[supplied:supplied + 3])
+                    stream.seek(position)
+                    underruns += 1
+
+                assert underruns > 1
+                assert supplied == stream.tell() == len(substrate)
+                self._assertValue(value, expectedType, tagIds)
+
+
+suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
+
+if __name__ == '__main__':
+    unittest.TextTestRunner(verbosity=2).run(suite)

Reply via email to