From c36b1e5fc4f308fecc60b42a42a3bc5424ae854e Mon Sep 17 00:00:00 2001 From: micheal65536 Date: Sun, 11 Feb 2018 12:33:46 +0000 Subject: [PATCH] Fix item duplication vulnerability --- 3d_armor/README.txt | 3 ++ 3d_armor/api.lua | 81 +++++++++++++++++++++++++++++++++++++-------- 3d_armor/init.lua | 32 ++++++++++-------- 3 files changed, 88 insertions(+), 28 deletions(-) diff --git a/3d_armor/README.txt b/3d_armor/README.txt index db445be..a0cb3b1 100644 --- a/3d_armor/README.txt +++ b/3d_armor/README.txt @@ -68,6 +68,9 @@ armor_fire_protect = false -- Enable punch damage effects. armor_punch_damage = true +-- Enable migration of old armor inventories +armor_migrate_old_inventory = true + API --- diff --git a/3d_armor/api.lua b/3d_armor/api.lua index e2496fd..92543a4 100644 --- a/3d_armor/api.lua +++ b/3d_armor/api.lua @@ -69,6 +69,7 @@ armor = { on_damage = {}, on_destroy = {}, }, + migrate_old_inventory = true, version = "0.4.9", } @@ -177,7 +178,7 @@ armor.update_player_visuals = function(self, player) end 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 return end @@ -205,7 +206,10 @@ armor.set_player_armor = function(self, player) change[group] = 1 levels[group] = 0 end - local list = player_inv:get_list("armor") or {} + local list = armor_inv:get_list("armor") + if type(list) ~= "table" then + return + end for i, stack in pairs(list) do if stack:get_count() == 1 then local def = stack:get_definition() @@ -221,6 +225,7 @@ armor.set_player_armor = function(self, player) local level = def.groups["armor_"..element] levels["fleshy"] = levels["fleshy"] + level end + break end -- DEPRECATED, use armor_groups instead if def.groups["armor_radiation"] and levels["radiation"] then @@ -299,7 +304,7 @@ armor.set_player_armor = function(self, player) end 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 return end @@ -307,7 +312,7 @@ armor.punch = function(self, player, hitter, time_from_last_punch, tool_capabili local count = 0 local recip = true 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 if stack:get_count() == 1 then local name = stack:get_name() @@ -399,12 +404,64 @@ armor.get_armor_formspec = function(self, name, listring) for _, attr in pairs(self.attributes) do formspec = formspec:gsub("armor_attr_"..attr, armor.def[name][attr]) end - for _, group in pairs(self.attributes) do - formspec = formspec:gsub("armor_group_"..group, armor.def[name][group]) + for group, _ in pairs(self.registered_groups) do + formspec = formspec:gsub("armor_group_"..group, + armor.def[name].groups[group]) end return formspec 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) -- DEPRECATED: Legacy inventory support end @@ -416,17 +473,13 @@ armor.set_inventory_stack = function(self, player, i, stack) minetest.log("warning", "3d_armor: Player name is nil "..msg) return end - local player_inv = player:get_inventory() local armor_inv = minetest.get_inventory({type="detached", name=name.."_armor"}) - if not player_inv then - minetest.log("warning", "3d_armor: Player inventory is nil "..msg) - return - elseif not armor_inv then - minetest.log("warning", "3d_armor: Detached armor inventory is nil "..msg) + if not armor_inv then + minetest.log("warning", S("3d_armor: Detached armor inventory is nil @1", msg)) return end - player_inv:set_stack("armor", i, stack) armor_inv:set_stack("armor", i, stack) + self:save_armor_inventory(player) end armor.get_valid_player = function(self, player, msg) @@ -440,7 +493,7 @@ armor.get_valid_player = function(self, player, msg) minetest.log("warning", "3d_armor: Player name is nil "..msg) return end - local inv = player:get_inventory() + local inv = minetest.get_inventory({type="detached", name=name.."_armor"}) if not inv then minetest.log("warning", "3d_armor: Player inventory is nil "..msg) return diff --git a/3d_armor/init.lua b/3d_armor/init.lua index 4c7264b..6cc96b8 100644 --- a/3d_armor/init.lua +++ b/3d_armor/init.lua @@ -101,27 +101,23 @@ end) local function init_player_armor(player) local name = player:get_player_name() - local player_inv = player:get_inventory() local pos = player:getpos() - if not name or not player_inv or not pos then + if not name or not pos then return false end local armor_inv = minetest.create_detached_inventory(name.."_armor", { 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:set_player_armor(player) end, 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:set_player_armor(player) end, on_move = function(inv, from_list, from_index, to_list, to_index, count, player) - local plaver_inv = player:get_inventory() - 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:save_armor_inventory(player) armor:set_player_armor(player) end, allow_put = function(inv, listname, index, stack, player) @@ -148,10 +144,18 @@ local function init_player_armor(player) end, }, name) armor_inv:set_size("armor", 6) - player_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) + for i=1, 6 do + local stack = player_inv:get_stack("armor", i) + 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 = player_inv:get_stack("armor", i) - armor_inv:set_stack("armor", i, stack) + local stack = armor_inv:get_stack("armor", i) armor:run_callbacks("on_equip", player, i, stack) end armor.def[name] = { @@ -220,13 +224,13 @@ end) if armor.config.drop == true or armor.config.destroy == true then 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 return end local drop = {} - for i=1, player_inv:get_size("armor") do - local stack = player_inv:get_stack("armor", i) + for i=1, armor_inv:get_size("armor") do + local stack = armor_inv:get_stack("armor", i) if stack:get_count() > 0 then table.insert(drop, stack) armor:set_inventory_stack(player, i, nil)