Skip to content

Commit

Permalink
add file clean and SafeOpen utils
Browse files Browse the repository at this point in the history
  • Loading branch information
tarunKoyalwar committed Sep 27, 2023
1 parent a78f808 commit f5132c8
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 0 deletions.
128 changes: 128 additions & 0 deletions file/clean.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package fileutil

import (
"os"
"path/filepath"

errorutil "github.com/projectdiscovery/utils/errors"
)

var (
DefaultFilePermission = os.FileMode(0644)
)

// CleanPath cleans paths to migtigate any possible path traversal attacks.
// and it always returns an absolute path
func CleanPath(inputPath string) (string, error) {
// check if path is abs
if filepath.IsAbs(inputPath) {
// clean it using filepath.Abs
// Abs always calls Clean, so we don't need to call it again
return filepath.Abs(inputPath)
}
// get current working directory
cwd, err := os.Getwd()
if err != nil {
return "", err
}
// join cwd with inputPath
joined := filepath.Join(cwd, inputPath)
// clean it using filepath.Abs
return filepath.Abs(joined)
}

// CleanPathOrDefault cleans paths to migtigate any possible path traversal attacks.
func CleanPathOrDefault(inputPath string, defaultPath string) string {
if inputPath == "" {
return defaultPath
}
if val, err := CleanPath(inputPath); err == nil {
return val
}
return defaultPath
}

// ResolveNClean resolves the path and cleans it
// ex: a nuclei template can be either abs or relative to a specified directory
// this function uses given path as a base to resolve the path instead of cwd
func ResolveNClean(inputPath string, baseDir ...string) (string, error) {
// check if path is abs
if filepath.IsAbs(inputPath) {
// clean it using filepath.Abs
// Abs always calls Clean, so we don't need to call it again
return filepath.Abs(inputPath)
}
for _, dir := range baseDir {
// join cwd with inputPath
joined := filepath.Join(dir, inputPath)
// clean it using filepath.Abs
abs, err := filepath.Abs(joined)
if err == nil && FileOrFolderExists(abs) {
return abs, nil
}
}
return "", errorutil.NewWithErr(os.ErrNotExist).Msgf("failed to resolve path: %s", inputPath)
}

// ResolveNCleanOrDefault resolves the path and cleans it
func ResolveNCleanOrDefault(inputPath string, defaultPath string, baseDir ...string) string {
if inputPath == "" {
return defaultPath
}
if val, err := ResolveNClean(inputPath, baseDir...); err == nil {
return val
}
return defaultPath
}

// SafeOpen opens a file after cleaning the path
// in read mode
func SafeOpen(path string) (*os.File, error) {
abs, err := CleanPath(path)
if err != nil {
return nil, err
}
return os.Open(abs)
}

// SafeOpenAppend opens a file after cleaning the path
// in append mode and creates any missing directories in chain /path/to/file
func SafeOpenAppend(path string) (*os.File, error) {
abs, err := CleanPath(path)
if err != nil {
return nil, err
}
_ = FixMissingDirs(abs)
return os.OpenFile(abs, os.O_APPEND|os.O_CREATE|os.O_WRONLY, DefaultFilePermission)
}

// SafeOpenWrite opens a file after cleaning the path
// in write mode and creates any missing directories in chain /path/to/file
func SafeOpenWrite(path string) (*os.File, error) {
abs, err := CleanPath(path)
if err != nil {
return nil, err
}
_ = FixMissingDirs(abs)
return os.OpenFile(abs, os.O_CREATE|os.O_WRONLY, DefaultFilePermission)
}

// SafeWriteFile writes data to a file after cleaning the path
// in write mode and creates any missing directories in chain /path/to/file
func SafeWriteFile(path string, data []byte) error {
abs, err := CleanPath(path)
if err != nil {
return err
}
_ = FixMissingDirs(abs)
return os.WriteFile(abs, data, DefaultFilePermission)
}

// FixMissingDirs creates any missing directories in chain /path/to/file
func FixMissingDirs(path string) error {
abs, err := CleanPath(path)
if err != nil {
return err
}
return os.MkdirAll(filepath.Dir(abs), os.ModePerm)
}
34 changes: 34 additions & 0 deletions file/clean_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package fileutil

import (
"path/filepath"
"testing"
)

func FuzzCleanPath(f *testing.F) {
// // Define your custom payloads here
// customPayloads := []string{
// "../../etc/passwd",
// "/absolute/path/to/file",
// "./relative/path",
// // Add more payloads as needed
// }

// // Use each custom payload for fuzzing
// for _, payload := range customPayloads {
// f.Add(payload)
// }

f.Fuzz(func(t *testing.T, inputPath string) {
result, err := CleanPath(inputPath)
if err != nil {
t.Fatal(err)
}

// You can add more assertions here based on your requirements
// For example, you might want to check if the returned path is absolute
if !filepath.IsAbs(result) {
t.Errorf("CleanPath(%q) returned a non-absolute path", result)
}
})
}

0 comments on commit f5132c8

Please sign in to comment.