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,134 @@
|
||||
import { Arbiter } from '../src/core/Arbiter.js';
|
||||
|
||||
function parseArgs(argv) {
|
||||
const args = new Map();
|
||||
for (let i = 2; i < argv.length; i++) {
|
||||
const value = argv[i];
|
||||
if (!value.startsWith('--')) continue;
|
||||
const [key, inline] = value.slice(2).split('=');
|
||||
if (inline !== undefined) {
|
||||
args.set(key, inline);
|
||||
continue;
|
||||
}
|
||||
const next = argv[i + 1];
|
||||
if (next && !next.startsWith('--')) {
|
||||
args.set(key, next);
|
||||
i++;
|
||||
} else {
|
||||
args.set(key, true);
|
||||
}
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
function parseSizes(value) {
|
||||
if (!value) return null;
|
||||
return value.split(',').map(item => Number(item.trim())).filter(Number.isFinite);
|
||||
}
|
||||
|
||||
function formatBytes(bytes) {
|
||||
const units = ['B', 'KB', 'MB', 'GB'];
|
||||
let idx = 0;
|
||||
let val = bytes;
|
||||
while (val >= 1024 && idx < units.length - 1) {
|
||||
val /= 1024;
|
||||
idx++;
|
||||
}
|
||||
return `${val.toFixed(2)}${units[idx]}`;
|
||||
}
|
||||
|
||||
function gc() {
|
||||
if (global.gc) {
|
||||
global.gc();
|
||||
global.gc();
|
||||
}
|
||||
}
|
||||
|
||||
function measureMemory(label) {
|
||||
const mem = process.memoryUsage();
|
||||
return {
|
||||
label,
|
||||
rss: mem.rss,
|
||||
heapUsed: mem.heapUsed,
|
||||
heapTotal: mem.heapTotal,
|
||||
external: mem.external
|
||||
};
|
||||
}
|
||||
|
||||
function buildNodes(arbiter, size) {
|
||||
for (let i = 0; i < size; i++) {
|
||||
arbiter.addNode(`user:${i}`, 'user');
|
||||
arbiter.addNode(`resource:${i}`, 'resource');
|
||||
}
|
||||
}
|
||||
|
||||
function addEdges(arbiter, size, relation, perNode, srcPrefix = 'user', dstPrefix = 'resource') {
|
||||
for (let i = 0; i < size; i++) {
|
||||
for (let j = 0; j < perNode; j++) {
|
||||
const dstId = (i + j) % size;
|
||||
arbiter.addRelation(`${srcPrefix}:${i}`, relation, `${dstPrefix}:${dstId}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function buildGraph(arbiter, size) {
|
||||
buildNodes(arbiter, size);
|
||||
|
||||
addEdges(arbiter, size, 'owner', 1);
|
||||
addEdges(arbiter, size, 'viewer', 1);
|
||||
addEdges(arbiter, size, 'group_tuple_to_userset', 1);
|
||||
addEdges(arbiter, size, 'plan_chain', 1);
|
||||
addEdges(arbiter, size, 'account_admin_chain', 1);
|
||||
addEdges(arbiter, size, 'blocked_unless', 1);
|
||||
addEdges(arbiter, size, 'age_abac', 2);
|
||||
addEdges(arbiter, size, 'risk_limit', 2);
|
||||
addEdges(arbiter, size, 'feature_flag', 1);
|
||||
addEdges(arbiter, size, 'owa_union', 1);
|
||||
addEdges(arbiter, size, 'owa_comparator_nested', 5);
|
||||
|
||||
arbiter.setRelationConfig('owner', { type: 'direct' });
|
||||
arbiter.setRelationConfig('viewer', { type: 'direct' });
|
||||
arbiter.setRelationConfig('group_tuple_to_userset', { type: 'direct' });
|
||||
arbiter.setRelationConfig('plan_chain', { type: 'direct' });
|
||||
arbiter.setRelationConfig('account_admin_chain', { type: 'direct' });
|
||||
arbiter.setRelationConfig('blocked_unless', { type: 'direct' });
|
||||
arbiter.setRelationConfig('age_abac', { type: 'direct' });
|
||||
arbiter.setRelationConfig('risk_limit', { type: 'direct' });
|
||||
arbiter.setRelationConfig('feature_flag', { type: 'direct' });
|
||||
arbiter.setRelationConfig('owa_union', { type: 'direct' });
|
||||
arbiter.setRelationConfig('owa_comparator_nested', { type: 'direct' });
|
||||
}
|
||||
|
||||
const args = parseArgs(process.argv);
|
||||
const sizes = parseSizes(args.get('sizes')) || [10000, 50000, 100000];
|
||||
|
||||
console.log('memory_profile');
|
||||
console.log('size,nodes,edges,rss,heap_used,heap_total,external');
|
||||
|
||||
for (const size of sizes) {
|
||||
const arbiter = new Arbiter();
|
||||
gc();
|
||||
const before = measureMemory('before');
|
||||
buildGraph(arbiter, size);
|
||||
gc();
|
||||
const after = measureMemory('after');
|
||||
|
||||
const nodes = size * 2;
|
||||
const edges = size * (1 + 1 + 1 + 1 + 1 + 1 + 2 + 2 + 1 + 1 + 5);
|
||||
const delta = {
|
||||
rss: after.rss - before.rss,
|
||||
heapUsed: after.heapUsed - before.heapUsed,
|
||||
heapTotal: after.heapTotal - before.heapTotal,
|
||||
external: after.external - before.external
|
||||
};
|
||||
|
||||
console.log([
|
||||
size,
|
||||
nodes,
|
||||
edges,
|
||||
formatBytes(delta.rss),
|
||||
formatBytes(delta.heapUsed),
|
||||
formatBytes(delta.heapTotal),
|
||||
formatBytes(delta.external)
|
||||
].join(','));
|
||||
}
|
||||
Reference in New Issue
Block a user