Skip to content

Commit d33d3ef

Browse files
authored
Merge pull request go-git#848 from josephvusich/fix/cve-2018-11235
config: modules, worktree: Submodule fixes for CVE-2018-11235
2 parents ae788cf + d87faec commit d33d3ef

File tree

5 files changed

+73
-12
lines changed

5 files changed

+73
-12
lines changed

config/config.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ func (c *Config) Unmarshal(b []byte) error {
135135
if err := c.unmarshalPack(); err != nil {
136136
return err
137137
}
138-
c.unmarshalSubmodules()
138+
unmarshalSubmodules(c.Raw, c.Submodules)
139139

140140
if err := c.unmarshalBranches(); err != nil {
141141
return err
@@ -182,13 +182,17 @@ func (c *Config) unmarshalRemotes() error {
182182
return nil
183183
}
184184

185-
func (c *Config) unmarshalSubmodules() {
186-
s := c.Raw.Section(submoduleSection)
185+
func unmarshalSubmodules(fc *format.Config, submodules map[string]*Submodule) {
186+
s := fc.Section(submoduleSection)
187187
for _, sub := range s.Subsections {
188188
m := &Submodule{}
189189
m.unmarshal(sub)
190190

191-
c.Submodules[m.Name] = m
191+
if m.Validate() == ErrModuleBadPath {
192+
continue
193+
}
194+
195+
submodules[m.Name] = m
192196
}
193197
}
194198

config/modules.go

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,20 @@ package config
33
import (
44
"bytes"
55
"errors"
6+
"regexp"
67

78
format "gopkg.in/src-d/go-git.v4/plumbing/format/config"
89
)
910

1011
var (
1112
ErrModuleEmptyURL = errors.New("module config: empty URL")
1213
ErrModuleEmptyPath = errors.New("module config: empty path")
14+
ErrModuleBadPath = errors.New("submodule has an invalid path")
15+
)
16+
17+
var (
18+
// Matches module paths with dotdot ".." components.
19+
dotdotPath = regexp.MustCompile(`(^|[/\\])\.\.([/\\]|$)`)
1320
)
1421

1522
// Modules defines the submodules properties, represents a .gitmodules file
@@ -44,14 +51,7 @@ func (m *Modules) Unmarshal(b []byte) error {
4451
return err
4552
}
4653

47-
s := m.raw.Section(submoduleSection)
48-
for _, sub := range s.Subsections {
49-
mod := &Submodule{}
50-
mod.unmarshal(sub)
51-
52-
m.Submodules[mod.Path] = mod
53-
}
54-
54+
unmarshalSubmodules(m.raw, m.Submodules)
5555
return nil
5656
}
5757

@@ -102,6 +102,10 @@ func (m *Submodule) Validate() error {
102102
return ErrModuleEmptyURL
103103
}
104104

105+
if dotdotPath.MatchString(m.Path) {
106+
return ErrModuleBadPath
107+
}
108+
105109
return nil
106110
}
107111

config/modules_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,29 @@ func (s *ModulesSuite) TestValidateMissingURL(c *C) {
1111
c.Assert(m.Validate(), Equals, ErrModuleEmptyURL)
1212
}
1313

14+
func (s *ModulesSuite) TestValidateBadPath(c *C) {
15+
input := []string{
16+
`..`,
17+
`../`,
18+
`../bar`,
19+
20+
`/..`,
21+
`/../bar`,
22+
23+
`foo/..`,
24+
`foo/../`,
25+
`foo/../bar`,
26+
}
27+
28+
for _, p := range input {
29+
m := &Submodule{
30+
Path: p,
31+
URL: "https://example.com/",
32+
}
33+
c.Assert(m.Validate(), Equals, ErrModuleBadPath)
34+
}
35+
}
36+
1437
func (s *ModulesSuite) TestValidateMissingName(c *C) {
1538
m := &Submodule{URL: "bar"}
1639
c.Assert(m.Validate(), Equals, ErrModuleEmptyPath)
@@ -39,6 +62,9 @@ func (s *ModulesSuite) TestUnmarshall(c *C) {
3962
path = foo/bar
4063
url = https://github.com/foo/bar.git
4164
branch = dev
65+
[submodule "suspicious"]
66+
path = ../../foo/bar
67+
url = https://github.com/foo/bar.git
4268
`)
4369

4470
cfg := NewModules()

submodule_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,21 @@ func (s *SubmoduleSuite) TestSubmodulesInit(c *C) {
196196
}
197197
}
198198

199+
func (s *SubmoduleSuite) TestGitSubmodulesSymlink(c *C) {
200+
f, err := s.Worktree.Filesystem.Create("badfile")
201+
c.Assert(err, IsNil)
202+
defer f.Close()
203+
204+
err = s.Worktree.Filesystem.Remove(gitmodulesFile)
205+
c.Assert(err, IsNil)
206+
207+
err = s.Worktree.Filesystem.Symlink("badfile", gitmodulesFile)
208+
c.Assert(err, IsNil)
209+
210+
_, err = s.Worktree.Submodules()
211+
c.Assert(err, Equals, ErrGitModulesSymlink)
212+
}
213+
199214
func (s *SubmoduleSuite) TestSubmodulesStatus(c *C) {
200215
sm, err := s.Worktree.Submodules()
201216
c.Assert(err, IsNil)

worktree.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ var (
2828
ErrWorktreeNotClean = errors.New("worktree is not clean")
2929
ErrSubmoduleNotFound = errors.New("submodule not found")
3030
ErrUnstagedChanges = errors.New("worktree contains unstaged changes")
31+
ErrGitModulesSymlink = errors.New(gitmodulesFile + " is a symlink")
3132
)
3233

3334
// Worktree represents a git worktree.
@@ -680,7 +681,18 @@ func (w *Worktree) newSubmodule(fromModules, fromConfig *config.Submodule) *Subm
680681
return m
681682
}
682683

684+
func (w *Worktree) isSymlink(path string) bool {
685+
if s, err := w.Filesystem.Lstat(path); err == nil {
686+
return s.Mode()&os.ModeSymlink != 0
687+
}
688+
return false
689+
}
690+
683691
func (w *Worktree) readGitmodulesFile() (*config.Modules, error) {
692+
if w.isSymlink(gitmodulesFile) {
693+
return nil, ErrGitModulesSymlink
694+
}
695+
684696
f, err := w.Filesystem.Open(gitmodulesFile)
685697
if err != nil {
686698
if os.IsNotExist(err) {

0 commit comments

Comments
 (0)