* add security hardening features - Add authentication vault (`auth save/login/list/show/delete`) so credentials are stored locally and never exposed to the LLM (fixes Snyk W007) - Add `--content-boundaries` flag to wrap page-sourced output in structural markers, helping LLMs distinguish tool output from untrusted page content (fixes Snyk W011) - Add `--allowed-domains` flag to restrict browser navigation to trusted domains - Add `--action-policy` for static allow/deny gating of action categories, with opt-in `--confirm-actions`/`--confirm-interactive` for orchestrator or human-in-the-loop confirmation - Add `--max-output` flag to truncate large page outputs, preventing context flooding - New docs page at /security, updated README, SKILL.md, CLI help text, and templates * fixes * fixes * fixes * fixes * fixes * fixes * fixes * docs
54 lines
1.1 KiB
TypeScript
54 lines
1.1 KiB
TypeScript
import { randomBytes } from 'node:crypto';
|
|
|
|
interface PendingConfirmation {
|
|
id: string;
|
|
action: string;
|
|
category: string;
|
|
description: string;
|
|
command: Record<string, unknown>;
|
|
timer: ReturnType<typeof setTimeout>;
|
|
}
|
|
|
|
const AUTO_DENY_TIMEOUT_MS = 60_000;
|
|
|
|
const pending = new Map<string, PendingConfirmation>();
|
|
|
|
function generateId(): string {
|
|
return `c_${randomBytes(8).toString('hex')}`;
|
|
}
|
|
|
|
export function requestConfirmation(
|
|
action: string,
|
|
category: string,
|
|
description: string,
|
|
command: Record<string, unknown>
|
|
): { confirmationId: string } {
|
|
const id = generateId();
|
|
|
|
const timer = setTimeout(() => {
|
|
pending.delete(id);
|
|
}, AUTO_DENY_TIMEOUT_MS);
|
|
|
|
pending.set(id, {
|
|
id,
|
|
action,
|
|
category,
|
|
description,
|
|
command,
|
|
timer,
|
|
});
|
|
|
|
return { confirmationId: id };
|
|
}
|
|
|
|
export function getAndRemovePending(
|
|
id: string
|
|
): { command: Record<string, unknown>; action: string } | null {
|
|
const entry = pending.get(id);
|
|
if (!entry) return null;
|
|
|
|
clearTimeout(entry.timer);
|
|
pending.delete(id);
|
|
return { command: entry.command, action: entry.action };
|
|
}
|