Skip to content

Validate generic type arguments (simplified alternative to #6254) - #6259

Open
martinfrancois wants to merge 3 commits into
FasterXML:2.18from
martinfrancois:validate-generic-type-arguments-typefactory
Open

martinfrancois wants to merge 3 commits into
FasterXML:2.18from
martinfrancois:validate-generic-type-arguments-typefactory

Conversation

@martinfrancois

@martinfrancois martinfrancois commented Sep 30, 2026 •

Copy link
Copy Markdown

Alternative to #6254


Summary by Gitar

  • Validation improvements:
    • Validate generic type arguments against declared type parameters of base type in DatabindContext
    • Add BaseTypeAllowingValidator and _validateGenericSubType hook for unsafe class checks

This will update automatically on new commits.

martinfrancois and others added 2 commits September 29, 2026 20:33
…nceof, validate self-referenced arguments

- Error messages now carry the polymorphic base type and the complete type id,
  the same as the container-level checks, and name the offending type argument
  and the type parameter it was declared as.
- Replace the `instanceof DeserializationContext` check with a protected no-op
  hook in `DatabindContext` that `DeserializationContext` overrides.
- Use `introspectClassAnnotations` for the `SubTypeValidator` call; the bean
  description is only used to report a denial.
- Compare the approved base type with `equals` in `BaseTypeAllowingValidator`.
- Validate the type a `ResolvedRecursiveType` refers to: the projection of
  `SelfSupplier<X>` onto `Supplier` yields `Supplier<SelfSupplier<X>>` whose
  argument is a self-reference with no type parameters of its own, so `X`
  was never validated.
- Bring the `_validateTypeParameter` javadoc up to date and add a release
  note naming both behaviour changes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@martinfrancois
martinfrancois force-pushed the validate-generic-type-arguments-typefactory branch from 284d63f to 57c410f Compare September 30, 2026 23:34
Comment on lines +385 to +390
if (!param.isEnumType()) {
Validity baseValidity = ptv.validateBaseType(config, declaredType);
if (baseValidity == Validity.DENIED) {
throw _typeParameterDeniedException(polymorphicBase, typeId,
declaredType, param, ptv, true);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Bug: Denied declared base type rejects type arguments that used to pass

_validateTypeParameter now calls ptv.validateBaseType(config, declaredType) for every type argument and throws right away on DENIED. declaredType is often the unknown type (Object), because it stands in for unbound, wildcard, Object and subtype-only parameters. So a BasicPolymorphicTypeValidator built with denyForExactBaseType(Object.class) plus allowIfSubType("com.example.") will now reject ids like java.util.ArrayList<com.example.Foo> for a List<Object> or Map<String,Object> base. Before this change, the same argument was approved through validateSubClassName, and that rule still passes on the name check.

The top-level path only calls validateBaseType once, when the type deserializer is built; it is not re-checked for each type id. This PR applies it to a type the user never declared as a polymorphic base, which breaks existing configurations in a patch release. Fix: treat DENIED from the declared parameter's base check like INDETERMINATE and fall through to the name- and class-based checks, or skip the base check when declaredType is Object/unknown.

Was this helpful? React with 👍 / 👎

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intended. The declared type argument is treated as the base type in both directions: allowIfBaseType(X) approves subtypes of X as arguments, and denyForExactBaseType(X) rejects them, the same way both work for the top-level type. Letting a DENIED fall through to the name checks would make an Object argument easier to pass than an Object property. Jackson itself only writes generic type ids for EnumSet and EnumMap, where the arguments are enums (exempt from the PTV) or Object (skipped), so ids Jackson wrote are unaffected.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gitar can change code and merge on your behalf, so it only acts on requests from people who can push. This needs Write access to this repository.

@gitar-bot

gitar-bot Bot commented Sep 30, 2026

Copy link
Copy Markdown
Code Review ⚠️ Changes requested 0 closed / 1 findings

🔴 High risk · Polymorphic deserialization now validates generic arguments against declared types and subtype rules

Adds validation of generic type arguments during type factory operations, but the check against the declared base type now rejects valid type arguments that previously passed. When a BasicPolymorphicTypeValidator denies Object.class as a base type, type arguments like java.util.ArrayList<com.example.Foo> are incorrectly rejected for declarations like List<Object> or Map<String,Object>, breaking existing configurations. Treat DENIED results from declared parameter base checks as indeterminate to fall through to name- and class-based validation, or skip the base check when the declared type is Object or unknown.

⚠️ Bug: Denied declared base type rejects type arguments that used to pass

📄 src/main/java/com/fasterxml/jackson/databind/DatabindContext.java:385-390

_validateTypeParameter now calls ptv.validateBaseType(config, declaredType) for every type argument and throws right away on DENIED. declaredType is often the unknown type (Object), because it stands in for unbound, wildcard, Object and subtype-only parameters. So a BasicPolymorphicTypeValidator built with denyForExactBaseType(Object.class) plus allowIfSubType("com.example.") will now reject ids like java.util.ArrayList<com.example.Foo> for a List<Object> or Map<String,Object> base. Before this change, the same argument was approved through validateSubClassName, and that rule still passes on the name check.

The top-level path only calls validateBaseType once, when the type deserializer is built; it is not re-checked for each type id. This PR applies it to a type the user never declared as a polymorphic base, which breaks existing configurations in a patch release. Fix: treat DENIED from the declared parameter's base check like INDETERMINATE and fall through to the name- and class-based checks, or skip the base check when declaredType is Object/unknown.

🤖 Prompt for agents
Code Review: Adds validation of generic type arguments during type factory operations, but the check against the declared base type now rejects valid type arguments that previously passed. When a `BasicPolymorphicTypeValidator` denies `Object.class` as a base type, type arguments like `java.util.ArrayList<com.example.Foo>` are incorrectly rejected for declarations like `List<Object>` or `Map<String,Object>`, breaking existing configurations. Treat `DENIED` results from declared parameter base checks as indeterminate to fall through to name- and class-based validation, or skip the base check when the declared type is `Object` or unknown.

1. ⚠️ Bug: Denied declared base type rejects type arguments that used to pass
   Files: src/main/java/com/fasterxml/jackson/databind/DatabindContext.java:385-390

   `_validateTypeParameter` now calls `ptv.validateBaseType(config, declaredType)` for every type argument and throws right away on `DENIED`. `declaredType` is often the unknown type (`Object`), because it stands in for unbound, wildcard, `Object` and subtype-only parameters. So a `BasicPolymorphicTypeValidator` built with `denyForExactBaseType(Object.class)` plus `allowIfSubType("com.example.")` will now reject ids like `java.util.ArrayList<com.example.Foo>` for a `List<Object>` or `Map<String,Object>` base. Before this change, the same argument was approved through `validateSubClassName`, and that rule still passes on the name check.
   
   The top-level path only calls `validateBaseType` once, when the type deserializer is built; it is not re-checked for each type id. This PR applies it to a type the user never declared as a polymorphic base, which breaks existing configurations in a patch release. Fix: treat `DENIED` from the declared parameter's base check like `INDETERMINATE` and fall through to the name- and class-based checks, or skip the base check when `declaredType` is `Object`/unknown.

Review coverage

📋 Rules No rules evaluated

🧪 Functional validation Not enabled · Set up

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Counting what did not apply, without listing it.

Comment with these commands to change the behavior for this request:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Powered by Gitar — free for open source

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant