Skip to content

Instantly share code, notes, and snippets.

@watsuprico
Created May 2, 2020 02:01
Show Gist options
  • Save watsuprico/3503587e9e0cb2e391da2d2919a91a8a to your computer and use it in GitHub Desktop.
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…
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