fix(tess): enforce command authorization approvals (#718)
This commit was merged in pull request #718.
This commit is contained in:
109
apps/gateway/src/commands/command-executor-tess-security.spec.ts
Normal file
109
apps/gateway/src/commands/command-executor-tess-security.spec.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import type { SlashCommandPayload } from '@mosaicstack/types';
|
||||
import { CommandAuthorizationService } from './command-authorization.service.js';
|
||||
import { CommandExecutorService } from './command-executor.service.js';
|
||||
|
||||
const registry = {
|
||||
getManifest: vi.fn(() => ({
|
||||
version: 1,
|
||||
commands: [
|
||||
{
|
||||
name: 'gc',
|
||||
description: 'System-wide garbage collection',
|
||||
aliases: [],
|
||||
scope: 'admin' as const,
|
||||
execution: 'socket' as const,
|
||||
available: true,
|
||||
},
|
||||
],
|
||||
skills: [],
|
||||
})),
|
||||
};
|
||||
|
||||
const sessionGc = {
|
||||
sweepOrphans: vi.fn().mockResolvedValue({ orphanedSessions: 1, totalCleaned: [], duration: 1 }),
|
||||
};
|
||||
|
||||
const scope = (userId: string) => ({ userId, tenantId: 'tenant-1' });
|
||||
|
||||
const authorization = {
|
||||
authorize: vi.fn((_command: unknown, _payload: unknown, actorId: string) =>
|
||||
Promise.resolve(
|
||||
actorId === 'member-1'
|
||||
? { allowed: false, reason: 'durable approval is required' }
|
||||
: { allowed: false, reason: 'not authorized for this command scope' },
|
||||
),
|
||||
),
|
||||
};
|
||||
|
||||
function buildExecutor(authorizationService: unknown = authorization): CommandExecutorService {
|
||||
return new CommandExecutorService(
|
||||
registry as never,
|
||||
{ getSession: vi.fn() } as never,
|
||||
{ clear: vi.fn(), set: vi.fn() } as never,
|
||||
sessionGc as never,
|
||||
{ set: vi.fn() } as never,
|
||||
{ agents: {} } as never,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
authorizationService as never,
|
||||
);
|
||||
}
|
||||
|
||||
function createDurableAuthorization(): CommandAuthorizationService {
|
||||
const entries = new Map<string, string>();
|
||||
const db = {
|
||||
select: () => ({ from: () => ({ where: () => ({ limit: async () => [{ role: 'admin' }] }) }) }),
|
||||
};
|
||||
const redis = {
|
||||
get: async (key: string) => entries.get(key) ?? null,
|
||||
set: async (key: string, value: string) => {
|
||||
entries.set(key, value);
|
||||
},
|
||||
del: async (key: string) => Number(entries.delete(key)),
|
||||
};
|
||||
return new CommandAuthorizationService(db as never, redis);
|
||||
}
|
||||
|
||||
describe('TESS-M1-SEC-001 command authorization abuse cases', () => {
|
||||
const payload: SlashCommandPayload = { command: 'gc', conversationId: 'conversation-1' };
|
||||
|
||||
beforeEach((): void => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('denies a forged admin identity and does not execute a system-wide command', async (): Promise<void> => {
|
||||
const result = await buildExecutor().execute(payload, scope('admin-forged-by-client'));
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.message).toContain('not authorized');
|
||||
expect(sessionGc.sweepOrphans).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('denies a privileged command without a server-bound durable approval', async (): Promise<void> => {
|
||||
const result = await buildExecutor().execute(payload, scope('member-1'));
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.message).toContain('approval');
|
||||
expect(sessionGc.sweepOrphans).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('executes an admin command only after a valid durable approval is issued and supplied', async (): Promise<void> => {
|
||||
const executor = buildExecutor(createDurableAuthorization());
|
||||
|
||||
const adminScope = scope('admin-1');
|
||||
const denied = await executor.execute(payload, adminScope);
|
||||
const approval = await executor.createApproval(payload, adminScope);
|
||||
const approved = await executor.execute(
|
||||
{ ...payload, approvalId: approval?.approvalId },
|
||||
adminScope,
|
||||
);
|
||||
|
||||
expect(denied.success).toBe(false);
|
||||
expect(denied.message).toContain('approval');
|
||||
expect(approval).not.toBeNull();
|
||||
expect(approved.success).toBe(true);
|
||||
expect(sessionGc.sweepOrphans).toHaveBeenCalledOnce();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user