mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-07 22:11:23 +00:00
fix: guard resolveUserPath against undefined input (#10176)
* fix: guard resolveUserPath against undefined input When subagent spawner omits workspaceDir, resolveUserPath receives undefined and crashes on .trim(). Add a falsy guard that falls back to process.cwd(), matching the behavior callers already expect. Closes #10089 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: harden runner workspace fallback (#10176) (thanks @Yida-Dev) * fix: harden workspace fallback scoping (#10176) (thanks @Yida-Dev) * refactor: centralize workspace fallback classification and redaction (#10176) (thanks @Yida-Dev) * test: remove explicit any from utils mock (#10176) (thanks @Yida-Dev) * security: reject malformed agent session keys for workspace resolution (#10176) (thanks @Yida-Dev) --------- Co-authored-by: Yida-Dev <reyifeijun@gmail.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Gustavo Madeira Santana <gumadeiras@gmail.com>
This commit is contained in:
106
src/agents/workspace-run.ts
Normal file
106
src/agents/workspace-run.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { redactIdentifier } from "../logging/redact-identifier.js";
|
||||
import {
|
||||
classifySessionKeyShape,
|
||||
DEFAULT_AGENT_ID,
|
||||
normalizeAgentId,
|
||||
parseAgentSessionKey,
|
||||
} from "../routing/session-key.js";
|
||||
import { resolveUserPath } from "../utils.js";
|
||||
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "./agent-scope.js";
|
||||
|
||||
export type WorkspaceFallbackReason = "missing" | "blank" | "invalid_type";
|
||||
type AgentIdSource = "explicit" | "session_key" | "default";
|
||||
|
||||
export type ResolveRunWorkspaceResult = {
|
||||
workspaceDir: string;
|
||||
usedFallback: boolean;
|
||||
fallbackReason?: WorkspaceFallbackReason;
|
||||
agentId: string;
|
||||
agentIdSource: AgentIdSource;
|
||||
};
|
||||
|
||||
function resolveRunAgentId(params: {
|
||||
sessionKey?: string;
|
||||
agentId?: string;
|
||||
config?: OpenClawConfig;
|
||||
}): {
|
||||
agentId: string;
|
||||
agentIdSource: AgentIdSource;
|
||||
} {
|
||||
const rawSessionKey = params.sessionKey?.trim() ?? "";
|
||||
const shape = classifySessionKeyShape(rawSessionKey);
|
||||
if (shape === "malformed_agent") {
|
||||
throw new Error("Malformed agent session key; refusing workspace resolution.");
|
||||
}
|
||||
|
||||
const explicit =
|
||||
typeof params.agentId === "string" && params.agentId.trim()
|
||||
? normalizeAgentId(params.agentId)
|
||||
: undefined;
|
||||
if (explicit) {
|
||||
return { agentId: explicit, agentIdSource: "explicit" };
|
||||
}
|
||||
|
||||
const defaultAgentId = resolveDefaultAgentId(params.config ?? {});
|
||||
if (shape === "missing" || shape === "legacy_or_alias") {
|
||||
return {
|
||||
agentId: defaultAgentId || DEFAULT_AGENT_ID,
|
||||
agentIdSource: "default",
|
||||
};
|
||||
}
|
||||
|
||||
const parsed = parseAgentSessionKey(rawSessionKey);
|
||||
if (parsed?.agentId) {
|
||||
return {
|
||||
agentId: normalizeAgentId(parsed.agentId),
|
||||
agentIdSource: "session_key",
|
||||
};
|
||||
}
|
||||
|
||||
// Defensive fallback, should be unreachable for non-malformed shapes.
|
||||
return {
|
||||
agentId: defaultAgentId || DEFAULT_AGENT_ID,
|
||||
agentIdSource: "default",
|
||||
};
|
||||
}
|
||||
|
||||
export function redactRunIdentifier(value: string | undefined): string {
|
||||
return redactIdentifier(value, { len: 12 });
|
||||
}
|
||||
|
||||
export function resolveRunWorkspaceDir(params: {
|
||||
workspaceDir: unknown;
|
||||
sessionKey?: string;
|
||||
agentId?: string;
|
||||
config?: OpenClawConfig;
|
||||
}): ResolveRunWorkspaceResult {
|
||||
const requested = params.workspaceDir;
|
||||
const { agentId, agentIdSource } = resolveRunAgentId({
|
||||
sessionKey: params.sessionKey,
|
||||
agentId: params.agentId,
|
||||
config: params.config,
|
||||
});
|
||||
if (typeof requested === "string") {
|
||||
const trimmed = requested.trim();
|
||||
if (trimmed) {
|
||||
return {
|
||||
workspaceDir: resolveUserPath(trimmed),
|
||||
usedFallback: false,
|
||||
agentId,
|
||||
agentIdSource,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const fallbackReason: WorkspaceFallbackReason =
|
||||
requested == null ? "missing" : typeof requested === "string" ? "blank" : "invalid_type";
|
||||
const fallbackWorkspace = resolveAgentWorkspaceDir(params.config ?? {}, agentId);
|
||||
return {
|
||||
workspaceDir: resolveUserPath(fallbackWorkspace),
|
||||
usedFallback: true,
|
||||
fallbackReason,
|
||||
agentId,
|
||||
agentIdSource,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user