class Foo {
private final Bar bar;
Foo(Bar bar) {
this.bar = bar;
}
}
With the parameter-names-module enabled I'd expect this class to be correctly deserialized from {"bar":{...}}.
More than once have users hit this issue and asked why isn't it handled this way (#1135, #8, #21).
It might also be a good idea to provide an option to change this behavior (HANDLE_SINGLE_ARG_CONSTRUCTOR_AS_PROPERTY_CREATOR).
I still have big misgivings on this helping some users and hurting others; mostly for cases where constructor with String argument would lend itself naturally to delegation.
But! With 3.0 planning, I think this is reasonable thing to do. Support for JDK datatypes that rely on delegation will need to be handled separately, but I also don't know how many such types are left.
Since there are users that rely on this feature a feature flag should also be provided.
+1
I think it should be the default behavior in 3.x.
Yet no one has explained how to protect against very likely case of accidental discovery of constructor that are not meant to be used as creators. In fact this would likely open a new attack vector in case of polymorphic deserialization, as constructors of JDK/3rd-party libraries that were not previously considered will now be discovered.
Further, ambiguity between constructors is unsolved issue.
With Jackson 3, new discovery mechanism needs to be defined (not just or mainly for this issue), and we can and should tackle this. But I have concerns about how far we can get with zero annotations.
@cowtowncoder do you have a valid use case for a constructor not meant to be used as creator ?
@cowtowncoder, what does it mean accidental discovery of constructor? I don't see a case where that would happen because we always know the type we want jackson to deserialize and it's either our own classes or JDK ones. JDK ones aren't compiled with '-parameters'.
@sylvainlecoy About any class that has not been primarily created for use as json POJO, such as JDK types. I am not worried about ones designed for use, but (especially for security reasons) ones that are not -- with polymorphic typing it is possible in some cases to exploit methods/constructors/fields in clever ways.
@lpandzic Ok, good info wrt JDK parameter names (or lack thereof). You may have mentioned it earlier but I keep forgetting.
Other than that:
I will need to create issues at:
https://github.com/FasterXML/jackson3-dev
to kick start design discussions on mailing list: this will need to occur after 2.9.1 patch, and probably will not be the first thing to tackle. But should be among earlier ones as it was number 3 on postponed features at https://github.com/FasterXML/jackson/wiki/Jackson-Release-2.9
@lpandzic forgot one other thing: even if JDK types do not have parameter names (which is good here), 3rd party libraries probably would. A good document of problems is this:
https://github.com/mbechler/marshalsec/blob/master/marshalsec.pdf
which outlines existing security problems: expansion here could allow more constructors as potential attack vectors (esp. if it'd be for any number of parameters, not just one).
Note that this specifically relates to polymorphic deserialization, in which attacker specifies class name of class to use -- in which case all they have to do is to fine suitable attack vector.
While not trivial, it is quite doable with enough material. And with modern Java services, dependencies, there's a lot of material to sift through.
It is difficult to really gauge potential for abuse here since I do not think many Java deserializers allow detection of such constructors (and since parameter name inclusion is still relatively new feature for bytecode). But absence of known cases at this point does not prove, in my opinion, that there are none. Rather that we simply do not know, but that there is good reason to suspect there are constructors that would be problematic, within widely used 3rd party libraries.
This is still different, of course, from my earlier objection of mismatched constructors.
I think that is still somewhat valid concern too. But possibly something that we can design around, with good combination of visibility requirements, precedence.
@cowtowncoder I understand your point, but is this a blocker for this feature? Considering default constructors and setters vs constructor I'm not sure why it would make much of a difference for an attacker? Don't you have to specify for polymorphic classes all instances of hierarchy on parent class and don't you always have to specify parent target in your code for deserialization? If target is ever concluded from serialized format (json) then that is the cause of security risks as you can program through serialized format and that is not good security wise.
If I want to deserialize library class and that library is compromised, there is no rescue.
@cowtowncoder any update on this?
Hi there, any chances to see that landing one day ?
@sylvainlecoy In some form, yes. Either by letting users configure default, or, perhaps, by allowing both bindings. But I do not have timeline for this. 2.11.0 is being finalized and I doubt I have time to tackle this for that.
@cowtowncoder thanks for the quick reply. I am currently using @JsonCreator to fix this issue but I don鈥檛 like the idea having to put an annotation for these classes as the rest works flawlessly without the need of annotation. Did I missed a configuration property which could help me avoid having to put such annotations or using MixIns ? Thanks for your time.
@sylvainlecoy No, unfortunately there is no such option.
However, you can make it work in the meantime for your case by overriding this method:
public JsonCreator.Mode findCreatorAnnotation(MapperConfig<?> config, Annotated a) { }
in JacksonAnnotationIntrospector (or implement in custom introspector).
This is used by databind to find @JsonCreator annotation, but you can make it think it saw appropriate mode for specific cases, to prevent heuristics from being applied.
Or even use for cases where no annotation exists (but make sure to filter out unwanted cases to avoid conflicts).
That worked perfectly for my use case thanks
@cowtowncoder I just noticed the most wanted tag. Any reason why this issue is not on that list?
@lpandzic Only because I just added the label and have added whenever encountering one. This definitely warrants one-- thank you for update.
I know I have promised to tackle this "real soon now" for a while. But with caveat of this sorry history, I do plan to have another look with 2.12, having some new ideas.
Was about to add a simple setting ("defaultCreatorMode" of type JsonCreator.Mode) for smaller problem of ambiguous annotated 1-argument creator (just @JsonCreator, no mode), as that would be easy enough to add.
But once again re-reading the issue, bigger ask is introspection of un-annotated constructors.
It seems pointless to add a new configuration option that does not solve the main problem, but I think there is one possible case if would cover: legacy code that does not use mode property of @JsonCreator.
If anyone thinks such smaller patch (which does not affect possible work for 2.12 regarding introspection improvement) is worth addition, please let me know.
On 2.12 work wrt introspection: this would likely mean addition of an Enum that defines aspects of introspection for un-annotated constructors; configuration using "config overrides" style which allows for at least defaults, but also with some work per-type different definition.
The main question is that of options to use.
But some constraints first:
@JsonAutoDetect/visibility should be used for finding (or not) of candidates.With that, here are some possibilities:
@JacksonInject)but I assume other cases might make sense -- I did not consider possibility of what to do if parameter names are not found (quietly ignore otherwise valid candidate, or throw exception?), for example.
@lpandzic What do you think? Thank you for all your work, feedback; I know it has been a frustrating journey.
I think it's a good approach but I wouldn't dwell on it. For 0 arg constructor case or more constructors present - use the default behavior we had until now. I don't see any value in solving or rethinking that part really, especially after introduction of records.
So for me, jackson should handle javabean and record style of data holders out of the box and for everything else you have options that you can take but with a cost - it's not out of the box but certain effort is required like annotations or something else.
No problem, I'm glad I can help make java ecosystem a little bit more robust and user friendly :)
Finally finding time to work on this, possibly the last "must-have"/most-wanted feature for 2.12.
Current thinking:
ConstructorDetector), with 4 pre-configured instances to use (plus builder/mutant-factory to create different configurations)ConstructorSelector?) In case of multiple alternatives, gets called to select one (and only one) to use (or throw exception)SingleArgConstructor?) In case of @JsonCreator without "mode" or @JsonProperty for name, what to do: HEURISTIC (current, try to guess), PROPERTIES (mode=PROPERTIES, must find name), DELEGATING (mode=DELEGATING), FAIL (throw an exception for ambiguous case)With this, pre-configured instances would be:
DEFAULT except SingleArgConstructor = PROPERTIESDEFAULT except SingleArgConstructor = DELEGATINGDEFAULT except SingleArgConstructor = FAILThe real power would probably come from ConstructorResolver as that could resolve potentially complex case of multiple constructor -- for example by selecting the one that takes most arguments, or using some application-/model-specific heuristics.
Work here progressing, and now the original case passes when mapper is constructed like so:
ObjectMapper mapper = JsonMapper.builder()
.constructorDetector(ConstructorDetector.USE_PROPERTIES_BASED)
// add modules, configure etc
.build();
I realized that implementing ConstructorSelector for selecting across multiple alternatives would get quite complicated so will for now leave only 3 configurable pieces:
SingleArgConstructor, the most relevant here (choice of SingleArgConstructor.PROPERTIES)requireCtorAnnotation: Whether to always require annotation (@JsonCreator) or not (for those who want more not less explicit) (default: false)allowJDKTypeCtors: Even if implicit discovery allowed in general, is it allowed for JDK types (java.*, javax.)? (default: false)Of these I implemented the bigger piece (1) and need to add (2) and (3).
To anyone that can't upgrade jackson you might want to try this module - https://github.com/infobip/infobip-jackson-extension#SingleArgumentPropertyCreatorAnnotationlessSupport.
It might even work for some cases where ConstructorDetector.USE_PROPERTIES_BASED doesn't work as it works differently:
Here's a test that probably speaks better for itself what is supported - https://github.com/infobip/infobip-jackson-extension/blob/master/infobip-jackson-extension-module/src/test/java/com/infobip/jackson/SingleArgumentPropertiesCreatorModeAnnotationIntrospectorTest.java.
Most helpful comment
Work here progressing, and now the original case passes when mapper is constructed like so:
I realized that implementing
ConstructorSelectorfor selecting across multiple alternatives would get quite complicated so will for now leave only 3 configurable pieces:SingleArgConstructor, the most relevant here (choice ofSingleArgConstructor.PROPERTIES)requireCtorAnnotation: Whether to always require annotation (@JsonCreator) or not (for those who want more not less explicit) (default: false)allowJDKTypeCtors: Even if implicit discovery allowed in general, is it allowed for JDK types (java.*, javax.)? (default: false)Of these I implemented the bigger piece (1) and need to add (2) and (3).