fix(security): harden archive extraction (#16203)
* fix(browser): confine upload paths for file chooser * fix(browser): sanitize suggested download filenames * chore(lint): avoid control regex in download sanitizer * test(browser): cover absolute escape paths * docs(browser): update upload example path * refactor(browser): centralize upload path confinement * fix(infra): harden tmp dir selection * fix(security): harden archive extraction * fix(infra): harden tar extraction filter
This commit is contained in:
committed by
GitHub
parent
9a134c8a10
commit
3aa94afcfd
@@ -1,10 +1,23 @@
|
||||
import type { Command } from "commander";
|
||||
import { DEFAULT_UPLOAD_DIR, resolvePathsWithinRoot } from "../../browser/paths.js";
|
||||
import { danger } from "../../globals.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { shortenHomePath } from "../../utils.js";
|
||||
import { callBrowserRequest, type BrowserParentOpts } from "../browser-cli-shared.js";
|
||||
import { resolveBrowserActionContext } from "./shared.js";
|
||||
|
||||
function normalizeUploadPaths(paths: string[]): string[] {
|
||||
const result = resolvePathsWithinRoot({
|
||||
rootDir: DEFAULT_UPLOAD_DIR,
|
||||
requestedPaths: paths,
|
||||
scopeLabel: `uploads directory (${DEFAULT_UPLOAD_DIR})`,
|
||||
});
|
||||
if (!result.ok) {
|
||||
throw new Error(result.error);
|
||||
}
|
||||
return result.paths;
|
||||
}
|
||||
|
||||
export function registerBrowserFilesAndDownloadsCommands(
|
||||
browser: Command,
|
||||
parentOpts: (cmd: Command) => BrowserParentOpts,
|
||||
@@ -12,7 +25,10 @@ export function registerBrowserFilesAndDownloadsCommands(
|
||||
browser
|
||||
.command("upload")
|
||||
.description("Arm file upload for the next file chooser")
|
||||
.argument("<paths...>", "File paths to upload")
|
||||
.argument(
|
||||
"<paths...>",
|
||||
"File paths to upload (must be within OpenClaw temp uploads dir, e.g. /tmp/openclaw/uploads/file.pdf)",
|
||||
)
|
||||
.option("--ref <ref>", "Ref id from snapshot to click after arming")
|
||||
.option("--input-ref <ref>", "Ref id for <input type=file> to set directly")
|
||||
.option("--element <selector>", "CSS selector for <input type=file>")
|
||||
@@ -25,6 +41,7 @@ export function registerBrowserFilesAndDownloadsCommands(
|
||||
.action(async (paths: string[], opts, cmd) => {
|
||||
const { parent, profile } = resolveBrowserActionContext(cmd, parentOpts);
|
||||
try {
|
||||
const normalizedPaths = normalizeUploadPaths(paths);
|
||||
const timeoutMs = Number.isFinite(opts.timeoutMs) ? opts.timeoutMs : undefined;
|
||||
const result = await callBrowserRequest<{ download: { path: string } }>(
|
||||
parent,
|
||||
@@ -33,7 +50,7 @@ export function registerBrowserFilesAndDownloadsCommands(
|
||||
path: "/hooks/file-chooser",
|
||||
query: profile ? { profile } : undefined,
|
||||
body: {
|
||||
paths,
|
||||
paths: normalizedPaths,
|
||||
ref: opts.ref?.trim() || undefined,
|
||||
inputRef: opts.inputRef?.trim() || undefined,
|
||||
element: opts.element?.trim() || undefined,
|
||||
|
||||
Reference in New Issue
Block a user