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:
John Dvorak
2026-07-31 13:44:06 -07:00
commit 717ae1031e
373 changed files with 654131 additions and 0 deletions
+123
View File
@@ -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('.');
}