on master help sign in
Merge pull request #584 from obsidiansystems/use-posix-escape

Start using more robust shell escaping

commitd97c3ad95fa02c12ec2e2dde544ae8c43d60f9b8 authorElliot Cameron <3noch@users.noreply.github.com> dateThu 14 May 2020, 14:26 (6 years ago) parent34548d131e782a
6 files changed+32−21
lib/cliapp/obelisk-cliapp.cabal+2 lib/cliapp/src/Obelisk/CliApp/Process.hs+4−2 lib/command/obelisk-command.cabal+2 lib/command/src/Obelisk/Command.hs+2−2 lib/command/src/Obelisk/Command/Project.hs+11−5 lib/command/src/Obelisk/Command/Run.hs+11−12
lib/cliapp/obelisk-cliapp.cabal+2
@@ -19,10 +19,12 @@ library
1919 , monad-loops
2020 , mtl
2121 , process
22+ , shell-escape
2223 , terminal-size
2324 , text
2425 , time
2526 , transformers
27+ , utf8-string
2628 exposed-modules:
2729 Obelisk.CliApp
2830 other-modules:
lib/cliapp/src/Obelisk/CliApp/Process.hs+4−2
@@ -38,6 +38,7 @@ import Control.Monad.IO.Class (MonadIO, liftIO)
3838import Control.Lens (Prism', review)
3939import qualified Data.ByteString as BS
4040import qualified Data.ByteString.Char8 as BSC
41+import qualified Data.ByteString.UTF8 as BSU
4142import Data.Function (fix)
4243import Data.Map (Map)
4344import qualified Data.Map as Map
@@ -54,6 +55,7 @@ import qualified System.IO.Streams as Streams
5455import System.IO.Streams.Concurrent (concurrentMerge)
5556import System.Process (CreateProcess, ProcessHandle, StdStream (CreatePipe), std_err, std_out)
5657import qualified System.Process as Process
58+import Text.ShellEscape (bash, bytes)
5759import qualified Data.Aeson as Aeson
5860
5961import Control.Monad.Log (Severity (..))
@@ -246,8 +248,8 @@ reconstructCommand p = case p of
246248 Process.ShellCommand str -> T.pack str
247249 Process.RawCommand c as -> processToShellString c as
248250 where
249− processToShellString cmd args = T.unwords $ map quoteAndEscape (cmd : args)
250− quoteAndEscape x = "'" <> T.replace "'" "'\''" (T.pack x) <> "'"
251+ processToShellString cmd args = T.pack $ unwords $
252+ map (BSU.toString . bytes . bash . BSU.fromString) (cmd : args)
251253
252254reconstructProcSpec :: ProcessSpec -> Text
253255reconstructProcSpec = reconstructCommand . Process.cmdspec . _processSpec_createProcess
lib/command/obelisk-command.cabal+2
@@ -39,6 +39,7 @@ library
3939 , optparse-applicative
4040 , placeholders
4141 , process
42+ , shell-escape
4243 , temporary
4344 , terminal-size
4445 , text
@@ -46,6 +47,7 @@ library
4647 , transformers
4748 , unix
4849 , unordered-containers
50+ , utf8-string
4951 , which
5052 , yaml
5153 exposed-modules:
lib/command/src/Obelisk/Command.hs+2−2
@@ -398,11 +398,11 @@ ob = \case
398398 ObCommand_Repl interpretPathsList -> withInterpretPaths interpretPathsList runRepl
399399 ObCommand_Watch interpretPathsList -> withInterpretPaths interpretPathsList runWatch
400400 ObCommand_Shell (ShellOpts shellAttr interpretPathsList cmd) -> withInterpretPaths interpretPathsList $ \root interpretPaths ->
401− nixShellForInterpretPaths False shellAttr root interpretPaths cmd
401+ nixShellForInterpretPaths False shellAttr root interpretPaths (bashEscape <$> cmd)
402402 ObCommand_Doc shellAttr pkgs -> withInterpretPaths [] $ \root interpretPaths ->
403403 nixShellForInterpretPaths True shellAttr root interpretPaths $ Just $ haddockCommand pkgs
404404 ObCommand_Hoogle shell' port -> withProjectRoot "." $ \root -> do
405− nixShellWithHoogle root True shell' $ Just $ "hoogle server -p " <> show port <> " --local"
405+ nixShellWithHoogle root True shell' $ Just $ "hoogle server -p" <> show port <> " --local"
406406 ObCommand_Internal icmd -> case icmd of
407407 ObInternal_ApplyPackages origPath inPath outPath packagePaths -> do
408408 liftIO $ Preprocessor.applyPackages origPath inPath outPath packagePaths
lib/command/src/Obelisk/Command/Project.hs+11−5
@@ -15,6 +15,7 @@ module Obelisk.Command.Project
1515 , toImplDir
1616 , toObeliskDir
1717 , withProjectRoot
18+ , bashEscape
1819 ) where
1920
2021import Control.Concurrent.MVar (MVar, newMVar, withMVarMasked)
@@ -23,6 +24,7 @@ import Control.Monad
2324import Control.Monad.IO.Class (liftIO)
2425import Control.Monad.State
2526import qualified Data.Aeson as Json
27+import qualified Data.ByteString.UTF8 as BSU
2628import Data.Bits
2729import qualified Data.ByteString.Lazy as BSL
2830import Data.Default (def)
@@ -30,7 +32,7 @@ import Data.Function ((&), on)
3032import Data.Map (Map)
3133import Data.Text (Text)
3234import qualified Data.Text as T
33−import Data.Text.Encoding (decodeUtf8)
35+import Data.Text.Encoding (decodeUtf8, encodeUtf8)
3436import Data.Traversable (for)
3537import System.Directory
3638import System.Environment (lookupEnv)
@@ -39,6 +41,7 @@ import System.IO.Temp
3941import System.IO.Unsafe (unsafePerformIO)
4042import System.Posix (FileStatus, FileMode, CMode (..), UserID, deviceID, fileID, fileMode, fileOwner, getFileStatus, getRealUserID)
4143import System.Posix.Files
44+import Text.ShellEscape (bash, bytes)
4245
4346import GitHub.Data.GitData (Branch)
4447import GitHub.Data.Name (Name)
@@ -278,12 +281,15 @@ nixShellRunConfig root isPure command = do
278281 pure $ def
279282 & nixShellConfig_pure .~ isPure
280283 & nixShellConfig_common . nixCmdConfig_target .~ (def & target_path .~ Nothing)
281− & nixShellConfig_run .~ (command <&> \c -> mconcat
282− [ "export NIX_PATH=nixpkgs=", T.unpack nixpkgsPath, "; "
283− , maybe "" (\v -> "export NIX_REMOTE=" <> v <> "; ") nixRemote
284− , c
284+ & nixShellConfig_run .~ (command <&> \cs -> unwords $ concat
285+ [ ["export", BSU.toString . bytes . bash $ "NIX_PATH=nixpkgs=" <> encodeUtf8 nixpkgsPath, ";"]
286+ , maybe [] (\v -> ["export", BSU.toString . bytes . bash $ "NIX_REMOTE=" <> encodeUtf8 (T.pack v), ";"]) nixRemote
287+ , [cs]
285288 ])
286289
290+bashEscape :: String -> String
291+bashEscape = BSU.toString . bytes . bash . BSU.fromString
292+
287293nixShellRunProc :: NixShellConfig -> ProcessSpec
288294nixShellRunProc cfg = setDelegateCtlc True $ proc "nix-shell" $ runNixShellConfig cfg
289295
lib/command/src/Obelisk/Command/Run.hs+11−12
@@ -78,7 +78,7 @@ import Obelisk.CliApp (
7878 withSpinner,
7979 )
8080import Obelisk.Command.Nix
81−import Obelisk.Command.Project (nixShellWithoutPkgs, withProjectRoot, findProjectAssets)
81+import Obelisk.Command.Project (nixShellWithoutPkgs, withProjectRoot, findProjectAssets, bashEscape)
8282import Obelisk.Command.Thunk (attrCacheFileName)
8383import Obelisk.Command.Utils (findExePath, ghcidExePath)
8484
@@ -466,7 +466,7 @@ runGhciRepl root (toList -> packages) ghciArgs =
466466 -- NOTE: We do *not* want to use $(staticWhich "ghci") here because we need the
467467 -- ghc that is provided by the shell in the user's project.
468468 nixShellWithoutPkgs root True False (packageInfoToNamePathMap packages) "ghc" $
469− Just $ unwords $ "ghci" : ghciArgs -- TODO: Shell escape
469+ Just $ unwords $ fmap bashEscape $ "ghci" : ghciArgs
470470
471471-- | Run ghcid
472472runGhcid
@@ -479,17 +479,16 @@ runGhcid
479479 -> m ()
480480runGhcid root chdirToRoot ghciArgs (toList -> packages) mcmd =
481481 nixShellWithoutPkgs root True chdirToRoot (packageInfoToNamePathMap packages) "ghc" $
482− Just $ unwords $ ghcidExePath : opts -- TODO: Shell escape
482+ Just $ unwords $ fmap bashEscape $ ghcidExePath : opts
483483 where
484− opts =
485− [ "-W"
486− , "--outputfile=ghcid-output.txt"
487− ] <> map (\x -> "--reload='" <> x <> "'") reloadFiles
488− <> map (\x -> "--restart='" <> x <> "'") restartFiles
489− <> testCmd
490− <> ["--command='" <> unwords ("ghci" : ghciArgs) <> "'"] -- TODO: Shell escape
491− testCmd = maybeToList (flip fmap mcmd $ \cmd -> "--test='" <> cmd <> "'") -- TODO: Shell escape
492−
484+ opts = concat
485+ [ ["-W"]
486+ , ["--outputfile=ghcid-output.txt"]
487+ , map (\x -> "--reload=" <> x) reloadFiles
488+ , map (\x -> "--restart=" <> x) restartFiles
489+ , maybe [] (\cmd -> ["--test=" <> cmd]) mcmd
490+ , ["--command=" <> unwords ("ghci" : ghciArgs)]
491+ ]
493492 adjustRoot x = if chdirToRoot then makeRelative root x else x
494493 reloadFiles = map adjustRoot [root </> "config"]
495494 restartFiles = map (adjustRoot . _cabalPackageInfo_packageFile) packages