Maven & Gradle integration

Point Maven and Gradle at your mShield repository and every artifact download becomes a policy decision. Setup is one mirror or repository URL — or none, in egress-interception mode.

What this integration does

Maven and Gradle resolve through mShield instead of talking to Maven Central directly. Every artifact download — POM, JAR, or other file — is evaluated against your policies before it reaches the machine. Metadata and listings behave normally; an artifact that fails policy stops with an HTTP 403 naming the check that refused it.

The integration is one mirror in Maven's settings.xml, or one repository URL in Gradle. Nothing is installed on the developer's machine, and in egress-interception mode there is no client change at all.

For SaaS and on-prem. This guide uses client configuration. DNS mode is an alternative available only on-prem; see deployment models.

Point Maven and Gradle at your mShield repository

Your deployment provides the hostname and the credentials to use. Replace maven.example.mshield.dev below with your own.

Maven

~/.m2/settings.xml
<settings>
  <mirrors>
    <mirror>
      <id>mshield</id>
      <url>https://maven.example.mshield.dev/</url>
      <mirrorOf>*</mirrorOf>
    </mirror>
  </mirrors>
  <servers>
    <server>
      <id>mshield</id>
      <username>token</username>
      <password><your-token></password>
    </server>
  </servers>
</settings>

mirrorOf is * so no repository declared in a POM can route around mShield. The server id must match the mirror id.

For a project-scoped setup, keep the file in the repository's tooling directory and pass it explicitly: mvn -s settings.xml <goals>. Only builds run that way go through mShield.

Gradle

build.gradle
repositories {
    maven {
        url = uri("https://maven.example.mshield.dev/")
        credentials {
            username = "token"
            password = providers.environmentVariable("MSHIELD_TOKEN").get()
        }
    }
}

Use it for pluginManagement and dependencyResolutionManagement too, and remove mavenCentral(), google() and gradlePluginPortal() — each one is a second, ungated path to the same artifacts. To apply it to every project for an account, place the same block in an init script under ~/.gradle/init.d/.

On tokens. Anyone who can read settings.xml can use the token. Keep the file out of version control, and have Gradle read the token from the environment rather than writing it into build.gradle. Rotate the token if it is shared or committed.

Java trust store. The JVM uses its own certificate store, not the operating system's. If your deployment uses a private CA, import it into the JDK trust store used by the build.

Verify it took effect

Terminal
rm -rf ~/.m2/repository/junit
mvn -s settings.xml dependency:get -Dartifact=junit:junit:4.13.2

Maven prints the repository each artifact is downloaded from; it should be your mShield host and nothing else. Remove the artifact from the local repository first — Maven does not fetch what it already has. For Gradle, run a build with --refresh-dependencies and check the same.

CI runners

  • Write settings.xml in the job from a secret, or mount it from the runner's secret store. Both are discarded with the workspace.
  • Bake it into the build image so every job is covered without editing pipelines.

A persisted ~/.m2/repository or Gradle cache hides the proxy: anything already cached is never fetched again and so is never evaluated. Version ranges are read from metadata at resolve time, so a build can pull a different version than last week's — which is exactly what the gate is for.

What a blocked download looks like

Example
[ERROR] Failed to execute goal on project app: Could not resolve dependencies:
  Could not transfer artifact org.apache.logging.log4j:log4j-core:pom:2.14.1 from/to mshield
  (https://maven.example.mshield.dev/): status code: 403
  org.apache.logging.log4j:log4j-core 2.14.1 was refused by policy "Production dependencies"
  | More details: https://mshield.example.com/...

The build fails the way it fails any rejected download, and the refusal names the artifact and the rule behind it.

The refusal also carries a link to a page that explains the block in full — what was refused, why, the version that would satisfy the policy, and a way to request an exclusion. It needs no login, and it is scoped to that single refusal. See what the developer opens.

If the block is wrong for a case your team has accepted, the developer requests an exclusion from that page; an operator grants or refuses it, and a granted exclusion is package-scoped, time-boxed, and recorded like any other decision.

Rolling back

Remove the mshield mirror and server from settings.xml (or stop passing -s), and remove the mShield repository block from your Gradle files or init script. Decisions already recorded stay in the dashboard.

Discuss a pilot

See whether package policy fits your build.

Tell us your team, ecosystem, and current dependency-policy challenge. Pilot scope and commercial terms are agreed before provisioning.

Email about a pilot ↗