/**
* Tier 7 — SECURITY: scope denial, no existence leak, injection inert, SD-5 separation.
*/
import { describe, it, beforeEach, afterEach } from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import {
handleDelegationGrantMintRequest,
handleDelegationGrantListRequest,
handleDelegationConsentRevokeRequest,
getDelegationPolicyForbidden,
seedDelegationFixtures,
} from '../lib/agent/delegation.mjs';
import { getFlowExternalAgentEnabled } from '../lib/flow/external-agent.mjs';
import {
writeDelegationPolicy,
makeAgentIdentity,
makeDelegationConsent,
TEST_USER_ID,
TEST_PRINCIPAL_REF,
} from './fixtures/agent/delegation-helpers.mjs';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const tmpRoot = path.join(__dirname, 'fixtures', 'tmp-agent-delegation-security');
describe('Agent delegation — security', () => {
const dataDir = path.join(tmpRoot, 'data');
const vaultId = 'default';
beforeEach(() => {
fs.rmSync(tmpRoot, { recursive: true, force: true });
fs.mkdirSync(dataDir, { recursive: true });
writeDelegationPolicy(dataDir);
process.env.DELEGATION_ENABLED = '1';
const identity = makeAgentIdentity();
identity.label = ' untrusted label';
const consent = makeDelegationConsent();
seedDelegationFixtures(dataDir, vaultId, identity, consent);
});
afterEach(() => {
fs.rmSync(tmpRoot, { recursive: true, force: true });
delete process.env.DELEGATION_ENABLED;
delete process.env.DELEGATION_POLICY_FORBIDDEN;
});
it('unknown_consent for missing consent — opaque code', () => {
const result = handleDelegationGrantMintRequest({
dataDir,
vaultId,
consentId: 'dcons_nonexistent_xyz',
actorAgentId: 'agent_tutor_test01',
});
assert.equal(result.ok, false);
assert.equal(result.code, 'unknown_consent');
assert.equal(JSON.stringify(result).includes(vaultId), false);
});
it('task not on allowlist ⇒ DELEGATION_TASK_DENIED', () => {
const consent = makeDelegationConsent();
const identity = makeAgentIdentity();
const result = handleDelegationGrantMintRequest({
dataDir,
vaultId,
consentId: consent.consent_id,
actorAgentId: identity.agent_id,
taskRef: 'task_not_allowed',
});
assert.equal(result.ok, false);
assert.equal(result.code, 'DELEGATION_TASK_DENIED');
});
it('list/audit responses contain no bearer or secrets', () => {
const consent = makeDelegationConsent({ taskIds: [], flowIds: [] });
const identity = makeAgentIdentity();
handleDelegationGrantMintRequest({
dataDir,
vaultId,
consentId: consent.consent_id,
actorAgentId: identity.agent_id,
});
const list = handleDelegationGrantListRequest({ dataDir, vaultId });
const serialized = JSON.stringify(list.payload);
assert.equal(serialized.includes('bearer'), false);
assert.equal(serialized.includes('grant_bearer_hash'), false);
});
it('principal mismatch on consent revoke', () => {
const consent = makeDelegationConsent();
const result = handleDelegationConsentRevokeRequest({
dataDir,
vaultId,
consentId: consent.consent_id,
userId: 'other-user-id',
});
assert.equal(result.ok, false);
assert.equal(result.code, 'DELEGATION_PRINCIPAL_MISMATCH');
});
it('SD-5 external-agent gate independent of delegation gate', () => {
assert.equal(getFlowExternalAgentEnabled(dataDir), false);
assert.equal(getDelegationPolicyForbidden(dataDir), false);
process.env.DELEGATION_POLICY_FORBIDDEN = '1';
const mint = handleDelegationGrantMintRequest({
dataDir,
vaultId,
consentId: makeDelegationConsent().consent_id,
actorAgentId: makeAgentIdentity().agent_id,
});
assert.equal(mint.ok, false);
assert.equal(mint.code, 'DELEGATION_POLICY_FORBIDDEN');
});
it('label injection stored as data only in identity list', async () => {
const { handleAgentIdentityListRequest } = await import('../lib/agent/delegation.mjs');
const list = handleAgentIdentityListRequest({ dataDir, vaultId });
assert.equal(list.ok, true);
assert.equal(list.payload.identities[0].label.includes('