Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for fetching from repository mentioned in pom file #289

Closed
wants to merge 13 commits into from
Closed
45 changes: 42 additions & 3 deletions pkg/java/pom/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ type parser struct {
localRepository string
remoteRepositories []string
offline bool
servers []Server
}

func NewParser(filePath string, opts ...option) types.Parser {
Expand All @@ -77,6 +78,7 @@ func NewParser(filePath string, opts ...option) types.Parser {
localRepository: localRepository,
remoteRepositories: o.remoteRepos,
offline: o.offline,
servers: s.Servers,
}
}

Expand All @@ -86,6 +88,32 @@ func (p *parser) Parse(r dio.ReadSeekerAt) ([]types.Library, []types.Dependency,
return nil, nil, xerrors.Errorf("failed to parse POM: %w", err)
}

var remoteRepositories []string
for _, rep := range content.Repositories.Repository {
if rep.Releases.Enabled == "false" && rep.Snapshots.Enabled == "false" {
continue
}

repoURL, err := url.Parse(rep.URL)
if err != nil {
log.Logger.Debugf("Unable to parse remote repository url: %s", err)
continue
}

for _, server := range p.servers {
if rep.ID == server.ID && server.Username != "" && server.Password != "" {
repoURL.User = url.UserPassword(server.Username, server.Password)
break
}
}

log.Logger.Debugf("Adding repository %s: %s", rep.ID, rep.URL)
remoteRepositories = append(remoteRepositories, repoURL.String())
}

// Add central maven repository or repositories obtained using `WithRemoteRepos` function.
remoteRepositories = append(remoteRepositories, p.remoteRepositories...)

root := &pom{
filePath: p.rootPath,
content: content,
Expand Down Expand Up @@ -628,13 +656,24 @@ func (p *parser) fetchPOMFromRemoteRepository(paths []string) (*pom, error) {
continue
}

paths = append([]string{repoURL.Path}, paths...)
repoURL.Path = path.Join(paths...)
repoPaths := append([]string{repoURL.Path}, paths...)
repoURL.Path = path.Join(repoPaths...)

client := &http.Client{}
req, err := http.NewRequest("GET", repoURL.String(), nil)
if err != nil {
continue
}
if repoURL.User != nil {
password, _ := repoURL.User.Password()
req.SetBasicAuth(repoURL.User.Username(), password)
}

resp, err := http.Get(repoURL.String())
resp, err := client.Do(req)
if err != nil || resp.StatusCode != http.StatusOK {
continue
}
defer resp.Body.Close()

content, err := parsePom(resp.Body)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion pkg/java/pom/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1226,7 +1226,7 @@ func TestPom_Parse(t *testing.T) {
var remoteRepos []string
if tt.local {
// for local repository
t.Setenv("MAVEN_HOME", "testdata")
t.Setenv("MAVEN_HOME", "testdata/settings/global")
} else {
// for remote repository
h := http.FileServer(http.Dir(filepath.Join("testdata", "repository")))
Expand Down
2 changes: 1 addition & 1 deletion pkg/java/pom/pom.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ package pom
import (
"encoding/xml"
"fmt"
"golang.org/x/xerrors"
"io"
"maps"
"reflect"
"strings"

"github.com/samber/lo"
"golang.org/x/xerrors"

"github.com/aquasecurity/go-dep-parser/pkg/types"
"github.com/aquasecurity/go-dep-parser/pkg/utils"
Expand Down
49 changes: 42 additions & 7 deletions pkg/java/pom/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,59 @@ import (
"golang.org/x/net/html/charset"
)

type Server struct {
ID string `xml:"id"`
Username string `xml:"username"`
Password string `xml:"password"`
}

type settings struct {
LocalRepository string `xml:"localRepository"`
LocalRepository string `xml:"localRepository"`
Servers []Server `xml:"servers>server"`
}

// serverFound checks that servers already contain server.
// Maven compares servers by ID only.
func serverFound(servers []Server, id string) bool {
for _, server := range servers {
if server.ID == id {
return true
}
}
return false
}

func readSettings() settings {
s := settings{}

userSettingsPath := filepath.Join(os.Getenv("HOME"), ".m2", "settings.xml")
userSettings, err := openSettings(userSettingsPath)
if err == nil && userSettings.LocalRepository != "" {
return userSettings
if err == nil {
s = userSettings
}

globalSettingsPath := filepath.Join(os.Getenv("MAVEN_HOME"), "conf", "settings.xml")
// Some package managers use this path by default
mavenHome := "/usr/share/maven"
if mHome := os.Getenv("MAVEN_HOME"); mHome != "" {
mavenHome = mHome
}
globalSettingsPath := filepath.Join(mavenHome, "conf", "settings.xml")
globalSettings, err := openSettings(globalSettingsPath)
if err == nil && globalSettings.LocalRepository != "" {
return globalSettings
if err == nil {
// We need to merge global and user settings. User settings being dominant.
// https://maven.apache.org/settings.html#quick-overview
if s.LocalRepository == "" {
s.LocalRepository = globalSettings.LocalRepository
}
// Maven checks user servers first, then global servers
for _, server := range globalSettings.Servers {
if !serverFound(s.Servers, server.ID) {
s.Servers = append(s.Servers, server)
}
}
}

return settings{}
return s
}

func openSettings(filePath string) (settings, error) {
Expand Down
136 changes: 136 additions & 0 deletions pkg/java/pom/settings_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package pom

import (
"path/filepath"
"testing"

"github.com/stretchr/testify/require"
)

func Test_ReadSettings(t *testing.T) {
tests := []struct {
name string
envs map[string]string
wantSettings settings
}{
{
name: "happy path with only global settings",
envs: map[string]string{
"HOME": "",
"MAVEN_HOME": filepath.Join("testdata", "settings", "global"),
},
wantSettings: settings{
LocalRepository: "testdata/repository",
Servers: []Server{
{
ID: "global-server",
},
{
ID: "server-with-credentials",
Username: "test-user",
Password: "test-password-from-global",
},
{
ID: "server-with-name-only",
Username: "test-user-only",
},
},
},
},
{
name: "happy path with only user settings",
envs: map[string]string{
"HOME": filepath.Join("testdata", "settings", "user"),
"MAVEN_HOME": "NOT_EXISTING_PATH",
},
wantSettings: settings{
LocalRepository: "testdata/user/repository",
Servers: []Server{
{
ID: "user-server",
},
{
ID: "server-with-credentials",
Username: "test-user",
Password: "test-password",
},
{
ID: "server-with-name-only",
Username: "test-user-only",
},
},
},
},
{
// $ mvn help:effective-settings
//[INFO] ------------------< org.apache.maven:standalone-pom >-------------------
//[INFO] --- maven-help-plugin:3.4.0:effective-settings (default-cli) @ standalone-pom ---
//Effective user-specific configuration settings:
//
//<?xml version="1.0" encoding="UTF-8"?>
//<settings xmlns="http://maven.apache.org/SETTINGS/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.1.0 http://maven.apache.org/xsd/settings-1.1.0.xsd">
// <localRepository>/root/testdata/user/repository</localRepository>
// <servers>
// <server>
// <id>user-server</id>
// </server>
// <server>
// <username>test-user</username>
// <password>***</password>
// <id>server-with-credentials</id>
// </server>
// <server>
// <username>test-user-only</username>
// <id>server-with-name-only</id>
// </server>
// <server>
// <id>global-server</id>
// </server>
// </servers>
//</settings>
name: "happy path with global and user settings",
envs: map[string]string{
"HOME": filepath.Join("testdata", "settings", "user"),
"MAVEN_HOME": filepath.Join("testdata", "settings", "global"),
},
wantSettings: settings{
LocalRepository: "testdata/user/repository",
Servers: []Server{
{
ID: "user-server",
},
{
ID: "server-with-credentials",
Username: "test-user",
Password: "test-password",
},
{
ID: "server-with-name-only",
Username: "test-user-only",
},
{
ID: "global-server",
},
},
},
},
{
name: "without settings",
envs: map[string]string{
"HOME": "",
"MAVEN_HOME": "NOT_EXISTING_PATH",
},
wantSettings: settings{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
for env, settingsDir := range tt.envs {
t.Setenv(env, settingsDir)
}

gotSettings := readSettings()
require.Equal(t, tt.wantSettings, gotSettings)
})
}
}
66 changes: 0 additions & 66 deletions pkg/java/pom/testdata/conf/settings.xml

This file was deleted.

21 changes: 21 additions & 0 deletions pkg/java/pom/testdata/settings/global/conf/settings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<localRepository>testdata/repository</localRepository>

<servers>
<server>
<id>global-server</id>
</server>
<server>
<id>server-with-credentials</id>
<username>test-user</username>
<password>test-password-from-global</password>
</server>
<server>
<id>server-with-name-only</id>
<username>test-user-only</username>
</server>
</servers>
</settings>
Loading