Skip to content

ConfigurationPropertiesReportEndpoint exposes AOP proxy internals#50273

Merged
philwebb merged 2 commits into
spring-projects:3.5.xfrom
zxuhan:gh-50231-configprops-aop-proxy-leak
May 13, 2026
Merged

ConfigurationPropertiesReportEndpoint exposes AOP proxy internals#50273
philwebb merged 2 commits into
spring-projects:3.5.xfrom
zxuhan:gh-50231-configprops-aop-proxy-leak

Conversation

@zxuhan

@zxuhan zxuhan commented May 1, 2026

Copy link
Copy Markdown
Contributor

The configprops actuator endpoint serializes each @ConfigurationProperties
bean using its bean-factory instance. When that instance is an AOP proxy
(commonly a CGLIB scoped proxy created by @RefreshScope, but the same
applies to any AOP proxy), the serializer also picks up the Advised
interface getters that the proxy exposes (targetSource, exposeProxy,
preFiltered).

With BufferingApplicationStartup configured, the targetSource walk reaches
beanFactory.applicationStartup.bufferedTimeline.events[…], inflating the
response by ~200 KB per refresh-scoped bean.

This change unwraps AOP proxies via Advised.getTargetSource().getTarget()
in safeSerialize before delegating to the serializer, so only the user-defined
configuration properties are exposed. The unwrap is wrapped in a try/catch so
that any failure to obtain the target falls back to the existing behaviour.

A test has been added that creates a ProxyFactory-based CGLIB proxy around a
@ConfigurationProperties bean and verifies that the resulting descriptor
contains only the user properties, not targetSource / exposeProxy /
preFiltered.

See gh-50231

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label May 1, 2026

@wilkinsona wilkinsona left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for the PR, @zxuhan. I've left a few comments for your consideration.

}
catch (Exception ex) {
return new HashMap<>(Collections.singletonMap("error", "Cannot serialize '" + prefix + "'"));
}
}

private @Nullable Object unwrapAopProxy(@Nullable Object bean) {
if (bean instanceof Advised advised) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

A bean could have been proxied multiple times so a while loop would be better here.

}
}
catch (Exception ex) {
// Fall through and serialize the proxy

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we should exclude the bean from the result rather than serializing the proxy.

@@ -168,13 +169,28 @@ private ConfigurationPropertiesBeanDescriptor describeBean(ConfigurationProperti
*/
private Map<String, @Nullable Object> safeSerialize(@Nullable Object bean, String prefix) {
try {
return new HashMap<>(this.serializer.serialize(bean));
return new HashMap<>(this.serializer.serialize(unwrapAopProxy(bean)));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think the unwrapping call would be better done earlier. If it returns null (no target, getting the target fails, etc), we should skip the bean entirely.

@zxuhan

zxuhan commented May 1, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review, @wilkinsona. Pushed 41a538fd055:

  • moved the unwrap into describeBean so it runs before safeSerialize,
  • looped on Advised to handle proxies-of-proxies,
  • return null from describeBean (and skip the entry in describeBeans) when the target can't be obtained.

Added two tests covering nested proxies and an unresolvable TargetSource.

@snicoll snicoll self-assigned this May 4, 2026
@snicoll snicoll added type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged labels May 4, 2026
@snicoll snicoll added this to the 3.5.15 milestone May 4, 2026
@snicoll snicoll changed the title Unwrap AOP proxies in configprops endpoint serialization ConfigurationPropertiesReportEndpoint exposes AOP proxy internals May 4, 2026
@snicoll
snicoll changed the base branch from main to 3.5.x May 4, 2026 13:42
@snicoll
snicoll force-pushed the gh-50231-configprops-aop-proxy-leak branch from 41a538f to 5ca02ab Compare May 4, 2026 13:42
Update `ConfigurationPropertiesReportEndpoint` to unwrap AOP
proxies. Prior to this commit, the configprops endpoint serialized
AOP proxies directly, exposing the Advised interface getters
(targetSource, exposeProxy, preFiltered) into the response.

See spring-projectsgh-50273

Signed-off-by: zxuhan7 <zxuhan7@gmail.com>
@philwebb
philwebb force-pushed the gh-50231-configprops-aop-proxy-leak branch from 5ca02ab to 70ff2ad Compare May 12, 2026 23:40
@philwebb
philwebb force-pushed the gh-50231-configprops-aop-proxy-leak branch from 70ff2ad to 3762d39 Compare May 12, 2026 23:57
@philwebb
philwebb merged commit c66ea41 into spring-projects:3.5.x May 13, 2026
3 of 4 checks passed
@philwebb

Copy link
Copy Markdown
Member

Thanks @zxuhan

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

Labels

type: bug A general bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants