Repository navigation
feat(decoder): expose the maximum nesting depth as a configuration option - #77
Merged
Merged
Conversation
…tion
spomky-labs/cbor-php 3.3.3 bounds how deeply nested a decoded data item may be,
so that a small payload of nested arrays can no longer build an object graph deep
enough to crash the process when it is released. That bound is now reachable from
the bundle configuration:
cbor:
max_depth: 1000 # CBOR\Decoder::DEFAULT_MAX_DEPTH
The value is passed to the Decoder service as its $maxDepth argument, while the
tag and other object managers keep being autowired. A much lower value is worth
setting when the decoded data comes from an untrusted source.
Spomky
added a commit
that referenced
this pull request
Sep 12, 2026
…php 3.4 registry (#84) * feat(decoder): expose the maximum nesting depth as a configuration option (#77) spomky-labs/cbor-php 3.3.3 bounds how deeply nested a decoded data item may be, so that a small payload of nested arrays can no longer build an object graph deep enough to crash the process when it is released. That bound is now reachable from the bundle configuration: cbor: max_depth: 1000 # CBOR\Decoder::DEFAULT_MAX_DEPTH The value is passed to the Decoder service as its $maxDepth argument, while the tag and other object managers keep being autowired. A much lower value is worth setting when the decoded data comes from an untrusted source. * feat(decoder): build the tag and other object managers from the cbor-php 3.4 registry cbor-php 3.4.0 implements the 58 tags of the IANA registry that used to fall through to GenericTag, and files them in its decoder through the new TagManager::register(), which names a class without loading it. The bundle did not benefit from any of it: the TagManager and OtherObjectManager services it declared were never injected into the Decoder (the compiled container read "new Decoder(null, null, 64)"), and the two compiler passes looked for a definition of the *interface*, which nothing declared, so the documented "cbor.tag" and "cbor.other_object" service tags were no-ops. Had they run, they would have handed service *instances* to add(), which takes a class name -- and a tag class, whose constructor takes the decoded data, is not something the container can build anyway. The managers are now built by two factories that carry the registry the library keeps private (a test compares them by reflection so the copy cannot fall behind), and are injected into the Decoder and aliased by their interfaces. An application registers its own classes by name, three ways: the new "tags" / "other_objects" configuration keys, the service tags, or the autoconfiguration of the interfaces. The passes only take the class of a tagged definition, compute the tag number at compile time and file it with register(); the container never instantiates the service, and a class that does not implement the interface is refused with a message naming it. The COSE messages of the appendices of RFC 8152 and RFC 8392, as published by the IETF COSE working group (public domain), are decoded by the bundle's decoder into the library's COSE tag classes and written back byte for byte. * fix(releases): update minor release support matrix for versions 3.1.x and 3.0.x * fix(di): declare the manager factories as arrays, which Symfony 6.4 understands ServiceConfigurator::factory() only takes a Closure since Symfony 7.1; the first-class callable Rector wrote broke the container on the 6.4 version this branch supports. The rule is skipped on the service configuration.
Spomky
added a commit
that referenced
this pull request
Sep 12, 2026
* origin/3.1.x: feat(decoder): expose the maximum nesting depth as a configuration option (#77) # Conflicts: # .ci-tools/phpstan-baseline.neon # README.md # composer.json # src/DependencyInjection/SpomkyLabsCborExtension.php # src/Resources/config/services.php
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.
Targets the new
3.1.xbranch, cut from3.0.x: this adds a feature, so it does not belong on a patch branch.What
spomky-labs/cbor-php3.3.3 bounds how deeply nested a decoded data item may be — without it, a payload of a few dozen kilobytes made only of nested arrays builds an object graph deep enough to segfault the process when it is released, afterdecode()has already returned. The bundle now exposes that bound:A much lower value is worth setting when the decoded data comes from an untrusted source:
How
The bundle had no configuration at all —
getConfiguration()returnednull. It now returns aConfigurationwith a singlemax_depthinteger node (defaultDecoder::DEFAULT_MAX_DEPTH, minimum 1,info()filled in forconfig:dump-reference). The value lands in thecbor.max_depthcontainer parameter and is passed to theDecoderservice as its$maxDepthargument; the tag and other object managers keep being autowired, so nothing else about the service changes.spomky-labs/cbor-phpmoves from^3.0to^3.3.3, the release that introducesDecoder::DEFAULT_MAX_DEPTH.The PHPStan baseline is regenerated: narrowing
getConfiguration()to a non-nullable return type removes one of its entries.Tests
tests/Functional/MaxDepthTest.phpboots the test kernel withmax_depth: 64and checks the parameter value, that 64 levels decode, and that 65 are rejected with the exact message. Full suite: 32 tests, 35 assertions.Verified locally in
ghcr.io/spomky-labs/phpqa:8.4: PHPStan, ECS, Rector, parallel-lint, Deptrac, licenses, PHPUnit and Infection all pass.