I just got the idea, personally I can’t find a use for it. I was hesitant to use loadstring, so I ended up usingmodulescripts instead.
Made this in about an hour, I wasn’t sure how to do it at first because this is somewhat my first time doing something like this. I’m also not sure how functional this is, considering the only script I tested it on was the TestCode module seen below.
The code:
local sandBox = { };
local proxy = { };
local oldInstance = Instance;
local allowedGlobalEnvironment = {
Instance = {
new = function(className, parent)
return proxy:create(oldInstance.new(className, parent));
end;
};
assert = assert;
collectgarbage = collectgarbage;
error = error;
_G = _G;
gcinfo = gcinfo;
getmetatable = getmetatable;
ipairs = ipairs;
loadstring = loadstring;
newproxy = newproxy;
next = next;
os = os;
pairs = pairs;
pcall = pcall;
print = print;
select = select;
setmetatable = setmetatable;
tonumber = tonumber;
tostring = tostring;
type = type;
unpack = unpack;
_VERSION = _VERSION;
xpcall = xpcall;
Delay, delay = Delay, delay;
ElapsedTime = ElapsedTime;
LoadLibrary = LoadLibrary;
printidentity = printidentity;
require = require;
Spawn = Spawn;
tick = tick;
time = time;
version, Version = version, Version;
wait, Wait = wait, Wait;
warn = warn;
ypcall = ypcall;
};
--============================================================================================--
proxy.Proxies = { };
proxy.BlackList = {
[{
ClassNames = { 'BasePart', 'BaseScript' };
LockedProperties = { 'Parent', 'Name' };
BlockedFunctions = { 'Destroy', 'ClearAllChildren' };
}] = function(self, index)
if type(self[index]) == 'function' then
warn(string.format('Attempted to use %s (a protected function)', self:GetFullName() .. '::' .. index));
else
warn(string.format('Attempted to edit %s (a protected property)', self:GetFullName() .. '.' .. index));
end;
end;
};
function proxy:create(object)
local newObj = newproxy(true);
local newObjMT = getmetatable(newObj);
function newObjMT.__index(self, index)
local value = object[index];
if type(value) == 'userdata' then
return proxy:get(value);
elseif type(value) == 'table' then
local ret = { };
for ind, val in next, value do
ret[ind] = proxy:get(val);
end
return ret;
elseif type(value) == 'function' then
return function(self, ...)
-- Check if we're trying to call a blacklisted method
-- on a specific baseclass. If we are, we need to
-- override it with our own function.
for tbl, overrideFunc in next, proxy.BlackList do
for _, oClass in next, tbl.ClassNames do
for _, fName in next, tbl.BlockedFunctions do
if object:IsA(oClass) and string.lower(fName) == string.lower(index) then
overrideFunc(object, index);
return 0;
end;
end;
end;
end;
return proxy:get(object[index](self, ...));
end;
else
return value;
end
end;
function newObjMT.__newindex(self, index, value)
--LockedProperties
for tbl, overrideFunc in next, proxy.BlackList do
for _, oClass in next, tbl.ClassNames do
for _, pName in next, tbl.LockedProperties do
if object:IsA(oClass) and string.lower(pName) == string.lower(index) then
overrideFunc(object, index);
return 0;
end;
end;
end;
end;
object[index] = proxy:get(value);
end;
newObjMT.__metatable = 'The metatable is locked';
function newObjMT.__tostring(...)
return tostring(object);
end;
proxy.Proxies[object] = newObj;
return newObj;
end
function proxy:get(object)
local got_object = proxy.Proxies[object];
if got_object then
return got_object;
else
if type(object) == 'userdata' then
return proxy:create(object);
else
return object;
end;
end
end
--============================================================================================--
local addToGlobalEnvironment = {
[{ 'Game', 'game' }] = proxy:get(Game);
[{ 'Workspace', 'workspace' }] = proxy:get(Workspace);
[{ 'script' }] = proxy:get(script);
}
for varTbl, val in next, addToGlobalEnvironment do
for _, var in next, varTbl do
allowedGlobalEnvironment[var] = val;
end
end
setmetatable(allowedGlobalEnvironment, {
__index = function(self, index)
local obj = rawget(self, index);
if type(obj) == 'userdata' and proxy.Proxies[obj] then
obj = proxy:get(obj);
end;
return obj;
end;
__newindex = function(self, index, value)
rawset(self, index, proxy:get(value));
end
});
--============================================================================================--
function sandBox:Clean(func)
return setfenv(func, allowedGlobalEnvironment);
end;
function sandBox:Execute(module)
local f = require(module);
sandBox:Clean(f);
local didNotError, stringError = ypcall(f);
return not didNotError and false, stringError or true;
end;
--============================================================================================--
_G.SandBox = sandBox;
local ran, err = _G.SandBox:Execute(script.TestModule);
if not ran then print(err); end;
TestModule code:
return function()
Instance.new(‘Part’):Destroy();
Workspace.BasePlate:Destroy();
Workspace.BasePlate.Name = ‘lel’;
getfenv(print);
end;
The result:
23:21:22.176 - Attempted to use Part::Destroy (a protected function)
23:21:22.177 - Attempted to use Workspace.BasePlate::Destroy (a protected function)
23:21:22.178 - Attempted to edit Workspace.BasePlate.Name (a protected property)
ServerScriptService.TestCode:5: attempt to call global 'getfenv' (a nil value)