feat(security): add /elevate command and audit events
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
|
||||
import { createModelCommand } from './index.js';
|
||||
import { createElevateCommand, createModelCommand } from './index.js';
|
||||
|
||||
describe('builtin /model command', () => {
|
||||
it('passes through the full argument string', async () => {
|
||||
@@ -35,3 +35,35 @@ describe('builtin /model command', () => {
|
||||
expect(result).toEqual({ handled: true, text: 'switched' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('builtin /elevate command', () => {
|
||||
it('passes through the full argument string', async () => {
|
||||
const cmd = createElevateCommand();
|
||||
const setElevation = vi.fn(() => 'ok');
|
||||
|
||||
const result = await cmd.execute(['10m', 'reason', '--yes'], {
|
||||
channel: 'test',
|
||||
senderId: 'user',
|
||||
sessionId: 's1',
|
||||
rawInput: '/elevate 10m reason --yes',
|
||||
services: { setElevation },
|
||||
});
|
||||
|
||||
expect(setElevation).toHaveBeenCalledWith('10m reason --yes');
|
||||
expect(result).toEqual({ handled: true, text: 'ok' });
|
||||
});
|
||||
|
||||
it('shows status when no args are provided', async () => {
|
||||
const cmd = createElevateCommand();
|
||||
const getElevation = vi.fn(() => 'status');
|
||||
const result = await cmd.execute([], {
|
||||
channel: 'test',
|
||||
senderId: 'user',
|
||||
sessionId: 's1',
|
||||
rawInput: '/elevate',
|
||||
services: { getElevation },
|
||||
});
|
||||
expect(getElevation).toHaveBeenCalledOnce();
|
||||
expect(result).toEqual({ handled: true, text: 'status' });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -128,6 +128,27 @@ export function createResetCommand(): CommandDefinition {
|
||||
};
|
||||
}
|
||||
|
||||
export function createElevateCommand(): CommandDefinition {
|
||||
return {
|
||||
name: 'elevate',
|
||||
description: 'Enable or disable time-bounded elevated host mode',
|
||||
execute: async (args, ctx) => {
|
||||
if (args.length === 0) {
|
||||
if (!ctx.services?.getElevation) {
|
||||
return notAvailable('Elevate command');
|
||||
}
|
||||
return { handled: true, text: await ctx.services.getElevation() };
|
||||
}
|
||||
|
||||
if (!ctx.services?.setElevation) {
|
||||
return notAvailable('Elevate command');
|
||||
}
|
||||
|
||||
return { handled: true, text: await ctx.services.setElevation(args.join(' ')) };
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function registerBuiltinCommands(registry: CommandRegistry): void {
|
||||
registry.register(createHelpCommand(registry));
|
||||
registry.register(createStatusCommand());
|
||||
@@ -135,4 +156,5 @@ export function registerBuiltinCommands(registry: CommandRegistry): void {
|
||||
registry.register(createModelCommand());
|
||||
registry.register(createCompactCommand());
|
||||
registry.register(createResetCommand());
|
||||
registry.register(createElevateCommand());
|
||||
}
|
||||
|
||||
@@ -25,4 +25,7 @@ export interface CommandServices {
|
||||
setModel?: (tier: string) => Promise<string> | string;
|
||||
compact?: () => Promise<string> | string;
|
||||
reset?: () => Promise<string> | string;
|
||||
|
||||
getElevation?: () => Promise<string> | string;
|
||||
setElevation?: (input: string) => Promise<string> | string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user