- snip -
Haha, never thought of that. Easy to fix (afaik). Override setmetatable? BTW, are you ACTUALLY using this? If so, thats freaking awesome!
I AM Actually Using this, In larger programs I like having parameters that people can call for default actions, like if you pass 32 into a function it does something useful, but instead of having the person guess or look at docs for the number you can do api.def_actions.SOMETHING_USEFUL, which in most cases (in my experience) is better than just having the number, this is even better for Rednet Messages for your programs. net.commands.RESTART which is shorter and easier to remember than "MNET_RESTART_COMP" which is usually how I format my Rednet and Modem messages for API specific actions.
And you wont need to override setmetatable or getmetatable.
I tested __metatable and it does work in as early as CC 1.57 (my version). So literally to secure it all you have to do is this block of code (Again, Tested on CC 1.57)
local nativeRawset = _G.rawset
local registered_ids = {}
local direction = {["north"] = 1, ["east"] = 2, ["south"] = 3, ["west"] = 4} -- Demonstration Code
math.randomseed(os.clock()) -- Ensure Random-ness
local function getUID()
local template = "xyxx-yxxx-yxxyyyxx"
local uid = string.gsub(template, '[xy]', function(c)
local v = (c == 'x') and math.random(0, 0xf) or math.random(8, 0xb);
return string.format('%x', v);
end)
table.insert(registered_ids, uid)
return uid
end
end
function rawset(tab, key, val)
local uid = t.__uid -- Just to indicate its a "Meta Value"... kinda
if uid then
for i=1, #registered_ids do
if(registered_ids[i] == uid) then
error("Can Not Modify a Readonly Table!", 2)
end
end
end
return nativeRawset(tab, key, val)
end
function protect(tab)
local id = getUID()
return setmetatable({}, {
__index = function(t, k)
if k == "__uid" then
return id
end
return rawget(t, k)
end,
__newindex = function(t, k, v)
error("Can Not Modify a Readonly Table!", 2)
end,
__metatable = false
})
end
-- Demonstration Code --
local function assign(t, k, v)
t[k] = v
end
direction = protect(direction)
print(direction.north)
-- Attempt to Modify Table via Assignment
-- If Fails, Print Message
ok, msg = pcall(assign, direction, "north", 6)
if not ok then print(msg) end
-- Attempt to Modify Table using Rawset
-- If fails, print message
ok, msg = pcall(rawset, direction, "north", 6)
if not ok then print(msg) end
-- Demonstration Code --
Most of this functions in the same way as your code, But I streamlined the
protect function a bit. It instead will return a proxy table that does not contain ANY information itself. This prevents us from having to make a whole other copy to hold in memory, this proxy table is just an empty table. __index = function(t, k)[indent=1]
if k == "__uid" return id end[/indent]
[indent=1]
return rawget(t, k)[/indent]
endThis snippet will return the id if it is asked for and returns the local
id we created. The way you were doing it before made it easy for someone to change the uid, or just assign one since we were only checking for existence, I am just super secure :P/> So I have a code snippet of a UUID Generator that is a nice String UID that is extremely difficult to replicate, and now they can't even grab one. Also the UID has to be contained in a table.__newindex has not changed.__metatable = falseThis line means two things. If
getmetatable(table) is called, the value of __metatable is returned, so instead of the actually metatable, the caller gets the boolean value, false. If setmetatable(table, metatable) is called, an error is thrown saying that this table is a protected table and so the metatable can not be modified. This code also allows us to use other tables in large programs that contain a
__uid key, but is not a protected table.This is the most secure method I know of and I use for things (it can be overkill in many cases). It uses a highly random Unique Identifier, can not be hacked into in anyway, and uses arguably less memory than just copying a table. There could be better methods, but this is what I use. It's 4 in the morning again so I may have confused some words and that demonstration most likely works but this method of securing tables is useful for use in Internal's and Enumeration's and this method is very secure. If you have any questions about the code I will be happy to answer, it's 4 in the morning and I want to sleep :P/> so I may have left some important details out.
Also Note: The method I am using DOES break using
#protected_table (uses proxy table so returns 0) and ipairs/pairs (attempts to iterate through empty proxy table). My method is ENTIRELY FOR STATIC ENUMERATIONS not dynamic lists that need iteration! in this case YOU CAN modify my method and combine it with sci4me's method and you will be able to get length and pairs/ipairs and all that. It sort of is application specific and you need to decide what is best. Also Read More at
http://lua-users.org/wiki/ReadOnlyTablesEdit: Formatting