diff --git a/Mueval/Context.hs b/Mueval/Context.hs
--- a/Mueval/Context.hs
+++ b/Mueval/Context.hs
@@ -1,6 +1,18 @@
-module Mueval.Context (cleanModules, defaultModules) where
+module Mueval.Context (cleanModules, defaultModules, unsafed) where
 
-import Data.List (elem)
+import Data.List (elem, isInfixOf)
+
+{- | Return true if the String contains anywhere in it any keywords associated
+   with dangerous functions. Unfortunately, this blacklist leaks like a sieve
+   and will return many false positives (eg. unsafed "id \"unsafed\"" -> True). But it
+   will at least catch naive and simplistic invocations of "unsafePerformIO",
+   "inlinePerformIO", and "unsafeCoerce". -}
+unsafed :: String -> Bool
+unsafed = \z -> any (`isInfixOf` z) ["unsafe", "inlinePerform", "liftIO", "Coerce", "Foreign",
+                                    "Typeable", "Array", "IOBase", "Handle", "ByteString",
+                                    "Editline", "GLUT", "lock", "ObjectIO", "System.Time",
+                                    "OpenGL", "Control.Concurrent", "System.Posix",
+                                    "throw", "Dyn", "cache", "stdin", "stdout", "stderr"]
 
 -- | Return false if any of the listed modules cannot be found in the whitelist.
 cleanModules :: [String] -> Bool
diff --git a/Mueval/Interpreter.hs b/Mueval/Interpreter.hs
--- a/Mueval/Interpreter.hs
+++ b/Mueval/Interpreter.hs
@@ -9,6 +9,8 @@
 import Control.Monad.Trans (liftIO)
 import System.Exit (exitWith, ExitCode(ExitFailure))
 
+import qualified Mueval.Resources (limitResources)
+
 say :: String -> Interpreter ()
 say = liftIO . putStr . take 1024
 
@@ -23,6 +25,9 @@
                                                        -- more programs terminate.
                                   reset -- Make sure nothing is available
                                   setImports modules
+
+                                  liftIO Mueval.Resources.limitResources
+
                                   checks <- typeChecks expr
                                   if checks then do
                                               if prt then do say =<< typeOf expr
diff --git a/Mueval/Resources.hs b/Mueval/Resources.hs
--- a/Mueval/Resources.hs
+++ b/Mueval/Resources.hs
@@ -1,6 +1,5 @@
 module Mueval.Resources (limitResources) where
 
-import Control.Monad (zipWithM_)
 import System.Posix.Process (nice)
 import System.Posix.Resource -- (Resource(..), ResourceLimits, setResourceLimit)
 import System.Directory (setCurrentDirectory)
@@ -10,7 +9,7 @@
 limitResources :: IO ()
 limitResources = do setCurrentDirectory "/tmp" -- will at least mess up relative links
                     nice 19 -- Set our process priority way down
-                    zipWithM_ (setResourceLimit) resources limits
+                    mapM_ (uncurry setResourceLimit) limits
 
 -- | Set all the available rlimits.
 --   These values have been determined through trial-and-error
@@ -26,31 +25,23 @@
 -- doesn't seem to be security problem because it'll be opened at the module
 -- stage, before code ever evaluates.
 openFilesLimitSoft = openFilesLimitHard
-openFilesLimitHard = ResourceLimit 7
+openFilesLimitHard = ResourceLimit 8
 fileSizeLimitSoft = fileSizeLimitHard
 fileSizeLimitHard = zero
 dataSizeLimitSoft = dataSizeLimitHard
-dataSizeLimitHard = ResourceLimit $ 5^(12::Int)
+dataSizeLimitHard = ResourceLimit $ 6^(12::Int)
 -- These should not be identical, to give the XCPU handler time to trigger
-cpuTimeLimitSoft = ResourceLimit 3
-cpuTimeLimitHard = ResourceLimit 4
+cpuTimeLimitSoft = ResourceLimit 4
+cpuTimeLimitHard = ResourceLimit 5
 coreSizeLimitSoft = coreSizeLimitHard
 coreSizeLimitHard = zero
 zero = ResourceLimit 0
 
-resources :: [Resource]
-resources = [ResourceStackSize,
-             ResourceTotalMemory,
-             ResourceOpenFiles,
-             ResourceFileSize,
-             ResourceDataSize,
-             ResourceCoreFileSize,
-             ResourceCPUTime]
-limits :: [ResourceLimits]
-limits = [  (ResourceLimits stackSizeLimitSoft stackSizeLimitHard)
-          , (ResourceLimits totalMemoryLimitSoft totalMemoryLimitHard)
-          , (ResourceLimits openFilesLimitSoft openFilesLimitHard)
-          , (ResourceLimits fileSizeLimitSoft fileSizeLimitHard)
-          , (ResourceLimits dataSizeLimitSoft dataSizeLimitHard)
-          , (ResourceLimits coreSizeLimitSoft coreSizeLimitHard)
-          , (ResourceLimits cpuTimeLimitSoft cpuTimeLimitHard)]
+limits :: [(Resource, ResourceLimits)]
+limits = [ (ResourceStackSize,    ResourceLimits stackSizeLimitSoft stackSizeLimitHard)
+         , (ResourceTotalMemory,  ResourceLimits totalMemoryLimitSoft totalMemoryLimitHard)
+         , (ResourceOpenFiles,    ResourceLimits openFilesLimitSoft openFilesLimitHard)
+         , (ResourceFileSize,     ResourceLimits fileSizeLimitSoft fileSizeLimitHard)
+         , (ResourceDataSize,     ResourceLimits dataSizeLimitSoft dataSizeLimitHard)
+         , (ResourceCoreFileSize, ResourceLimits coreSizeLimitSoft coreSizeLimitHard)
+         , (ResourceCPUTime,      ResourceLimits cpuTimeLimitSoft cpuTimeLimitHard)]
diff --git a/mueval.cabal b/mueval.cabal
--- a/mueval.cabal
+++ b/mueval.cabal
@@ -1,5 +1,5 @@
 name:                mueval
-version:             0.3
+version:             0.3.1
 
 license:             BSD3
 license-file:        LICENSE
@@ -9,14 +9,25 @@
 category:            Development, Language
 synopsis:            Safely evaluate Haskell expressions
 description:         Mueval is a Haskell interpreter. It
-                     uses the GHC API to evaluate arbitrary Haskell
-                     expressions. Importantly, mueval takes many precautions
-                     to avoid 'evil' code. It uses resource limits, whitelisted modules,
+                     uses the GHC API to evaluate arbitrary Haskell expressions.
+                     Importantly, mueval takes many precautions to defang and avoid "evil"
+                     code.  It uses resource limits, whitelisted modules,
                      special Show instances for IO, threads, changes of directory, and so
                      on to sandbox the Haskell code. (It is much like Lambdabot's famous
                      evaluation functionality.)
                      .
-                     Mueval is POSIX-only.
+                     Currently there is a major hole in Mueval: it is possible use a function
+                     without importing it, which allows the module whitelisting to be bypassed,
+                     and hence, unsafePerformIO and its various type-breaking friends can be used
+                     to do arbitrary things. Mueval uses a blacklist to avoid the most naive and obvious
+                     dangerous function imports, but this is a very weak mechanism and cannot be relied
+                     upon.
+                     .
+                     Until this hole is fixed, Mueval should *not* be used on potentially hostile input.
+                     .
+                     Mueval is currently POSIX-only.
+homepage:            http://code.haskell.org/mubot/
+
 build-type:          Simple
 Cabal-Version:       >= 1.2
 Tested-with:         GHC==6.8.2
@@ -26,7 +37,7 @@
 Library
         exposed-modules:     Mueval.Context, Mueval.Interpreter,
                              Mueval.ParseArgs, Mueval.Resources
-        build-Depends:       base, directory, mtl, unix, hint>=0.2.1, show
+        build-Depends:       base, directory, mtl, unix, hint>=0.2.2, show
         ghc-options:         -Wall -static
 
 Executable mueval
diff --git a/mueval.hs b/mueval.hs
--- a/mueval.hs
+++ b/mueval.hs
@@ -12,24 +12,24 @@
 import System.IO (hSetBuffering, stdout, BufferMode(NoBuffering))
 import System.Posix.Signals (sigXCPU, installHandler, Handler(CatchOnce))
 
-import qualified Mueval.Context (cleanModules)
+import qualified Mueval.Context (cleanModules, unsafed)
 import Mueval.Interpreter
 import Mueval.ParseArgs
-import qualified Mueval.Resources (limitResources)
 
 main :: IO ()
 main = do input <- getArgs
           (opts,_) <- interpreterOpts input
           if (Mueval.Context.cleanModules $ modules opts) then do
-              mvar <- newEmptyMVar
+              if (not $ Mueval.Context.unsafed $ expression opts) then do
+                                               mvar <- newEmptyMVar
 
-              Mueval.Resources.limitResources
-              myThreadId >>= watchDog (timeLimit opts)
+                                               myThreadId >>= watchDog (timeLimit opts)
 
-              forkIO $ forkedMain (mvar) opts
-              takeMVar mvar -- block until a ErrorCall or the forkedMain succeeds
+                                               forkIO $ forkedMain (mvar) opts
+                                               takeMVar mvar -- block until a ErrorCall or the forkedMain succeeds
 
-              return ()
+                                               return ()
+               else error "Unsafe functions to use mentioned."
            else error "Unknown or untrusted module supplied! Aborting."
 
 -- Set a watchdog, and then evaluate.
diff --git a/tests.sh b/tests.sh
--- a/tests.sh
+++ b/tests.sh
@@ -39,6 +39,9 @@
 mu --module Data.List --module System.IO.Unsafe --module Control.Monad --expression 1+1
 mu --module System.IO.Unsafe --expression "let foo = unsafePerformIO readFile \"/etc/passwd\" in foo"
 mu --module Data.List --module Text.HTML.Download --expression "head [1..]"
+### Can we bypass the whitelisting by fully qualified module names?
+mu --expression "Foreign.unsafePerformIO $ readFile \"/etc/passwd\""
+mu --expression "Data.ByteString.Internal.inlinePerformIO $ readFile \"/etc/passwd\""
 ## We need a bunch of IO tests, but I guess this will do for now.
 mu --expression "let foo = readFile \"/etc/passwd\" >>= print in foo"
 mu --expression "writeFile \"tmp.txt\" \"foo bar\""
