I'm building Marshal, a behavioral supply-chain scanner for JVM dependencies. Instead of waiting for a CVE, it watches how packages change. Maintainer swaps, signature drops, repo URL changes, new install hooks. Each update gets a score from 0 to 100. Risky ones get blocked at PR time.
This week I ran it against real Maven Central packages for the first time. Here's what fired, what didn't, and where the tool embarrassed itself.
What fired
javax.activation:activation scored ORANGE at 55/100. Two signals:
-
SIGNATURE_DROPPED(40 pts): version 1.1-rev-1 was signed with GPG key2D6641C6AF88103E, a Sun Microsystems key. Version 1.1.1 has no.ascfile at all. -
MISSING_SIGNATURE(15 pts): current release is unsigned.
That's a real finding. The prior version had cryptographic provenance. The new one has none. In an attack scenario, that's exactly what you'd see after an account takeover. The attacker doesn't have the original signing key, so the new release goes unsigned.
The actual explanation here is almost certainly Oracle's acquisition of Sun in 2010. Oracle inherited the Java EE components but not the signing infrastructure. It's a known pattern across javax.* artifacts from that era. Not an attack. But the CVE database has nothing to say about this at all. The behavioral scanner at least surfaces the question.
Where I was wrong
log4j:log4j scored ORANGE at 55/100 between 1.2.16 and 1.2.17. Two signals fired:
-
NEW_MAINTAINER: signing key changed fromD3EC499070C9C3D0to86E02C5A42196CA8 -
REPO_CHANGED: SCM URL changed from.../tags/v1_2_16to.../tags/v1_2_17_rc3
Both are false positives.
The keys are both verifiable Apache Software Foundation keys. The two-year gap between those releases is when Apache rotated their signing infrastructure across many projects. The repo URL difference is just a tag name suffix. Same SVN domain, same Apache org.
commons-collections fired the same way for the same reason. Seven-year gap between 3.2.1 and 3.2.2, Apache key rotation in between. Also a false positive.
I cut both from the demo rather than pretend they were real findings.
What the rules need
NEW_MAINTAINER treats any signing key change as suspicious. That's too broad. The fix is checking whether both keys belong to the same organisation. Apache, Eclipse, Google, Spring all rotate keys on long-lived projects. If both fingerprints resolve to the same org, it's infrastructure maintenance, not an attacker.
REPO_CHANGED needs same-domain awareness. A URL change within the same GitHub org or the same Apache SVN domain is not the same risk as a package suddenly pointing to github.com/random-user/previously-org-owned-lib.
Both are on the v0.2 backlog now.
Where things stand
The CLI works. The GitHub Action posts PR comments. JSON output schema is stable. The replay test suite covers event-stream, ua-parser-js, node-ipc, PyTorch-nightly, and XZ Utils. ua-parser-js and node-ipc score RED. event-stream scores ORANGE. XZ Utils only partially fires — the initial maintainer handoff triggers the NEW_MAINTAINER signal, but the slow social engineering that preceded it is designed to evade automated detection. That limitation is documented and intentional.
The repo goes public with the v0.1.0 launch. I'll post here when it ships.
If you're running Renovate or Dependabot with auto-merge on a Java project, that's the use case this is built for. What would you want from a tool like this?
I'm Usman, building Marshal in public from Tilburg. Follow along at marshalhq.dev, X, and Bluesky.