@@ -29,14 +29,10 @@ import (
2929 xio "github.com/aquasecurity/trivy/pkg/x/io"
3030)
3131
32- const (
33- centralURL = "https://repo.maven.apache.org/maven2/"
34- )
35-
3632type options struct {
37- offline bool
38- releaseRemoteRepos [] string
39- snapshotRemoteRepos []string
33+ offline bool
34+ defaultRepo repository
35+ settingsRepos []repository
4036}
4137
4238type option func (* options )
@@ -47,55 +43,71 @@ func WithOffline(offline bool) option {
4743 }
4844}
4945
50- func WithReleaseRemoteRepos ( repos [] string ) option {
46+ func WithDefaultRepo ( repoURL string , releaseEnabled , snapshotEnabled bool ) option {
5147 return func (opts * options ) {
52- opts .releaseRemoteRepos = repos
48+ u , _ := url .Parse (repoURL )
49+ opts .defaultRepo = repository {
50+ url : * u ,
51+ releaseEnabled : releaseEnabled ,
52+ snapshotEnabled : snapshotEnabled ,
53+ }
5354 }
5455}
5556
56- func WithSnapshotRemoteRepos ( repos []string ) option {
57+ func WithSettingsRepos ( repoURLs []string , releaseEnabled , snapshotEnabled bool ) option {
5758 return func (opts * options ) {
58- opts .snapshotRemoteRepos = repos
59+ opts .settingsRepos = lo .Map (repoURLs , func (repoURL string , _ int ) repository {
60+ u , _ := url .Parse (repoURL )
61+ return repository {
62+ url : * u ,
63+ releaseEnabled : releaseEnabled ,
64+ snapshotEnabled : snapshotEnabled ,
65+ }
66+ })
5967 }
6068}
6169
6270type Parser struct {
63- logger * log.Logger
64- rootPath string
65- cache pomCache
66- localRepository string
67- releaseRemoteRepos []string
68- snapshotRemoteRepos []string
69- offline bool
70- servers []Server
71+ logger * log.Logger
72+ rootPath string
73+ cache pomCache
74+ localRepository string
75+ remoteRepos repositories
76+ offline bool
77+ servers []Server
7178}
7279
7380func NewParser (filePath string , opts ... option ) * Parser {
7481 o := & options {
75- offline : false ,
76- releaseRemoteRepos : []string {centralURL }, // Maven doesn't use central repository for snapshot dependencies
77- }
78-
79- for _ , opt := range opts {
80- opt (o )
82+ offline : false ,
83+ defaultRepo : mavenCentralRepo ,
8184 }
8285
8386 s := readSettings ()
87+ o .settingsRepos = s .effectiveRepositories ()
8488 localRepository := s .LocalRepository
8589 if localRepository == "" {
8690 homeDir , _ := os .UserHomeDir ()
8791 localRepository = filepath .Join (homeDir , ".m2" , "repository" )
8892 }
8993
94+ for _ , opt := range opts {
95+ opt (o )
96+ }
97+
98+ remoteRepos := repositories {
99+ defaultRepo : o .defaultRepo ,
100+ settings : o .settingsRepos ,
101+ }
102+
90103 return & Parser {
91- logger : log .WithPrefix ("pom" ),
92- rootPath : filepath .Clean (filePath ),
93- cache : newPOMCache (),
94- localRepository : localRepository ,
95- releaseRemoteRepos : o .releaseRemoteRepos ,
96- snapshotRemoteRepos : o .snapshotRemoteRepos ,
97- offline : o .offline ,
98- servers : s .Servers ,
104+ logger : log .WithPrefix ("pom" ),
105+ rootPath : filepath .Clean (filePath ),
106+ cache : newPOMCache (),
107+ localRepository : localRepository ,
108+ remoteRepos : remoteRepos ,
109+ offline : o .offline ,
110+ servers : s .Servers ,
99111 }
100112}
101113
@@ -362,9 +374,10 @@ func (p *Parser) analyze(ctx context.Context, pom *pom, opts analysisOptions) (a
362374 opts .exclusions = set .New [string ]()
363375 }
364376 // Update remoteRepositories
365- pomReleaseRemoteRepos , pomSnapshotRemoteRepos := pom .repositories (p .servers )
366- p .releaseRemoteRepos = lo .Uniq (append (pomReleaseRemoteRepos , p .releaseRemoteRepos ... ))
367- p .snapshotRemoteRepos = lo .Uniq (append (pomSnapshotRemoteRepos , p .snapshotRemoteRepos ... ))
377+ pomRepos := pom .repositories (p .servers )
378+ p .remoteRepos .pom = lo .UniqBy (append (pomRepos , p .remoteRepos .pom ... ), func (r repository ) url.URL {
379+ return r .url
380+ })
368381
369382 // Resolve parent POM
370383 if err := p .resolveParent (ctx , pom ); err != nil {
@@ -710,17 +723,19 @@ func (p *Parser) fetchPOMFromRemoteRepositories(ctx context.Context, paths []str
710723 return nil , xerrors .New ("offline mode" )
711724 }
712725
713- remoteRepos := p .releaseRemoteRepos
714- // Maven uses only snapshot repos for snapshot artifacts
715- if snapshot {
716- remoteRepos = p .snapshotRemoteRepos
717- }
726+ // Try all remoteRepositories by following order:
727+ // 1. remoteRepositories from settings.xml
728+ // 2. remoteRepositories from pom.xml
729+ // 3. default remoteRepository (Maven Central for Release repository)
730+ for _ , repo := range slices .Concat (p .remoteRepos .settings , p .remoteRepos .pom , []repository {p .remoteRepos .defaultRepo }) {
731+ // Skip Release only repositories for snapshot artifacts and vice versa
732+ if snapshot && ! repo .snapshotEnabled || ! snapshot && ! repo .releaseEnabled {
733+ continue
734+ }
718735
719- // try all remoteRepositories
720- for _ , repo := range remoteRepos {
721736 repoPaths := slices .Clone (paths ) // Clone slice to avoid overwriting last element of `paths`
722737 if snapshot {
723- pomFileName , err := p .fetchPomFileNameFromMavenMetadata (ctx , repo , repoPaths )
738+ pomFileName , err := p .fetchPomFileNameFromMavenMetadata (ctx , repo . url , repoPaths )
724739 if err != nil {
725740 return nil , xerrors .Errorf ("fetch maven-metadata.xml error: %w" , err )
726741 }
@@ -729,7 +744,7 @@ func (p *Parser) fetchPOMFromRemoteRepositories(ctx context.Context, paths []str
729744 repoPaths [len (repoPaths )- 1 ] = pomFileName
730745 }
731746 }
732- fetched , err := p .fetchPOMFromRemoteRepository (ctx , repo , repoPaths )
747+ fetched , err := p .fetchPOMFromRemoteRepository (ctx , repo . url , repoPaths )
733748 if err != nil {
734749 return nil , xerrors .Errorf ("fetch repository error: %w" , err )
735750 } else if fetched == nil {
@@ -740,12 +755,7 @@ func (p *Parser) fetchPOMFromRemoteRepositories(ctx context.Context, paths []str
740755 return nil , xerrors .Errorf ("the POM was not found in remote remoteRepositories" )
741756}
742757
743- func (p * Parser ) remoteRepoRequest (ctx context.Context , repo string , paths []string ) (* http.Request , error ) {
744- repoURL , err := url .Parse (repo )
745- if err != nil {
746- return nil , xerrors .Errorf ("unable to parse URL: %w" , err )
747- }
748-
758+ func (p * Parser ) remoteRepoRequest (ctx context.Context , repoURL url.URL , paths []string ) (* http.Request , error ) {
749759 paths = append ([]string {repoURL .Path }, paths ... )
750760 repoURL .Path = path .Join (paths ... )
751761
@@ -762,14 +772,14 @@ func (p *Parser) remoteRepoRequest(ctx context.Context, repo string, paths []str
762772}
763773
764774// fetchPomFileNameFromMavenMetadata fetches `maven-metadata.xml` file to detect file name of pom file.
765- func (p * Parser ) fetchPomFileNameFromMavenMetadata (ctx context.Context , repo string , paths []string ) (string , error ) {
775+ func (p * Parser ) fetchPomFileNameFromMavenMetadata (ctx context.Context , repoURL url. URL , paths []string ) (string , error ) {
766776 // Overwrite pom file name to `maven-metadata.xml`
767777 mavenMetadataPaths := slices .Clone (paths [:len (paths )- 1 ]) // Clone slice to avoid shadow overwriting last element of `paths`
768778 mavenMetadataPaths = append (mavenMetadataPaths , "maven-metadata.xml" )
769779
770- req , err := p .remoteRepoRequest (ctx , repo , mavenMetadataPaths )
780+ req , err := p .remoteRepoRequest (ctx , repoURL , mavenMetadataPaths )
771781 if err != nil {
772- p .logger .Debug ("Unable to create request" , log .String ("repo" , repo ), log .Err (err ))
782+ p .logger .Debug ("Unable to create request" , log .String ("repo" , repoURL . Redacted () ), log .Err (err ))
773783 return "" , nil
774784 }
775785
@@ -779,14 +789,16 @@ func (p *Parser) fetchPomFileNameFromMavenMetadata(ctx context.Context, repo str
779789 if shouldReturnError (err ) {
780790 return "" , err
781791 }
782- p .logger .Debug ("Failed to fetch" , log .String ("url" , req .URL .String ()), log .Err (err ))
783- return "" , nil
784- } else if resp .StatusCode != http .StatusOK {
785- p .logger .Debug ("Failed to fetch" , log .String ("url" , req .URL .String ()), log .Int ("statusCode" , resp .StatusCode ))
792+ p .logger .Debug ("Failed to fetch" , log .String ("url" , req .URL .Redacted ()), log .Err (err ))
786793 return "" , nil
787794 }
788795 defer resp .Body .Close ()
789796
797+ if resp .StatusCode != http .StatusOK {
798+ p .logger .Debug ("Failed to fetch" , log .String ("url" , req .URL .Redacted ()), log .Int ("statusCode" , resp .StatusCode ))
799+ return "" , nil
800+ }
801+
790802 mavenMetadata , err := parseMavenMetadata (resp .Body )
791803 if err != nil {
792804 return "" , xerrors .Errorf ("failed to parse maven-metadata.xml file: %w" , err )
@@ -803,10 +815,10 @@ func (p *Parser) fetchPomFileNameFromMavenMetadata(ctx context.Context, repo str
803815 return pomFileName , nil
804816}
805817
806- func (p * Parser ) fetchPOMFromRemoteRepository (ctx context.Context , repo string , paths []string ) (* pom , error ) {
807- req , err := p .remoteRepoRequest (ctx , repo , paths )
818+ func (p * Parser ) fetchPOMFromRemoteRepository (ctx context.Context , repoURL url. URL , paths []string ) (* pom , error ) {
819+ req , err := p .remoteRepoRequest (ctx , repoURL , paths )
808820 if err != nil {
809- p .logger .Debug ("Unable to create request" , log .String ("repo" , repo ), log .Err (err ))
821+ p .logger .Debug ("Unable to create request" , log .String ("repo" , repoURL . Redacted () ), log .Err (err ))
810822 return nil , nil
811823 }
812824
@@ -816,14 +828,16 @@ func (p *Parser) fetchPOMFromRemoteRepository(ctx context.Context, repo string,
816828 if shouldReturnError (err ) {
817829 return nil , err
818830 }
819- p .logger .Debug ("Failed to fetch" , log .String ("url" , req .URL .String ()), log .Err (err ))
820- return nil , nil
821- } else if resp .StatusCode != http .StatusOK {
822- p .logger .Debug ("Failed to fetch" , log .String ("url" , req .URL .String ()), log .Int ("statusCode" , resp .StatusCode ))
831+ p .logger .Debug ("Failed to fetch" , log .String ("url" , req .URL .Redacted ()), log .Err (err ))
823832 return nil , nil
824833 }
825834 defer resp .Body .Close ()
826835
836+ if resp .StatusCode != http .StatusOK {
837+ p .logger .Debug ("Failed to fetch" , log .String ("url" , req .URL .Redacted ()), log .Int ("statusCode" , resp .StatusCode ))
838+ return nil , nil
839+ }
840+
827841 content , err := parsePom (resp .Body , false )
828842 if err != nil {
829843 return nil , xerrors .Errorf ("failed to parse the remote POM: %w" , err )
0 commit comments