mirror of
https://github.com/kemko/nomad.git
synced 2026-01-06 10:25:42 +03:00
ensure file doesn't escape
This commit is contained in:
@@ -411,7 +411,7 @@ func (d *AllocDir) LogDir() string {
|
||||
|
||||
// List returns the list of files at a path relative to the alloc dir
|
||||
func (d *AllocDir) List(path string) ([]*AllocFileInfo, error) {
|
||||
if escapes, err := structs.PathEscapesAllocDir(path); err != nil {
|
||||
if escapes, err := structs.PathEscapesAllocDir("", path); err != nil {
|
||||
return nil, fmt.Errorf("Failed to check if path escapes alloc directory: %v", err)
|
||||
} else if escapes {
|
||||
return nil, fmt.Errorf("Path escapes the alloc directory")
|
||||
@@ -437,7 +437,7 @@ func (d *AllocDir) List(path string) ([]*AllocFileInfo, error) {
|
||||
|
||||
// Stat returns information about the file at a path relative to the alloc dir
|
||||
func (d *AllocDir) Stat(path string) (*AllocFileInfo, error) {
|
||||
if escapes, err := structs.PathEscapesAllocDir(path); err != nil {
|
||||
if escapes, err := structs.PathEscapesAllocDir("", path); err != nil {
|
||||
return nil, fmt.Errorf("Failed to check if path escapes alloc directory: %v", err)
|
||||
} else if escapes {
|
||||
return nil, fmt.Errorf("Path escapes the alloc directory")
|
||||
@@ -460,7 +460,7 @@ func (d *AllocDir) Stat(path string) (*AllocFileInfo, error) {
|
||||
|
||||
// ReadAt returns a reader for a file at the path relative to the alloc dir
|
||||
func (d *AllocDir) ReadAt(path string, offset int64) (io.ReadCloser, error) {
|
||||
if escapes, err := structs.PathEscapesAllocDir(path); err != nil {
|
||||
if escapes, err := structs.PathEscapesAllocDir("", path); err != nil {
|
||||
return nil, fmt.Errorf("Failed to check if path escapes alloc directory: %v", err)
|
||||
} else if escapes {
|
||||
return nil, fmt.Errorf("Path escapes the alloc directory")
|
||||
@@ -489,7 +489,7 @@ func (d *AllocDir) ReadAt(path string, offset int64) (io.ReadCloser, error) {
|
||||
// BlockUntilExists blocks until the passed file relative the allocation
|
||||
// directory exists. The block can be cancelled with the passed tomb.
|
||||
func (d *AllocDir) BlockUntilExists(path string, t *tomb.Tomb) (chan error, error) {
|
||||
if escapes, err := structs.PathEscapesAllocDir(path); err != nil {
|
||||
if escapes, err := structs.PathEscapesAllocDir("", path); err != nil {
|
||||
return nil, fmt.Errorf("Failed to check if path escapes alloc directory: %v", err)
|
||||
} else if escapes {
|
||||
return nil, fmt.Errorf("Path escapes the alloc directory")
|
||||
@@ -510,7 +510,7 @@ func (d *AllocDir) BlockUntilExists(path string, t *tomb.Tomb) (chan error, erro
|
||||
// allocation directory. The offset should be the last read offset. The tomb is
|
||||
// used to clean up the watch.
|
||||
func (d *AllocDir) ChangeEvents(path string, curOffset int64, t *tomb.Tomb) (*watch.FileChanges, error) {
|
||||
if escapes, err := structs.PathEscapesAllocDir(path); err != nil {
|
||||
if escapes, err := structs.PathEscapesAllocDir("", path); err != nil {
|
||||
return nil, fmt.Errorf("Failed to check if path escapes alloc directory: %v", err)
|
||||
} else if escapes {
|
||||
return nil, fmt.Errorf("Path escapes the alloc directory")
|
||||
|
||||
@@ -1706,6 +1706,18 @@ func (d *DispatchInputConfig) Copy() *DispatchInputConfig {
|
||||
return nd
|
||||
}
|
||||
|
||||
func (d *DispatchInputConfig) Validate() error {
|
||||
// Verify the destination doesn't escape
|
||||
escaped, err := PathEscapesAllocDir("task/local/", d.File)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid destination path: %v", err)
|
||||
} else if escaped {
|
||||
return fmt.Errorf("destination escapes allocation directory")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
defaultServiceJobRestartPolicy = RestartPolicy{
|
||||
Delay: 15 * time.Second,
|
||||
@@ -2462,6 +2474,13 @@ func (t *Task) Validate(ephemeralDisk *EphemeralDisk) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the dispatch input block if there
|
||||
if t.DispatchInput != nil {
|
||||
if err := t.DispatchInput.Validate(); err != nil {
|
||||
mErr.Errors = append(mErr.Errors, err)
|
||||
}
|
||||
}
|
||||
|
||||
return mErr.ErrorOrNil()
|
||||
}
|
||||
|
||||
@@ -2603,7 +2622,7 @@ func (t *Template) Validate() error {
|
||||
}
|
||||
|
||||
// Verify the destination doesn't escape
|
||||
escaped, err := PathEscapesAllocDir(t.DestPath)
|
||||
escaped, err := PathEscapesAllocDir("task", t.DestPath)
|
||||
if err != nil {
|
||||
mErr.Errors = append(mErr.Errors, fmt.Errorf("invalid destination path: %v", err))
|
||||
} else if escaped {
|
||||
@@ -2955,14 +2974,16 @@ func (ta *TaskArtifact) GoString() string {
|
||||
}
|
||||
|
||||
// PathEscapesAllocDir returns if the given path escapes the allocation
|
||||
// directory
|
||||
func PathEscapesAllocDir(path string) (bool, error) {
|
||||
// directory. The prefix allows adding a prefix if the path will be joined, for
|
||||
// example a "task/local" prefix may be provided if the path will be joined
|
||||
// against that prefix.
|
||||
func PathEscapesAllocDir(prefix, path string) (bool, error) {
|
||||
// Verify the destination doesn't escape the tasks directory
|
||||
alloc, err := filepath.Abs(filepath.Join("/", "foo/", "bar/"))
|
||||
alloc, err := filepath.Abs(filepath.Join("/", "alloc-dir/", "alloc-id/"))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
abs, err := filepath.Abs(filepath.Join(alloc, path))
|
||||
abs, err := filepath.Abs(filepath.Join(alloc, prefix, path))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -2981,7 +3002,7 @@ func (ta *TaskArtifact) Validate() error {
|
||||
mErr.Errors = append(mErr.Errors, fmt.Errorf("source must be specified"))
|
||||
}
|
||||
|
||||
escaped, err := PathEscapesAllocDir(ta.RelativeDest)
|
||||
escaped, err := PathEscapesAllocDir("task", ta.RelativeDest)
|
||||
if err != nil {
|
||||
mErr.Errors = append(mErr.Errors, fmt.Errorf("invalid destination path: %v", err))
|
||||
} else if escaped {
|
||||
|
||||
@@ -1267,7 +1267,7 @@ func TestTaskArtifact_Validate_Dest(t *testing.T) {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
valid.RelativeDest = "local/../.."
|
||||
valid.RelativeDest = "local/../../.."
|
||||
if err := valid.Validate(); err == nil {
|
||||
t.Fatalf("expected error: %v", err)
|
||||
}
|
||||
@@ -1482,3 +1482,26 @@ func TestConstructorConfig_Canonicalize(t *testing.T) {
|
||||
t.Fatalf("Canonicalize failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDispatchInputConfig_Validate(t *testing.T) {
|
||||
d := &DispatchInputConfig{
|
||||
File: "foo",
|
||||
}
|
||||
|
||||
// task/local/haha
|
||||
if err := d.Validate(); err != nil {
|
||||
t.Fatalf("bad: %v", err)
|
||||
}
|
||||
|
||||
// task/haha
|
||||
d.File = "../haha"
|
||||
if err := d.Validate(); err != nil {
|
||||
t.Fatalf("bad: %v", err)
|
||||
}
|
||||
|
||||
// ../haha
|
||||
d.File = "../../../haha"
|
||||
if err := d.Validate(); err == nil {
|
||||
t.Fatalf("bad: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user