refactor(security): unify hook rate-limit and hook module loading

This commit is contained in:
Peter Steinberger
2026-02-22 08:56:24 +01:00
parent 7cf280805c
commit 9f97555b5e
6 changed files with 152 additions and 82 deletions

View File

@@ -6,7 +6,6 @@
*/
import path from "node:path";
import { pathToFileURL } from "node:url";
import type { OpenClawConfig } from "../config/config.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
import { isPathInsideWithRealpath } from "../security/scan-paths.js";
@@ -14,6 +13,7 @@ import { resolveHookConfig } from "./config.js";
import { shouldIncludeHook } from "./config.js";
import type { InternalHookHandler } from "./internal-hooks.js";
import { registerInternalHook } from "./internal-hooks.js";
import { importFileModule, resolveFunctionModuleExport } from "./module-loader.js";
import { loadWorkspaceHookEntries } from "./workspace.js";
const log = createSubsystemLogger("hooks:loader");
@@ -82,16 +82,18 @@ export async function loadInternalHooks(
);
continue;
}
// Import handler module with cache-busting
const url = pathToFileURL(entry.hook.handlerPath).href;
const cacheBustedUrl = `${url}?t=${Date.now()}`;
const mod = (await import(cacheBustedUrl)) as Record<string, unknown>;
// Get handler function (default or named export)
const exportName = entry.metadata?.export ?? "default";
const handler = mod[exportName];
const mod = await importFileModule({
modulePath: entry.hook.handlerPath,
cacheBust: true,
});
const handler = resolveFunctionModuleExport<InternalHookHandler>({
mod,
exportName,
});
if (typeof handler !== "function") {
if (!handler) {
log.error(`Handler '${exportName}' from ${entry.hook.name} is not a function`);
continue;
}
@@ -104,7 +106,7 @@ export async function loadInternalHooks(
}
for (const event of events) {
registerInternalHook(event, handler as InternalHookHandler);
registerInternalHook(event, handler);
}
log.info(
@@ -157,21 +159,23 @@ export async function loadInternalHooks(
continue;
}
// Import the module with cache-busting to ensure fresh reload
const url = pathToFileURL(modulePath).href;
const cacheBustedUrl = `${url}?t=${Date.now()}`;
const mod = (await import(cacheBustedUrl)) as Record<string, unknown>;
// Get the handler function
const exportName = handlerConfig.export ?? "default";
const handler = mod[exportName];
const mod = await importFileModule({
modulePath,
cacheBust: true,
});
const handler = resolveFunctionModuleExport<InternalHookHandler>({
mod,
exportName,
});
if (typeof handler !== "function") {
if (!handler) {
log.error(`Handler '${exportName}' from ${modulePath} is not a function`);
continue;
}
registerInternalHook(handlerConfig.event, handler as InternalHookHandler);
registerInternalHook(handlerConfig.event, handler);
log.info(
`Registered hook (legacy): ${handlerConfig.event} -> ${modulePath}${exportName !== "default" ? `#${exportName}` : ""}`,
);