In Part 1, the minimal deployment showed constraints traveling with the proxy: authentication, encryption, hardened deserialization, all declared in configuration and enforced at the call boundary. The proxy is a JAR. That JAR was downloaded and unmarshalled before any constraint ran. That step is the earlier problem.
Distributed Java systems that load remote code are vulnerable to supply chain compromise: an attacker can replace a legitimate JAR with one containing malicious bytecode. The usual answer is to carefully vet your dependencies once, before deployment, by a human. That doesn’t scale to a dynamic service mesh where new proxies register at runtime.
The Safe Codebase Audit Pipeline solves this. SCAP analyzes every third-party JAR for safety before any client ever deserializes an object from it. The verdict travels by SHA-256 content hash, not by URL. The same JAR is analyzed once, regardless of how many services serve it.
The Supply-Chain Threat
SCAP targets two distinct threat categories.
The first is malicious bytecode.
A JAR carrying deserialization gadget chains, unexpected native calls, or code designed to escape the permission model.
LoadClassPermission blocks explicit untrusted codebase loads at the policy level.
SCAP catches what comes through legitimate channels: JARs that hold a valid permission grant but still carry dangerous bytecode.
The second is subtler and easier to miss: blocking static initializers.
Every class has a <clinit> block that runs exactly once, when the class is first loaded.
A <clinit> that performs network I/O, acquires a file lock, or waits on a monitor pins the virtual thread carrier thread for its entire duration.
The JVM has a fixed-size carrier thread pool, one thread per CPU core by default.
As few as Runtime.availableProcessors() concurrent requests on a class with a blocking <clinit> is enough to bring the scheduler to a full stall.
No exceptions thrown.
No timeouts.
The JVM simply stops scheduling new work.
Neither of these requires a zero-day. Both are visible in the bytecode before the JAR is loaded.
Five Hosts, Five Responsibilities
SCAP distributes work across five hosts. No host is trusted to do everything. No host has more network access than it needs.
- Lookup Service
-
Stores marshalled service items opaquely and fires an event when a new service registers. Clients query it for discovery. It has no connection to the analysis hosts.
- Codebase Downloader
-
The only component with outbound internet access. Downloads JARs proactively on new-service events and forwards them to the BAE pool.
- Bytecode Analysis Engine pool
-
SELinux-isolated, stateless, and replicated N×. Analyzes JAR bytecode using ASM visitors and signs each
JarAnalysisReportwith its own private key. No outbound internet, noexec, no JNI, no foreign memory access. Stateless by design: any instance can handle anyAnalysisRequest, so adding capacity is just starting another engine and letting it self-register. - Verdict Registry
-
Accumulates signed reports and issues a
RegistryVerdictonly when a quorum of independent engines agrees. Clients query it by SHA-256 hash before unmarshalling any proxy. Keyed by content, not URL: a JAR is analyzed once regardless of CDN migrations, service moves, or URL changes. - JFR Telemetry Service
-
Receives
VirtualThreadPinnedJFR events from client JVMs and triggers re-analysis when pinning is detected. Clients cannot influence verdicts directly. No connection to the Codebase Downloader or BAE Pool.
Two isolation invariants carry the security guarantee:
- BAE Pool → Verdict Registry: no direct connection. A compromised analysis engine cannot write its own verdict to the registry.
- Codebase Downloader ↔ JFR Telemetry: no connection. A flood of JFR events from a misbehaving client cannot prevent the analysis pipeline from serving.
There is one more signal.
An abnormal JVM exit on the BAE Pool, called a Phoenix crash, is a security event in its own right.
The Phoenix daemon submits a CrashReport directly to the Verdict Registry, which condemns the codebase that caused the crash.
A JAR that kills an analysis engine is automatically untrusted.
What the Engine Analyzes
The BAE applies four ASM-based visitors to every class in a submitted JAR.
ClinitBlockingVisitor-
A breadth-first search from every
<clinit>to a registry of blocking sinks: network I/O, file locks, thread creation, native calls. Classifies risk asCLEAN,BLOCKING_GUARDED, orBLOCKING_DECLARED(DANGEROUS).BLOCKING_GUARDEDmeans a blocking call is present but behind a permission check.BLOCKING_DECLAREDmeans the blocking call is unconditional: a guaranteed carrier-thread pin. AtomicSerialComplianceVisitor-
Standard Java deserialization constructs objects before validating them. That window is where gadget chains, circular references, and reference theft live.
@AtomicSerialcloses it: validation runs before any field is assigned, or deserialization fails. Classes that don’t implement the protocol get aDANGEROUSverdict. - Cyclic
<clinit>detector -
Detects circular class-initializer dependency chains. A cycle between two
<clinit>methods deadlocks the JVM class-loading lock with no recovery short of a JVM restart. PERMISSIONS.LISTintegration-
Reads each JAR’s declared permission requirements. If the JAR already requests the permission that guards a blocking sink,
BLOCKING_GUARDEDis upgraded toBLOCKING_DECLARED. A JAR that declares it needsSocketPermissionand callsSocket.connectfrom<clinit>is DANGEROUS, not merely guarded. It will block, and it said so up front.
Each analysis produces a signed JarAnalysisReport; the Verdict Registry verifies the signature before accepting it.
No client trusts any single BAE instance directly.
They trust only the RegistryVerdict that emerges after a quorum of independent engines has signed off.
Conclusion
The constraint system from Part 1 stops a bad call before it leaves the JVM. SCAP stops bad code before it gets that far. Between them, the attack surface that remains is identity: who is calling, and can you verify it?
I’ll cover in the series' next part, WorkerSubject, UserSubject, and how JWT/OIDC plugs into a JERI dispatch.