The filepath package has some functions for processing paths and filenames. I am using it extensively in a current project. You can do cool stuff with it, like traversing a path recursively with filepath.Walk.
filepath.Ext returns the extension of a filename (or path). It returns whatever is after the last dot. It has some gotchas that might have security implications.
Code is at: https://github.com/parsiya/Parsia-Code/tree/master/filepath-ext
https://golang.org/src/path/filepath/path.go?s=6131:6159#L207
// Ext returns the file name extension used by path.
// The extension is the suffix beginning at the final dot
// in the final element of path; it is empty if there is
// no dot.
func Ext(path string) string {
for i := len(path) - 1; i >= 0 && !os.IsPathSeparator(path[i]); i-- {
if path[i] == '.' {
return path[i:]
}
}
return ""
}
To demonstrate the tips, we are going to create a simple example. Let's assume we have a file hosting service. We have created a blacklist of banned extensions. It checks the files by extension. This is not really a good way to do this:
exe
to txt
or another random extension, the user needs to manually rename it back and execute it.But let's use it for demonstrating our points. This is the blacklist function:
// Blacklist returns true if a file is one of the banned types by checking its extension.
func Blacklist(filename string) bool {
if filepath.Ext(filename) == ".exe" {
return false
}
return true
}
The input's letter case does not change. Everything is just passed through. Consider the following code (case.go
):
package main
import (
"fmt"
"path/filepath"
)
func main() {
fmt.Println("filepath.Ext(\"whatever.txt\"):", filepath.Ext("whatever.txt"))
// filepath.Ext("whatever.txt"): .txt
fmt.Println("filepath.Ext(\"whatever.TXT\"):", filepath.Ext("whatever.TXT"))
// filepath.Ext("whatever.TXT"): .TXT
fmt.Println("filepath.Ext(\"whatever.Txt\"):", filepath.Ext("whatever.Txt"))
// filepath.Ext("whatever.Txt"): .Txt
}
To bypass the blacklist, we can just change the case case_blacklist.go
:
func main() {
fmt.Println("Blacklist(\"whatever.exe\"):", Blacklist("whatever.exe"))
// true
fmt.Println("Blacklist(\"whatever.ExE\"):", Blacklist("whatever.ExE"))
// false
}
In our mind, the extension of a file is just the letters after the dot. It does not include the dot. However, Ext returns the dot. This is really unintuitive and has caught me off guard a few times.
package main
import (
"fmt"
"path/filepath"
)
func main() {
fmt.Println("Blacklist(\"whatever.exe\"):", Blacklist("whatever.exe"))
// true
}
// Blacklist returns true if a file is one of the banned types by checking its extension.
func Blacklist(filename string) bool {
// Developers did not expect the dot to be part of the output.
if filepath.Ext(filename) == "exe" {
return false
}
return true
}
This blacklist is useless, the condition is never true.
This is mentioned in the docs but aI did not expect it.