Skip to content
Permalink
Browse files Browse the repository at this point in the history
Prevent cpio.Extract from writing into parent directories
  • Loading branch information
mtharp committed Jun 12, 2020
1 parent e314ffa commit a64058c
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 8 deletions.
25 changes: 17 additions & 8 deletions cpio/extract.go
Expand Up @@ -21,6 +21,8 @@ import (
"io"
"os"
"path"
"path/filepath"
"strings"

"github.com/sassoftware/go-rpmutils/fileutil"
)
Expand All @@ -39,7 +41,9 @@ const (
S_ISSOCK = 0140000 // Socket
)

// Extract the contents of a cpio stream from r to the destination directory dest
func Extract(rs io.Reader, dest string) error {
dest = filepath.Clean(filepath.FromSlash(dest))
linkMap := make(map[int][]string)

stream := NewCpioStream(rs)
Expand All @@ -54,16 +58,21 @@ func Extract(rs io.Reader, dest string) error {
break
}

target := path.Join(dest, path.Clean(entry.Header.filename))
parent := path.Dir(target)

// sanitize path
target := path.Clean(entry.Header.filename)
for strings.HasPrefix(target, "../") {
target = target[3:]
}
target = filepath.Join(dest, filepath.FromSlash(target))
if !strings.HasPrefix(target, dest+string(filepath.Separator)) && dest != target {
// this shouldn't happen due to the sanitization above but always check
return fmt.Errorf("invalid cpio path %q", entry.Header.filename)
}
// Create the parent directory if it doesn't exist.
if _, err := os.Stat(parent); os.IsNotExist(err) {
if err := os.MkdirAll(parent, 0755); err != nil {
return err
}
parent := filepath.Dir(target)
if err := os.MkdirAll(parent, 0755); err != nil {
return err
}

// FIXME: Need a makedev implementation in go.

switch entry.Header.Mode() &^ 07777 {
Expand Down
22 changes: 22 additions & 0 deletions cpio/extract_test.go
Expand Up @@ -19,6 +19,7 @@ package cpio
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
"testing/iotest"

Expand Down Expand Up @@ -58,6 +59,27 @@ func TestExtract(t *testing.T) {
}
}

func TestExtractDotdot(t *testing.T) {
setupLogging()
f, err := os.Open("../testdata/dotdot.cpio")
if err != nil {
t.Fatal(err)
}
defer f.Close()
tmpdir, err := ioutil.TempDir("", "cpio")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpdir)
err = Extract(f, tmpdir)
if err != nil {
t.Fatal(err)
}
if _, err := os.Stat(filepath.Join(tmpdir, "aaaaaaaaa")); err != nil {
t.Error("expected file with ../ to extract into top of destdir:", err)
}
}

var _format = logging.MustStringFormatter(
`%{color}%{time:15:04:05.000} %{shortfunc} ▶ %{level:.4s} %{id:03x}%{color:reset} %{message}`,
)
Expand Down
4 changes: 4 additions & 0 deletions rpmutils_test.go
Expand Up @@ -28,6 +28,7 @@ func TestReadHeader(t *testing.T) {
if err != nil {
t.Fatal(err)
}
defer f.Close()

hdr, err := ReadHeader(iotest.HalfReader(f))
if err != nil {
Expand Down Expand Up @@ -62,6 +63,7 @@ func TestPayloadReader(t *testing.T) {
if err != nil {
t.Fatal(err)
}
defer f.Close()

rpm, err := ReadRpm(iotest.HalfReader(f))
if err != nil {
Expand Down Expand Up @@ -92,6 +94,7 @@ func TestExpandPayload(t *testing.T) {
if err != nil {
t.Fatal(err)
}
defer f.Close()

rpm, err := ReadRpm(iotest.HalfReader(f))
if err != nil {
Expand All @@ -102,6 +105,7 @@ func TestExpandPayload(t *testing.T) {
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpdir)

if err := rpm.ExpandPayload(tmpdir); err != nil {
t.Fatal(err)
Expand Down
Binary file added testdata/dotdot.cpio
Binary file not shown.

0 comments on commit a64058c

Please sign in to comment.