mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-14 13:06:10 +00:00
* refactor(extensions): trim long-tail plugin exports * refactor(whatsapp): remove dead media relay
72 lines
1.8 KiB
TypeScript
72 lines
1.8 KiB
TypeScript
// Leaf contract shared by write-time schema validation and resolve-time data reads.
|
|
// Kept dependency-free so schema.ts and data-read.ts never import each other.
|
|
import path from "node:path";
|
|
|
|
export const DATA_READ_RPC_ALLOWLIST = [
|
|
"health",
|
|
"system-presence",
|
|
"usage.status",
|
|
"usage.cost",
|
|
"agents.list",
|
|
"sessions.list",
|
|
"sessions.resolve",
|
|
"sessions.get",
|
|
"sessions.usage",
|
|
"sessions.usage.timeseries",
|
|
"sessions.usage.logs",
|
|
"node.list",
|
|
"node.describe",
|
|
"cron.get",
|
|
"cron.list",
|
|
"cron.status",
|
|
"cron.runs",
|
|
] as const;
|
|
|
|
type WorkspaceBindingErrorCode =
|
|
| "binding_denied"
|
|
| "binding_not_found"
|
|
| "binding_too_large"
|
|
| "binding_invalid"
|
|
| "binding_client_resolved";
|
|
|
|
export class WorkspaceBindingResolutionError extends Error {
|
|
constructor(
|
|
readonly code: WorkspaceBindingErrorCode,
|
|
message: string,
|
|
) {
|
|
super(message);
|
|
this.name = "WorkspaceBindingResolutionError";
|
|
}
|
|
}
|
|
|
|
function hasControlCharacter(value: string): boolean {
|
|
for (const char of value) {
|
|
const code = char.charCodeAt(0);
|
|
if (code < 0x20 || code === 0x7f) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
export function normalizeWorkspaceDataLogicalPath(value: string): string {
|
|
if (
|
|
value.startsWith("/") ||
|
|
path.isAbsolute(value) ||
|
|
path.win32.isAbsolute(value) ||
|
|
hasControlCharacter(value)
|
|
) {
|
|
throw new WorkspaceBindingResolutionError("binding_invalid", "file binding path is invalid");
|
|
}
|
|
const normalized = value.replaceAll("\\", "/");
|
|
const parts = normalized.split("/").filter(Boolean);
|
|
if (
|
|
parts.length === 0 ||
|
|
parts[0] === "~" ||
|
|
parts.some((part) => part === "." || part === ".." || part.includes(":"))
|
|
) {
|
|
throw new WorkspaceBindingResolutionError("binding_invalid", "file binding path is invalid");
|
|
}
|
|
return parts.join("/");
|
|
}
|