github.com/google/osv-scalibr@v0.4.1/clients/resolution/maven_registry_client.go (about) 1 // Copyright 2025 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package resolution 16 17 import ( 18 "context" 19 "errors" 20 "fmt" 21 "strings" 22 23 "deps.dev/util/maven" 24 "deps.dev/util/resolve" 25 "deps.dev/util/resolve/version" 26 "github.com/google/osv-scalibr/clients/datasource" 27 "github.com/google/osv-scalibr/internal/mavenutil" 28 ) 29 30 // MavenRegistryClient is a client to fetch data from Maven registry. 31 type MavenRegistryClient struct { 32 api *datasource.MavenRegistryAPIClient 33 } 34 35 // NewMavenRegistryClient makes a new MavenRegistryClient. 36 func NewMavenRegistryClient(ctx context.Context, remote, local string, disableGoogleAuth bool) (*MavenRegistryClient, error) { 37 client, err := datasource.NewMavenRegistryAPIClient(ctx, datasource.MavenRegistry{URL: remote, ReleasesEnabled: true}, local, disableGoogleAuth) 38 if err != nil { 39 return nil, err 40 } 41 return &MavenRegistryClient{api: client}, nil 42 } 43 44 // NewMavenRegistryClientWithAPI makes a new MavenRegistryClient with the given Maven registry client. 45 func NewMavenRegistryClientWithAPI(api *datasource.MavenRegistryAPIClient) *MavenRegistryClient { 46 if api == nil { 47 panic("NewMavenRegistryClientWithAPI: api must not be nil") 48 } 49 return &MavenRegistryClient{api: api} 50 } 51 52 // Version returns metadata of a version specified by the VersionKey. 53 func (c *MavenRegistryClient) Version(ctx context.Context, vk resolve.VersionKey) (resolve.Version, error) { 54 g, a, found := strings.Cut(vk.Name, ":") 55 if !found { 56 return resolve.Version{}, fmt.Errorf("invalid Maven package name %s", vk.Name) 57 } 58 proj, err := c.api.GetProject(ctx, g, a, vk.Version) 59 if err != nil { 60 return resolve.Version{}, err 61 } 62 63 regs := make([]string, len(proj.Repositories)) 64 // Repositories are served as dependency registries. 65 // https://github.com/google/deps.dev/blob/main/util/resolve/api.go#L106 66 for i, repo := range proj.Repositories { 67 regs[i] = "dep:" + string(repo.URL) 68 } 69 var attr version.AttrSet 70 if len(regs) > 0 { 71 attr.SetAttr(version.Registries, strings.Join(regs, "|")) 72 } 73 74 return resolve.Version{VersionKey: vk, AttrSet: attr}, nil 75 } 76 77 // Versions returns all the available versions of the package specified by the given PackageKey. 78 // TODO: we should also include versions not listed in the metadata file 79 // There exist versions in the repository but not listed in the metada file, 80 // for example version 20030203.000550 of package commons-io:commons-io 81 // https://repo1.maven.org/maven2/commons-io/commons-io/20030203.000550/. 82 // A package may depend on such version if a soft requirement of this version 83 // is declared. 84 // We need to find out if there are such versions and include them in the 85 // returned versions. 86 func (c *MavenRegistryClient) Versions(ctx context.Context, pk resolve.PackageKey) ([]resolve.Version, error) { 87 if pk.System != resolve.Maven { 88 return nil, fmt.Errorf("wrong system: %v", pk.System) 89 } 90 91 g, a, found := strings.Cut(pk.Name, ":") 92 if !found { 93 return nil, fmt.Errorf("invalid Maven package name %s", pk.Name) 94 } 95 versions, err := c.api.GetVersions(ctx, g, a) 96 if err != nil { 97 return nil, err 98 } 99 100 vks := make([]resolve.Version, len(versions)) 101 for i, v := range versions { 102 vks[i] = resolve.Version{ 103 VersionKey: resolve.VersionKey{ 104 PackageKey: pk, 105 Version: string(v), 106 VersionType: resolve.Concrete, 107 }} 108 } 109 110 return vks, nil 111 } 112 113 // Requirements returns requirements of a version specified by the VersionKey. 114 func (c *MavenRegistryClient) Requirements(ctx context.Context, vk resolve.VersionKey) ([]resolve.RequirementVersion, error) { 115 if vk.System != resolve.Maven { 116 return nil, fmt.Errorf("wrong system: %v", vk.System) 117 } 118 119 g, a, found := strings.Cut(vk.Name, ":") 120 if !found { 121 return nil, fmt.Errorf("invalid Maven package name %s", vk.Name) 122 } 123 proj, err := c.api.GetProject(ctx, g, a, vk.Version) 124 if err != nil { 125 return nil, err 126 } 127 128 // Only merge default profiles by passing empty JDK and OS information. 129 if err := proj.MergeProfiles("", maven.ActivationOS{}); err != nil { 130 return nil, err 131 } 132 133 // We need to merge parents for potential dependencies in parents. 134 if err := mavenutil.MergeParents(ctx, proj.Parent, &proj, mavenutil.Options{ 135 Client: c.api, 136 // We should not add registries defined in dependencies pom.xml files. 137 AddRegistry: false, 138 AllowLocal: false, 139 InitialParentIndex: 1, 140 }); err != nil { 141 return nil, err 142 } 143 proj.ProcessDependencies(func(groupID, artifactID, version maven.String) (maven.DependencyManagement, error) { 144 return mavenutil.GetDependencyManagement(ctx, c.api, groupID, artifactID, version) 145 }) 146 147 reqs := make([]resolve.RequirementVersion, 0, len(proj.Dependencies)) 148 for _, d := range proj.Dependencies { 149 reqs = append(reqs, resolve.RequirementVersion{ 150 VersionKey: resolve.VersionKey{ 151 PackageKey: resolve.PackageKey{ 152 System: resolve.Maven, 153 Name: d.Name(), 154 }, 155 VersionType: resolve.Requirement, 156 Version: string(d.Version), 157 }, 158 Type: resolve.MavenDepType(d, ""), 159 }) 160 } 161 162 return reqs, nil 163 } 164 165 // MatchingVersions returns versions matching the requirement specified by the VersionKey. 166 func (c *MavenRegistryClient) MatchingVersions(ctx context.Context, vk resolve.VersionKey) ([]resolve.Version, error) { 167 if vk.System != resolve.Maven { 168 return nil, fmt.Errorf("wrong system: %v", vk.System) 169 } 170 171 versions, err := c.Versions(ctx, vk.PackageKey) 172 if err != nil { 173 return nil, err 174 } 175 176 return resolve.MatchRequirement(vk, versions), nil 177 } 178 179 // AddRegistries adds registries to the MavenRegistryClient. 180 func (c *MavenRegistryClient) AddRegistries(ctx context.Context, registries []Registry) error { 181 for _, reg := range registries { 182 specific, ok := reg.(datasource.MavenRegistry) 183 if !ok { 184 return errors.New("invalid Maven registry information") 185 } 186 if err := c.api.AddRegistry(ctx, specific); err != nil { 187 return err 188 } 189 } 190 191 return nil 192 }