-- This module validates the data in .
-- Load moduleslocal mSIPA_API = require('Module:Sensitive IP addresses/API')local Subnet = require('Module:IP').Subnet
-- Constantslocal DATA_MODULE = 'Module:Sensitive IP addresses/list'
local p =
local function makeErrorLogger -- Return an object for formatting errors. return end
local function loadData(logger) -- Load the data table, logging any errors in the process.
-- Check whether the data module can be successfully loaded. local success, data = pcall(mw.loadData, DATA_MODULE) if not success then logger:addError('%s could not be parsed by mw.loadData; check for invalid data', DATA_MODULE) return nil end
-- Check that the data table is a table. if type(data) ~= 'table' then logger:addError('%s returned a %s; table expected', DATA_MODULE, type(data)) end
return dataend
local function checkDataStructure(logger, data) -- Check the structure of the individual entries in the data table. for dataIndex, subtable in ipairs(data) do -- Check that subtables are tables. if type(subtable) ~= 'table' then logger:addError('Data entry #%d is not a table', dataIndex) end
-- Check that we have required string fields. for _, field in ipairs do if type(subtable[field]) ~= 'string' then logger:addError("Missing field '%s' in data entry #%d", field, dataIndex ) elseif subtable[field]
-- Check that optional string fields are strings if they are present. for _, field in ipairs do local val = subtable[field] if val ~= nil and type(val) ~= 'string' then logger:addEntryTypeError(dataIndex, field, type(val), 'string or nil') end end
-- Check that the reason is valid if it is present. if subtable.reason ~= nil then if type(subtable.reason) ~= 'string' then logger:addEntryTypeError(dataIndex, 'reason', type(subtable.reason), 'string or nil' ) elseif not mSIPA_API._isValidSensitivityReason(subtable.reason) then logger:addError("The reason field in data entry #%d was invalid (should be '%s')", dataIndex, mSIPA_API._getSensitivityReasons("', '", "', or '") ) end end
-- Check IP range tables. for i, field in ipairs do local ranges = subtable[field] if ranges ~= nil then if type(ranges) ~= 'table' then logger:addEntryTypeError(dataIndex, field, type(ranges), 'table or nil') else for j, range in ipairs(ranges) do if type(range) ~= 'string' then logger:addError('Range #%d in the %s field of entry #%d was type %s (expected string)', j, field, type(range) ) elseif range
local function makeSubnet(cidr) -- Make a subnet object from a CIDR string. Returns a subnet object, or nil -- if there were any errors. local success, obj = pcall(Subnet.new, cidr) if success then return obj endend
local function checkDuplicateIds(logger, data) -- Check that there are no duplicate IDs in the data. local ids = for dataIndex, subtable in ipairs(data) do if ids[subtable.id] then logger:addError("Data entry #%d (%s) and data entry #%d (%s) have duplicate ID '%s'", ids[subtable.id], data[ids[subtable.id]].name, dataIndex, subtable.name, subtable.id ) else ids[subtable.id] = dataIndex end endend
local function checkRanges(logger, data) -- Check the ranges in the data table to make sure they are all valid and -- that they don't overlap with each other. This function assumes that the -- structure of the data table is valid.
-- Make an array of subnet data for easy comparison local ranges = for dataIndex, subtable in ipairs(data) do for i, field in ipairs do local cidrs = subtable[field] if cidrs then for j, cidr in ipairs(cidrs) do local subnet = makeSubnet(cidr) if subnet then local ipVersion = field
subnet:getVersion then table.insert(ranges[rangeKey],) else logger:addError("Found %s CIDR string '%s' in range #%d in the %s field of entry #%d (%s); should be %s", subnet:getVersion, cidr, j, field, dataIndex, subtable.name, ipVersion ) end else logger:addError("Invalid CIDR string '%s' in range #%d in the %s field of entry #%d (%s)", cidr, j, field, dataIndex, subtable.name ) end end end end end -- Check for overlapping subnets local nComparisons = 0 for ipVersion, versionData in pairs(ranges) do local lim = #versionData for i = 1, lim - 1 do local subnetData1 = versionData[i] for j = i + 1, lim do local subnetData2 = versionData[j] nComparisons = nComparisons + 1 if subnetData1.subnet:overlapsSubnet(subnetData2.subnet) then logger:addError("%s range #%d '%s' in data entry #%d (%s) overlaps range #%d '%s' in data entry #%d (%s)", ipVersion
function p.main local logger = makeErrorLogger local data = loadData(logger) if logger:hasErrors then return logger:makeReport end checkDataStructure(logger, data) if logger:hasErrors then return logger:makeReport end checkDuplicateIds(logger, data) checkRanges(logger, data) return logger:makeReportend
return p