Skip to content

fix: close Serializable check bypass in PojoUtils.realize1 for no-class-key generic calls#16273

Open
cvictory wants to merge 1 commit into
3.3from
task/fix-pojo-serialize-check-bypass-20260516-215724
Open

fix: close Serializable check bypass in PojoUtils.realize1 for no-class-key generic calls#16273
cvictory wants to merge 1 commit into
3.3from
task/fix-pojo-serialize-check-bypass-20260516-215724

Conversation

@cvictory
Copy link
Copy Markdown
Contributor

Bug

  • Issue: [Bug] Generic invocation without class key bypasses Serializable check on PojoUtils.realize #16270
  • Symptom: A generic invocation Map without a class key bypasses the Serializable check in PojoUtils.realize1. A non-Serializable POJO is silently instantiated via reflection even when dubbo.application.checkSerializable=true.
  • Root cause: DefaultSerializeClassChecker.loadClass — the only check site — is gated behind if (className instanceof String). When the Map has no class key, className is null, the guard short-circuits, and newInstance(type) runs without any Serializable validation. The target type (already resolved from the method signature) is never checked.
Call style Serializable enforced? (before fix)
Strong-typed RPC Yes
Generic call, Map with class key Yes
Generic call, Map without class key No ← fixed

Fix

DefaultSerializeClassChecker — add checkClass(Class<?>):

Extracts the Serializable validation logic from loadClass into a dedicated method that accepts an already-resolved Class object, avoiding a redundant class-name lookup.

PojoUtils.realize1 — add else branch after the class-key block:

} else if (type != Object.class && !type.isInterface() && !Map.class.isAssignableFrom(type)) {
    // type resolved from method signature — still must pass Serializable check
    DefaultSerializeClassChecker.getInstance().checkClass(type);
}

The guard excludes Object, interfaces, and Map subclasses — types that are not POJO-instantiated on this code path and would cause false positives.

Verification

  • PojoUtilsSerializableCheckTest (new, 6 tests): covers happy/error/edge paths including the exact regression case from the issue.
  • PojoUtilsTest (existing, 44 tests): all pass — no regression.
Tests run: 6,  Failures: 0, Errors: 0, Skipped: 0  ← PojoUtilsSerializableCheckTest
Tests run: 44, Failures: 0, Errors: 0, Skipped: 0  ← PojoUtilsTest (regression)
BUILD SUCCESS

…ss-key generic calls

Issue #16270: When a generic invocation Map does not contain a "class" key, the type is
resolved from the method signature and passed directly to PojoUtils.realize1. Previously
the Serializable check in DefaultSerializeClassChecker.loadClass was gated behind an
`if (className instanceof String)` guard that short-circuited when the key was absent,
allowing non-Serializable POJOs to be instantiated via reflection without any validation.

Changes:
- DefaultSerializeClassChecker: add checkClass(Class<?>) that enforces the same
  Serializable contract as loadClass() for an already-resolved Class object.
- PojoUtils.realize1: add else-branch after the "class"-key block to call
  DefaultSerializeClassChecker.checkClass(type) when type is a concrete, non-Map,
  non-Object class and no "class" key was present in the Map.
- Add PojoUtilsSerializableCheckTest with 6 tests covering happy/error/edge paths,
  including the exact regression case from the issue report.
@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented May 16, 2026

Codecov Report

❌ Patch coverage is 0% with 11 lines in your changes missing coverage. Please review.
✅ Project coverage is 36.97%. Comparing base (3dbba26) to head (50fe82c).

Files with missing lines Patch % Lines
...bbo/common/utils/DefaultSerializeClassChecker.java 0.00% 9 Missing ⚠️
.../java/org/apache/dubbo/common/utils/PojoUtils.java 0.00% 2 Missing ⚠️

❗ There is a different number of reports uploaded between BASE (3dbba26) and HEAD (50fe82c). Click for more details.

HEAD has 10 uploads less than BASE
Flag BASE (3dbba26) HEAD (50fe82c)
unit-tests-java8 2 0
unit-tests-java25 2 0
unit-tests-java11 2 0
unit-tests-java17 2 0
unit-tests-java21 2 0
Additional details and impacted files
@@              Coverage Diff              @@
##                3.3   #16273       +/-   ##
=============================================
- Coverage     60.85%   36.97%   -23.89%     
+ Complexity    11779    11764       -15     
=============================================
  Files          1953     1952        -1     
  Lines         89186    89175       -11     
  Branches      13454    13373       -81     
=============================================
- Hits          54277    32974    -21303     
- Misses        29353    51622    +22269     
+ Partials       5556     4579      -977     
Flag Coverage Δ
integration-tests-java21 32.12% <0.00%> (-0.06%) ⬇️
integration-tests-java8 32.34% <0.00%> (+0.09%) ⬆️
samples-tests-java21 32.21% <0.00%> (-0.02%) ⬇️
samples-tests-java8 29.77% <0.00%> (-0.12%) ⬇️
unit-tests-java11 ?
unit-tests-java17 ?
unit-tests-java21 ?
unit-tests-java25 ?
unit-tests-java8 ?

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.

2 participants