NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
Packagist · #263 most downloaded on Packagist
CBOR Encoder/Decoder for PHP
Last release 22 days ago
15 Sep 2026
Ships fairly regularly
a new release about every 5 months
Some releases are documented
notes for 18 of 34 stable releases
Nothing withdrawn
no release was ever pulled
8 years old
34 releases · first in 2018
Total pull requests resolved: 1
3.4.x bugfix release (patch)
Total pull requests resolved: 1
3.4.x bugfix release (patch)
One column per quarter.
Total pull requests resolved: 1
Feature release (minor)
This release fixes GHSA-jfrf-557c-963v (High). Everyone decoding CBOR that comes from outside the application should upgrade.
This release fixes GHSA-jfrf-557c-963v (High). Everyone decoding CBOR that comes from outside the application should upgrade.
Five paths let a document written by an attacker cost far more than its size, or fail outside the documented InvalidArgumentException contract:
MapObject::normalize() and IndefiniteLengthMapObject::normalize() were quadratic in the number of entries. A 469 kB map took 45.5 s, and the same cost was paid inside decode() itself when a map was used as a map key.decode(), with a fatal error no try/catch can intercept.brick/math performs in time quadratic in the length on every calculator but GMP. On an installation with neither ext-gmp nor ext-bcmath — what composer require gives by default — 504 bytes cost 1.8 s and 2 kB close to a hundred, inside decode().ValueError, which is an Error, so a caller guarding the parse with InvalidArgumentException never caught it.RuntimeException, which the documentation reserves for a missing extension.| Before | After | |
|---|---|---|
A, 100 000-entry map normalize() |
45.5 s | 0.098 s |
A, same map used as a key, inside decode() |
37.6 s | 0.54 s |
B, 6000 tag 4 keys at memory_limit=128M |
fatal OOM, exit 255 | InvalidArgumentException in 0.009 s |
| C, 2 kB big number, no gmp/bcmath | 99 s | rejected, < 1 ms |
| D, E | ValueError / RuntimeException |
InvalidArgumentException |
Two documents that used to decode no longer do. Both are deliberate and both raise InvalidArgumentException, so a caller that guards the parse is unaffected:
DecimalFractionTag::MAX_ABSOLUTE_EXPONENT and BigFloatTag::MAX_ABSOLUTE_EXPONENT, previously 8192;UnsignedBigIntegerTag::MAX_BYTE_LENGTH and NegativeBigIntegerTag::MAX_BYTE_LENGTH.An indefinite-length string with an invalid chunk raises InvalidArgumentException rather than RuntimeException.
Install ext-gmp when the CBOR input is untrusted. Without it, big number conversion stays on a slow path; the new length bound keeps its cost proportionate to the document, but GMP is what removes it.
web-auth/webauthn-framework usersNo action beyond upgrading. WebAuthn never reaches tags 0, 2, 3, 4 or 5, so neither new bound applies to it, and it catches Throwable. Verified by decoding the 25 attestation objects in the webauthn-framework fixtures — the whole object and the embedded COSE public key — against 3.3.4 and 3.3.5: identical results. The one measurable effect is in its favour: the normalize() AttestationObjectLoader performs on the whole attestation object before any validation is no longer quadratic.
This release fixes three vulnerabilities in the decoding of untrusted CBOR documents. Every version up to and including 3.3.3 is affected. Application…
This release fixes three vulnerabilities in the decoding of untrusted CBOR documents. Every version up to and including 3.3.3 is affected. Applications that decode CBOR coming from a network peer, a file or any other untrusted source should upgrade.
BigFloatTag::normalize() and DecimalFractionTag::normalize() raised 2 (resp. 10) to an exponent read straight from the document, with no upper bound. An eleven byte payload made bcpow() request 103 GB in a single allocation, which kills the process with a fatal error that no try/catch can intercept; under memory_limit=-1 the process instead grinds until the OOM killer stops it. The tag only had to be embedded anywhere in the document, since normalize() recurses into children.
The exponent is now rejected when its absolute value exceeds MAX_ABSOLUTE_EXPONENT (8192), before bcpow() is reached. The bound is far above any legitimate use: 2^8192 already has more than 2400 digits.
Map entries were keyed by $key->normalize() in a native PHP array. A key normalizing to an array — a CBOR list or map, which RFC 8949 permits — raised a TypeError, so a three byte document crashed the decoder. And because PHP casts numeric-string offsets to int, structurally distinct keys silently overwrote one another: the integer 1, the text string "1", the byte string h'31' and the half-precision float 1.0 all landed on the same slot. {1:"A", "1":"B"} decoded to a single entry and get(1) returned the text key's value.
Keys are now tracked with the major type they came from, which separates a genuine duplicate from two distinct keys colliding on one offset. Both are rejected, as are keys that do not normalize to an integer or a string.
Eight byte length, count and tag headers surfaced a Brick\Math\IntegerOverflowException, and empty bignum payloads a NumberFormatException — the latter because the guard was an assert(), compiled out under the production default zend.assertions=-1 and therefore protecting nothing. Both now raise InvalidArgumentException, like the rest of the library.
The decoder is stricter about maps, which is what closes the second advisory. Documents that are now rejected with an InvalidArgumentException were previously either crashing the parser or being silently mangled, so nothing that genuinely worked before stops working:
Building a map programmatically is unaffected: set() and ArrayAccess still replace the value of an existing key.
3.3.x bugfix release (patch)
The decoder now bounds the nesting depth of the data it parses: anything nested deeper than Decoder::DEFAULT_MAX_DEPTH (1000 levels) is rejected with
The decoder now bounds the nesting depth of the data it parses: anything nested deeper than Decoder::DEFAULT_MAX_DEPTH (1000 levels) is rejected with an InvalidArgumentException instead of being turned into an object graph deep enough to crash the process when it is released. Nested arrays, maps, tag chains and indefinite-length containers all count towards that limit, which is configurable as the third argument of Decoder::create():
// Recommended when decoding data from an untrusted source
$decoder = Decoder::create(null, null, 32);Reported by Ivan Tse. Thanks!
Total issues resolved: 0
Total pull requests resolved: 2
Total contributors: 1
143: fix(decoder): limit the nesting depth of the decoded data thanks to @Spomky
Total pull requests resolved: 1
3.3.x bugfix release (patch)
Total pull requests resolved: 1
3.3.x bugfix release (patch)
Total pull requests resolved: 3
Feature release (minor)
Total pull requests resolved: 1
3.2.x bugfix release (patch)
Total issues resolved: 0
Total pull requests resolved: 1
Total contributors: 1
116: Add support for brick/math v0.15, v0.16 and v0.17 thanks to @Spomky
Total pull requests resolved: 1
3.2.x bugfix release (patch)
Total pull requests resolved: 1
3.2.x bugfix release (patch)
Total pull requests resolved: 0
Feature release (minor)
Total pull requests resolved: 1
3.1.x bugfix release (patch)
Total pull requests resolved: 2
73: Update CI/CD thanks to @Spomky
45: Deps updated thanks to @Spomky
Total pull requests resolved: 1
Total issues resolved: 0
Total pull requests resolved: 1
Total contributors: 1
54: deps: allow brick/math 0.12 thanks to @Spomky
Total pull requests resolved: 1
3.0.x bugfix release (patch)
Total issues resolved: 0
Total pull requests resolved: 1
Total contributors: 1
51: Allow brick/math 0.12 thanks to @jbtronics
Total pull requests resolved: 1
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →