fix(browser): allow inbound media uploads

Allow the browser upload tool to resolve OpenClaw-managed inbound media refs such as `media://inbound/<id>` and sandbox-relative `media/inbound/<id>` while preserving the existing upload-root path contract.

Keep upload-root files ahead of sandbox-relative inbound fallback, reject nested absolute inbound media files, and validate raw `media://` paths before URL normalization so traversal-shaped refs cannot resolve to direct media ids.

Verification:
- `OPENCLAW_VITEST_MAX_WORKERS=1 node scripts/run-vitest.mjs extensions/browser/src/browser/paths.test.ts --reporter=verbose`
- `OPENCLAW_VITEST_MAX_WORKERS=1 node scripts/run-vitest.mjs extensions/browser/src/browser/paths.test.ts --reporter=dot`
- `OPENCLAW_HEAVY_CHECK_LOCK_SCOPE=worktree node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.extensions.test.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/extensions-test.tsbuildinfo`
- `pnpm lint --threads=8`
- `.agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main`
- `git diff --check`
- GitHub PR checks on be08e6c8a8: dependency-guard, check-lint, check-test-types, check-additional-extension-bundled, checks-fast-contracts-plugins-a, checks-fast-contracts-plugins-b all passed.

Fixes #83544.

Co-authored-by: Zee Zheng <zheng.zuo0@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Zee Zheng
2026-05-31 06:49:07 +08:00
committed by GitHub
parent d05e4a4bc6
commit 8be581cbf8
16 changed files with 736 additions and 61 deletions

View File

@@ -8,7 +8,6 @@ import {
import {
type AnyAgentTool,
type NodeListNode,
DEFAULT_UPLOAD_DIR,
BrowserToolSchema,
applyBrowserProxyPaths,
browserAct,
@@ -37,8 +36,8 @@ import {
readStringParam,
readStringValue,
resolveBrowserConfig,
resolveExistingUploadPaths,
resolveRuntimeImageSanitization,
resolveExistingPathsWithinRoot,
resolveNodeIdFromList,
resolveProfile,
selectDefaultNodeFromList,
@@ -821,15 +820,11 @@ export function createBrowserTool(opts?: {
if (paths.length === 0) {
throw new Error("paths required");
}
const uploadPathsResult = await resolveExistingPathsWithinRoot({
rootDir: DEFAULT_UPLOAD_DIR,
requestedPaths: paths,
scopeLabel: `uploads directory (${DEFAULT_UPLOAD_DIR})`,
});
if (!uploadPathsResult.ok) {
throw new Error(uploadPathsResult.error);
const resolvedResult = await resolveExistingUploadPaths({ requestedPaths: paths });
if (!resolvedResult.ok) {
throw new Error(resolvedResult.error);
}
const normalizedPaths = uploadPathsResult.paths;
const normalizedPaths = resolvedResult.paths;
const ref = readStringParam(params, "ref");
const inputRef = readStringParam(params, "inputRef");
const element = readStringParam(params, "element");