addurl --preserve-filename and a few related changes
* addurl --preserve-filename: New option, uses server-provided filename
without any sanitization, but with some security checking.
Not yet implemented for remotes other than the web.
* addurl, importfeed: Avoid adding filenames with leading '.', instead
it will be replaced with '_'.
This might be considered a security fix, but a CVE seems unwattanted.
It was possible for addurl to create a dotfile, which could change
behavior of some program. It was also possible for a web server to say
the file name was ".git" or "foo/.git". That would not overrwrite the
.git directory, but would cause addurl to fail; of course git won't
add "foo/.git".
sanitizeFilePath is too opinionated to remain in Utility, so moved it.
The changes to mkSafeFilePath are because it used sanitizeFilePath.
In particular:
isDrive will never succeed, because "c:" gets munged to "c_"
".." gets sanitized now
".git" gets sanitized now
It will never be null, because sanitizeFilePath keeps the length
the same, and splitDirectories never returns a null path.
Also, on the off chance a web server suggests a filename of "",
ignore that, rather than trying to save to such a filename, which would
fail in some way.
2020-05-08 20:09:29 +00:00
|
|
|
{- handling untrusted filepaths
|
|
|
|
-
|
|
|
|
- Copyright 2010-2020 Joey Hess <id@joeyh.name>
|
|
|
|
-
|
|
|
|
- Licensed under the GNU AGPL version 3 or higher.
|
|
|
|
-}
|
|
|
|
|
|
|
|
module Annex.UntrustedFilePath where
|
|
|
|
|
|
|
|
import Data.Char
|
|
|
|
import System.FilePath
|
|
|
|
|
|
|
|
{- Given a string that we'd like to use as the basis for FilePath, but that
|
|
|
|
- was provided by a third party and is not to be trusted, returns the closest
|
|
|
|
- sane FilePath.
|
|
|
|
-
|
|
|
|
- All spaces and punctuation and other wacky stuff are replaced
|
2020-05-11 17:50:49 +00:00
|
|
|
- with '_', except for '.' and '-'
|
addurl --preserve-filename and a few related changes
* addurl --preserve-filename: New option, uses server-provided filename
without any sanitization, but with some security checking.
Not yet implemented for remotes other than the web.
* addurl, importfeed: Avoid adding filenames with leading '.', instead
it will be replaced with '_'.
This might be considered a security fix, but a CVE seems unwattanted.
It was possible for addurl to create a dotfile, which could change
behavior of some program. It was also possible for a web server to say
the file name was ".git" or "foo/.git". That would not overrwrite the
.git directory, but would cause addurl to fail; of course git won't
add "foo/.git".
sanitizeFilePath is too opinionated to remain in Utility, so moved it.
The changes to mkSafeFilePath are because it used sanitizeFilePath.
In particular:
isDrive will never succeed, because "c:" gets munged to "c_"
".." gets sanitized now
".git" gets sanitized now
It will never be null, because sanitizeFilePath keeps the length
the same, and splitDirectories never returns a null path.
Also, on the off chance a web server suggests a filename of "",
ignore that, rather than trying to save to such a filename, which would
fail in some way.
2020-05-08 20:09:29 +00:00
|
|
|
-
|
|
|
|
- "../" becomes ".._", which is safe.
|
|
|
|
- "/foo" becomes "_foo", which is safe.
|
|
|
|
- "c:foo" becomes "c_foo", which is safe even on windows.
|
|
|
|
-
|
2020-05-11 17:50:49 +00:00
|
|
|
- Leading '.' and '-' are also replaced with '_', so
|
|
|
|
- so no dotfiles that might control a program are inadvertently created,
|
|
|
|
- and to avoid filenames being treated as options to commands the user
|
|
|
|
- might run.
|
2020-05-11 18:04:56 +00:00
|
|
|
-
|
|
|
|
- Also there's an off chance the string might be empty, so to avoid
|
|
|
|
- needing to handle such an invalid filepath, return a dummy "file" in
|
|
|
|
- that case.
|
addurl --preserve-filename and a few related changes
* addurl --preserve-filename: New option, uses server-provided filename
without any sanitization, but with some security checking.
Not yet implemented for remotes other than the web.
* addurl, importfeed: Avoid adding filenames with leading '.', instead
it will be replaced with '_'.
This might be considered a security fix, but a CVE seems unwattanted.
It was possible for addurl to create a dotfile, which could change
behavior of some program. It was also possible for a web server to say
the file name was ".git" or "foo/.git". That would not overrwrite the
.git directory, but would cause addurl to fail; of course git won't
add "foo/.git".
sanitizeFilePath is too opinionated to remain in Utility, so moved it.
The changes to mkSafeFilePath are because it used sanitizeFilePath.
In particular:
isDrive will never succeed, because "c:" gets munged to "c_"
".." gets sanitized now
".git" gets sanitized now
It will never be null, because sanitizeFilePath keeps the length
the same, and splitDirectories never returns a null path.
Also, on the off chance a web server suggests a filename of "",
ignore that, rather than trying to save to such a filename, which would
fail in some way.
2020-05-08 20:09:29 +00:00
|
|
|
-}
|
|
|
|
sanitizeFilePath :: String -> FilePath
|
2020-08-05 15:35:00 +00:00
|
|
|
sanitizeFilePath = sanitizeLeadingFilePathCharacter . sanitizeFilePathComponent
|
|
|
|
|
|
|
|
{- For when the filepath is being built up out of components that should be
|
|
|
|
- individually sanitized, this can be used for each component, followed by
|
|
|
|
- sanitizeLeadingFilePathCharacter for the whole thing.
|
|
|
|
-}
|
|
|
|
sanitizeFilePathComponent :: String -> String
|
|
|
|
sanitizeFilePathComponent = map sanitize
|
addurl --preserve-filename and a few related changes
* addurl --preserve-filename: New option, uses server-provided filename
without any sanitization, but with some security checking.
Not yet implemented for remotes other than the web.
* addurl, importfeed: Avoid adding filenames with leading '.', instead
it will be replaced with '_'.
This might be considered a security fix, but a CVE seems unwattanted.
It was possible for addurl to create a dotfile, which could change
behavior of some program. It was also possible for a web server to say
the file name was ".git" or "foo/.git". That would not overrwrite the
.git directory, but would cause addurl to fail; of course git won't
add "foo/.git".
sanitizeFilePath is too opinionated to remain in Utility, so moved it.
The changes to mkSafeFilePath are because it used sanitizeFilePath.
In particular:
isDrive will never succeed, because "c:" gets munged to "c_"
".." gets sanitized now
".git" gets sanitized now
It will never be null, because sanitizeFilePath keeps the length
the same, and splitDirectories never returns a null path.
Also, on the off chance a web server suggests a filename of "",
ignore that, rather than trying to save to such a filename, which would
fail in some way.
2020-05-08 20:09:29 +00:00
|
|
|
where
|
|
|
|
sanitize c
|
2020-05-11 17:50:49 +00:00
|
|
|
| c == '.' || c == '-' = c
|
addurl --preserve-filename and a few related changes
* addurl --preserve-filename: New option, uses server-provided filename
without any sanitization, but with some security checking.
Not yet implemented for remotes other than the web.
* addurl, importfeed: Avoid adding filenames with leading '.', instead
it will be replaced with '_'.
This might be considered a security fix, but a CVE seems unwattanted.
It was possible for addurl to create a dotfile, which could change
behavior of some program. It was also possible for a web server to say
the file name was ".git" or "foo/.git". That would not overrwrite the
.git directory, but would cause addurl to fail; of course git won't
add "foo/.git".
sanitizeFilePath is too opinionated to remain in Utility, so moved it.
The changes to mkSafeFilePath are because it used sanitizeFilePath.
In particular:
isDrive will never succeed, because "c:" gets munged to "c_"
".." gets sanitized now
".git" gets sanitized now
It will never be null, because sanitizeFilePath keeps the length
the same, and splitDirectories never returns a null path.
Also, on the off chance a web server suggests a filename of "",
ignore that, rather than trying to save to such a filename, which would
fail in some way.
2020-05-08 20:09:29 +00:00
|
|
|
| isSpace c || isPunctuation c || isSymbol c || isControl c || c == '/' = '_'
|
|
|
|
| otherwise = c
|
|
|
|
|
2020-08-05 15:35:00 +00:00
|
|
|
sanitizeLeadingFilePathCharacter :: String -> FilePath
|
|
|
|
sanitizeLeadingFilePathCharacter [] = "file"
|
|
|
|
sanitizeLeadingFilePathCharacter ('.':s) = '_':s
|
|
|
|
sanitizeLeadingFilePathCharacter ('-':s) = '_':s
|
|
|
|
sanitizeLeadingFilePathCharacter ('/':s) = '_':s
|
|
|
|
sanitizeLeadingFilePathCharacter s = s
|
addurl --preserve-filename and a few related changes
* addurl --preserve-filename: New option, uses server-provided filename
without any sanitization, but with some security checking.
Not yet implemented for remotes other than the web.
* addurl, importfeed: Avoid adding filenames with leading '.', instead
it will be replaced with '_'.
This might be considered a security fix, but a CVE seems unwattanted.
It was possible for addurl to create a dotfile, which could change
behavior of some program. It was also possible for a web server to say
the file name was ".git" or "foo/.git". That would not overrwrite the
.git directory, but would cause addurl to fail; of course git won't
add "foo/.git".
sanitizeFilePath is too opinionated to remain in Utility, so moved it.
The changes to mkSafeFilePath are because it used sanitizeFilePath.
In particular:
isDrive will never succeed, because "c:" gets munged to "c_"
".." gets sanitized now
".git" gets sanitized now
It will never be null, because sanitizeFilePath keeps the length
the same, and splitDirectories never returns a null path.
Also, on the off chance a web server suggests a filename of "",
ignore that, rather than trying to save to such a filename, which would
fail in some way.
2020-05-08 20:09:29 +00:00
|
|
|
|
|
|
|
escapeSequenceInFilePath :: FilePath -> Bool
|
|
|
|
escapeSequenceInFilePath f = '\ESC' `elem` f
|
|
|
|
|
|
|
|
{- ../ is a path traversal, no matter where it appears.
|
|
|
|
-
|
|
|
|
- An absolute path is, of course.
|
|
|
|
-}
|
|
|
|
pathTraversalInFilePath :: FilePath -> Bool
|
|
|
|
pathTraversalInFilePath f
|
|
|
|
| isAbsolute f = True
|
|
|
|
| any (== "..") (splitPath f) = True
|
|
|
|
-- On windows, C:foo with no directory is not considered absolute
|
|
|
|
| hasDrive f = True
|
|
|
|
| otherwise = False
|
|
|
|
|
|
|
|
gitDirectoryInFilePath :: FilePath -> Bool
|
|
|
|
gitDirectoryInFilePath = any (== ".git")
|
|
|
|
. map dropTrailingPathSeparator
|
|
|
|
. splitPath
|