问题

安全团队会使用IP地址进行漏洞扫描,由于IP众多,需要根据漏洞类型来分配相应的责任人。由于识别IP的owner是根据服务器的资产信息来确认,有些IP比如虚拟IP无法匹配。 因此会维护一个类似IP和责任人的表格。但是由于目前的实现是根据精确的IP地址匹配,导致维护工作量比较大。所以考虑使用子网来匹配这些IP。

实现

主要是两个主要功能。

第一个是根据IP和子网来判断IP是否属于该子网。由于PowerQuery本身没有提供IP的相关函数,所以只能自定义。 第二个功能就是根据IP子网信息来返回owner。参数为需要匹配的IP和一个包含IP或者子网以及owner的表。

第一个表为vulnerability。关键字段如下

IP PluginID Output Solution Found
192.168.10.1 11111 glib<1.0 Upgrade to 1.0 2025-7-8 10:00
192.168.20.2 11111 glib<1.0 Upgrade to 1.0 2025-7-8 10:00
192.168.30.1 11111 glib<1.0 Upgrade to 1.0 2025-7-8 10:00

第二个表为override, 关键字段如下

IP Owner
192.168.10.0/24 Tom
192.168.20.2 Jerry
let
    Owner = (ipAddress as text, subnetTable as table) as text =>
    let
        // Function to check if IP is in a single subnet
        IsIPInSubnet = (ip as text, subnet as text) as logical =>
        let
            // Normalize subnet to CIDR format if it's a plain IP address
            normalizedSubnet = 
                if Text.Contains(subnet, "/") then 
                    subnet 
                else 
                    subnet & "/32",
            
            // Split subnet into network address and CIDR
            subnetParts = Text.Split(normalizedSubnet, "/"),
            networkAddress = subnetParts{0},
            cidrMask = Number.From(subnetParts{1}),
            
            // Convert IP to integer
            IPToInteger = (ip as text) as number =>
                let
                    parts = Text.Split(ip, "."),
                    part1 = Number.From(parts{0}) * 256 * 256 * 256,
                    part2 = Number.From(parts{1}) * 256 * 256,
                    part3 = Number.From(parts{2}) * 256,
                    part4 = Number.From(parts{3})
                in
                    part1 + part2 + part3 + part4,
            
            // Convert IP to binary mask
            SubnetMask = (cidr as number) as number =>
                let
                    mask = Number.Power(2, 32) - Number.Power(2, 32 - cidr)
                in
                    mask,
            
            // Get network portion
            ipInt = IPToInteger(ip),
            networkInt = IPToInteger(networkAddress),
            mask = SubnetMask(cidrMask)
        in
            Number.BitwiseAnd(ipInt, mask) = Number.BitwiseAnd(networkInt, mask),
        
        // Filter table to find matching subnets
        matchingRows = Table.SelectRows(subnetTable, each IsIPInSubnet(ipAddress, [IP])),
        
        // Return owner if a match is found, otherwise return empty text
        result = 
            if Table.RowCount(matchingRows) > 0 then 
                matchingRows[Owner]{0}
            else 
                ""
    in
        result
in
    Owner