@@ -60,16 +60,18 @@ import Control.Monad.Trans (MonadIO(liftIO))
6060import qualified Data.ByteString.Lazy.Char8 as L
6161import qualified Data.ByteString.Char8 as S
6262import Data.Data (Data , Typeable )
63+ import Data.Foldable (toList )
6364import Data.List (sort )
6465import Data.Maybe (fromMaybe )
6566import Data.Map (Map )
6667import qualified Data.Map as Map
67- import Filesystem.Path.CurrentOS (commonPrefix , encodeString , decodeString , collapse , append )
68+ import Data.Sequence (Seq ((:|>) ), (|>) )
69+ import qualified Data.Sequence as Seq
6870import Happstack.Server.Monads (ServerMonad (askRq ), FilterMonad , WebMonad )
6971import Happstack.Server.Response (ToMessage (toResponse ), ifModifiedSince , forbidden , ok , seeOther )
7072import Happstack.Server.Types (Length (ContentLength ), Request (rqPaths , rqUri ), Response (SendFile ), RsFlags (rsfLength ), nullRsFlags , result , resultBS , setHeader )
7173import System.Directory (doesDirectoryExist , doesFileExist , getDirectoryContents , getModificationTime )
72- import System.FilePath ((</>) , addTrailingPathSeparator , hasDrive , isPathSeparator , joinPath , takeExtension , isValid )
74+ import System.FilePath ((</>) , addTrailingPathSeparator , hasDrive , isPathSeparator , joinPath , takeExtension , isValid , normalise , splitPath )
7375import System.IO (IOMode (ReadMode ), hFileSize , hClose , openBinaryFile , withBinaryFile )
7476import System.Log.Logger (Priority (DEBUG ), logM )
7577import Text.Blaze.Html ((!) )
@@ -384,14 +386,22 @@ fileServe' serveFn mimeFn indexFn localPath = do
384386-- >>> combineSafe "/var/uploads/" "../uploads/home/../etc/passwd"
385387-- Just "/var/uploads/etc/passwd"
386388combineSafe :: FilePath -> FilePath -> Maybe FilePath
387- combineSafe root path =
388- if commonPrefix [root', joined] == root'
389- then Just $ encodeString joined
390- else Nothing
389+ combineSafe root path = do
390+ pathSeq <- go Seq. empty $ splitPath $ normalise path
391+ let root' = normalise root
392+ let path' = joinPath (toList pathSeq)
393+ Just $ root' </> path'
391394 where
392- root' = decodeString root
393- path' = decodeString path
394- joined = collapse $ append root' path'
395+ -- | Build up a 'Seq' representation of @path@, reducing any @..@ elements and returning
396+ -- @'Nothing'@ if it tries to go up beyond its top level.
397+ --
398+ -- Note that this functionality has been removed from the filepath package
399+ -- See: <https://neilmitchell.blogspot.com/2015/10/filepaths-are-subtle-symlinks-are-hard.html>
400+ go :: Seq FilePath -> [FilePath ] -> Maybe (Seq FilePath )
401+ go p [] = Just p
402+ go Seq. Empty (" .." : _ ) = Nothing -- Going up beyond the top level is not allowed
403+ go (s :|> _) (" .." : ps) = go s ps -- Going up a level pops an element off the right side of the Seq
404+ go s (p : ps) = go (s |> p) ps -- Just add an element to the right side of the Seq
395405
396406isSafePath :: [FilePath ] -> Bool
397407isSafePath [] = True
0 commit comments