Java Dependency Security: Scanning Maven... | GeekWala
Loading...
Skip to main content
Ecosystem Guide

Java Dependency Scanning: What OWASP Dependency-Check Misses

Java's dependency ecosystem spans Maven Central, Gradle, and enterprise repositories — with transitive dependency trees that routinely exceed 200 packages. Learn how to scan your pom.xml and build.gradle files for vulnerabilities and prioritize the ones attackers are actually exploiting.

GeekWala Team14 min read

Your enterprise Java platform pulls in 240 transitive dependencies. Last week, a critical vulnerability dropped in a third-level transitive of Apache Commons. Your scanning tool flagged it as "High" severity. Your team spent 48 hours investigating whether it actually affected your code path, only to discover that the vulnerable function wasn't reachable in your application flow.

Welcome to Java dependency security in 2026. The ecosystem is mature, the tooling is mature, but the signal-to-noise ratio is brutal. A single Spring Boot application can depend on 150+ JAR files transitively. Maven Central hosts millions of packages, many abandoned. When a vulnerability disclosure hits, you need to know not just "do I have it?" but "can attackers actually exploit it in my app?" and "is anyone actually exploiting it right now?"

This is where most Java teams fail. They run OWASP Dependency-Check, get a list of 40 findings, treat CVSS 6.0 as equally critical as CVSS 9.0, and patch everything. Or worse, they ignore everything because the signal is too noisy. Neither approach scales.

TL;DR: Java's dependency ecosystem is fragmented across Maven Central, Gradle, Ivy, and enterprise repositories—with transitive dependency trees that explode across 100-300 packages. OWASP Dependency-Check is thorough but doesn't tell you which vulnerabilities are actually being exploited. You need EPSS (exploitation probability) and CISA KEV (confirmed active exploitation) layered on top of NVD advisories to prioritize effectively. Add dependency tree analysis to understand the exact attack surface—sometimes the vulnerable package is shaded into your JAR, sometimes it's optional, sometimes you're not actually affected despite CVSS 9.0. Always use lock files (Maven BOM or Gradle lock files). When they're missing, scanning is educated guessing.

What We'll Cover

Why Java Dependency Security Is Different

Java's dependency ecosystem has unique characteristics that make vulnerability scanning harder than npm or Python:

1. Transitive dependency explosion

Simple Spring Boot Application
└── spring-boot-starter-web
    ├── spring-boot v3.0.0
    ├── spring-framework (4 jars)
    ├── tomcat-embed (3 jars)
    ├── jackson (6 jars)
    │   ├── jackson-core
    │   ├── jackson-databind    ← Vulnerability vector!
    │   ├── jackson-annotations
    │   ├── jackson-dataformat-xml (pulls xmlbeans)
    │   ├── jackson-datatype-jsr310
    │   └── jackson-module-jaxb-annotations
    ├── log4j-api
    ├── log4j-core             ← Historical nightmare
    ├── slf4j-api
    ├── jakarta-servlet (4 jars)
    ├── jakarta-validation (2 jars)
    ├── jakarta-json (3 jars)
    └── ... (200+ more transitively)

A Spring Boot starter pulls in 40-50 direct dependencies and 200+ transitive. Each is a potential vulnerability vector.

2. Multiple artifact repositories with inconsistent vulnerability data

Java packages live in Maven Central, JCenter (deprecated), Gradle plugins portal, corporate nexus instances, and custom private repositories. Unlike npm's unified advisory database, Java's vulnerability data is fragmented:

  • NVD (NIST): Comprehensive but slow to propagate
  • GitHub Security Advisories: Fast but incomplete for Java-specific issues
  • CVE Details: Niche, sometimes contradicts NVD
  • Package maintainer security pages: Highly variable coverage
  • Apache security advisories: Often the first source, but outside NVD for weeks

3. Dependency resolution complexity

Maven and Gradle resolve dependencies at build time, not install time. This means:

  • Version ranges ([1.0, 2.0)) are evaluated during build, not at definition time
  • Transitive dependency trees can be resolved differently based on build order, exclusions, and classloader hierarchy
  • BOM (Bill of Materials) imports can shadow or override direct dependencies
  • Maven's "nearest wins" strategy is different from npm's deterministic lock-file approach

4. Shading, relocation, and bundling

Java projects often "shade" dependencies—bundling them inside the JAR with relocated package names. Example:

<!-- In pom.xml -->
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-shade-plugin</artifactId>
  <executions>
    <execution>
      <phase>package</phase>
      <goals><goal>shade</goal></goals>
      <configuration>
        <relocations>
          <relocation>
            <pattern>org.apache.commons</pattern>
            <shadedPattern>com.mycompany.shaded.commons</shadedPattern>
          </relocation>
        </relocations>
      </configuration>
    </execution>
  </executions>
</plugin>

Now the commons library is embedded, version-locked, and isolated. A vulnerable version inside the shade is still a problem, but it's invisible to standard classpath scanning. Tools need to understand shading to catch these.

5. Long patch cycles in enterprise

Enterprise Java teams have SOC2/compliance constraints. Patching a critical Spring Framework vulnerability might require:

  • Code review (1-2 weeks)
  • Testing in staging (1-2 weeks)
  • Change approval board (1-2 weeks)
  • Staged rollout (1-2 weeks)

That 4-8 week cycle means zero-day vulns in Java hit production before patches land. Knowing which vulns are actually being exploited (CISA KEV) vs. which are theoretical attacks (high CVSS, low EPSS) changes the urgency entirely.

What Dependency Files GeekWala Accepts for Java

GeekWala scans Java projects using:

Maven:

✓ pom.xml                 Manifest file with version specs
✓ pom.lock                Maven's dependency lock file (optional, newer)
✓ dependency-tree.txt     Output of `mvn dependency:tree` (for analysis)

Gradle:

✓ build.gradle            Groovy-based build manifest
✓ build.gradle.kts        Kotlin-based build manifest
✓ gradle.lock             Gradle's lock file (strongly recommended)
✓ settings.gradle         Multi-project configuration

Build outputs:

✓ .gradle/dependencies.txt    Cached dependency resolution
✓ libs.versions.toml          Gradle version catalog

Best practice: For Maven, generate a lock file with mvn dependency:go-offline and commit the dependency tree output. For Gradle, always commit gradle.lock — it guarantees reproducible builds and accurate vulnerability scanning.

Without lock files, scanning Maven pom.xml or Gradle build.gradle is version-range guesswork. GeekWala will scan the highest available version matching your range and flag any vulnerabilities in it, but your actual build might resolve to a different version entirely.

OWASP Dependency-Check vs GeekWala

OWASP Dependency-Check is the de facto standard for Java vulnerability scanning. It's open-source, free, integrated into Jenkins/GitLab/GitHub Actions, and widely trusted. But "standard" doesn't mean "sufficient."

OWASP Dependency-Check's approach is conservative: it scans all JAR files in your classpath, including transitive dependencies, and cross-references them against NVD. It's thorough and catches nearly everything in its database. But it has three limitations:

  1. No exploitation signals: It tells you "Apache Commons Collections 3.2 has a deserialization vulnerability" but not "is anyone actually exploiting this specific vulnerability right now?" A vulnerability disclosed in 2014 might have CVSS 9.8 but EPSS 0.01 if no one has exploited it in years.

  2. Limited cross-database coverage: OWASP Dependency-Check relies primarily on NVD, which has gaps for Java-specific advisories that appear in GitHub Security Advisories or Apache security pages first.

  3. No trend analysis: EPSS scores change over time. A vulnerability might start at EPSS 0.05, spike to 0.8 when a public PoC emerges, then settle at 0.6. Knowing when a threat is rising vs. stable changes urgency dramatically.

CapabilityOWASP Dependency-CheckGeekWala
Advisory sourcesNVD primaryNVD + OSV + GitHub Advisories + Apache Security pages
JAR file scanning✓ (analyzes bytecode + checksums)✓ (via pom.xml/build.gradle)
Transitive dependency analysis✓ (comprehensive)✓ (Maven/Gradle resolution)
Exploitation signals (EPSS)EPSS score for each finding
Actively exploited tracking (KEV)CISA KEV status
Shading detectionLimited (checksum-based)✓ (understands Maven shade plugin)
Gradle support✓ (scans JAR output)✓ (parses build.gradle + gradle.lock)
BOM/version catalog support✓ (understands Maven BOM imports, Gradle catalogs)
Historical trend tracking✓ (EPSS score evolution over weeks)
Local vs. continuousBest for CI/CD automationBest for production monitoring

Use OWASP Dependency-Check for local development and CI/CD gates—it's thorough, free, offline-capable, and integrates everywhere. Build it into your pipeline.

Use GeekWala for production monitoring and risk-based prioritization—when you need to know not just "what's vulnerable?" but "what's being exploited right now?" Scan your Maven or Gradle dependencies →

Java's Transitive Dependency Explosion

Here's where Java differs most from npm or Python: transitive dependency trees can exceed 300 packages in a single application. Understanding this explosion is critical for effective scanning.

Typical Spring Boot Application Dependency Tree
───────────────────────────────────────────────────

my-app (1 artifact)
  └── spring-boot-starter-web (1 direct, 45+ transitive)
      ├── Spring Framework ecosystem (10+ artifacts)
      │   ├── spring-core
      │   ├── spring-context
      │   ├── spring-web
      │   └── spring-beans
      ├── Jackson (data binding, 8+ artifacts)
      ├── Tomcat embedded (5+ artifacts)
      ├── Servlet API (Jakarta/javax, 3+ artifacts)
      ├── Validation API (2+ artifacts)
      ├── Logback + SLF4J (3+ artifacts)
      └── More...

  └── spring-boot-starter-data-jpa (1 direct, 30+ transitive)
      ├── Hibernate (8+ artifacts)
      ├── Jakarta Persistence (3+ artifacts)
      ├── ByteBuddy (2+ artifacts)
      └── Antlr (2+ artifacts)

  └── spring-boot-starter-security (1 direct, 15+ transitive)
      ├── Spring Security (4+ artifacts)
      ├── JJWT (if enabled, 3+ artifacts)
      └── More...

  └── Custom libraries
      └── common-utils (your artifact, 20+ transitive)

Total direct: 5 | Total transitive: 150+ | Grand total: 155+

When multiplied across teams:
  - 5 Spring Boot starters: ~400-500 transitive
  - Add spring-cloud, Netflix OSS, observability: 600+
  - Add enterprise frameworks: 800+

This explosion is why effective scanning requires:

  1. Accurate dependency resolution: Tools must understand Maven version ranges and Gradle constraint resolution
  2. Transitive path tracing: When a vulnerability is found 4 levels deep, you need to see the exact chain: my-app → spring-boot-starter-data-jpa → Hibernate → ByteBuddy → CVE-2023-xxx
  3. Scope filtering: Understanding which dependencies are compile, runtime, test, or optional — test dependencies in production vulnerabilities are less critical

Real-World Java Vulnerability Examples

Java's vulnerability landscape has evolved dramatically. Here are three patterns from recent years:

Example 1: Log4Shell — CVE-2021-44228

The single most impactful Java vulnerability ever. Log4j v2 versions < 2.17.0 allow arbitrary code execution through deserialization:

your-app/
├── pom.xml
│   └── org.apache.logging.log4j:log4j-core:2.14.1  ← VULNERABLE
│       └── 🔴 CVE-2021-44228 (CVSS 10.0, EPSS 0.97, actively exploited)
└── ...

The cascade was massive because log4j is in:

  • Spring Boot (via spring-boot-starter-logging)
  • Apache Kafka
  • Elasticsearch
  • Cassandra
  • Hadoop
  • Nearly every enterprise Java stack

Impact: Within hours of disclosure, threat actors exploited unpatched systems. CISA KEV was populated instantly. EPSS spiked to 0.97. Within 48 hours, patching became an emergency. Teams with continuous vulnerability scanning caught it before attackers; teams with quarterly scans got breached.

Example 2: Jackson Databind Deserialization — CVE-2017-7525

Jackson-databind allows arbitrary code execution through polymorphic deserialization if certain gadget chains are in the classpath:

your-app/
├── pom.xml
│   └── com.fasterxml.jackson.core:jackson-databind:2.8.8
│       └── 🔴 CVE-2017-7525 (CVSS 8.1, EPSS 0.35, actively exploited)
│
├── If classpath contains any of:
│   ├── org.springframework:spring-orm → uses jackson → VULNERABLE
│   ├── org.codehaus.groovy:groovy-all → VULNERABLE
│   └── More...

This vulnerability required a gadget chain in the classpath. Your app might have CVE-2017-7525 flagged but still be safe if you don't include the vulnerable gadget chain. OWASP Dependency-Check flags it as critical. GeekWala with EPSS context shows "actively exploited but requires specific gadget chains — defer if gadgets not present."

Example 3: Spring4Shell — CVE-2022-22965

Spring Framework versions 5.3.0-5.3.16 and 6.0.0-6.0.2 have a remote code execution vulnerability in data binding:

your-app/
├── pom.xml
│   └── org.springframework:spring-webmvc:5.3.10
│       └── 🔴 CVE-2022-22965 (CVSS 9.8, EPSS 0.96, actively exploited)
│
│       Risk depends on:
│       ├── Are you accepting untrusted input in web requests?
│       ├── Are you binding request parameters to POJO fields?
│       └── If both: CRITICAL. Patch immediately.
│       └── If neither: Still patch, but less emergency.

The signal-to-noise problem: CVSS 9.8 looks like universal emergency. But exploitation requires specific code patterns. EPSS helps: 96% shows real attack activity. But context matters — some Spring apps are pure REST APIs that don't use the vulnerable data binding pattern.

Major Java Vulnerabilities That Changed the Game

Java's vulnerability landscape shifted after Log4Shell. Before 2021, enterprise teams patched on quarterly cycles. Log4Shell showed that Java ecosystems could have zero-day exploits in the wild within hours.

This changed three things:

  1. Enterprise monitoring expectations: Executives now expect vulnerability detection to happen in days, not months
  2. Transitive dependency visibility: Teams realized they couldn't manually track 200+ transitive dependencies
  3. Prioritization matured: CVSS alone became insufficient; EPSS and KEV became table stakes

The pattern: Major Java vulns now spike to EPSS 80-95+ and hit CISA KEV within 72 hours if exploitable. Tools that only look at CVSS flag everything equally. Tools that include EPSS and KEV let you sort 40 findings down to the 3-4 that actually require emergency response.

How to Prioritize Java Vulnerabilities

Effective Java vulnerability prioritization requires abandoning CVSS as your primary sort key and adopting a multi-signal approach:

Java Vulnerability Decision Tree
─────────────────────────────────────────────────────────────

Finding: Vulnerability in transitive dependency

│
├─ CISA KEV (Actively exploited)?
│  ├─ YES → PRIORITY: 🔴 CRITICAL (patch this week)
│  └─ NO → Continue to EPSS
│
├─ EPSS > 0.8 (>80% probability of exploitation)?
│  ├─ YES → PRIORITY: 🟠 HIGH (patch this month)
│  └─ NO → Continue to context
│
├─ Vulnerability Category
│  ├─ Deserialization (Jackson, YAML, Kryo)?
│  │  └─ → Escalate to 🟠 HIGH if EPSS > 0.4
│  │
│  ├─ Remote Code Execution (Spring, log4j)?
│  │  └─ → Escalate to 🟠 HIGH if EPSS > 0.3
│  │
│  ├─ Authentication bypass (Spring Security)?
│  │  └─ → Escalate to 🟠 HIGH if EPSS > 0.5
│  │
│  ├─ Information disclosure (low EPSS)?
│  │  └─ → Keep as 🟡 MEDIUM if EPSS < 0.3
│  │
│  ├─ Denial of service (regex, XML bomb)?
│  │  └─ → 🟡 MEDIUM (depends on public exposure)
│  │
│  └─ Other → Continue to context
│
├─ Attack Surface
│  ├─ Vulnerability in public API (Spring controller)?
│  │  └─ → Escalate one level
│  │
│  ├─ Vulnerability in internal lib (only used internally)?
│  │  └─ → Defer one level
│  │
│  ├─ Vulnerability in test dependency?
│  │  └─ → Defer to maintenance cycle (🟢 LOW)
│  │
│  └─ Vulnerability in optional feature (disabled)?
│      └─ → Defer to next maintenance window
│
└─ Final Priority Assignment
   ├─ 🔴 CRITICAL (CISA KEV or EPSS > 0.95): Patch this week
   ├─ 🟠 HIGH (EPSS > 0.6 or high category): Patch this month
   ├─ 🟡 MEDIUM (EPSS 0.3-0.6 or medium category): Patch next quarter
   └─ 🟢 LOW (EPSS < 0.3 and low category): Patch at next release

Real example application:

Finding: org.apache.commons:commons-collections:3.2 → CVE-2015-6420
CVSS: 9.8 (Arbitrary code execution)
EPSS: 0.05 (5% exploitation probability)
KEV: Not listed (not actively exploited)
Category: Deserialization (normally high)
Context: Used only internally by custom audit logging, not in main request path

Decision: 🟡 MEDIUM → Patch in next maintenance release, not emergency
Reasoning: High CVSS but historical vulnerability with minimal active exploitation
Finding: org.springframework:spring-webmvc:5.3.10 → CVE-2022-22965
CVSS: 9.8 (Remote code execution)
EPSS: 0.96 (96% exploitation probability)
KEV: YES (CISA actively exploited list)
Category: Remote code execution
Context: Direct public API, handles untrusted user input

Decision: 🔴 CRITICAL → Patch this week (48-hour SLA)
Reasoning: RCE, actively exploited, accessible from internet

Setting Up Java Dependency Scanning

Step 1: Audit your current dependency state

For Maven:

# Generate dependency tree
mvn dependency:tree > dependency-tree.txt

# Optional: generate lock file (Maven 3.9.1+)
mvn dependency:go-offline

# Or use enhanced-dependency-management if available
mvn help:describe -Dplugin=org.apache.maven.plugins:maven-dependency-plugin

For Gradle:

# Generate lock file (must commit this)
gradle dependencies --write-locks

# Or view all dependencies
gradle dependencies > dependencies.txt

Step 2: Upload to GeekWala

Visit GeekWala's Java scanning page and upload your pom.xml (Maven) or build.gradle (Gradle). Include any lock files (gradle.lock, dependency tree output, or BOM files) for maximum accuracy.

GeekWala parses the manifest, resolves all transitive dependencies, and cross-references them against NVD, OSV, GitHub Security Advisories, and CISA KEV.

Step 3: Interpret the results

GeekWala enriches each finding with:

  • CVSS: Severity (how bad is it if exploited)
  • EPSS: Exploitation probability (how likely is actual exploitation)
  • CISA KEV: Active exploitation confirmation
  • Dependency path: my-app → spring-boot-starter-data-jpa → Hibernate → ByteBuddy → CVE-2023-xxx

Sort by KEV first, then EPSS descending. Ignore CVSS as your primary sort key.

For Java specifically, pay special attention to vulnerabilities in:

High-impact Java packages:
  ├── Spring Framework (core, security, data, cloud)
  ├── Log4j (logging framework)
  ├── Jackson (JSON databinding — deserialization risks)
  ├── Hibernate (ORM — SQL injection, deserialization)
  ├── Apache Commons (collections, lang, io — widely used)
  ├── Tomcat (servlet container)
  ├── Jetty (servlet container)
  ├── Kafka (message broker — often handles untrusted data)
  ├── Elasticsearch (search — injection risks)
  └── OpenJDK/JDK modules (cryptography, XML parsing, etc.)

Step 4: Trace the dependency path

Click any vulnerability and trace the path. Understanding the dependency chain matters:

Scenario A:
your-app → spring-boot-starter-security → spring-security → commons-codec → CVE
Fix: Update spring-security (pulls patched commons-codec)

Scenario B:
your-app → custom-internal-lib → commons-codec → CVE
Fix: Either update custom-internal-lib, or add direct exclusion/override in pom.xml

Scenario C:
your-app → spring-boot-starter-web → jackson-databind → CVE
But: You only use jackson for REST parsing, not deserialization
Risk: Medium (unlikely to hit vulnerable code path, but still patch)

Step 5: Set up continuous monitoring

For production, set up scheduled scans:

<!-- Maven: pom.xml for CI integration -->
<plugin>
  <groupId>org.owasp</groupId>
  <artifactId>dependency-check-maven</artifactId>
  <version>9.0.0</version>
  <executions>
    <execution>
      <goals>
        <goal>check</goal>
      </goals>
    </execution>
  </executions>
</plugin>

Or use GeekWala's scheduled scans:

  • Daily or weekly automatic scans
  • Slack/email webhooks notify on new vulnerabilities
  • Custom thresholds (e.g., "fail CI if any KEV finding detected")

Java-Specific Challenges: Shading, Relocation, and Bundling

One of Java's quirks: developers can shade (bundle and relocate) dependencies to avoid classpath conflicts. This makes scanning harder.

Example: Shaded dependency in Kafka client

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-shade-plugin</artifactId>
  <configuration>
    <relocations>
      <relocation>
        <pattern>org.apache.commons</pattern>
        <shadedPattern>org.apache.kafka.common.utils.org.apache.commons</shadedPattern>
      </relocation>
    </relocations>
  </configuration>
</plugin>

Now commons-codec is embedded inside the Kafka JAR with a relocated package name. A scanner that only looks at the JAR's classpath might miss it. GeekWala's pom.xml/build.gradle analysis still catches it because it reads the source build configuration.

Handling shading in your scanning workflow:

  1. Always run GeekWala on your source pom.xml/build.gradle, not just on the compiled JAR
  2. If scanning compiled JARs, understand that shaded dependencies might not be flagged with full context
  3. When patching, remember that shaded vulnerabilities are "version-locked" — if your Kafka depends on commons-codec 1.10 shaded internally, and commons-codec 1.10 has a vulnerability, you must upgrade Kafka to a version that shades a patched commons-codec

Java Dependency Security Best Practices

1. Use Maven BOM or Gradle dependency catalog for consistency

Instead of hardcoding versions across multiple pom.xml files, use a BOM:

<!-- parent-pom.xml or shared bom -->
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-dependencies</artifactId>
      <version>3.2.0</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

Or in Gradle:

// gradle/libs.versions.toml
[versions]
spring-boot = "3.2.0"
jackson = "2.17.0"

[libraries]
spring-boot-web = { group = "org.springframework.boot", name = "spring-boot-starter-web", version.ref = "spring-boot" }

This centralizes version management — when a vulnerability is found, you update one version and it propagates across all projects.

2. Always commit lock files

Maven: Commit dependency tree output or use Maven lock file. Gradle: Always commit gradle.lock.

Without lock files, your build might resolve to different versions in different environments. Scanning becomes guesswork.

# Gradle: Lock dependencies
./gradlew dependencies --write-locks
git add gradle.lock
git commit -m "Update dependency locks"

3. Scan continuously, not just before release

EPSS scores spike when public exploits emerge. A vulnerability at EPSS 0.1 on Monday might be at EPSS 0.85 by Wednesday. Weekly scans catch rising threats early.

Set up webhooks to Slack so your team gets notified instantly when a high-EPSS vulnerability is discovered in a dependency you already use.

4. Understand Maven's dependency resolution strategy

Maven uses "nearest wins" — if multiple versions of a library are in your tree, the closest to your application wins. This can lead to surprising resolution:

your-app
├── depends on: commons-lang v2.5
└── spring-boot-starter-web
    └── depends on: commons-lang v3.0

Result: commons-lang v2.5 wins (closer to your app)

If v3.0 has a fix and v2.5 is vulnerable, your explicit dependency just created a vulnerability. Use mvn dependency:tree to audit this.

5. Review optional dependencies carefully

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-orm</artifactId>
  <optional>true</optional>
</dependency>

Optional dependencies are in your classpath during development but might not be in production. If you're not using the optional feature, don't include it. If you do use it, patch vulnerabilities in it just as aggressively as non-optional dependencies.

6. Keep Spring Framework and Log4j up to date

Both are high-velocity ecosystems with frequent security patches. Subscribe to their mailing lists:

Check weekly. When a new version drops, prioritize testing and upgrading.

7. Test updates in staging before production

A newer version of Spring Security might fix a vulnerability but break an integration point. Test in staging first. For critical vulnerabilities (CISA KEV), you might skip this and patch production directly, but normally: test first.

8. Monitor for transitive dependency updates

Dependabot and Renovate can automatically create PRs to update transitive dependencies. This is valuable — it catches vulnerabilities in packages you didn't directly specify:

# Dependabot PR: "Bump commons-collections from 3.2 to 3.2.2"
# (dependency of dependency of your code)

Review and merge these automatically. They're usually low-risk patches to deep transitive dependencies.

9. Understand the difference between "patched" and "patched for your code path"

A deserialization vulnerability in Jackson exists but might only be exploitable if you:

  1. Deserialize untrusted JSON
  2. Enable polymorph type handling
  3. Have specific gadget chains in your classpath

If none of these apply, you're technically safe, but still patch. Supply chain attacks could inject new code later.

10. Keep dependency tree depth in check

Deep dependency trees are vulnerability multiplication. Every additional layer is a potential supply chain risk. When you have the option:

  • Use libraries with fewer transitive dependencies
  • Disable optional features you don't use
  • Break large monoliths into separate services with smaller dependency footprints

FAQ

Why does CVSS say 9.8 but EPSS says 0.1?

CVSS measures severity if exploited. EPSS measures likelihood of exploitation. A vulnerability might be severe (CVSS 9.8) but hard to exploit (EPSS 0.1) due to:

  • Specific preconditions required
  • No public exploit available
  • Difficult attack vector
  • Limited real-world impact

Example: YAML deserialization in PyYAML has CVSS 9.8 (arbitrary code execution) but EPSS 0.05 (most users aren't deserializing untrusted YAML).

Can I use gradle.lock for accurate scanning?

Yes. gradle.lock is the source of truth for Gradle builds. Always commit it. GeekWala scans it with full accuracy — exact versions, no guessing.

My JAR has a shaded dependency. How do I scan it?

Scan the source pom.xml or build.gradle, not the compiled JAR. GeekWala reads the shade plugin configuration and understands which dependencies are bundled internally.

A vulnerability was published, but my scanner didn't catch it for 48 hours. Why?

Java's advisory fragmentation means vulns can appear in GitHub Security Advisories days before NVD. GeekWala queries multiple sources to minimize this gap, but propagation takes time. Subscribe to mailing lists (Spring, Apache, etc.) for early warning.

I have 60+ vulnerabilities found. Where do I start?

Filter by CISA KEV first, then sort by EPSS descending. For the full prioritization framework, see our EPSS deep dive.

For Java specifically, prioritize vulnerabilities in:

  1. Remote code execution findings (RCE)
  2. Authentication/authorization bypass
  3. Deserialization vulnerabilities (Jackson, Kryo, etc.)
  4. Injection vulnerabilities (SQL, YAML, expression language)

Then sort by EPSS within each category.

Does GeekWala work with private Maven repositories?

Not yet. GeekWala scans against public Maven Central and OSV databases. Private packages are checked based on their version against known public CVEs. If your private package wraps a public library, vulnerabilities in the public library are still detected. Enterprise private registry support is planned.

Can I patch a transitive dependency without updating its parent?

Only by adding a direct dependency override in your pom.xml or build.gradle. But be cautious:

<!-- pom.xml: Force newer version of transitive dependency -->
<dependency>
  <groupId>commons-codec</groupId>
  <artifactId>commons-codec</artifactId>
  <version>1.16</version>
</dependency>

This works but can break compatibility if the parent library expects the old version. Patch the parent library instead when possible.


Discover which Java vulnerabilities are actually being exploited — not just which ones exist.

Upload your pom.xml or build.gradle → — get results enriched with EPSS exploitation scores and CISA KEV active exploitation status in under a minute. No account needed.