Skip to content

Commit 5aade69

Browse files
authored
feat(java): dereference all maven settings.xml env placeholders (#9024)
1 parent 99c5151 commit 5aade69

File tree

7 files changed

+131
-66
lines changed

7 files changed

+131
-66
lines changed

pkg/dependency/parser/java/pom/artifact.go

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@ package pom
22

33
import (
44
"fmt"
5-
"os"
6-
"regexp"
7-
"slices"
85
"strings"
96
"sync"
107

@@ -17,7 +14,6 @@ import (
1714
)
1815

1916
var (
20-
varRegexp = regexp.MustCompile(`\${(\S+?)}`)
2117
emptyVersionWarn = sync.OnceFunc(func() {
2218
log.WithPrefix("pom").Warn("Dependency version cannot be determined. Child dependencies will not be found.",
2319
// e.g. https://trivy.dev/latest/docs/coverage/language/java/#empty-dependency-version
@@ -133,44 +129,3 @@ func (v1 version) shouldOverride(v2 version) bool {
133129
func (v1 version) String() string {
134130
return v1.ver
135131
}
136-
137-
func evaluateVariable(s string, props map[string]string, seenProps []string) string {
138-
if props == nil {
139-
props = make(map[string]string)
140-
}
141-
142-
for _, m := range varRegexp.FindAllStringSubmatch(s, -1) {
143-
var newValue string
144-
145-
// env.X: https://maven.apache.org/pom.html#Properties
146-
// e.g. env.PATH
147-
if strings.HasPrefix(m[1], "env.") {
148-
newValue = os.Getenv(strings.TrimPrefix(m[1], "env."))
149-
} else {
150-
// <properties> might include another property.
151-
// e.g. <animal.sniffer.skip>${skipTests}</animal.sniffer.skip>
152-
ss, ok := props[m[1]]
153-
if ok {
154-
// search for looped properties
155-
if slices.Contains(seenProps, ss) {
156-
printLoopedPropertiesStack(m[0], seenProps)
157-
return ""
158-
}
159-
seenProps = append(seenProps, ss) // save evaluated props to check if we get this prop again
160-
newValue = evaluateVariable(ss, props, seenProps)
161-
seenProps = []string{} // clear props if we returned from recursive. Required for correct work with 2 same props like ${foo}-${foo}
162-
}
163-
164-
}
165-
s = strings.ReplaceAll(s, m[0], newValue)
166-
}
167-
return strings.TrimSpace(s)
168-
}
169-
170-
func printLoopedPropertiesStack(env string, usedProps []string) {
171-
var s string
172-
for _, prop := range usedProps {
173-
s += fmt.Sprintf("%s -> ", prop)
174-
}
175-
log.Warn("Lopped properties were detected", log.String("prop", s+env))
176-
}

pkg/dependency/parser/java/pom/parse.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -832,3 +832,11 @@ func packageID(name, version string) string {
832832
func isSnapshot(ver string) bool {
833833
return strings.HasSuffix(ver, "SNAPSHOT") || ver == "LATEST"
834834
}
835+
836+
func isDirectory(path string) (bool, error) {
837+
fileInfo, err := os.Stat(path)
838+
if err != nil {
839+
return false, err
840+
}
841+
return fileInfo.IsDir(), err
842+
}

pkg/dependency/parser/java/pom/settings.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,5 +76,17 @@ func openSettings(filePath string) (settings, error) {
7676
if err = decoder.Decode(&s); err != nil {
7777
return settings{}, err
7878
}
79+
80+
expandAllEnvPlaceholders(&s)
81+
7982
return s, nil
8083
}
84+
85+
func expandAllEnvPlaceholders(s *settings) {
86+
s.LocalRepository = evaluateVariable(s.LocalRepository, nil, nil)
87+
for i, server := range s.Servers {
88+
s.Servers[i].ID = evaluateVariable(server.ID, nil, nil)
89+
s.Servers[i].Username = evaluateVariable(server.Username, nil, nil)
90+
s.Servers[i].Password = evaluateVariable(server.Password, nil, nil)
91+
}
92+
}

pkg/dependency/parser/java/pom/settings_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,35 @@ func Test_ReadSettings(t *testing.T) {
122122
},
123123
wantSettings: settings{},
124124
},
125+
{
126+
name: "environment placeholders are dereferenced",
127+
envs: map[string]string{
128+
"HOME": filepath.Join("testdata", "settings", "user-settings-with-env-placeholders"),
129+
"MAVEN_HOME": "NOT_EXISTING_PATH",
130+
"LOCAL_REPO_PT_1": "part1",
131+
"LOCAL_REPO_PT_2": "part2",
132+
"SERVER_ID": "server-id-from-env",
133+
"USERNAME": "username-from-env",
134+
"PASSWORD": "password-from-env",
135+
},
136+
wantSettings: settings{
137+
LocalRepository: "part1/part2/.m2/repository",
138+
Servers: []Server{
139+
{
140+
ID: "user-server",
141+
},
142+
{
143+
ID: "server-id-from-env",
144+
Username: "username-from-env",
145+
Password: "password-from-env",
146+
},
147+
{
148+
ID: "server-with-name-only",
149+
Username: "test-user-only",
150+
},
151+
},
152+
},
153+
},
125154
}
126155
for _, tt := range tests {
127156
t.Run(tt.name, func(t *testing.T) {
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
5+
<localRepository>${env.LOCAL_REPO_PT_1}/${env.LOCAL_REPO_PT_2}/.m2/repository</localRepository>
6+
7+
<servers>
8+
<server>
9+
<id>user-server</id>
10+
</server>
11+
<server>
12+
<id>${env.SERVER_ID}</id>
13+
<username>${env.USERNAME}</username>
14+
<password>${env.PASSWORD}</password>
15+
</server>
16+
<server>
17+
<id>server-with-name-only</id>
18+
<username>test-user-only</username>
19+
</server>
20+
</servers>
21+
</settings>

pkg/dependency/parser/java/pom/utils.go

Lines changed: 0 additions & 21 deletions
This file was deleted.

pkg/dependency/parser/java/pom/var.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package pom
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"regexp"
7+
"slices"
8+
"strings"
9+
10+
"github.com/aquasecurity/trivy/pkg/log"
11+
)
12+
13+
var varRegexp = regexp.MustCompile(`\${(\S+?)}`)
14+
15+
func isProperty(s string) bool {
16+
if s != "" && strings.HasPrefix(s, "${") && strings.HasSuffix(s, "}") {
17+
return true
18+
}
19+
return false
20+
}
21+
22+
func evaluateVariable(s string, props map[string]string, seenProps []string) string {
23+
if props == nil {
24+
props = make(map[string]string)
25+
}
26+
27+
for _, m := range varRegexp.FindAllStringSubmatch(s, -1) {
28+
var newValue string
29+
30+
// env.X: https://maven.apache.org/pom.html#Properties
31+
// e.g. env.PATH
32+
if strings.HasPrefix(m[1], "env.") {
33+
newValue = os.Getenv(strings.TrimPrefix(m[1], "env."))
34+
} else {
35+
// <properties> might include another property.
36+
// e.g. <animal.sniffer.skip>${skipTests}</animal.sniffer.skip>
37+
ss, ok := props[m[1]]
38+
if ok {
39+
// search for looped properties
40+
if slices.Contains(seenProps, ss) {
41+
printLoopedPropertiesStack(m[0], seenProps)
42+
return ""
43+
}
44+
seenProps = append(seenProps, ss) // save evaluated props to check if we get this prop again
45+
newValue = evaluateVariable(ss, props, seenProps)
46+
seenProps = []string{} // clear props if we returned from recursive. Required for correct work with 2 same props like ${foo}-${foo}
47+
}
48+
49+
}
50+
s = strings.ReplaceAll(s, m[0], newValue)
51+
}
52+
return strings.TrimSpace(s)
53+
}
54+
55+
func printLoopedPropertiesStack(env string, usedProps []string) {
56+
var s string
57+
for _, prop := range usedProps {
58+
s += fmt.Sprintf("%s -> ", prop)
59+
}
60+
log.Warn("Lopped properties were detected", log.String("prop", s+env))
61+
}

0 commit comments

Comments
 (0)