jackson-databind icon indicating copy to clipboard operation
jackson-databind copied to clipboard

javax.mail.internet.InternetAddress string-argument constructor not being recognized.

Open HarshSainiJobvite opened this issue 2 years ago • 2 comments

Describe the bug Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of javax.mail.internet.InternetAddress (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('[email protected]')

        ValueInstantiator inst = this.getValueInstantiator();
        Class<?> rawTargetType = this.handledType();
        String value = p.getValueAsString();
        if (inst != null && inst.canCreateFromString()) {
            return inst.createFromString(ctxt, value);
        } else {
            CoercionAction act;
            if (value.isEmpty()) {
                act = ctxt.findCoercionAction(this.logicalType(), rawTargetType, CoercionInputShape.EmptyString);
                return this._deserializeFromEmptyString(p, ctxt, act, rawTargetType, "empty String (\"\")");
            } else if (_isBlank(value)) {
                act = ctxt.findCoercionFromBlankString(this.logicalType(), rawTargetType, CoercionAction.Fail);
                return this._deserializeFromEmptyString(p, ctxt, act, rawTargetType, "blank String (all whitespace)");
            } else {
                if (inst != null) {
                    value = value.trim();
                    if (inst.canCreateFromInt() && ctxt.findCoercionAction(LogicalType.Integer, Integer.class, CoercionInputShape.String) == CoercionAction.TryConvert) {
                        return inst.createFromInt(ctxt, this._parseIntPrimitive(ctxt, value));
                    }

                    if (inst.canCreateFromLong() && ctxt.findCoercionAction(LogicalType.Integer, Long.class, CoercionInputShape.String) == CoercionAction.TryConvert) {
                        return inst.createFromLong(ctxt, this._parseLongPrimitive(ctxt, value));
                    }

                    if (inst.canCreateFromBoolean() && ctxt.findCoercionAction(LogicalType.Boolean, Boolean.class, CoercionInputShape.String) == CoercionAction.TryConvert) {
                        String str = value.trim();
                        if ("true".equals(str)) {
                            return inst.createFromBoolean(ctxt, true);
                        }

                        if ("false".equals(str)) {
                            return inst.createFromBoolean(ctxt, false);
                        }
                    }
                }

                return ctxt.handleMissingInstantiator(rawTargetType, inst, ctxt.getParser(), "no String-argument constructor/factory method to deserialize from String value ('%s')", new Object[]{value});
            }
        }
 }

In the above code, default behaviour is throwing MissingInstantiator error, when canCreateFromString resolves to false.

We see the behaviour has changed for 2.6.7 databind version to 2.13.2.2 databind version when canCreateFromString resolves to false.

In 2.6.7, the default behaviour is as below:

        if (this._objectIdReader != null) {
            return this.deserializeFromObjectId(p, ctxt);
        } else if (this._delegateDeserializer != null && !this._valueInstantiator.canCreateFromString()) {
            Object bean = this._valueInstantiator.createUsingDelegate(ctxt, this._delegateDeserializer.deserialize(p, ctxt));
            if (this._injectables != null) {
                this.injectValues(ctxt, bean);
            }

            return bean;
        } else {
            return this._valueInstantiator.createFromString(ctxt, p.getText());
        }
 }

If canCreateFromString is false, the default behaviour of createFromString is execute.

This behaviour change is breaking our application. Is there a workaround for this issue?

Version information 2.13.2.2

To Reproduce If you have a way to reproduce this with:

  1. javax.mail.internet.InternetAddress being passed as string in JSON data.
  2. Longer example stored somewhere else (diff repo, snippet), add a link
  3. Textual explanation: include here

Expected behavior The original behaviour should work, for backward compatibility.

Additional context

https://github.com/FasterXML/jackson-databind/issues/1318 https://github.com/FasterXML/jackson-dataformat-xml/issues/254

HarshSainiJobvite avatar Jun 06 '22 14:06 HarshSainiJobvite

This could be a regression, or could be a fix if constructor was detected earlier for cases it should not have been. I do not off-hand know what the change here would have been. This could be related to security concerns on auto-detecting constructors from java.* / javax.* packages, although I do not remember exact details of possible changes.

But one quick question: which Constructor of javax.mail.internet.InternetAddress you think should be auto-detected, based on looking at Javadocs?

Also: it might be useful to know more specifically minor version in which change was observed, assuming this is easy to do.

cowtowncoder avatar Jun 06 '22 20:06 cowtowncoder

Ok, I think this was due to what I described above, so type was "accidentally" supported. With 2.12 and beyond this does not (and should not) work -- JDK types need explicit support. Partly this is wrt security (to avoid possible unintended "gadget" types), and partly since later JDKs are unlikely to allow access to these constructors via module system without user configuration. So it is better to have explicit support.

Now, it does not look like this type is part of core JDK even in JDK 8 (and definitely not in later modular JDKs). So it would need to be provided by a module, for whichever API package it is part of.

I will keep this open for now but will annotate with "wont-fix" since there is no plan to change behavior of core jackson-databind itself here.

cowtowncoder avatar Jul 28 '22 22:07 cowtowncoder