Skip to content

Commit 35b36a3

Browse files
authored
refactor: move Maven utility to a separate package (#1193)
#1045 Considering that we want to support native Maven registry, we need `MergeMavenParents` from `internal/manifest` in `internal/resolution/client`, however `internal/manifest` imports `internal/resolution/client` for `DependencyClient` for dependency resolution, and this causes an import cycle. This PR moves the Maven utility in `internal/manifest` to a separate package `internal/utility/maven`.
1 parent 8bbcb62 commit 35b36a3

File tree

10 files changed

+244
-216
lines changed

10 files changed

+244
-216
lines changed

cmd/osv-scanner/__snapshots__/main_test.snap

+1
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,7 @@ Scanned <rootdir>/fixtures/sbom-insecure/postgres-stretch.cdx.xml as CycloneDX S
463463
| https://osv.dev/GHSA-v95c-p5hm-xq8f | 6.0 | Go | github.com/opencontainers/runc | v1.0.1 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
464464
| https://osv.dev/GO-2022-0274 | | | | | |
465465
| https://osv.dev/GHSA-f3fp-gc8g-vw66 | 5.9 | Go | github.com/opencontainers/runc | v1.0.1 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
466+
| https://osv.dev/GO-2022-0452 | | | | | |
466467
| https://osv.dev/GHSA-g2j6-57v7-gm8c | 6.1 | Go | github.com/opencontainers/runc | v1.0.1 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
467468
| https://osv.dev/GO-2023-1683 | | | | | |
468469
| https://osv.dev/GHSA-m8cg-xc2p-r3fc | 2.5 | Go | github.com/opencontainers/runc | v1.0.1 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |

internal/manifest/maven.go

+4-118
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@ package manifest
33
import (
44
"context"
55
"encoding/xml"
6-
"errors"
76
"fmt"
8-
"os"
97
"path/filepath"
108

119
"deps.dev/util/maven"
@@ -15,17 +13,11 @@ import (
1513
"github.com/google/osv-scanner/internal/resolution/client"
1614
"github.com/google/osv-scanner/internal/resolution/datasource"
1715
"github.com/google/osv-scanner/internal/resolution/util"
16+
mavenutil "github.com/google/osv-scanner/internal/utility/maven"
1817
"github.com/google/osv-scanner/pkg/lockfile"
1918
"golang.org/x/exp/maps"
2019
)
2120

22-
const (
23-
OriginManagement = "management"
24-
OriginParent = "parent"
25-
OriginPlugin = "plugin"
26-
OriginProfile = "profile"
27-
)
28-
2921
type MavenResolverExtractor struct {
3022
client.DependencyClient
3123
datasource.MavenRegistryAPIClient
@@ -43,7 +35,7 @@ func (e MavenResolverExtractor) Extract(f lockfile.DepFile) ([]lockfile.PackageD
4335
return []lockfile.PackageDetails{}, fmt.Errorf("could not extract from %s: %w", f.Path(), err)
4436
}
4537
// Merging parents data by parsing local parent pom.xml or fetching from upstream.
46-
if err := MergeMavenParents(ctx, e.MavenRegistryAPIClient, &project, project.Parent, 1, f.Path(), true); err != nil {
38+
if err := mavenutil.MergeParents(ctx, e.MavenRegistryAPIClient, &project, project.Parent, 1, f.Path(), true); err != nil {
4739
return []lockfile.PackageDetails{}, fmt.Errorf("failed to merge parents: %w", err)
4840
}
4941
// Process the dependencies:
@@ -53,7 +45,7 @@ func (e MavenResolverExtractor) Extract(f lockfile.DepFile) ([]lockfile.PackageD
5345
project.ProcessDependencies(func(groupID, artifactID, version maven.String) (maven.DependencyManagement, error) {
5446
root := maven.Parent{ProjectKey: maven.ProjectKey{GroupID: groupID, ArtifactID: artifactID, Version: version}}
5547
var result maven.Project
56-
if err := MergeMavenParents(ctx, e.MavenRegistryAPIClient, &result, root, 0, f.Path(), false); err != nil {
48+
if err := mavenutil.MergeParents(ctx, e.MavenRegistryAPIClient, &result, root, 0, f.Path(), false); err != nil {
5749
return maven.DependencyManagement{}, err
5850
}
5951

@@ -97,7 +89,7 @@ func (e MavenResolverExtractor) Extract(f lockfile.DepFile) ([]lockfile.PackageD
9789
VersionType: resolve.Requirement,
9890
Version: string(d.Version),
9991
},
100-
Type: resolve.MavenDepType(d, OriginManagement),
92+
Type: resolve.MavenDepType(d, mavenutil.OriginManagement),
10193
}
10294
}
10395
overrideClient.AddVersion(root, reqs)
@@ -133,112 +125,6 @@ func (e MavenResolverExtractor) Extract(f lockfile.DepFile) ([]lockfile.PackageD
133125
return maps.Values(details), nil
134126
}
135127

136-
// MaxParent sets a limit on the number of parents to avoid indefinite loop.
137-
const MaxParent = 100
138-
139-
// MergeMavenParents parses local accessible parent pom.xml or fetches it from
140-
// upstream, merges into root project, then interpolate the properties.
141-
// result holds the merged Maven project.
142-
// current holds the current parent project to merge.
143-
// start indicates the index of the current parent project, which is used to
144-
// check if the packaging has to be `pom`.
145-
// allowLocal indicates whether parsing local parent pom.xml is allowed.
146-
// path holds the path to the current pom.xml, which is used to compute the
147-
// relative path of parent.
148-
func MergeMavenParents(ctx context.Context, mavenClient datasource.MavenRegistryAPIClient, result *maven.Project, current maven.Parent, start int, path string, allowLocal bool) error {
149-
currentPath := path
150-
visited := make(map[maven.ProjectKey]bool, MaxParent)
151-
for n := start; n < MaxParent; n++ {
152-
if current.GroupID == "" || current.ArtifactID == "" || current.Version == "" {
153-
break
154-
}
155-
if visited[current.ProjectKey] {
156-
// A cycle of parents is detected
157-
return errors.New("a cycle of parents is detected")
158-
}
159-
visited[current.ProjectKey] = true
160-
161-
var proj maven.Project
162-
parentFound := false
163-
if parentPath := MavenParentPOMPath(currentPath, string(current.RelativePath)); allowLocal && parentPath != "" {
164-
currentPath = parentPath
165-
f, err := os.Open(parentPath)
166-
if err != nil {
167-
return fmt.Errorf("failed to open parent file %s: %w", parentPath, err)
168-
}
169-
if err := xml.NewDecoder(f).Decode(&proj); err != nil {
170-
return fmt.Errorf("failed to unmarshal project: %w", err)
171-
}
172-
if MavenProjectKey(proj) == current.ProjectKey && proj.Packaging == "pom" {
173-
// Only mark parent is found when the identifiers and packaging are exptected.
174-
parentFound = true
175-
}
176-
}
177-
if !parentFound {
178-
// Once we fetch a parent pom.xml from upstream, we should not
179-
// allow parsing parent pom.xml locally anymore.
180-
allowLocal = false
181-
182-
var err error
183-
proj, err = mavenClient.GetProject(ctx, string(current.GroupID), string(current.ArtifactID), string(current.Version))
184-
if err != nil {
185-
return fmt.Errorf("failed to get Maven project %s:%s:%s: %w", current.GroupID, current.ArtifactID, current.Version, err)
186-
}
187-
if n > 0 && proj.Packaging != "pom" {
188-
// A parent project should only be of "pom" packaging type.
189-
return fmt.Errorf("invalid packaging for parent project %s", proj.Packaging)
190-
}
191-
if MavenProjectKey(proj) != current.ProjectKey {
192-
// The identifiers in parent does not match what we want.
193-
return fmt.Errorf("parent identifiers mismatch: %v, expect %v", proj.ProjectKey, current.ProjectKey)
194-
}
195-
}
196-
// Empty JDK and ActivationOS indicates merging the default profiles.
197-
if err := proj.MergeProfiles("", maven.ActivationOS{}); err != nil {
198-
return err
199-
}
200-
result.MergeParent(proj)
201-
current = proj.Parent
202-
}
203-
// Interpolate the project to resolve the properties.
204-
return result.Interpolate()
205-
}
206-
207-
// MavenProjectKey returns a project key with empty groupId/version
208-
// filled by corresponding fields in parent.
209-
func MavenProjectKey(proj maven.Project) maven.ProjectKey {
210-
if proj.GroupID == "" {
211-
proj.GroupID = proj.Parent.GroupID
212-
}
213-
if proj.Version == "" {
214-
proj.Version = proj.Parent.Version
215-
}
216-
217-
return proj.ProjectKey
218-
}
219-
220-
// Maven looks for the parent POM first in 'relativePath',
221-
// then the local repository '../pom.xml',
222-
// and lastly in the remote repo.
223-
func MavenParentPOMPath(currentPath, relativePath string) string {
224-
if relativePath == "" {
225-
relativePath = "../pom.xml"
226-
}
227-
path := filepath.Join(filepath.Dir(currentPath), relativePath)
228-
if info, err := os.Stat(path); err == nil {
229-
if !info.IsDir() {
230-
return path
231-
}
232-
// Current path is a directory, so look for pom.xml in the directory.
233-
path = filepath.Join(path, "pom.xml")
234-
if _, err := os.Stat(path); err == nil {
235-
return path
236-
}
237-
}
238-
239-
return ""
240-
}
241-
242128
func ParseMavenWithResolver(depClient client.DependencyClient, mavenClient datasource.MavenRegistryAPIClient, pathToLockfile string) ([]lockfile.PackageDetails, error) {
243129
f, err := lockfile.OpenLocalDepFile(pathToLockfile)
244130
if err != nil {

internal/manifest/maven_test.go

-64
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ package manifest_test
22

33
import (
44
"io/fs"
5-
"os"
6-
"path/filepath"
75
"testing"
86

97
"github.com/google/osv-scanner/internal/manifest"
@@ -360,65 +358,3 @@ func TestParseMavenWithResolver_Transitive(t *testing.T) {
360358
},
361359
})
362360
}
363-
364-
func TestParentPOMPath(t *testing.T) {
365-
t.Parallel()
366-
dir, err := os.Getwd()
367-
if err != nil {
368-
t.Fatalf("failed to get current directory: %v", err)
369-
}
370-
tests := []struct {
371-
currentPath, relativePath string
372-
want string
373-
}{
374-
// fixtures
375-
// |- maven
376-
// | |- my-app
377-
// | | |- pom.xml
378-
// | |- parent
379-
// | | |- pom.xml
380-
// |- pom.xml
381-
{
382-
// Parent path is specified correctly.
383-
currentPath: filepath.Join(dir, "fixtures", "maven", "my-app", "pom.xml"),
384-
relativePath: "../parent/pom.xml",
385-
want: filepath.Join(dir, "fixtures", "maven", "parent", "pom.xml"),
386-
},
387-
{
388-
// Wrong file name is specified in relative path.
389-
currentPath: filepath.Join(dir, "fixtures", "maven", "my-app", "pom.xml"),
390-
relativePath: "../parent/abc.xml",
391-
want: "",
392-
},
393-
{
394-
// Wrong directory is specified in relative path.
395-
currentPath: filepath.Join(dir, "fixtures", "maven", "my-app", "pom.xml"),
396-
relativePath: "../not-found/pom.xml",
397-
want: "",
398-
},
399-
{
400-
// Only directory is specified.
401-
currentPath: filepath.Join(dir, "fixtures", "maven", "my-app", "pom.xml"),
402-
relativePath: "../parent",
403-
want: filepath.Join(dir, "fixtures", "maven", "parent", "pom.xml"),
404-
},
405-
{
406-
// Parent relative path is default to '../pom.xml'.
407-
currentPath: filepath.Join(dir, "fixtures", "maven", "my-app", "pom.xml"),
408-
relativePath: "",
409-
want: filepath.Join(dir, "fixtures", "maven", "pom.xml"),
410-
},
411-
{
412-
// No pom.xml is found even in the default path.
413-
currentPath: filepath.Join(dir, "fixtures", "maven", "pom.xml"),
414-
relativePath: "",
415-
want: "",
416-
},
417-
}
418-
for _, test := range tests {
419-
got := manifest.MavenParentPOMPath(test.currentPath, test.relativePath)
420-
if got != test.want {
421-
t.Errorf("parentPOMPath(%s, %s): got %s, want %s", test.currentPath, test.relativePath, got, test.want)
422-
}
423-
}
424-
}

internal/remediation/override.go

+8-8
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ import (
88

99
"deps.dev/util/resolve"
1010
"deps.dev/util/resolve/dep"
11-
"github.com/google/osv-scanner/internal/manifest"
1211
"github.com/google/osv-scanner/internal/remediation/upgrade"
1312
"github.com/google/osv-scanner/internal/resolution"
1413
"github.com/google/osv-scanner/internal/resolution/client"
15-
resolutionmanifest "github.com/google/osv-scanner/internal/resolution/manifest"
14+
"github.com/google/osv-scanner/internal/resolution/manifest"
1615
"github.com/google/osv-scanner/internal/resolution/util"
16+
"github.com/google/osv-scanner/internal/utility/maven"
1717
"github.com/google/osv-scanner/internal/utility/vulns"
1818
)
1919

@@ -79,9 +79,9 @@ func ComputeOverridePatches(ctx context.Context, cl client.ResolutionClient, res
7979
// CalculateDiff does not compute override manifest patches correctly, manually fill it out.
8080
// TODO: CalculateDiff maybe should not be reconstructing patches.
8181
// Refactor CalculateDiff, Relaxer, Override to make patches in a more sane way.
82-
diff.Deps = make([]resolutionmanifest.DependencyPatch, len(res.patches))
82+
diff.Deps = make([]manifest.DependencyPatch, len(res.patches))
8383
for i, p := range res.patches {
84-
diff.Deps[i] = resolutionmanifest.DependencyPatch{
84+
diff.Deps[i] = manifest.DependencyPatch{
8585
Pkg: p.PackageKey,
8686
Type: dep.Type{},
8787
OrigRequire: "", // Using empty original to signal this is an override patch
@@ -280,9 +280,9 @@ func getVersionsGreater(ctx context.Context, cl client.DependencyClient, vk reso
280280
}
281281

282282
// patchManifest applies the overridePatches to the manifest in-memory. Returns a copy of the manifest that has been patched.
283-
func patchManifest(patches []overridePatch, m resolutionmanifest.Manifest) (resolutionmanifest.Manifest, error) {
283+
func patchManifest(patches []overridePatch, m manifest.Manifest) (manifest.Manifest, error) {
284284
if m.System() != resolve.Maven {
285-
return resolutionmanifest.Manifest{}, errors.New("unsupported ecosystem")
285+
return manifest.Manifest{}, errors.New("unsupported ecosystem")
286286
}
287287

288288
// TODO: The overridePatch does not have an artifact's type or classifier, which is part of what uniquely identifies them.
@@ -301,7 +301,7 @@ func patchManifest(patches []overridePatch, m resolutionmanifest.Manifest) (reso
301301
continue
302302
}
303303
origin, hasOrigin := r.Type.GetAttr(dep.MavenDependencyOrigin)
304-
if !hasOrigin || origin == manifest.OriginManagement {
304+
if !hasOrigin || origin == maven.OriginManagement {
305305
found = true
306306
r.Version = p.NewVersion
307307
patched.Requirements[i] = r
@@ -317,7 +317,7 @@ func patchManifest(patches []overridePatch, m resolutionmanifest.Manifest) (reso
317317
VersionType: resolve.Requirement,
318318
},
319319
}
320-
newReq.Type.AddAttr(dep.MavenDependencyOrigin, manifest.OriginManagement)
320+
newReq.Type.AddAttr(dep.MavenDependencyOrigin, maven.OriginManagement)
321321
patched.Requirements = append(patched.Requirements, newReq)
322322
}
323323
}

0 commit comments

Comments
 (0)