Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
a583f30
feat(java): apply config-file Maven mirrors in the pom parser
DmitriyLewen Jul 24, 2026
0283653
feat(flag): add scan.maven.mirrors config option
DmitriyLewen Jul 24, 2026
b406c3c
feat(java): wire scan.maven.mirrors to the pom analyzer
DmitriyLewen Jul 24, 2026
6e8c620
docs(java): document scan.maven.mirrors
DmitriyLewen Jul 27, 2026
c8611d5
feat(flag): reject non-absolute Maven mirror URLs
DmitriyLewen Jul 27, 2026
283355f
refactor(java): simplify pom mirror comments and drop redundant debug…
DmitriyLewen Jul 27, 2026
3ca9d19
test: refactor and dedupe Maven mirror tests
DmitriyLewen Jul 27, 2026
48fa082
test(java): merge resolveMirrors settings and config-file cases
DmitriyLewen Jul 27, 2026
017028b
test(java): build TestParser_mirrorFor inputs via resolveMirrors
DmitriyLewen Jul 27, 2026
2e8edf5
chore(java): fix golangci-lint issues (gci import order, map make)
DmitriyLewen Jul 27, 2026
405a8a4
fix(flag): restrict Maven mirror URLs to http/https
DmitriyLewen Jul 28, 2026
1b5aec6
fix(java): fall back to the next mirror on 429 instead of aborting
DmitriyLewen Jul 28, 2026
b43e591
fix(flag): reject empty Maven mirror target lists
DmitriyLewen Jul 28, 2026
77e2a0f
refactor(flag): configure Maven mirrors as a list instead of a map
DmitriyLewen Jul 30, 2026
0bb1700
fix(java): ignore credentials and host case when matching Maven mirrors
DmitriyLewen Jul 30, 2026
07b1cf7
refactor(java): use errors.AsType for the rate limit check
DmitriyLewen Jul 30, 2026
8f734a1
fix(java): clean the path when building a Maven mirror key
DmitriyLewen Aug 3, 2026
eb6d748
docs(java): correct how a 429 from a Maven mirror is described
DmitriyLewen Aug 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions docs/guide/coverage/language/java.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,38 @@ The vulnerability database will be downloaded anyway.
!!! Warning
Trivy may skip some dependencies (that were not found on your local machine) when the `--offline-scan` flag is passed.

### mirrors
Trivy supports several ways to set up mirrors for Maven repositories:

- `<mirrors>` in your Maven [`settings.xml`][maven-mirror-settings] — both the global and the user file.
- The Trivy [config file][config-file] — see [config-file mirrors](#config-file-mirrors) below.

#### resolving priority
For each package that needs to be fetched from a remote repository, Trivy applies the following order:

1. mirror from `settings.xml`;
2. mirrors[^10] from the config file.

!!! note
Trivy supports chained resolution across the two sources: if `settings.xml` maps `repo1 -> repo2` and the config file maps `repo2 -> repo3`, then `repo1` resolves to `repo3`.

#### config-file mirrors
`scan.maven.mirrors` maps each source repository URL to an ordered list of mirror URLs, tried in turn. Use it to avoid modifying `settings.xml` (for example in CI) and to configure several fallback mirrors[^10] for a single repository:

```yaml
scan:
maven:
mirrors:
https://repo.maven.apache.org/maven2/:
- https://my-internal-mirror/maven2/
- https://backup-mirror/maven2/
```

To mirror Maven Central, use `https://repo.maven.apache.org/maven2/` as the source repository URL.

!!! warning "Credentials"
Config-file mirrors do not read credentials from `settings.xml` `<server>` entries. To authenticate, embed them in the mirror URL (`https://user:password@host/...`), which stores the password in plaintext in the config file. For a secure setup, configure the mirror in `settings.xml` instead.

### supported scopes
Trivy only scans `import`, `compile`, `runtime` and empty [maven scopes][maven-scopes]. Other scopes and `Optional` dependencies are not currently being analyzed.

Expand Down Expand Up @@ -142,11 +174,14 @@ Make sure that you have cache[^8] directory to find licenses from `*.pom` depend
[^7]: To avoid confusion, Trivy only finds locations for direct dependencies from the base pom.xml file.
[^8]: The supported directories are `$GRADLE_USER_HOME/caches` and `$HOME/.gradle/caches` (`%HOMEPATH%\.gradle\caches` for Windows).
[^9]: License detection is limited. See [Licenses](#licenses) for details.
[^10]: The mirrors are tried in order, falling back to the next one when the requested POM is not found.

[dependency-graph]: ../../configuration/reporting.md#show-origins-of-vulnerable-dependencies
[maven-invoker-plugin]: https://maven.apache.org/plugins/maven-invoker-plugin/usage.html
[maven-central]: https://repo.maven.apache.org/maven2/
[maven-pom-repos]: https://maven.apache.org/settings.html#repositories
[maven-mirror-settings]: https://maven.apache.org/guides/mini/guide-mirror-settings.html
[config-file]: ../../references/configuration/config-file.md
[maven-scopes]: https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Scope
[sbt-dependency-lock]: https://stringbean.github.io/sbt-dependency-lock
[detection-priority]: ../../scanner/vulnerability.md#detection-priority
Expand Down
3 changes: 3 additions & 0 deletions docs/guide/references/configuration/config-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,9 @@ scan:
# Same as '--file-patterns'
file-patterns: []

maven:
mirrors:

# Same as '--offline-scan'
offline: false

Expand Down
1 change: 1 addition & 0 deletions pkg/commands/artifact/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,7 @@ func (r *runner) initScannerConfig(ctx context.Context, opts flag.Options) (Scan
AWSEndpoint: opts.Endpoint,
FileChecksum: fileChecksum,
DetectionPriority: opts.DetectionPriority,
MavenMirrors: opts.MavenMirrors,

// For image scanning
ImageOption: ftypes.ImageOptions{
Expand Down
63 changes: 54 additions & 9 deletions pkg/dependency/parser/java/pom/mirror.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"net/url"
"strings"

"github.com/samber/lo"

"github.com/aquasecurity/trivy/pkg/log"
)

Expand All @@ -17,14 +19,21 @@ type mirror struct {
url url.URL // parsed URL with userinfo from the matching <server>
}

// resolveMirrors converts <mirror> entries from settings.xml into the runtime
// mirror form: split and trim the mirrorOf patterns, parse the URL, and embed
// credentials from the <server> whose id equals the mirror id. Mirrors with
// no usable pattern or an unparsable URL are dropped.
func resolveMirrors(mirrors []Mirror, servers []Server) []mirror {
// mirrors holds the resolved mirrors from settings.xml and from the config file.
type mirrors struct {
settings []mirror // settings.xml mirrors
configFile map[string][]url.URL // config-file mirrors; key: mirrorKey(source), value: ordered parsed mirror URL
}

// resolveMirrors resolves and validates both mirror sources into their runtime form:
// it parses every URL — embedding <server> credentials into settings.xml mirrors and
// normalizing config-file keys via mirrorKey — and drops any entry with an unusable
// pattern or an unparsable URL.
func resolveMirrors(settingsMirrors []Mirror, servers []Server, configFileMirrors map[string][]string) mirrors {
logger := log.WithPrefix("pom")
var result []mirror
for _, m := range mirrors {

var resolved mirrors
for _, m := range settingsMirrors {
var patterns []string
for p := range strings.SplitSeq(m.MirrorOf, ",") {
p = strings.TrimSpace(p)
Expand Down Expand Up @@ -55,13 +64,49 @@ func resolveMirrors(mirrors []Mirror, servers []Server) []mirror {
}

logger.Debug("Adding mirror", log.String("id", m.ID), log.String("url", u.Redacted()))
result = append(result, mirror{
resolved.settings = append(resolved.settings, mirror{
id: m.ID,
patterns: patterns,
url: *u,
})
}
return result

for src, targets := range configFileMirrors {
// Config-file mirror URLs are validated when the config file is parsed (fail-fast).
srcURL, err := url.Parse(src)
if err != nil {
continue
}

var mirrorURLs []url.URL
for _, target := range targets {
mirrorURL, err := url.Parse(target)
if err != nil {
continue
}
mirrorURLs = append(mirrorURLs, *mirrorURL)
}
if len(mirrorURLs) == 0 {
continue
}
logger.Debug("Added config-file mirror", log.String("source", srcURL.Redacted()),
log.Any("mirrors", lo.Map(mirrorURLs, func(u url.URL, _ int) string {
return u.Redacted()
})))
if resolved.configFile == nil {
resolved.configFile = make(map[string][]url.URL)
}
resolved.configFile[mirrorKey(*srcURL)] = mirrorURLs
}

return resolved
}

// mirrorKey normalizes a repository URL to the key used for config-file mirror
// lookup: its string form with any trailing slash trimmed, so that
// "https://host/maven2/" and "https://host/maven2" resolve to the same key.
func mirrorKey(u url.URL) string {
return strings.TrimRight(u.String(), "/")
Comment thread
knqyf263 marked this conversation as resolved.
Outdated
}

// matches reports whether this mirror should serve the given repository.
Expand Down
Loading