mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-18 15:04:46 +00:00
fix(hooks): allow dot-prefixed handler paths
This commit is contained in:
@@ -20,6 +20,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Telegram: allow trusted local Bot API media files whose filenames start with dots instead of falling back to remote download.
|
||||
- Agents/Codex app-server: remap injected context files under dot-dot-prefixed workspace directories when a run switches to an effective sandbox workspace.
|
||||
- Control UI/i18n: use the installed workspace pi runtime for locale refreshes, update the fallback package pin, and skip scheduled refreshes with invalid provider credentials instead of failing main.
|
||||
- Hooks: load workspace-relative legacy hook modules from dot-dot-prefixed directories without treating the filename prefix as parent traversal.
|
||||
- Plugins: preserve installed package metadata and persisted registry freshness checks for plugin package paths under dot-dot-prefixed directories.
|
||||
- Agents: allow dot-dot-prefixed filenames such as `..note.txt` through sandbox FS bridge, remote sandbox reads, and apply_patch summaries without mistaking the name for parent traversal.
|
||||
- CLI/migrate: hide per-item source/plugin hints on non-conflicting Codex skill and plugin selection prompts, keeping the hint text reserved for rows that actually need attention. Thanks @sjf.
|
||||
|
||||
@@ -261,6 +261,28 @@ describe("loader", () => {
|
||||
expect(keys).toContain("command:stop");
|
||||
});
|
||||
|
||||
it("loads legacy handler modules from dot-prefixed workspace paths", async () => {
|
||||
await fs.mkdir(path.join(tmpDir, "..hooks"), { recursive: true });
|
||||
await writeHandlerModule(
|
||||
path.join("..hooks", "legacy-handler.js"),
|
||||
'export default async function(event) { event.messages.push("dot-prefixed-hook"); }\n',
|
||||
);
|
||||
|
||||
const cfg = createEnabledHooksConfig([
|
||||
{
|
||||
event: "command:new",
|
||||
module: path.join("..hooks", "legacy-handler.js"),
|
||||
},
|
||||
]);
|
||||
|
||||
const count = await loadInternalHooks(cfg, tmpDir);
|
||||
expect(count).toBe(1);
|
||||
|
||||
const event = createInternalHookEvent("command", "new", "test-session");
|
||||
await triggerInternalHook(event);
|
||||
expect(event.messages).toEqual(["dot-prefixed-hook"]);
|
||||
});
|
||||
|
||||
it("preserves plugin-registered hooks when workspace hooks reload", async () => {
|
||||
const pluginHandler = vi.fn();
|
||||
registerInternalHook("gateway:startup", pluginHandler);
|
||||
|
||||
@@ -34,6 +34,15 @@ function safeLogValue(value: string): string {
|
||||
return sanitizeForLog(value);
|
||||
}
|
||||
|
||||
function isNonEmptyRelativePathInsideRoot(relativePath: string): boolean {
|
||||
return (
|
||||
relativePath !== "" &&
|
||||
relativePath !== ".." &&
|
||||
!relativePath.startsWith(`..${path.sep}`) &&
|
||||
!path.isAbsolute(relativePath)
|
||||
);
|
||||
}
|
||||
|
||||
function maybeWarnTrustedHookSource(source: string): void {
|
||||
if (source === "openclaw-workspace") {
|
||||
log.warn(
|
||||
@@ -211,7 +220,7 @@ export async function loadInternalHooks(
|
||||
continue;
|
||||
}
|
||||
const rel = path.relative(baseDirReal, modulePathSafe);
|
||||
if (!rel || rel.startsWith("..") || path.isAbsolute(rel)) {
|
||||
if (!isNonEmptyRelativePathInsideRoot(rel)) {
|
||||
log.error(`Handler module path must stay within workspaceDir: ${safeLogValue(rawModule)}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user