initial commit: @arbiter/core authorization engine with js-rigor hardening
Zanzibar-style authorization graph engine (direct/chain/TTU/defeasible/ binary modes, condensed snapshots, value relations) with 39 rigor test campaigns. Includes fixes for snapshot binary writer/reader format mismatch (snapshot-of-snapshot corruption), possibility write-boundary validation, empty-graph snapshot serialization, relation lookup cache direction collision, config-redefinition cache invalidation, binary threshold semantics, defeasible compiled routing, and comparator reason whitelisting.
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* IP Address Utilities
|
||||
*/
|
||||
|
||||
import { createRequire } from 'node:module';
|
||||
|
||||
/**
|
||||
* Check if string is IPv4
|
||||
*/
|
||||
export function isIPv4(ip) {
|
||||
if (typeof ip !== 'string') return false;
|
||||
const parts = ip.split('.');
|
||||
if (parts.length !== 4) return false;
|
||||
|
||||
return parts.every(part => {
|
||||
const num = parseInt(part, 10);
|
||||
return !isNaN(num) && num >= 0 && num <= 255 && part === String(num);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if string is IPv6
|
||||
*/
|
||||
export function isIPv6(ip) {
|
||||
if (typeof ip !== 'string') return false;
|
||||
// Simple check - contains colons and valid hex
|
||||
return ip.includes(':') && /^[0-9a-fA-F:]+$/.test(ip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if IP is in private range (RFC 1918)
|
||||
*/
|
||||
export function isPrivateIp(ip) {
|
||||
if (!isIPv4(ip)) return false;
|
||||
|
||||
const parts = ip.split('.').map(Number);
|
||||
const [a, b, c, d] = parts;
|
||||
|
||||
// 10.0.0.0/8
|
||||
if (a === 10) return true;
|
||||
|
||||
// 172.16.0.0/12
|
||||
if (a === 172 && b >= 16 && b <= 31) return true;
|
||||
|
||||
// 192.168.0.0/16
|
||||
if (a === 192 && b === 168) return true;
|
||||
|
||||
// 127.0.0.0/8 (loopback)
|
||||
if (a === 127) return true;
|
||||
|
||||
// 169.254.0.0/16 (link-local)
|
||||
if (a === 169 && b === 254) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if IP is loopback
|
||||
*/
|
||||
export function isLoopbackIp(ip) {
|
||||
if (!isIPv4(ip)) return false;
|
||||
return ip.startsWith('127.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert IP to integer for range comparison
|
||||
*/
|
||||
export function ipToInt(ip) {
|
||||
return ip.split('.').reduce((acc, octet) => (acc << 8) + parseInt(octet, 10), 0) >>> 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse CIDR notation
|
||||
*/
|
||||
export function parseCidr(cidr) {
|
||||
const [ip, prefix] = cidr.split('/');
|
||||
const mask = -1 << (32 - parseInt(prefix, 10));
|
||||
return { ip: ipToInt(ip), mask };
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if IP is in CIDR range
|
||||
*/
|
||||
export function isIpInCidr(ip, cidr) {
|
||||
if (!isIPv4(ip)) return false;
|
||||
|
||||
try {
|
||||
const ipInt = ipToInt(ip);
|
||||
const { ip: networkInt, mask } = parseCidr(cidr);
|
||||
|
||||
return (ipInt & mask) === (networkInt & mask);
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if two IPs are equal
|
||||
*/
|
||||
export function ipEquals(ip1, ip2) {
|
||||
return ip1 === ip2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get IP version (4 or 6)
|
||||
*/
|
||||
export function getIpVersion(ip) {
|
||||
if (isIPv4(ip)) return 4;
|
||||
if (isIPv6(ip)) return 6;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize IP (remove leading zeros, etc.)
|
||||
*/
|
||||
export function normalizeIp(ip) {
|
||||
if (!isIPv4(ip)) return ip;
|
||||
|
||||
return ip
|
||||
.split('.')
|
||||
.map(part => parseInt(part, 10).toString())
|
||||
.join('.');
|
||||
}
|
||||
Reference in New Issue
Block a user