So i'm trying to improve my sandbox a little bit at a time, and i think the first thing i need to fix is getfenv(). So, here's my "sandbox" so far:

function saferun(func)
	setfenv(func,setmetatable({},{
		__index = function(t,k)
			if k == 'getfenv' then -- if getfenv is indexed, then...
				return -- don't know yet
			else
				return getfenv()[k] -- if not, just return it from the old environment.
			end
		end
	}))() -- run the function
end

saferun(function()
	print(getfenv) -- > nil
end)

However, i don't want to just disable the ability to use getfenv, it's just so easy to bypass the sandbox with it. For example, if i said something like:

var = 'protected'

function saferun(func)
	setfenv(func,setmetatable({},{
		__index = function(t,k)
			if k == 'var' then -- blocking "var" instead of getfenv
				return 'blocked'
			else
				return getfenv()[k]
			end
		end
	}))() -- run the function
end

saferun(function()

	print(var)  -- > "blocked"

	-- But using getfenv, i can easily bypass the sandbox like this:

	setfenv(1,getfenv(print))

	print(var) -- > "protected"

end)

So, i know the problem is with getfenv, i just have no idea how i could attempt to modify it. I've tried a few ways, none in which where close to succeeding:

var = 'nope'

function saferun(func)
	setfenv(func,setmetatable({},{
		__index = function(t,k)
			if k == 'getfenv' then
				return function(f) -- returns this function instead of getfenv
					local env = {}
					for i,v in pairs (getfenv(f)) do
						env[i]=v
					end
					return env
				end
			elseif k == 'var' then
				return 'blocked'
			else
				return getfenv()[k]
			end
		end
	}))() -- run the function
end

saferun(function()
	setfenv(1,getfenv(print)) -- bypass
	print(var) -- prints "nope" instead of "blocked".
end)

So if anyone has some insight on how it would be possible to modify getfenv, or maybe even another way of preventing this, i'd really appreciate it. Thanks.