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
@@ -0,0 +1,70 @@
/**
* Snapshot read-only guard tests — verifies that public write paths
* throw when _snapshotReadOnly is true.
*
* After the P1.4 fix, the following methods must reject writes:
* - addRelation (public, existing guard)
* - removeRelation (public, existing guard)
*
* Run: node --test --test-force-exit lib/tests/property-based/snapshot-read-only.test.js
*/
import { Arbiter } from '../../src/core/Arbiter.js';
import { describe, it, beforeEach } from 'node:test';
import assert from 'node:assert/strict';
describe('Snapshot Read-Only Guards', () => {
let arbiter;
beforeEach(() => {
arbiter = new Arbiter({ fastConstructionMode: true, enableInference: false });
arbiter.addNode('user:alice', 'user');
arbiter.addNode('project:secret', 'project');
arbiter._snapshotReadOnly = true;
});
it('addRelation throws when read-only', () => {
assert.throws(
() => arbiter.addRelation('user:alice', 'can_read', 'project:secret', 0.9),
/Cannot add relation while in snapshot read-only mode/
);
});
it('removeRelation throws when read-only', () => {
arbiter._snapshotReadOnly = false;
arbiter.addRelation('user:alice', 'can_read', 'project:secret', 0.9);
arbiter._snapshotReadOnly = true;
assert.throws(
() => arbiter.removeRelation('user:alice', 'can_read', 'project:secret'),
/Cannot remove relation while in snapshot read-only mode/
);
});
it('writes succeed when not in read-only mode', () => {
arbiter._snapshotReadOnly = false;
arbiter.addRelation('user:alice', 'can_read', 'project:secret', 0.9);
assert.strictEqual(arbiter.relations.length, 1);
arbiter.addRelation('user:alice', 'can_write', 'project:secret', 0.5);
assert.strictEqual(arbiter.relations.length, 2);
});
it('deserialized snapshot rejects writes', () => {
arbiter._snapshotReadOnly = false;
arbiter.addRelation('user:alice', 'can_read', 'project:secret', 0.9);
arbiter._snapshotReadOnly = true;
assert.throws(
() => arbiter.addRelation('user:alice', 'can_write', 'project:secret', 0.5),
/Cannot add relation while in snapshot read-only mode/
);
});
it('fresh arbiter in read-only mode rejects writes immediately', () => {
assert.throws(
() => arbiter.addRelation('user:alice', 'can_read', 'project:secret', 0.5),
/Cannot add relation while in snapshot read-only mode/
);
});
});