Repository: zyedidia/micro
File: internal/util/util.go (Go)

Code before:
	return fmt.Sprintf("%x", md5.Sum([]byte(str)))
}

// EscapePathUrl encodes the path in URL query form
func EscapePathUrl(path string) string {
	return url.QueryEscape(filepath.ToSlash(path))
}

// EscapePathLegacy replaces every path separator in a given path with a %
func EscapePathLegacy(path string) string {
	path = filepath.ToSlash(path)
	if runtime.GOOS == "windows" {
		// ':' is not valid in a path name on Windows but is ok on Unix
		path = strings.ReplaceAll(path, ":", "%")
	}
	return strings.ReplaceAll(path, "/", "%")
}

// DetermineEscapePath escapes a path, determining whether it should be escaped
// using URL encoding (preferred, since it encodes unambiguously) or
// legacy encoding with '%' (for backward compatibility, if the legacy-escaped
// path exists in the given directory).
// In case the escaped path, including the backup extension, exceeds the
// filename length limit it is hashed and the hash is used as final filename.
// A second filename is provided to optionally fill this file with the original
// path to resolve the hashed filename.
func DetermineEscapePath(dir string, path string) (string, string) {
	fresolve := ""
	fname := EscapePathUrl(path)
	length := len(fname)
	if length+len(".micro-backup") > 255 {
		fname = HashStringMd5(path)
		fresolve = filepath.Join(dir, fname+".path")
	}

	fpath := filepath.Join(dir, fname)
	if _, err := os.Stat(fpath); err == nil {
		return fpath, fresolve
	}

	legacy := filepath.Join(dir, EscapePathLegacy(path))
	if _, err := os.Stat(legacy); err == nil {
		return legacy, fresolve
	}

	return fpath, fresolve
}

// GetLeadingWhitespace returns the leading whitespace of the given byte array
func GetLeadingWhitespace(b []byte) []byte {
	ws := []byte{}

Review comment (security):
This last sentence sounds fancy but not quite clear.

Let's describe this clearly, e.g.:

"In case the length of the escaped path (plus the backup extension) exceeds the filename length limit, a hash of the path is returned instead. In such case the second return value is the name of a file the original path should be saved to (since the original path cannot be derived from its hash). Otherwise the second return value is an empty string."

Code after:

// EscapePathUrl encodes the path in URL query form
func EscapePathUrl(path string) string {
	return url.QueryEscape(filepath.ToSlash(path))
}

// EscapePathLegacy replaces every path separator in a given path with a %
func EscapePathLegacy(path string) string {
	path = filepath.ToSlash(path)
	if runtime.GOOS == "windows" {
		// ':' is not valid in a path name on Windows but is ok on Unix
		path = strings.ReplaceAll(path, ":", "%")
	}
	return strings.ReplaceAll(path, "/", "%")
}

// DetermineEscapePath escapes a path, determining whether it should be escaped
// using URL encoding (preferred, since it encodes unambiguously) or
// legacy encoding with '%' (for backward compatibility, if the legacy-escaped
// path exists in the given directory).
// In case the length of the escaped path (plus the backup extension) exceeds
// the filename length limit, a hash of the path is returned instead. In such
// case the second return value is the name of a file the original path should
// be saved to (since the original path cannot be derived from its hash).
// Otherwise the second return value is an empty string.
func DetermineEscapePath(dir string, path string) (string, string) {
	url := filepath.Join(dir, EscapePathUrl(path))
	if _, err := os.Stat(url); err == nil {
		return url, ""
	}

	legacy := filepath.Join(dir, EscapePathLegacy(path))
	if _, err := os.Stat(legacy); err == nil {
		return legacy, ""
	}

	if len(url)+len(BackupSuffix) > 255 {
		hash := HashStringMd5(path)
		return filepath.Join(dir, hash), filepath.Join(dir, hash+".path")
	}

	return url, ""
}

// GetLeadingWhitespace returns the leading whitespace of the given byte array
func GetLeadingWhitespace(b []byte) []byte {
	ws := []byte{}
	for len(b) > 0 {
		r, _, size := DecodeCharacter(b)
		if r == ' ' || r == '\t' {
			ws = append(ws, byte(r))