import test from 'brittle'
import b4a from 'b4a'
import { createApplyFunction } from '../lib/auth/identity-apply.js'
import { encodeOp, OpTypes } from '../lib/auth/identity-operations.js'
/**
* Security tests for hyper-identity contract validation.
*
* These tests verify that rogue Autobase writers cannot inject
* unauthorized operations. Each test creates a mock Hyperbee view
* and simulates operations from different writers.
*/
// Helper: mock Hyperbee batch with in-memory Map
function mockView () {
const data = new Map()
return {
data,
batch: () => ({
put: async (k, v) => data.set(k, { value: v }),
get: async (k) => data.get(k) || null,
del: async (k) => data.delete(k),
flush: async () => {},
close: async () => {},
createReadStream: (opts) => ({
[Symbol.asyncIterator]: async function * () {
for (const [k, v] of data) {
if (opts.gt && k <= opts.gt) continue
if (opts.lt && k >= opts.lt) continue
if (opts.limit && --opts.limit < 0) return
yield { key: k, value: v.value }
}
}
})
})
}
}
// Helper: create a node as if appended by a specific writer
function nodeFrom (writerKeyHex, op) {
return {
value: encodeOp(op),
from: { key: b4a.from(writerKeyHex, 'hex') }
}
}
const ADMIN_KEY = 'aa'.repeat(32)
const USER_KEY = 'bb'.repeat(32)
const ROGUE_KEY = 'cc'.repeat(32)
test('contract: bootstrap — first writer can create admin user', async (t) => {
const apply = createApplyFunction({ contractValidation: true })
const view = mockView()
// Empty registry: first writer is trusted
const nodes = [nodeFrom(ADMIN_KEY, {
type: OpTypes.CREATE_USER,
userId: 'admin-user',
username: 'admin',
name: 'Admin',
globalPermissions: ['read', 'write', 'admin'],
identities: [ADMIN_KEY],
createdAt: Date.now()
})]
await apply(nodes, view, {})
t.ok(view.data.has('user:admin-user'), 'admin user created')
const user = view.data.get('user:admin-user').value
t.ok(user.globalPermissions.includes('admin'), 'has admin permissions')
})
test('contract: admin can upgrade permissions', async (t) => {
const apply = createApplyFunction({ contractValidation: true })
const view = mockView()
// Setup: create admin user with writer key linked
view.data.set('user:admin-user', {
value: {
userId: 'admin-user',
globalPermissions: ['read', 'write', 'admin'],
identities: [ADMIN_KEY]
}
})
view.data.set('pubkey:' + ADMIN_KEY, { value: { identityId: ADMIN_KEY } })
view.data.set('user-identity:' + ADMIN_KEY, { value: { userId: 'admin-user' } })
// Setup: create regular user
view.data.set('user:regular-user', {
value: {
userId: 'regular-user',
globalPermissions: ['read'],
identities: [USER_KEY]
}
})
// Admin upgrades regular user — should succeed
const nodes = [nodeFrom(ADMIN_KEY, {
type: OpTypes.UPGRADE_PERMISSIONS,
userId: 'regular-user',
resourceName: '*',
permissions: ['read', 'write', 'admin']
})]
await apply(nodes, view, {})
const user = view.data.get('user:regular-user').value
t.ok(user.globalPermissions.includes('admin'), 'permissions upgraded by admin')
})
test('contract: non-admin CANNOT upgrade permissions', async (t) => {
const apply = createApplyFunction({ contractValidation: true })
const view = mockView()
// Setup: admin exists
view.data.set('user:admin-user', {
value: { userId: 'admin-user', globalPermissions: ['read', 'write', 'admin'], identities: [ADMIN_KEY] }
})
view.data.set('pubkey:' + ADMIN_KEY, { value: { identityId: ADMIN_KEY } })
view.data.set('user-identity:' + ADMIN_KEY, { value: { userId: 'admin-user' } })
// Setup: regular user
view.data.set('user:regular-user', {
value: { userId: 'regular-user', globalPermissions: ['read'], identities: [USER_KEY] }
})
view.data.set('pubkey:' + USER_KEY, { value: { identityId: USER_KEY } })
view.data.set('user-identity:' + USER_KEY, { value: { userId: 'regular-user' } })
// Regular user tries to upgrade themselves — should be BLOCKED
const nodes = [nodeFrom(USER_KEY, {
type: OpTypes.UPGRADE_PERMISSIONS,
userId: 'regular-user',
resourceName: '*',
permissions: ['read', 'write', 'admin']
})]
await apply(nodes, view, {})
const user = view.data.get('user:regular-user').value
t.alike(user.globalPermissions, ['read'], 'permissions NOT upgraded — contract blocked it')
})
test('contract: rogue writer CANNOT inject admin ops', async (t) => {
const apply = createApplyFunction({ contractValidation: true })
const view = mockView()
// Setup: admin and regular user exist
view.data.set('user:admin-user', {
value: { userId: 'admin-user', globalPermissions: ['read', 'write', 'admin'], identities: [ADMIN_KEY] }
})
view.data.set('pubkey:' + ADMIN_KEY, { value: { identityId: ADMIN_KEY } })
view.data.set('user-identity:' + ADMIN_KEY, { value: { userId: 'admin-user' } })
// Rogue writer (unknown key, no associated user) tries to:
// 1. Create an admin user
// 2. Delete an existing user
// 3. Merge accounts
const nodes = [
// CREATE_USER is allowed (but with read-only perms, autoRegister handles admin promotion)
nodeFrom(ROGUE_KEY, {
type: OpTypes.CREATE_USER,
userId: 'rogue-user',
name: 'Rogue',
globalPermissions: ['read', 'write', 'admin'],
identities: [ROGUE_KEY],
createdAt: Date.now()
}),
// DELETE_USER requires admin — should be BLOCKED
nodeFrom(ROGUE_KEY, {
type: OpTypes.DELETE_USER,
userId: 'admin-user'
}),
// MERGE_ACCOUNTS requires admin — should be BLOCKED
nodeFrom(ROGUE_KEY, {
type: OpTypes.MERGE_ACCOUNTS,
primaryUserId: 'rogue-user',
secondaryUserId: 'admin-user'
})
]
await apply(nodes, view, {})
// CREATE_USER goes through (it's always allowed)
t.ok(view.data.has('user:rogue-user'), 'rogue user created (CREATE_USER is open)')
// But admin still exists (DELETE was blocked)
t.ok(view.data.has('user:admin-user'), 'admin NOT deleted — contract blocked it')
// Admin not merged (MERGE was blocked)
const admin = view.data.get('user:admin-user').value
t.not(admin.mergedInto, 'rogue-user', 'admin NOT merged — contract blocked it')
})
test('contract: user can update their own profile', async (t) => {
const apply = createApplyFunction({ contractValidation: true })
const view = mockView()
view.data.set('user:alice', {
value: { userId: 'alice', name: 'Alice', globalPermissions: ['read'], identities: [USER_KEY] }
})
view.data.set('pubkey:' + USER_KEY, { value: { identityId: USER_KEY } })
view.data.set('user-identity:' + USER_KEY, { value: { userId: 'alice' } })
// Alice updates her own name — should succeed
const nodes = [nodeFrom(USER_KEY, {
type: OpTypes.UPDATE_USER,
userId: 'alice',
name: 'Alice Smith'
})]
await apply(nodes, view, {})
const user = view.data.get('user:alice').value
t.is(user.name, 'Alice Smith', 'self-update succeeded')
})
test('contract: user CANNOT update another user', async (t) => {
const apply = createApplyFunction({ contractValidation: true })
const view = mockView()
view.data.set('user:alice', {
value: { userId: 'alice', name: 'Alice', globalPermissions: ['read'], identities: [USER_KEY] }
})
view.data.set('pubkey:' + USER_KEY, { value: { identityId: USER_KEY } })
view.data.set('user-identity:' + USER_KEY, { value: { userId: 'alice' } })
view.data.set('user:bob', {
value: { userId: 'bob', name: 'Bob', globalPermissions: ['read'], identities: ['dd'.repeat(32)] }
})
// Alice tries to update Bob — should be BLOCKED
const nodes = [nodeFrom(USER_KEY, {
type: OpTypes.UPDATE_USER,
userId: 'bob',
name: 'Hacked'
})]
await apply(nodes, view, {})
const bob = view.data.get('user:bob').value
t.is(bob.name, 'Bob', 'Bob NOT modified — contract blocked cross-user update')
})
test('contract: revocation requires admin', async (t) => {
const apply = createApplyFunction({ contractValidation: true })
const view = mockView()
view.data.set('user:admin-user', {
value: { userId: 'admin-user', globalPermissions: ['read', 'write', 'admin'], identities: [ADMIN_KEY] }
})
view.data.set('pubkey:' + ADMIN_KEY, { value: { identityId: ADMIN_KEY } })
view.data.set('user-identity:' + ADMIN_KEY, { value: { userId: 'admin-user' } })
view.data.set('user:regular-user', {
value: { userId: 'regular-user', globalPermissions: ['read'], identities: [USER_KEY] }
})
view.data.set('pubkey:' + USER_KEY, { value: { identityId: USER_KEY } })
view.data.set('user-identity:' + USER_KEY, { value: { userId: 'regular-user' } })
// Regular user tries to revoke admin's identity — BLOCKED
const rogueRevoke = [nodeFrom(USER_KEY, {
type: OpTypes.REVOKE_IDENTITY,
identityId: ADMIN_KEY,
revokedAt: Date.now(),
reason: 'hostile takeover'
})]
await apply(rogueRevoke, view, {})
t.not(view.data.has('revoked:' + ADMIN_KEY), 'admin identity NOT revoked by non-admin')
// Admin revokes regular user's identity — succeeds
const adminRevoke = [nodeFrom(ADMIN_KEY, {
type: OpTypes.REVOKE_IDENTITY,
identityId: USER_KEY,
revokedAt: Date.now(),
reason: 'compromised'
})]
await apply(adminRevoke, view, {})
t.ok(view.data.has('revoked:' + USER_KEY), 'identity revoked by admin')
})
test('contract: disabled by default (backwards compat)', async (t) => {
// Default: no contractValidation option
const apply = createApplyFunction()
const view = mockView()
view.data.set('user:admin-user', {
value: { userId: 'admin-user', globalPermissions: ['read', 'write', 'admin'], identities: [ADMIN_KEY] }
})
// Rogue writer can do anything when contract is disabled
const nodes = [nodeFrom(ROGUE_KEY, {
type: OpTypes.DELETE_USER,
userId: 'admin-user'
})]
await apply(nodes, view, {})
// Without contract validation, the delete goes through
t.not(view.data.has('user:admin-user'), 'delete went through without contract validation')
})
test('contract: unknown writer blocked after bootstrap', async (t) => {
const apply = createApplyFunction({ contractValidation: true })
const view = mockView()
// Registry has users (not empty bootstrap)
view.data.set('user:existing', {
value: { userId: 'existing', globalPermissions: ['read', 'write', 'admin'], identities: [ADMIN_KEY] }
})
view.data.set('pubkey:' + ADMIN_KEY, { value: { identityId: ADMIN_KEY } })
view.data.set('user-identity:' + ADMIN_KEY, { value: { userId: 'existing' } })
// Unknown writer (not linked to any user) tries to update a user
const nodes = [nodeFrom(ROGUE_KEY, {
type: OpTypes.UPDATE_USER,
userId: 'existing',
name: 'Hacked'
})]
await apply(nodes, view, {})
const user = view.data.get('user:existing').value
t.is(user.name, undefined, 'update blocked — unknown writer has no associated user')
})