Module:Sensitive IP addresses/list/validate
This module validates the data in Module:Sensitive IP addresses/list.
This module validates the data in Module:Sensitive IP addresses/list.
-- This module validates the data in [[Module:Sensitive IP addresses/list]].
-- Load modules
local mSIPA_API = require('Module:Sensitive IP addresses/API')
local Subnet = require('Module:IP').Subnet
-- Constants
local DATA_MODULE = 'Module:Sensitive IP addresses/list'
local p = {}
local function makeErrorLogger()
-- Return an object for formatting errors.
return {
errors = {},
addError = function (self, msg, ...)
table.insert(self.errors, string.format(msg, ...))
end,
addEntryTypeError = function (self, entryIdx, field, actual, expected)
self:addError(
'The %s field in data entry #%d was type %s (should be string or nil)',
field, entryIdx, actual, expected
)
end,
hasErrors = function (self)
return #self.errors > 0
end,
makeReport = function (self)
if #self.errors < 1 then
return 'No errors found'
else
local ret = {'Found the following errors:'}
for i, msg in ipairs(self.errors) do
ret[#ret + 1] = string.format('* <strong class="error">%s</strong>', msg)
end
return table.concat(ret, '\n')
end
end,
}
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 [[mw:LUAREF#mw.loadData|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 data
end
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{'name', 'id', 'description'} do
if type(subtable[field]) ~= 'string' then
logger:addError(
"Missing field '%s' in data entry #%d",
field,
dataIndex
)
elseif subtable[field] == '' then
logger:addError(
"Blank field '%s' in data entry #%d",
field,
dataIndex
)
end
end
-- Check that optional string fields are strings if they are present.
for _, field in ipairs{'notes'} 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{'ipv4Ranges', 'ipv6Ranges'} 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 == '' then
logger:addError(
'Range #%d in the %s field of entry #%d was a blank string',
j, field
)
end
end
end
end
end
end
end
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
end
end
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
end
end
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 = {
ipv4 = {},
ipv6 = {},
}
for dataIndex, subtable in ipairs(data) do
for i, field in ipairs{'ipv4Ranges', 'ipv6Ranges'} 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 == 'ipv4Ranges' and 'IPv4' or 'IPv6'
local rangeKey = ipVersion:lower()
if ipVersion == subnet:getVersion() then
table.insert(ranges[rangeKey], {
dataIndex = dataIndex,
field = field,
rangeIndex = j,
subnet = subnet,
name = subtable.name,
})
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 == 'ipv4' and 'IPv4' or 'IPv6',
subnetData1.rangeIndex,
subnetData1.subnet:getCIDR(),
subnetData1.dataIndex,
subnetData1.name,
subnetData2.rangeIndex,
subnetData2.subnet:getCIDR(),
subnetData2.dataIndex,
subnetData2.name
)
end
end
end
end
mw.log(nComparisons .. ' subnet comparisons performed')
end
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:makeReport()
end
return p
Content Disclaimer
Informasi ini disarikan dari Wikipedia dan disajikan kembali untuk tujuan edukasi. Konten tersedia di bawah lisensi CC BY-SA 3.0. Kami tidak bertanggung jawab atas ketidakakuratan data yang bersumber dari kontribusi publik tersebut.
- The information displayed on this website is sourced in part or in whole from Wikipedia and has been adapted for the purpose of restating it. We strive to provide accurate and relevant information, however:
- There is no guarantee of absolute accuracy. Wikipedia is an open, collaborative project that can be edited by anyone, so information is subject to change.
- It is not intended to constitute professional advice. The content displayed is for informational and educational purposes only. For important decisions (e.g., medical, legal, or financial), please consult a professional.
- Content copyright. Wikipedia is licensed under the Creative Commons Attribution-ShareAlike License (CC BY-SA). This means that content may be reused with appropriate attribution and shared under a similar license.
- Responsible use. Any risk arising from the use of information from this website is entirely the responsibility of the user.