openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
80 lines (79 loc) • 3.31 kB
JavaScript
import path from "node:path";
//#region packages/media-core/src/inbound-path-policy.ts
const WILDCARD_SEGMENT = "*";
const WINDOWS_DRIVE_ABS_RE = /^[A-Za-z]:\//;
const WINDOWS_DRIVE_ROOT_RE = /^[A-Za-z]:$/;
function normalizePosixAbsolutePath(value) {
const trimmed = value.trim();
if (!trimmed || trimmed.includes("\0")) return;
const normalized = path.posix.normalize(trimmed.replaceAll("\\", "/"));
if (!(normalized.startsWith("/") || WINDOWS_DRIVE_ABS_RE.test(normalized)) || normalized === "/") return;
const withoutTrailingSlash = normalized.endsWith("/") ? normalized.slice(0, -1) : normalized;
if (WINDOWS_DRIVE_ROOT_RE.test(withoutTrailingSlash)) return;
return withoutTrailingSlash;
}
function splitPathSegments(value) {
return value.split("/").filter(Boolean);
}
function matchesRootPattern(params) {
const candidateSegments = splitPathSegments(params.candidatePath);
const rootSegments = splitPathSegments(params.rootPattern);
if (candidateSegments.length < rootSegments.length) return false;
for (let idx = 0; idx < rootSegments.length; idx += 1) {
const expected = rootSegments[idx];
const actual = candidateSegments[idx];
if (expected === WILDCARD_SEGMENT) continue;
if (expected !== actual) return false;
}
return true;
}
/** Validates an absolute inbound root pattern with whole-segment wildcards only. */
function isValidInboundPathRootPattern(value) {
const normalized = normalizePosixAbsolutePath(value);
if (!normalized) return false;
const segments = splitPathSegments(normalized);
if (segments.length === 0) return false;
return segments.every((segment) => segment === WILDCARD_SEGMENT || !segment.includes("*"));
}
/** Normalizes configured inbound attachment roots, dropping invalid or duplicate patterns. */
function normalizeInboundPathRoots(roots) {
const normalized = [];
const seen = /* @__PURE__ */ new Set();
for (const root of roots ?? []) {
if (typeof root !== "string") continue;
if (!isValidInboundPathRootPattern(root)) continue;
const candidate = normalizePosixAbsolutePath(root);
if (!candidate || seen.has(candidate)) continue;
seen.add(candidate);
normalized.push(candidate);
}
return normalized;
}
/** Merges inbound attachment root lists while preserving first-seen priority. */
function mergeInboundPathRoots(...rootsLists) {
const merged = [];
const seen = /* @__PURE__ */ new Set();
for (const roots of rootsLists) {
const normalized = normalizeInboundPathRoots(roots);
for (const root of normalized) {
if (seen.has(root)) continue;
seen.add(root);
merged.push(root);
}
}
return merged;
}
/** Checks whether a candidate inbound media path is covered by configured or fallback roots. */
function isInboundPathAllowed(params) {
const candidatePath = normalizePosixAbsolutePath(params.filePath);
if (!candidatePath) return false;
const roots = normalizeInboundPathRoots(params.roots);
const effectiveRoots = roots.length > 0 ? roots : normalizeInboundPathRoots(params.fallbackRoots ?? void 0);
if (effectiveRoots.length === 0) return false;
return effectiveRoots.some((rootPattern) => matchesRootPattern({
candidatePath,
rootPattern
}));
}
//#endregion
export { normalizeInboundPathRoots as i, isValidInboundPathRootPattern as n, mergeInboundPathRoots as r, isInboundPathAllowed as t };