<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-GB">
	<id>https://stockhub.co/index.php?action=history&amp;feed=atom&amp;title=Module%3ASensitive_IP_addresses%2Flist%2Fvalidate</id>
	<title>Module:Sensitive IP addresses/list/validate - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://stockhub.co/index.php?action=history&amp;feed=atom&amp;title=Module%3ASensitive_IP_addresses%2Flist%2Fvalidate"/>
	<link rel="alternate" type="text/html" href="https://stockhub.co/index.php?title=Module:Sensitive_IP_addresses/list/validate&amp;action=history"/>
	<updated>2026-06-07T05:12:36Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>https://stockhub.co/index.php?title=Module:Sensitive_IP_addresses/list/validate&amp;diff=146715&amp;oldid=prev</id>
		<title>imported&gt;Mr. Stradivarius: make description a required field</title>
		<link rel="alternate" type="text/html" href="https://stockhub.co/index.php?title=Module:Sensitive_IP_addresses/list/validate&amp;diff=146715&amp;oldid=prev"/>
		<updated>2016-09-22T09:50:05Z</updated>

		<summary type="html">&lt;p&gt;make description a required field&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;-- This module validates the data in [[Module:Sensitive IP addresses/list]].&lt;br /&gt;
&lt;br /&gt;
-- Load modules&lt;br /&gt;
local mSIPA_API = require(&amp;#039;Module:Sensitive IP addresses/API&amp;#039;)&lt;br /&gt;
local Subnet = require(&amp;#039;Module:IP&amp;#039;).Subnet&lt;br /&gt;
&lt;br /&gt;
-- Constants&lt;br /&gt;
local DATA_MODULE = &amp;#039;Module:Sensitive IP addresses/list&amp;#039;&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
local function makeErrorLogger()&lt;br /&gt;
	-- Return an object for formatting errors.&lt;br /&gt;
	return {&lt;br /&gt;
		errors = {},&lt;br /&gt;
		addError = function (self, msg, ...)&lt;br /&gt;
			table.insert(self.errors, string.format(msg, ...))&lt;br /&gt;
		end,&lt;br /&gt;
		addEntryTypeError = function (self, entryIdx, field, actual, expected)&lt;br /&gt;
			self:addError(&lt;br /&gt;
				&amp;#039;The %s field in data entry #%d was type %s (should be string or nil)&amp;#039;,&lt;br /&gt;
				field, entryIdx, actual, expected&lt;br /&gt;
			)&lt;br /&gt;
		end,&lt;br /&gt;
		hasErrors = function (self)&lt;br /&gt;
			return #self.errors &amp;gt; 0&lt;br /&gt;
		end,&lt;br /&gt;
		makeReport = function (self)&lt;br /&gt;
			if #self.errors &amp;lt; 1 then&lt;br /&gt;
				return &amp;#039;No errors found&amp;#039;&lt;br /&gt;
			else&lt;br /&gt;
				local ret = {&amp;#039;Found the following errors:&amp;#039;}&lt;br /&gt;
				for i, msg in ipairs(self.errors) do&lt;br /&gt;
					ret[#ret + 1] = string.format(&amp;#039;* &amp;lt;strong class=&amp;quot;error&amp;quot;&amp;gt;%s&amp;lt;/strong&amp;gt;&amp;#039;, msg)&lt;br /&gt;
				end&lt;br /&gt;
				return table.concat(ret, &amp;#039;\n&amp;#039;)&lt;br /&gt;
			end&lt;br /&gt;
		end,&lt;br /&gt;
	}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function loadData(logger)&lt;br /&gt;
	-- Load the data table, logging any errors in the process.&lt;br /&gt;
&lt;br /&gt;
	-- Check whether the data module can be successfully loaded.&lt;br /&gt;
	local success, data = pcall(mw.loadData, DATA_MODULE)&lt;br /&gt;
	if not success then&lt;br /&gt;
		logger:addError(&amp;#039;%s could not be parsed by mw.loadData; check for [[mw:LUAREF#mw.loadData|invalid data]]&amp;#039;, DATA_MODULE)&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Check that the data table is a table.&lt;br /&gt;
	if type(data) ~= &amp;#039;table&amp;#039; then&lt;br /&gt;
		logger:addError(&amp;#039;%s returned a %s; table expected&amp;#039;, DATA_MODULE, type(data))&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return data&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function checkDataStructure(logger, data)&lt;br /&gt;
	-- Check the structure of the individual entries in the data table.&lt;br /&gt;
	for dataIndex, subtable in ipairs(data) do&lt;br /&gt;
		-- Check that subtables are tables.&lt;br /&gt;
		if type(subtable) ~= &amp;#039;table&amp;#039; then&lt;br /&gt;
			logger:addError(&amp;#039;Data entry #%d is not a table&amp;#039;, dataIndex)&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		-- Check that we have required string fields.&lt;br /&gt;
		for _, field in ipairs{&amp;#039;name&amp;#039;, &amp;#039;id&amp;#039;, &amp;#039;description&amp;#039;} do&lt;br /&gt;
			if type(subtable[field]) ~= &amp;#039;string&amp;#039; then&lt;br /&gt;
				logger:addError(&lt;br /&gt;
					&amp;quot;Missing field &amp;#039;%s&amp;#039; in data entry #%d&amp;quot;,&lt;br /&gt;
					field,&lt;br /&gt;
					dataIndex&lt;br /&gt;
				)&lt;br /&gt;
			elseif subtable[field] == &amp;#039;&amp;#039; then&lt;br /&gt;
				logger:addError(&lt;br /&gt;
					&amp;quot;Blank field &amp;#039;%s&amp;#039; in data entry #%d&amp;quot;,&lt;br /&gt;
					field,&lt;br /&gt;
					dataIndex&lt;br /&gt;
				)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		-- Check that optional string fields are strings if they are present.&lt;br /&gt;
		for _, field in ipairs{&amp;#039;notes&amp;#039;} do&lt;br /&gt;
			local val = subtable[field]&lt;br /&gt;
			if val ~= nil and type(val) ~= &amp;#039;string&amp;#039; then&lt;br /&gt;
				logger:addEntryTypeError(dataIndex, field, type(val), &amp;#039;string or nil&amp;#039;)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		-- Check that the reason is valid if it is present.&lt;br /&gt;
		if subtable.reason ~= nil then&lt;br /&gt;
			if type(subtable.reason) ~= &amp;#039;string&amp;#039; then&lt;br /&gt;
				logger:addEntryTypeError(&lt;br /&gt;
					dataIndex,&lt;br /&gt;
					&amp;#039;reason&amp;#039;,&lt;br /&gt;
					type(subtable.reason),&lt;br /&gt;
					&amp;#039;string or nil&amp;#039;&lt;br /&gt;
				)&lt;br /&gt;
			elseif not mSIPA_API._isValidSensitivityReason(subtable.reason) then&lt;br /&gt;
				logger:addError(&lt;br /&gt;
					&amp;quot;The reason field in data entry #%d was invalid (should be &amp;#039;%s&amp;#039;)&amp;quot;,&lt;br /&gt;
					dataIndex,&lt;br /&gt;
					mSIPA_API._getSensitivityReasons(&amp;quot;&amp;#039;, &amp;#039;&amp;quot;, &amp;quot;&amp;#039;, or &amp;#039;&amp;quot;)&lt;br /&gt;
				)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		-- Check IP range tables.&lt;br /&gt;
		for i, field in ipairs{&amp;#039;ipv4Ranges&amp;#039;, &amp;#039;ipv6Ranges&amp;#039;} do&lt;br /&gt;
			local ranges = subtable[field]&lt;br /&gt;
			if ranges ~= nil then&lt;br /&gt;
				if type(ranges) ~= &amp;#039;table&amp;#039; then&lt;br /&gt;
					logger:addEntryTypeError(dataIndex, field, type(ranges), &amp;#039;table or nil&amp;#039;)&lt;br /&gt;
				else&lt;br /&gt;
					for j, range in ipairs(ranges) do&lt;br /&gt;
						if type(range) ~= &amp;#039;string&amp;#039; then&lt;br /&gt;
							logger:addError(&lt;br /&gt;
								&amp;#039;Range #%d in the %s field of entry #%d was type %s (expected string)&amp;#039;,&lt;br /&gt;
								j, field, type(range)&lt;br /&gt;
							)&lt;br /&gt;
						elseif range == &amp;#039;&amp;#039; then&lt;br /&gt;
							logger:addError(&lt;br /&gt;
								&amp;#039;Range #%d in the %s field of entry #%d was a blank string&amp;#039;,&lt;br /&gt;
								j, field&lt;br /&gt;
							)&lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function makeSubnet(cidr)&lt;br /&gt;
	-- Make a subnet object from a CIDR string. Returns a subnet object, or nil&lt;br /&gt;
	-- if there were any errors.&lt;br /&gt;
	local success, obj = pcall(Subnet.new, cidr)&lt;br /&gt;
	if success then&lt;br /&gt;
		return obj&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function checkDuplicateIds(logger, data)&lt;br /&gt;
	-- Check that there are no duplicate IDs in the data.&lt;br /&gt;
	local ids = {}&lt;br /&gt;
	for dataIndex, subtable in ipairs(data) do&lt;br /&gt;
		if ids[subtable.id] then&lt;br /&gt;
			logger:addError(&lt;br /&gt;
				&amp;quot;Data entry #%d (%s) and data entry #%d (%s) have duplicate ID &amp;#039;%s&amp;#039;&amp;quot;,&lt;br /&gt;
				ids[subtable.id],&lt;br /&gt;
				data[ids[subtable.id]].name,&lt;br /&gt;
				dataIndex,&lt;br /&gt;
				subtable.name,&lt;br /&gt;
				subtable.id&lt;br /&gt;
			)&lt;br /&gt;
		else&lt;br /&gt;
			ids[subtable.id] = dataIndex&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function checkRanges(logger, data)&lt;br /&gt;
	-- Check the ranges in the data table to make sure they are all valid and&lt;br /&gt;
	-- that they don&amp;#039;t overlap with each other. This function assumes that the&lt;br /&gt;
	-- structure of the data table is valid.&lt;br /&gt;
&lt;br /&gt;
	-- Make an array of subnet data for easy comparison&lt;br /&gt;
	local ranges = {&lt;br /&gt;
		ipv4 = {},&lt;br /&gt;
		ipv6 = {},&lt;br /&gt;
	}&lt;br /&gt;
	for dataIndex, subtable in ipairs(data) do&lt;br /&gt;
		for i, field in ipairs{&amp;#039;ipv4Ranges&amp;#039;, &amp;#039;ipv6Ranges&amp;#039;} do&lt;br /&gt;
			local cidrs = subtable[field]&lt;br /&gt;
			if cidrs then&lt;br /&gt;
				for j, cidr in ipairs(cidrs) do&lt;br /&gt;
					local subnet = makeSubnet(cidr)&lt;br /&gt;
					if subnet then&lt;br /&gt;
						local ipVersion = field == &amp;#039;ipv4Ranges&amp;#039; and &amp;#039;IPv4&amp;#039; or &amp;#039;IPv6&amp;#039;&lt;br /&gt;
						local rangeKey = ipVersion:lower()&lt;br /&gt;
						if ipVersion == subnet:getVersion() then&lt;br /&gt;
							table.insert(ranges[rangeKey], {&lt;br /&gt;
								dataIndex = dataIndex,&lt;br /&gt;
								field = field,&lt;br /&gt;
								rangeIndex = j,&lt;br /&gt;
								subnet = subnet,&lt;br /&gt;
								name = subtable.name,&lt;br /&gt;
							})&lt;br /&gt;
						else&lt;br /&gt;
							logger:addError(&lt;br /&gt;
								&amp;quot;Found %s CIDR string &amp;#039;%s&amp;#039; in range #%d in the %s field of entry #%d (%s); should be %s&amp;quot;,&lt;br /&gt;
								subnet:getVersion(), cidr, j, field, dataIndex, subtable.name, ipVersion&lt;br /&gt;
							)&lt;br /&gt;
						end&lt;br /&gt;
					else&lt;br /&gt;
						logger:addError(&lt;br /&gt;
							&amp;quot;Invalid CIDR string &amp;#039;%s&amp;#039; in range #%d in the %s field of entry #%d (%s)&amp;quot;,&lt;br /&gt;
							cidr, j, field, dataIndex, subtable.name&lt;br /&gt;
						)&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Check for overlapping subnets&lt;br /&gt;
	local nComparisons = 0&lt;br /&gt;
	for ipVersion, versionData in pairs(ranges) do&lt;br /&gt;
		local lim = #versionData&lt;br /&gt;
		for i = 1, lim - 1 do&lt;br /&gt;
			local subnetData1 = versionData[i]&lt;br /&gt;
			for j = i + 1, lim do&lt;br /&gt;
				local subnetData2 = versionData[j]&lt;br /&gt;
				nComparisons = nComparisons + 1&lt;br /&gt;
				if subnetData1.subnet:overlapsSubnet(subnetData2.subnet) then&lt;br /&gt;
					logger:addError(&lt;br /&gt;
						&amp;quot;%s range #%d &amp;#039;%s&amp;#039; in data entry #%d (%s) overlaps range #%d &amp;#039;%s&amp;#039; in data entry #%d (%s)&amp;quot;,&lt;br /&gt;
						ipVersion == &amp;#039;ipv4&amp;#039; and &amp;#039;IPv4&amp;#039; or &amp;#039;IPv6&amp;#039;,&lt;br /&gt;
						subnetData1.rangeIndex,&lt;br /&gt;
						subnetData1.subnet:getCIDR(),&lt;br /&gt;
						subnetData1.dataIndex,&lt;br /&gt;
						subnetData1.name,&lt;br /&gt;
						subnetData2.rangeIndex,&lt;br /&gt;
						subnetData2.subnet:getCIDR(),&lt;br /&gt;
						subnetData2.dataIndex,&lt;br /&gt;
						subnetData2.name&lt;br /&gt;
					)&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	mw.log(nComparisons .. &amp;#039; subnet comparisons performed&amp;#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.main()&lt;br /&gt;
	local logger = makeErrorLogger()&lt;br /&gt;
	local data = loadData(logger)&lt;br /&gt;
	if logger:hasErrors() then&lt;br /&gt;
		return logger:makeReport()&lt;br /&gt;
	end&lt;br /&gt;
	checkDataStructure(logger, data)&lt;br /&gt;
	if logger:hasErrors() then&lt;br /&gt;
		return logger:makeReport()&lt;br /&gt;
	end&lt;br /&gt;
	checkDuplicateIds(logger, data)&lt;br /&gt;
	checkRanges(logger, data)&lt;br /&gt;
	return logger:makeReport()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>imported&gt;Mr. Stradivarius</name></author>
	</entry>
</feed>