Version in base suite: 0.6.1-1+deb13u1 Base version: pyasn1_0.6.1-1+deb13u1 Target version: pyasn1_0.6.1-1+deb13u2 Base file: /srv/ftp-master.debian.org/ftp/pool/main/p/pyasn1/pyasn1_0.6.1-1+deb13u1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/p/pyasn1/pyasn1_0.6.1-1+deb13u2.dsc changelog | 8 + patches/CVE-2026-30922.patch | 249 +++++++++++++++++++++++++++++++++++++++++++ patches/series | 1 3 files changed, 258 insertions(+) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpzqrb7m31/pyasn1_0.6.1-1+deb13u1.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpzqrb7m31/pyasn1_0.6.1-1+deb13u2.dsc: no acceptable signature found diff -Nru pyasn1-0.6.1/debian/changelog pyasn1-0.6.1/debian/changelog --- pyasn1-0.6.1/debian/changelog 2026-01-19 19:21:45.000000000 +0000 +++ pyasn1-0.6.1/debian/changelog 2026-03-26 16:21:15.000000000 +0000 @@ -1,3 +1,11 @@ +pyasn1 (0.6.1-1+deb13u2) trixie-security; urgency=high + + * Non-maintainer upload by the Security Team. + * Denial of Service in pyasn1 via Unbounded Recursion (CVE-2026-30922) + (Closes: #1131371) + + -- Salvatore Bonaccorso Thu, 26 Mar 2026 17:21:15 +0100 + pyasn1 (0.6.1-1+deb13u1) trixie-security; urgency=high * Non-maintainer upload by the Security Team. diff -Nru pyasn1-0.6.1/debian/patches/CVE-2026-30922.patch pyasn1-0.6.1/debian/patches/CVE-2026-30922.patch --- pyasn1-0.6.1/debian/patches/CVE-2026-30922.patch 1970-01-01 00:00:00.000000000 +0000 +++ pyasn1-0.6.1/debian/patches/CVE-2026-30922.patch 2026-03-26 16:18:34.000000000 +0000 @@ -0,0 +1,249 @@ +From: Simon Pichugin +Date: Mon, 16 Mar 2026 17:23:11 -0700 +Subject: Merge commit from fork +Origin: https://github.com/pyasn1/pyasn1/commit/5a49bd1fe93b5b866a1210f6bf0a3924f21572c8 +Bug-Debian: https://bugs.debian.org/1131371 +Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2026-30922 + +--- + pyasn1/codec/ber/decoder.py | 10 +++ + tests/codec/ber/test_decoder.py | 116 ++++++++++++++++++++++++++++++++ + tests/codec/cer/test_decoder.py | 24 +++++++ + tests/codec/der/test_decoder.py | 42 ++++++++++++ + 4 files changed, 192 insertions(+) + +--- a/pyasn1/codec/ber/decoder.py ++++ b/pyasn1/codec/ber/decoder.py +@@ -36,6 +36,7 @@ SubstrateUnderrunError = error.Substrate + # Maximum number of continuation octets (high-bit set) allowed per OID arc. + # 20 octets allows up to 140-bit integers, supporting UUID-based OIDs + MAX_OID_ARC_CONTINUATION_OCTETS = 20 ++MAX_NESTING_DEPTH = 100 + + + class AbstractPayloadDecoder(object): +@@ -1565,6 +1566,15 @@ class SingleItemDecoder(object): + decodeFun=None, substrateFun=None, + **options): + ++ _nestingLevel = options.get('_nestingLevel', 0) ++ ++ if _nestingLevel > MAX_NESTING_DEPTH: ++ raise error.PyAsn1Error( ++ 'ASN.1 structure nesting depth exceeds limit (%d)' % MAX_NESTING_DEPTH ++ ) ++ ++ options['_nestingLevel'] = _nestingLevel + 1 ++ + allowEoo = options.pop('allowEoo', False) + + if LOG: +--- a/tests/codec/ber/test_decoder.py ++++ b/tests/codec/ber/test_decoder.py +@@ -2117,6 +2117,122 @@ class CompressedFilesTestCase(BaseTestCa + os.remove(path) + + ++class NestingDepthLimitTestCase(BaseTestCase): ++ """Test protection against deeply nested ASN.1 structures (CVE prevention).""" ++ ++ def testIndefLenSequenceNesting(self): ++ """Deeply nested indefinite-length SEQUENCEs must raise PyAsn1Error.""" ++ # Each \x30\x80 opens a new indefinite-length SEQUENCE ++ payload = b'\x30\x80' * 200 ++ try: ++ decoder.decode(payload) ++ except error.PyAsn1Error: ++ pass ++ else: ++ assert False, 'Deeply nested indef-length SEQUENCEs not rejected' ++ ++ def testIndefLenSetNesting(self): ++ """Deeply nested indefinite-length SETs must raise PyAsn1Error.""" ++ # Each \x31\x80 opens a new indefinite-length SET ++ payload = b'\x31\x80' * 200 ++ try: ++ decoder.decode(payload) ++ except error.PyAsn1Error: ++ pass ++ else: ++ assert False, 'Deeply nested indef-length SETs not rejected' ++ ++ def testDefiniteLenNesting(self): ++ """Deeply nested definite-length SEQUENCEs must raise PyAsn1Error.""" ++ inner = b'\x05\x00' # NULL ++ for _ in range(200): ++ length = len(inner) ++ if length < 128: ++ inner = b'\x30' + bytes([length]) + inner ++ else: ++ length_bytes = length.to_bytes( ++ (length.bit_length() + 7) // 8, 'big') ++ inner = b'\x30' + bytes([0x80 | len(length_bytes)]) + \ ++ length_bytes + inner ++ try: ++ decoder.decode(inner) ++ except error.PyAsn1Error: ++ pass ++ else: ++ assert False, 'Deeply nested definite-length SEQUENCEs not rejected' ++ ++ def testNestingUnderLimitWorks(self): ++ """Nesting within the limit must decode successfully.""" ++ inner = b'\x05\x00' # NULL ++ for _ in range(50): ++ length = len(inner) ++ if length < 128: ++ inner = b'\x30' + bytes([length]) + inner ++ else: ++ length_bytes = length.to_bytes( ++ (length.bit_length() + 7) // 8, 'big') ++ inner = b'\x30' + bytes([0x80 | len(length_bytes)]) + \ ++ length_bytes + inner ++ asn1Object, _ = decoder.decode(inner) ++ assert asn1Object is not None, 'Valid nested structure rejected' ++ ++ def testSiblingsDontIncreaseDepth(self): ++ """Sibling elements at the same level must not inflate depth count.""" ++ # SEQUENCE containing 200 INTEGER siblings - should decode fine ++ components = b'\x02\x01\x01' * 200 # 200 x INTEGER(1) ++ length = len(components) ++ length_bytes = length.to_bytes( ++ (length.bit_length() + 7) // 8, 'big') ++ payload = b'\x30' + bytes([0x80 | len(length_bytes)]) + \ ++ length_bytes + components ++ asn1Object, _ = decoder.decode(payload) ++ assert asn1Object is not None, 'Siblings incorrectly rejected' ++ ++ def testErrorMessageContainsLimit(self): ++ """Error message must indicate the nesting depth limit.""" ++ payload = b'\x30\x80' * 200 ++ try: ++ decoder.decode(payload) ++ except error.PyAsn1Error as exc: ++ assert 'nesting depth' in str(exc).lower(), \ ++ 'Error message missing depth info: %s' % exc ++ else: ++ assert False, 'Expected PyAsn1Error' ++ ++ def testNoRecursionError(self): ++ """Must raise PyAsn1Error, not RecursionError.""" ++ payload = b'\x30\x80' * 50000 ++ try: ++ decoder.decode(payload) ++ except error.PyAsn1Error: ++ pass ++ except RecursionError: ++ assert False, 'Got RecursionError instead of PyAsn1Error' ++ ++ def testMixedNesting(self): ++ """Mixed SEQUENCE and SET nesting must be caught.""" ++ # Alternate SEQUENCE (0x30) and SET (0x31) with indef length ++ payload = b'' ++ for i in range(200): ++ payload += b'\x30\x80' if i % 2 == 0 else b'\x31\x80' ++ try: ++ decoder.decode(payload) ++ except error.PyAsn1Error: ++ pass ++ else: ++ assert False, 'Mixed nesting not rejected' ++ ++ def testWithSchema(self): ++ """Deeply nested structures must be caught even with schema.""" ++ payload = b'\x30\x80' * 200 ++ try: ++ decoder.decode(payload, asn1Spec=univ.Sequence()) ++ except error.PyAsn1Error: ++ pass ++ else: ++ assert False, 'Deeply nested with schema not rejected' ++ ++ + class NonStreamingCompatibilityTestCase(BaseTestCase): + def setUp(self): + from pyasn1 import debug +--- a/tests/codec/cer/test_decoder.py ++++ b/tests/codec/cer/test_decoder.py +@@ -363,6 +363,30 @@ class SequenceDecoderWithExplicitlyTagge + assert s[1][0] == univ.OctetString(hexValue='02010C') + + ++class NestingDepthLimitTestCase(BaseTestCase): ++ """Test CER decoder protection against deeply nested structures.""" ++ ++ def testIndefLenNesting(self): ++ """Deeply nested indefinite-length SEQUENCEs must raise PyAsn1Error.""" ++ payload = b'\x30\x80' * 200 ++ try: ++ decoder.decode(payload) ++ except PyAsn1Error: ++ pass ++ else: ++ assert False, 'Deeply nested indef-length SEQUENCEs not rejected' ++ ++ def testNoRecursionError(self): ++ """Must raise PyAsn1Error, not RecursionError.""" ++ payload = b'\x30\x80' * 50000 ++ try: ++ decoder.decode(payload) ++ except PyAsn1Error: ++ pass ++ except RecursionError: ++ assert False, 'Got RecursionError instead of PyAsn1Error' ++ ++ + suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + + if __name__ == '__main__': +--- a/tests/codec/der/test_decoder.py ++++ b/tests/codec/der/test_decoder.py +@@ -361,6 +361,48 @@ class SequenceDecoderWithExplicitlyTagge + assert s[1][0] == univ.OctetString(hexValue='02010C') + + ++class NestingDepthLimitTestCase(BaseTestCase): ++ """Test DER decoder protection against deeply nested structures.""" ++ ++ def testDefiniteLenNesting(self): ++ """Deeply nested definite-length SEQUENCEs must raise PyAsn1Error.""" ++ inner = b'\x05\x00' # NULL ++ for _ in range(200): ++ length = len(inner) ++ if length < 128: ++ inner = b'\x30' + bytes([length]) + inner ++ else: ++ length_bytes = length.to_bytes( ++ (length.bit_length() + 7) // 8, 'big') ++ inner = b'\x30' + bytes([0x80 | len(length_bytes)]) + \ ++ length_bytes + inner ++ try: ++ decoder.decode(inner) ++ except PyAsn1Error: ++ pass ++ else: ++ assert False, 'Deeply nested definite-length SEQUENCEs not rejected' ++ ++ def testNoRecursionError(self): ++ """Must raise PyAsn1Error, not RecursionError.""" ++ inner = b'\x05\x00' ++ for _ in range(200): ++ length = len(inner) ++ if length < 128: ++ inner = b'\x30' + bytes([length]) + inner ++ else: ++ length_bytes = length.to_bytes( ++ (length.bit_length() + 7) // 8, 'big') ++ inner = b'\x30' + bytes([0x80 | len(length_bytes)]) + \ ++ length_bytes + inner ++ try: ++ decoder.decode(inner) ++ except PyAsn1Error: ++ pass ++ except RecursionError: ++ assert False, 'Got RecursionError instead of PyAsn1Error' ++ ++ + suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + + if __name__ == '__main__': diff -Nru pyasn1-0.6.1/debian/patches/series pyasn1-0.6.1/debian/patches/series --- pyasn1-0.6.1/debian/patches/series 2026-01-19 18:16:21.000000000 +0000 +++ pyasn1-0.6.1/debian/patches/series 2026-03-26 16:18:48.000000000 +0000 @@ -1,2 +1,3 @@ 0002-Remove-some-theme-options-to-avoid-needless-badges-i.patch CVE-2026-23490.patch +CVE-2026-30922.patch