fix(security): enforce plugin and hook path containment

This commit is contained in:
Peter Steinberger
2026-02-19 15:34:58 +01:00
parent 10379e7dcd
commit 81b19aaa1a
14 changed files with 387 additions and 8 deletions

View File

@@ -9,6 +9,7 @@ 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";
import { resolveHookConfig } from "./config.js";
import { shouldIncludeHook } from "./config.js";
import type { InternalHookHandler } from "./internal-hooks.js";
@@ -71,6 +72,16 @@ export async function loadInternalHooks(
}
try {
if (
!isPathInsideWithRealpath(entry.hook.baseDir, entry.hook.handlerPath, {
requireRealpath: true,
})
) {
log.error(
`Hook '${entry.hook.name}' handler path resolves outside hook directory: ${entry.hook.handlerPath}`,
);
continue;
}
// Import handler module with cache-busting
const url = pathToFileURL(entry.hook.handlerPath).href;
const cacheBustedUrl = `${url}?t=${Date.now()}`;
@@ -135,6 +146,16 @@ export async function loadInternalHooks(
log.error(`Handler module path must stay within workspaceDir: ${rawModule}`);
continue;
}
if (
!isPathInsideWithRealpath(baseDir, modulePath, {
requireRealpath: true,
})
) {
log.error(
`Handler module path resolves outside workspaceDir after symlink resolution: ${rawModule}`,
);
continue;
}
// Import the module with cache-busting to ensure fresh reload
const url = pathToFileURL(modulePath).href;