Created
May 2, 2020 02:01
-
-
Save watsuprico/3503587e9e0cb2e391da2d2919a91a8a to your computer and use it in GitHub Desktop.
ComputerCraft Environment Sandbox example. Places the 'user' into a seperate clone of the global environment. They can edit 'non-protected' values, but these edits are saved into a new 'usersapce.environment' table (therefore the user can only effect their environment). Optional read-only 'protected' environment available to prevent user changes…
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local native_rawset = rawset | |
local native_rawget = rawget | |
local Userspace = { -- Session data | |
protected = { -- User can NOT override these | |
test = "Protected", | |
username="abba", | |
userID="1001", | |
logout=function() return "logOut" end | |
}, | |
environment = { -- Whenever a modification to the global environment is made by the user, we save the changes here | |
}, | |
parentEnvironment = {}, -- The environment we inherit | |
Windows = {}, -- The window the user lives in | |
Tasks = {} -- All programs / services | |
} | |
-- Required: removal responds in error("Try to write to read only table",2) | |
Userspace.environment._ENV = Userspace.environment | |
Userspace.environment._G = Userspace.environment | |
--[[ | |
This chart shows where we grab a value (if it exists there) | |
1 -=> Read only environment | |
2 -=> environment | |
3 -=> parentEvironment (not a table) | |
3 -=> copy from parentEnvironment to environment, environment | |
]] | |
local mt = { | |
-- __tostring = "SandBoxed Environment", | |
__metatable = 'Attempt to get protected metatable', | |
__newindex = function(self, key, value) -- Write | |
-- write("write key: " .. key.." - val: ") print(value) | |
if Userspace.protected[key] == nil then -- Only allow none protected values | |
native_rawset(Userspace.environment,key,value) | |
end | |
end | |
} | |
mt.__index = function (self, key) -- Read | |
local data = {} | |
-- ~| Return read only value | |
-- ~| Return environment value | |
-- ~| Return parent value | |
-- print("Read key: " .. key) | |
if key == "_G" or key == "_ENV" then | |
-- print("Read global (" .. key..")") | |
return Userspace.environment -- Global fix | |
elseif Userspace.protected[key] ~= nil then | |
-- print("Read protected key: " .. key) | |
return Userspace.protected[key] -- Read only value | |
elseif native_rawget(Userspace.environment,key) ~= nil then | |
-- print("Read key: " .. key) | |
return native_rawget(Userspace.environment,key) -- Edited value | |
else | |
if Userspace.parentEnvironment[key] ~= nil then | |
-- print("Copy key: " .. key) | |
native_rawset(Userspace.environment, key, copy(Userspace.parentEnvironment[key])) -- So we can't edit global stuff (copies what we need) | |
return native_rawget(Userspace.environment,key) | |
end | |
-- print("Return parent: " .. key) | |
return Userspace.parentEnvironment[key] -- Grab from parent | |
end | |
end | |
-- Protect | |
_G.rawset = function (tab, key, value) | |
if tab==_G or tab==_ENV or key=="_G" or key=="_ENV" then error('Try to write to read only table', 2) end | |
return native_rawset(tab, key, value) | |
end | |
-- Clone table | |
function copy(obj, seen) | |
if type(obj) ~= 'table' then return obj end | |
if seen and seen[obj] then return seen[obj] end | |
local s = seen or {} | |
local res = setmetatable({}, getmetatable(obj)) | |
s[obj] = res | |
for k, v in pairs(obj) do | |
if k~="_G" or k~="_ENV" then | |
res[copy(k, s)] = copy(v, s) | |
end | |
end | |
return res | |
end | |
-- For sandboxed environment | |
-- Fix for (i)pairs | |
--[[ | |
I used to in testing by running my debug program 'getMemDump' | |
This program goes through all keys in the global variable space (_G) and outputs their values to a file | |
(Used this to see WHAT we have access to) | |
So, you can remove the 3 functions below. | |
]] | |
local getCombinedTable = function() | |
local nT = {} | |
nT = copy(Userspace.protected) -- Create a separate (sanitized) clone | |
for k,v in pairs(Userspace.environment) do | |
if Userspace.protected[k] == nil then | |
nT[k] = v | |
end | |
end | |
return nT | |
end | |
Userspace.environment.pairs=function(table) | |
if table == Userspace.environment then | |
return pairs(getCombinedTable()) | |
end | |
return pairs(table) | |
end | |
Userspace.environment.ipairs=function(table) | |
if table == Userspace.environment then | |
return ipairs(getCombinedTable()) | |
end | |
return ipairs(table) | |
end | |
-- Prevent escape | |
Userspace.environment.dofile = function( _sFile ) | |
local fnFile, e = loadfile( _sFile, Userspace.environment ) -- Original dofile() sets the environment to _G on the BIOS level (ultimate _G) | |
if fnFile then | |
return fnFile() | |
else | |
error( e, 2 ) | |
end | |
end | |
Userspace.environment.os = {} | |
Userspace.environment.os.run = function( _tEnv, _sPath, ... ) | |
local tArgs = { ... } | |
local tEnv = _tEnv | |
setmetatable( tEnv, { __index = Userspace.environment } ) -- Original os.run sets the index to _G on the BIOS level | |
local fnFile, err = loadfile( _sPath, tEnv ) | |
if fnFile then | |
local ok, err = pcall( function() | |
fnFile( table.unpack( tArgs ) ) | |
end ) | |
if not ok then | |
if err and err ~= "" then | |
printError( err ) | |
end | |
return false | |
end | |
return true | |
end | |
if err and err ~= "" then | |
printError( err ) | |
end | |
return false | |
end | |
Userspace.environment.getmetatable = function() -- Set these to block request out | |
end | |
Userspace.environment.getfenv = function() | |
end | |
setmetatable(Userspace.environment, mt) -- 'Protect' environment table (I.E enable sandbox) | Sent the program's environment TO Userspace.environment (setfenv) | |
Userspace.parentEnvironment = _G -- Set to whatever | |
local a = { -- for testing | |
table=table, | |
fs=fs, | |
pairs=pairs, | |
ipairs=ipairs, | |
string=string, | |
type=type, | |
tostring=tostring, | |
print = print, | |
write = write, | |
dofile=dofile, | |
} | |
local fnFile,e = loadfile("lua",Userspace.environment) | |
-- setfenv(a,SandBoxed) | |
if not fnFile then | |
print("Err: "..e) | |
else | |
fnFile() | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment