diff --git a/local.go b/local.go
index 4bafa62..d0bf6e8 100644
--- a/local.go
+++ b/local.go
@@ -52,12 +52,23 @@ func (f *LocalFetcher) walk(p *POM, dir string) {
return
}
rel := p.Parent.LocalPath()
- if rel == "" {
+ if rel == "" || filepath.IsAbs(rel) {
return
}
- path := filepath.Join(dir, rel)
- if fi, err := os.Stat(path); err == nil && fi.IsDir() {
+ path := filepath.Clean(filepath.Join(dir, rel))
+ fi, err := os.Lstat(path)
+ if err != nil {
+ return
+ }
+ if fi.Mode()&os.ModeSymlink != 0 {
+ return
+ }
+ if fi.IsDir() {
path = filepath.Join(path, "pom.xml")
+ fi, err = os.Lstat(path)
+ if err != nil || fi.Mode()&os.ModeSymlink != 0 {
+ return
+ }
}
parent, err := readPOMFile(path)
if err != nil {
diff --git a/local_test.go b/local_test.go
index 95b1c92..06b0f10 100644
--- a/local_test.go
+++ b/local_test.go
@@ -2,6 +2,8 @@ package pom
import (
"context"
+ "os"
+ "path/filepath"
"testing"
)
@@ -80,3 +82,58 @@ func TestParentLocalPath(t *testing.T) {
}
}
}
+
+func TestLocalFetcherRejectsAbsoluteRelativePath(t *testing.T) {
+ tmp := t.TempDir()
+ childDir := filepath.Join(tmp, "child")
+ _ = os.MkdirAll(childDir, 0o755)
+
+ targetPOM := filepath.Join(tmp, "target", "pom.xml")
+ _ = os.MkdirAll(filepath.Dir(targetPOM), 0o755)
+ _ = os.WriteFile(targetPOM, []byte(`org.evilevil1.0`), 0o644)
+
+ absPath := targetPOM
+ child := &POM{
+ GroupID: "org.example",
+ ArtifactID: "child",
+ Version: "1.0",
+ Parent: &Parent{GroupID: "org.evil", ArtifactID: "evil", Version: "1.0", RelativePath: &absPath},
+ }
+
+ f := NewLocalFetcherFrom(child, childDir)
+ _, err := f.Fetch(context.Background(), GAV{"org.evil", "evil", "1.0"})
+ if err == nil {
+ t.Error("expected error: absolute relativePath should be rejected")
+ }
+}
+
+func TestLocalFetcherRejectsSymlink(t *testing.T) {
+ tmp := t.TempDir()
+ childDir := filepath.Join(tmp, "child")
+ _ = os.MkdirAll(childDir, 0o755)
+
+ // Create a target POM outside the project tree
+ outsideDir := filepath.Join(tmp, "outside")
+ _ = os.MkdirAll(outsideDir, 0o755)
+ _ = os.WriteFile(filepath.Join(outsideDir, "pom.xml"), []byte(`org.evilevil1.0`), 0o644)
+
+ // Create symlink from child/parent -> outside
+ symlink := filepath.Join(childDir, "parent")
+ if err := os.Symlink(outsideDir, symlink); err != nil {
+ t.Skipf("symlinks not supported: %v", err)
+ }
+
+ rel := "parent"
+ child := &POM{
+ GroupID: "org.example",
+ ArtifactID: "child",
+ Version: "1.0",
+ Parent: &Parent{GroupID: "org.evil", ArtifactID: "evil", Version: "1.0", RelativePath: &rel},
+ }
+
+ f := NewLocalFetcherFrom(child, childDir)
+ _, err := f.Fetch(context.Background(), GAV{"org.evil", "evil", "1.0"})
+ if err == nil {
+ t.Error("expected error: symlink traversal should be rejected")
+ }
+}