fix(housekeeping): harden audited command dispatch

This commit is contained in:
2026-08-27 18:58:14 +02:00
parent 6aed71a8b2
commit 796d009c07
7 changed files with 775 additions and 124 deletions
@@ -1,4 +1,5 @@
import type { z } from "zod";
import { z } from "zod";
import { PERMS } from "@/lib/permission-slugs";
import {
HOUSEKEEPING_DOMAIN_IDS,
type HousekeepingDomainId,
@@ -10,39 +11,61 @@ import type {
} from "../contracts";
export interface HousekeepingCommandContext {
capability: HousekeepingCapabilityContext;
correlationId: string;
ipAddress: string;
readonly capability: HousekeepingCapabilityContext;
readonly correlationId: string;
readonly ipAddress: string;
}
export interface HousekeepingCommand<I, O> {
id: string;
owner: HousekeepingDomainId;
risk: "safe" | "sensitive";
capability: CapabilityRequirement;
input: z.ZodType<I>;
requiresReason: boolean;
rateLimit: { attempts: number; windowMs: number };
execute(
readonly id: string;
readonly owner: HousekeepingDomainId;
readonly risk: "safe" | "sensitive";
readonly capability: CapabilityRequirement;
readonly input: z.ZodType<I>;
readonly requiresReason: boolean;
readonly rateLimit: Readonly<{ attempts: number; windowMs: number }>;
readonly execute: (
context: HousekeepingCommandContext,
input: I,
): Promise<HousekeepingResult<O>>;
) => Promise<HousekeepingResult<O>>;
}
type RegisteredHousekeepingCommand = HousekeepingCommand<unknown, unknown>;
const approvedOwners = new Set<string>(HOUSEKEEPING_DOMAIN_IDS);
const knownCapabilitySlugs = new Set<string>(Object.values(PERMS));
const commandIdPattern = /^[a-z][a-z0-9-]*(?:\.[a-z0-9][a-z0-9-]*)+$/;
const commands = new Map<string, RegisteredHousekeepingCommand>();
let sealed = false;
export function registerHousekeepingCommand<I, O>(
command: HousekeepingCommand<I, O>,
): void {
if (sealed) throw new Error("command registry is sealed");
validateCommand(command);
if (commands.has(command.id)) {
throw new Error(`duplicate command id: ${command.id}`);
}
commands.set(command.id, command as RegisteredHousekeepingCommand);
const capability = Object.freeze({
mode: command.capability.mode,
slugs: Object.freeze([...command.capability.slugs]),
}) satisfies CapabilityRequirement;
const snapshot = Object.freeze({
id: command.id,
owner: command.owner,
risk: command.risk,
capability,
input: Object.freeze(command.input.clone()),
requiresReason: command.requiresReason,
rateLimit: Object.freeze({ ...command.rateLimit }),
execute: command.execute,
}) as RegisteredHousekeepingCommand;
commands.set(snapshot.id, snapshot);
}
export function sealHousekeepingCommandRegistry(): void {
sealed = true;
}
export function getHousekeepingCommand(
@@ -52,22 +75,37 @@ export function getHousekeepingCommand(
}
function validateCommand<I, O>(command: HousekeepingCommand<I, O>): void {
if (typeof command.id !== "string" || !command.id.trim()) {
throw new Error("empty command id");
if (
!isNormalizedIdentifier(command.id) ||
!commandIdPattern.test(command.id)
) {
throw new Error(`invalid command id: ${String(command.id)}`);
}
if (!approvedOwners.has(command.owner)) {
throw new Error(`unknown command owner: ${command.owner}`);
if (
!isNormalizedIdentifier(command.owner) ||
!approvedOwners.has(command.owner)
) {
throw new Error(`unknown command owner: ${String(command.owner)}`);
}
if (!command.id.startsWith(`${command.owner}.`)) {
throw new Error(`command owner mismatch: ${command.id}`);
}
if (
!command.input ||
typeof (command.input as { safeParse?: unknown }).safeParse !== "function"
) {
if (command.risk !== "safe" && command.risk !== "sensitive") {
throw new Error(`invalid command risk: ${command.id}`);
}
if (typeof command.requiresReason !== "boolean") {
throw new Error(`invalid command reason policy: ${command.id}`);
}
validateCapability(command.capability, command.id);
if (!(command.input instanceof z.ZodType)) {
throw new Error(`command input schema missing: ${command.id}`);
}
if (typeof command.execute !== "function") {
throw new Error(`command execute missing: ${command.id}`);
}
if (
typeof command.rateLimit !== "object" ||
command.rateLimit === null ||
!Number.isInteger(command.rateLimit.attempts) ||
command.rateLimit.attempts <= 0 ||
!Number.isInteger(command.rateLimit.windowMs) ||
@@ -76,3 +114,32 @@ function validateCommand<I, O>(command: HousekeepingCommand<I, O>): void {
throw new Error(`invalid command rate limit: ${command.id}`);
}
}
function validateCapability(
requirement: CapabilityRequirement,
commandId: string,
): void {
if (
typeof requirement !== "object" ||
requirement === null ||
(requirement.mode !== "any" && requirement.mode !== "all") ||
!Array.isArray(requirement.slugs) ||
requirement.slugs.length === 0
) {
throw new Error(`invalid command capability: ${commandId}`);
}
for (const slug of requirement.slugs) {
if (!isNormalizedIdentifier(slug) || !knownCapabilitySlugs.has(slug)) {
throw new Error(`invalid command capability: ${commandId}`);
}
}
}
function isNormalizedIdentifier(value: unknown): value is string {
return (
typeof value === "string" &&
value.length > 0 &&
value.length <= 160 &&
value === value.normalize("NFC").trim()
);
}