Keep instrumenting classes decorated by another agent's synthetic types - #333
Open
Mishenevd wants to merge 1 commit into
Open
Keep instrumenting classes decorated by another agent's synthetic types#333Mishenevd wants to merge 1 commit into
Mishenevd wants to merge 1 commit into
Conversation
Mishenevd
force-pushed
the
fix/opentelemetry-agent-coexistence
branch
2 times, most recently
from
August 10, 2026 13:36
fa285cd to
d87974b
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Mishenevd
force-pushed
the
fix/opentelemetry-agent-coexistence
branch
from
August 10, 2026 14:10
d87974b to
22822db
Compare
When the OpenTelemetry agent is loaded before Zen, it injects synthetic supertypes (its VirtualField markers) onto core types such as java.sql.*, jakarta.servlet.* and java.lang.Runnable. Those types have no .class resource, so resolving the hierarchy of any class implementing one threw NoSuchTypeException and the transform was skipped — silently turning off SQL injection, request-context/route and executor instrumentation. Add a lenient type pool that degrades an unresolvable type to an empty interface so resolution completes, and skip OpenTelemetry's own classes.
Mishenevd
force-pushed
the
fix/opentelemetry-agent-coexistence
branch
from
August 11, 2026 10:51
22822db to
9af214a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Zen silently stops instrumenting large parts of an app when it runs alongside the OpenTelemetry Java agent. In that setup SQL injection, SSRF, path traversal and command injection are no longer blocked, routes are not reported, and every request logs
SpringAnnotationCollector: Received Spring Annotations, but no context set.Reported by a customer running Zen v1.1.29 with the OTel agent v2.28.0 on Spring MVC.
Root cause
When the OTel agent is loaded first (
-javaagent:opentelemetry-javaagent.jar -javaagent:zen.jar), it adds synthetic marker interfaces — itsVirtualFieldaccessors — onto core types such asjava.sql.Connection/Statement/PreparedStatement,jakarta.servlet.Filter/Servletandjava.lang.Runnable.Those synthetic types have no
.classresource and live in OTel's own class loader. When Zen then transforms any class that implements one (PgStatement,RequestContextFilter,DispatcherServlet, …), Byte Buddy resolves the class hierarchy, fails to resolve the synthetic supertype, and throwsTypePool$Resolution$NoSuchTypeException. The whole transform is aborted, so the class is left uninstrumented. This is only logged at trace level, so it is invisible in normal logs.Losing the
RequestContextFiltertransform also removes the per-request context, which is why even sinks that are still instrumented (e.g.HttpURLConnection) stop blocking.Fix
OtelCompatPoolStrategy: a type pool that delegates to the normal lazy pool but degrades a genuinely unresolvable type to an empty interface instead of throwing, so hierarchy resolution completes and our transform is applied.io.opentelemetry.javaagentin the agent builder — those are the other agent's own classes, which we never need to instrument.Testing
Reproduced and verified with the OpenTelemetry Java agent loaded before Zen, using the existing end-to-end suites (
AIKIDO_BLOCK=1), comparing a Zen-enabled instance against a Zen-disabled one (safe request → 200, attack → 500 on enabled / 200 on disabled).sample-apps/SpringBootPostgres+end2end/spring_boot_postgres.py(SQLi, command injection, SSRF, path traversal):no context setAlso re-run under OTel with the same before/after result on
SpringBootHyperSQL,SpringMVCPostgresKotlin,SpringWebfluxSampleApp(WebFlux/reactor-netty) andJavalinPostgres— Spring MVC, WebFlux and a non-Spring framework.Adds a
runWithOpenteltarget (OTel loaded first) toSpringBootPostgresand a matchingopentel.ymljob so this cannot silently regress.Notes
RequestContextFilteris dropped by aRequestContextListener. Set Spring MVC context when RequestContextFilter is absent #327 alone does not help under OTel (itsDispatcherServletjoin point fails to transform for the same reason).