refactor(security): unify elevated mode handling across surfaces
This commit is contained in:
+19
-101
@@ -16,7 +16,7 @@ import { MODEL_PROVIDERS } from '../../config/index.js';
|
||||
import { createClientFromConfig } from '../../daemon/models.js';
|
||||
import { auditLogger } from '../../audit/index.js';
|
||||
import type { HookEngine } from '../../hooks/index.js';
|
||||
import { randomUUID } from 'crypto';
|
||||
import { getElevationStatusMessage, setElevationFromInput } from '../../security/elevation.js';
|
||||
|
||||
export interface AgentHandlerDeps {
|
||||
sessionBridge: SessionBridge;
|
||||
@@ -391,117 +391,35 @@ export function createAgentHandlers(deps: AgentHandlerDeps) {
|
||||
if (!sessionId || !deps.sessionManager) {
|
||||
return 'Elevated mode: off';
|
||||
}
|
||||
const untilRaw = deps.sessionManager.getSessionConfig('ws', sessionId, 'elevation.until_ms');
|
||||
const reason = deps.sessionManager.getSessionConfig('ws', sessionId, 'elevation.reason') ?? '';
|
||||
const id = deps.sessionManager.getSessionConfig('ws', sessionId, 'elevation.id') ?? '';
|
||||
if (!untilRaw || !id) {
|
||||
return 'Elevated mode: off';
|
||||
}
|
||||
const untilMs = Number.parseInt(untilRaw, 10);
|
||||
if (!Number.isFinite(untilMs)) {
|
||||
return 'Elevated mode: off';
|
||||
}
|
||||
const now = Date.now();
|
||||
if (untilMs <= now) {
|
||||
deps.sessionManager.deleteSessionConfig('ws', sessionId, 'elevation.until_ms');
|
||||
deps.sessionManager.deleteSessionConfig('ws', sessionId, 'elevation.reason');
|
||||
deps.sessionManager.deleteSessionConfig('ws', sessionId, 'elevation.id');
|
||||
auditLogger?.securityElevationExpired({
|
||||
session_id: `ws:${sessionId}`,
|
||||
return getElevationStatusMessage({
|
||||
get: (key) => deps.sessionManager!.getSessionConfig('ws', sessionId, key),
|
||||
set: (key, value) => deps.sessionManager!.setSessionConfig('ws', sessionId, key, value),
|
||||
delete: (key) => deps.sessionManager!.deleteSessionConfig('ws', sessionId, key),
|
||||
}, {
|
||||
showExpiredSuffix: true,
|
||||
auditContext: {
|
||||
sessionId: `ws:${sessionId}`,
|
||||
channel: 'ws',
|
||||
sender: connectionId,
|
||||
elevation_id: id,
|
||||
until_ms: untilMs,
|
||||
reason: reason || undefined,
|
||||
});
|
||||
return 'Elevated mode: off (expired)';
|
||||
}
|
||||
const remainingMs = untilMs - now;
|
||||
const remainingSec = Math.ceil(remainingMs / 1000);
|
||||
return `Elevated mode: on (${remainingSec}s remaining)${reason ? ` — ${reason}` : ''}`;
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
setElevation: (input: string) => {
|
||||
if (!sessionId || !deps.sessionManager) {
|
||||
return 'Elevate command is not available in this session.';
|
||||
}
|
||||
const raw = input.trim();
|
||||
const parts = raw.split(/\s+/);
|
||||
const hasYes = parts.includes('--yes') || parts.includes('--confirm');
|
||||
const filtered = parts.filter(p => p !== '--yes' && p !== '--confirm');
|
||||
|
||||
if (filtered.length === 0) {
|
||||
return 'Usage: /elevate <duration> <reason...> --yes | /elevate off --yes';
|
||||
}
|
||||
|
||||
if (filtered[0] === 'off') {
|
||||
if (!hasYes) {
|
||||
return 'Refusing to disable elevation without explicit confirmation. Use: /elevate off --yes';
|
||||
}
|
||||
const existingId = deps.sessionManager.getSessionConfig('ws', sessionId, 'elevation.id') ?? randomUUID();
|
||||
const existingUntil = deps.sessionManager.getSessionConfig('ws', sessionId, 'elevation.until_ms');
|
||||
const existingReason = deps.sessionManager.getSessionConfig('ws', sessionId, 'elevation.reason') ?? '';
|
||||
deps.sessionManager.deleteSessionConfig('ws', sessionId, 'elevation.until_ms');
|
||||
deps.sessionManager.deleteSessionConfig('ws', sessionId, 'elevation.reason');
|
||||
deps.sessionManager.deleteSessionConfig('ws', sessionId, 'elevation.id');
|
||||
auditLogger?.securityElevationDisabled({
|
||||
session_id: `ws:${sessionId}`,
|
||||
return setElevationFromInput({
|
||||
get: (key) => deps.sessionManager!.getSessionConfig('ws', sessionId, key),
|
||||
set: (key, value) => deps.sessionManager!.setSessionConfig('ws', sessionId, key, value),
|
||||
delete: (key) => deps.sessionManager!.deleteSessionConfig('ws', sessionId, key),
|
||||
}, input, {
|
||||
auditContext: {
|
||||
sessionId: `ws:${sessionId}`,
|
||||
channel: 'ws',
|
||||
sender: connectionId,
|
||||
elevation_id: existingId,
|
||||
until_ms: existingUntil ? Number.parseInt(existingUntil, 10) : undefined,
|
||||
reason: existingReason || undefined,
|
||||
});
|
||||
return 'Elevated mode: off';
|
||||
}
|
||||
|
||||
if (!hasYes) {
|
||||
return 'Refusing to enable elevation without explicit confirmation. Use: /elevate <duration> <reason...> --yes';
|
||||
}
|
||||
|
||||
const dur = filtered[0];
|
||||
const reason = filtered.slice(1).join(' ').trim();
|
||||
const ttlMs = (() => {
|
||||
const m = dur.match(/^(\d+)([smhd])$/i);
|
||||
if (!m) {
|
||||
return null;
|
||||
}
|
||||
const n = Number.parseInt(m[1], 10);
|
||||
if (!Number.isFinite(n) || n <= 0) {
|
||||
return null;
|
||||
}
|
||||
const unit = m[2].toLowerCase();
|
||||
if (unit === 's') {return n * 1000;}
|
||||
if (unit === 'm') {return n * 60_000;}
|
||||
if (unit === 'h') {return n * 3_600_000;}
|
||||
if (unit === 'd') {return n * 86_400_000;}
|
||||
return null;
|
||||
})();
|
||||
if (!ttlMs) {
|
||||
return 'Invalid duration. Use one of: 30s, 10m, 1h, 1d';
|
||||
}
|
||||
|
||||
const untilMs = Date.now() + ttlMs;
|
||||
const id = randomUUID();
|
||||
deps.sessionManager.setSessionConfig('ws', sessionId, 'elevation.until_ms', String(untilMs));
|
||||
deps.sessionManager.setSessionConfig('ws', sessionId, 'elevation.id', id);
|
||||
if (reason) {
|
||||
deps.sessionManager.setSessionConfig('ws', sessionId, 'elevation.reason', reason);
|
||||
} else {
|
||||
deps.sessionManager.deleteSessionConfig('ws', sessionId, 'elevation.reason');
|
||||
}
|
||||
|
||||
auditLogger?.securityElevationEnabled({
|
||||
session_id: `ws:${sessionId}`,
|
||||
channel: 'ws',
|
||||
sender: connectionId,
|
||||
elevation_id: id,
|
||||
until_ms: untilMs,
|
||||
ttl_ms: ttlMs,
|
||||
reason: reason || undefined,
|
||||
},
|
||||
});
|
||||
|
||||
return `Elevated mode: on until ${new Date(untilMs).toISOString()}`;
|
||||
},
|
||||
|
||||
getQueue: () => {
|
||||
|
||||
Reference in New Issue
Block a user