mirror of
https://github.com/qwibitai/nanoclaw.git
synced 2026-07-06 18:52:03 +08:00
fcee39ea14
Add '/start' back to the host FILTERED set (a Telegram fix from 866b791
was silently undone when host gating was added) so it is dropped instead
of reaching the agent as a normal message. Replace the inline isAdmin
query and its hasTable('user_roles') fail-open guard with a call to
hasAdminPrivilege; user_roles always exists (core migration 001-initial),
so the guard only ever masked a missing check. Add a focused command-gate
test covering both.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
84 lines
2.8 KiB
TypeScript
84 lines
2.8 KiB
TypeScript
/**
|
|
* Tests for the host-side command gate — filtered commands are dropped
|
|
* before reaching the container, and admin commands are gated against
|
|
* the user_roles table.
|
|
*/
|
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
|
|
import { gateCommand } from './command-gate.js';
|
|
import { closeDb, createAgentGroup, initTestDb, runMigrations } from './db/index.js';
|
|
import { createUser } from './modules/permissions/db/users.js';
|
|
import { grantRole } from './modules/permissions/db/user-roles.js';
|
|
|
|
function now(): string {
|
|
return new Date().toISOString();
|
|
}
|
|
|
|
function seedAgentGroup(id: string): void {
|
|
createAgentGroup({ id, name: id.toUpperCase(), folder: id, agent_provider: null, created_at: now() });
|
|
}
|
|
|
|
function seedUser(id: string): void {
|
|
createUser({ id, kind: 'telegram', display_name: null, created_at: now() });
|
|
}
|
|
|
|
beforeEach(() => {
|
|
const db = initTestDb();
|
|
runMigrations(db);
|
|
seedAgentGroup('ag-1');
|
|
seedAgentGroup('ag-2');
|
|
});
|
|
|
|
afterEach(() => {
|
|
closeDb();
|
|
});
|
|
|
|
describe('filtered commands', () => {
|
|
it('drops /start before it reaches the container', () => {
|
|
expect(gateCommand('/start', 'telegram:1', 'ag-1')).toEqual({ action: 'filter' });
|
|
});
|
|
|
|
it('drops /start regardless of sender', () => {
|
|
expect(gateCommand('/start', null, 'ag-1')).toEqual({ action: 'filter' });
|
|
});
|
|
});
|
|
|
|
describe('admin gating goes through roles', () => {
|
|
it('denies an admin command from a non-admin user', () => {
|
|
expect(gateCommand('/clear', 'telegram:nobody', 'ag-1')).toEqual({ action: 'deny', command: '/clear' });
|
|
});
|
|
|
|
it('denies an admin command with no sender', () => {
|
|
expect(gateCommand('/clear', null, 'ag-1')).toEqual({ action: 'deny', command: '/clear' });
|
|
});
|
|
|
|
it('allows an admin command from an owner', () => {
|
|
seedUser('telegram:owner');
|
|
grantRole({ user_id: 'telegram:owner', role: 'owner', agent_group_id: null, granted_by: null, granted_at: now() });
|
|
expect(gateCommand('/clear', 'telegram:owner', 'ag-1')).toEqual({ action: 'pass' });
|
|
});
|
|
|
|
it('allows an admin command from a scoped admin of the group', () => {
|
|
seedUser('telegram:admin');
|
|
grantRole({
|
|
user_id: 'telegram:admin',
|
|
role: 'admin',
|
|
agent_group_id: 'ag-1',
|
|
granted_by: null,
|
|
granted_at: now(),
|
|
});
|
|
expect(gateCommand('/clear', 'telegram:admin', 'ag-1')).toEqual({ action: 'pass' });
|
|
expect(gateCommand('/clear', 'telegram:admin', 'ag-2')).toEqual({ action: 'deny', command: '/clear' });
|
|
});
|
|
});
|
|
|
|
describe('normal messages pass through', () => {
|
|
it('passes a plain message', () => {
|
|
expect(gateCommand('hello there', 'telegram:1', 'ag-1')).toEqual({ action: 'pass' });
|
|
});
|
|
|
|
it('passes an unknown slash command', () => {
|
|
expect(gateCommand('/whatever', 'telegram:1', 'ag-1')).toEqual({ action: 'pass' });
|
|
});
|
|
});
|