DEV Community

Usman
Usman

Posted on • Originally published at marshalhq.dev

I scanned real Maven Central packages for supply-chain anomalies. Here's what I found, and where I was wrong

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 key 2D6641C6AF88103E, a Sun Microsystems key. Version 1.1.1 has no .asc file 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.
Marshal CLI terminal output showing ORANGE risk finding on javax.activation:activation 1.1-rev-1 to 1.1.1, with SIGNATURE_DROPPED and MISSING_SIGNATURE signals scoring 55/100

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 from D3EC499070C9C3D0 to 86E02C5A42196CA8
  • REPO_CHANGED: SCM URL changed from .../tags/v1_2_16 to .../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.

Top comments (0)