fix(security): M2-008 Valkey key audit — SCAN over KEYS, restrict /gc to admin (#298)
Some checks failed
ci/woodpecker/push/ci Pipeline failed

Co-authored-by: Jason Woltje <jason@diversecanvas.com>
Co-committed-by: Jason Woltje <jason@diversecanvas.com>
This commit was merged in pull request #298.
This commit is contained in:
2026-03-21 20:45:43 +00:00
committed by jason.woltje
parent 02ff3b3256
commit 5b089392fd
5 changed files with 58 additions and 26 deletions

View File

@@ -77,8 +77,8 @@ export class CommandExecutorService {
message: 'Retry last message requested.',
};
case 'gc': {
// User-scoped sweep for non-admin; system-wide for admin
const result = await this.sessionGC.sweepOrphans(userId);
// Admin-only: system-wide GC sweep across all sessions
const result = await this.sessionGC.sweepOrphans();
return {
command: 'gc',
success: true,

View File

@@ -190,9 +190,9 @@ export class CommandRegistryService implements OnModuleInit {
},
{
name: 'gc',
description: 'Trigger garbage collection sweep (user-scoped)',
description: 'Trigger garbage collection sweep (admin only — system-wide)',
aliases: [],
scope: 'core',
scope: 'admin',
execution: 'socket',
available: true,
},

View File

@@ -166,11 +166,11 @@ describe('CommandExecutorService — integration', () => {
expect(result.command).toBe('nonexistent');
});
// /gc handler calls SessionGCService.sweepOrphans
it('/gc calls SessionGCService.sweepOrphans with userId', async () => {
// /gc handler calls SessionGCService.sweepOrphans (admin-only, no userId arg)
it('/gc calls SessionGCService.sweepOrphans without arguments', async () => {
const payload: SlashCommandPayload = { command: 'gc', conversationId };
const result = await executor.execute(payload, userId);
expect(mockSessionGC.sweepOrphans).toHaveBeenCalledWith(userId);
expect(mockSessionGC.sweepOrphans).toHaveBeenCalledWith();
expect(result.success).toBe(true);
expect(result.message).toContain('GC sweep complete');
expect(result.message).toContain('3 orphaned sessions');