Fix item duplication vulnerability

This commit is contained in:
micheal65536 2018-02-11 12:33:46 +00:00 committed by stujones11
parent 7226dd6174
commit 59b26b37f9
3 changed files with 81 additions and 26 deletions

@ -68,6 +68,9 @@ armor_fire_protect = false
-- Enable punch damage effects. -- Enable punch damage effects.
armor_punch_damage = true armor_punch_damage = true
-- Enable migration of old armor inventories
armor_migrate_old_inventory = true
API API
--- ---

@ -72,6 +72,7 @@ armor = {
on_damage = {}, on_damage = {},
on_destroy = {}, on_destroy = {},
}, },
migrate_old_inventory = true,
version = "0.4.10", version = "0.4.10",
} }
@ -174,7 +175,7 @@ armor.update_player_visuals = function(self, player)
end end
armor.set_player_armor = function(self, player) armor.set_player_armor = function(self, player)
local name, player_inv = self:get_valid_player(player, "[set_player_armor]") local name, armor_inv = self:get_valid_player(player, "[set_player_armor]")
if not name then if not name then
return return
end end
@ -199,7 +200,7 @@ armor.set_player_armor = function(self, player)
change[group] = 1 change[group] = 1
levels[group] = 0 levels[group] = 0
end end
local list = player_inv:get_list("armor") local list = armor_inv:get_list("armor")
if type(list) ~= "table" then if type(list) ~= "table" then
return return
end end
@ -297,7 +298,7 @@ armor.set_player_armor = function(self, player)
end end
armor.punch = function(self, player, hitter, time_from_last_punch, tool_capabilities) armor.punch = function(self, player, hitter, time_from_last_punch, tool_capabilities)
local name, player_inv = self:get_valid_player(player, "[punch]") local name, armor_inv = self:get_valid_player(player, "[punch]")
if not name then if not name then
return return
end end
@ -305,7 +306,7 @@ armor.punch = function(self, player, hitter, time_from_last_punch, tool_capabili
local count = 0 local count = 0
local recip = true local recip = true
local default_groups = {cracky=3, snappy=3, choppy=3, crumbly=3, level=1} local default_groups = {cracky=3, snappy=3, choppy=3, crumbly=3, level=1}
local list = player_inv:get_list("armor") local list = armor_inv:get_list("armor")
for i, stack in pairs(list) do for i, stack in pairs(list) do
if stack:get_count() == 1 then if stack:get_count() == 1 then
local name = stack:get_name() local name = stack:get_name()
@ -427,6 +428,57 @@ armor.get_armor_formspec = function(self, name, listring)
return formspec return formspec
end end
armor.serialize_inventory_list = function(self, list)
local list_table = {}
for _, stack in ipairs(list) do
table.insert(list_table, stack:to_string())
end
return minetest.serialize(list_table)
end
armor.deserialize_inventory_list = function(self, list_string)
local list_table = minetest.deserialize(list_string)
local list = {}
for _, stack in ipairs(list_table or {}) do
table.insert(list, ItemStack(stack))
end
return list
end
armor.load_armor_inventory = function(self, player)
local msg = "[load_armor_inventory]"
local name = player:get_player_name()
if not name then
minetest.log("warning", S("3d_armor: Player name is nil @1", msg))
return
end
local armor_inv = minetest.get_inventory({type="detached", name=name.."_armor"})
if not armor_inv then
minetest.log("warning", S("3d_armor: Detached armor inventory is nil @1", msg))
return
end
local armor_list_string = player:get_attribute("3d_armor_inventory")
if armor_list_string then
armor_inv:set_list("armor", self:deserialize_inventory_list(armor_list_string))
return true
end
end
armor.save_armor_inventory = function(self, player)
local msg = "[save_armor_inventory]"
local name = player:get_player_name()
if not name then
minetest.log("warning", S("3d_armor: Player name is nil @1", msg))
return
end
local armor_inv = minetest.get_inventory({type="detached", name=name.."_armor"})
if not armor_inv then
minetest.log("warning", S("3d_armor: Detached armor inventory is nil @1", msg))
return
end
player:set_attribute("3d_armor_inventory", self:serialize_inventory_list(armor_inv:get_list("armor")))
end
armor.update_inventory = function(self, player) armor.update_inventory = function(self, player)
-- DEPRECATED: Legacy inventory support -- DEPRECATED: Legacy inventory support
end end
@ -438,17 +490,13 @@ armor.set_inventory_stack = function(self, player, i, stack)
minetest.log("warning", S("3d_armor: Player name is nil @1", msg)) minetest.log("warning", S("3d_armor: Player name is nil @1", msg))
return return
end end
local player_inv = player:get_inventory()
local armor_inv = minetest.get_inventory({type="detached", name=name.."_armor"}) local armor_inv = minetest.get_inventory({type="detached", name=name.."_armor"})
if not player_inv then if not armor_inv then
minetest.log("warning", S("3d_armor: Player inventory is nil @1", msg))
return
elseif not armor_inv then
minetest.log("warning", S("3d_armor: Detached armor inventory is nil @1", msg)) minetest.log("warning", S("3d_armor: Detached armor inventory is nil @1", msg))
return return
end end
player_inv:set_stack("armor", i, stack)
armor_inv:set_stack("armor", i, stack) armor_inv:set_stack("armor", i, stack)
self:save_armor_inventory(player)
end end
armor.get_valid_player = function(self, player, msg) armor.get_valid_player = function(self, player, msg)
@ -462,9 +510,9 @@ armor.get_valid_player = function(self, player, msg)
minetest.log("warning", S("3d_armor: Player name is nil @1", msg)) minetest.log("warning", S("3d_armor: Player name is nil @1", msg))
return return
end end
local inv = player:get_inventory() local inv = minetest.get_inventory({type="detached", name=name.."_armor"})
if not inv then if not inv then
minetest.log("warning", S("3d_armor: Player inventory is nil @1", msg)) minetest.log("warning", S("3d_armor: Detached armor inventory is nil @1", msg))
return return
end end
return name, inv return name, inv

@ -111,27 +111,23 @@ end)
local function init_player_armor(player) local function init_player_armor(player)
local name = player:get_player_name() local name = player:get_player_name()
local player_inv = player:get_inventory()
local pos = player:getpos() local pos = player:getpos()
if not name or not player_inv or not pos then if not name or not pos then
return false return false
end end
local armor_inv = minetest.create_detached_inventory(name.."_armor", { local armor_inv = minetest.create_detached_inventory(name.."_armor", {
on_put = function(inv, listname, index, stack, player) on_put = function(inv, listname, index, stack, player)
player:get_inventory():set_stack(listname, index, stack) armor:save_armor_inventory(player)
armor:run_callbacks("on_equip", player, index, stack) armor:run_callbacks("on_equip", player, index, stack)
armor:set_player_armor(player) armor:set_player_armor(player)
end, end,
on_take = function(inv, listname, index, stack, player) on_take = function(inv, listname, index, stack, player)
player:get_inventory():set_stack(listname, index, nil) armor:save_armor_inventory(player)
armor:run_callbacks("on_unequip", player, index, stack) armor:run_callbacks("on_unequip", player, index, stack)
armor:set_player_armor(player) armor:set_player_armor(player)
end, end,
on_move = function(inv, from_list, from_index, to_list, to_index, count, player) on_move = function(inv, from_list, from_index, to_list, to_index, count, player)
local plaver_inv = player:get_inventory() armor:save_armor_inventory(player)
local stack = inv:get_stack(to_list, to_index)
player_inv:set_stack(to_list, to_index, stack)
player_inv:set_stack(from_list, from_index, nil)
armor:set_player_armor(player) armor:set_player_armor(player)
end, end,
allow_put = function(inv, listname, index, stack, player) allow_put = function(inv, listname, index, stack, player)
@ -158,10 +154,18 @@ local function init_player_armor(player)
end, end,
}, name) }, name)
armor_inv:set_size("armor", 6) armor_inv:set_size("armor", 6)
if not armor:load_armor_inventory(player) and armor.migrate_old_inventory then
local player_inv = player:get_inventory()
player_inv:set_size("armor", 6) player_inv:set_size("armor", 6)
for i=1, 6 do for i=1, 6 do
local stack = player_inv:get_stack("armor", i) local stack = player_inv:get_stack("armor", i)
armor_inv:set_stack("armor", i, stack) armor_inv:set_stack("armor", i, stack)
end
armor:save_armor_inventory(player)
player_inv:set_size("armor", 0)
end
for i=1, 6 do
local stack = armor_inv:get_stack("armor", i)
armor:run_callbacks("on_equip", player, i, stack) armor:run_callbacks("on_equip", player, i, stack)
end end
armor.def[name] = { armor.def[name] = {
@ -256,13 +260,13 @@ end)
if armor.config.drop == true or armor.config.destroy == true then if armor.config.drop == true or armor.config.destroy == true then
minetest.register_on_dieplayer(function(player) minetest.register_on_dieplayer(function(player)
local name, player_inv = armor:get_valid_player(player, "[on_dieplayer]") local name, armor_inv = armor:get_valid_player(player, "[on_dieplayer]")
if not name then if not name then
return return
end end
local drop = {} local drop = {}
for i=1, player_inv:get_size("armor") do for i=1, armor_inv:get_size("armor") do
local stack = player_inv:get_stack("armor", i) local stack = armor_inv:get_stack("armor", i)
if stack:get_count() > 0 then if stack:get_count() > 0 then
table.insert(drop, stack) table.insert(drop, stack)
armor:set_inventory_stack(player, i, nil) armor:set_inventory_stack(player, i, nil)