mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-02 02:04:58 +00:00
fix: validate pdf byte cap
This commit is contained in:
@@ -287,6 +287,42 @@ describe("createPdfTool", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects invalid maxBytesMb before loading PDFs", async () => {
|
||||
await withConfiguredPdfTool(async (tool) => {
|
||||
const loadSpy = vi.spyOn(webMedia, "loadWebMediaRaw");
|
||||
|
||||
await expect(
|
||||
tool.execute("t1", {
|
||||
prompt: "test",
|
||||
pdf: "/tmp/doc.pdf",
|
||||
maxBytesMb: 0,
|
||||
}),
|
||||
).rejects.toThrow("maxBytesMb must be greater than 0");
|
||||
expect(loadSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it("passes validated maxBytesMb to PDF loading", async () => {
|
||||
await withTempPdfAgentDir(async (agentDir) => {
|
||||
const { loadSpy } = await stubPdfToolInfra(agentDir, {
|
||||
provider: "anthropic",
|
||||
input: ["text", "document"],
|
||||
});
|
||||
vi.spyOn(pdfNativeProviders, "anthropicAnalyzePdf").mockResolvedValue("native summary");
|
||||
const cfg = withPdfModel(ANTHROPIC_PDF_MODEL);
|
||||
const tool = requirePdfTool((await loadCreatePdfTool())({ config: cfg, agentDir }));
|
||||
|
||||
await tool.execute("t1", {
|
||||
prompt: "summarize",
|
||||
pdf: "/tmp/doc.pdf",
|
||||
maxBytesMb: "0.5",
|
||||
});
|
||||
|
||||
const [, loadOptions] = firstMockCall(loadSpy, "loadWebMediaRaw");
|
||||
expectFields(loadOptions, { maxBytes: 524_288 });
|
||||
});
|
||||
});
|
||||
|
||||
it("respects fsPolicy.workspaceOnly for non-sandbox pdf paths", async () => {
|
||||
await withTempPdfAgentDir(async (agentDir) => {
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-pdf-ws-"));
|
||||
@@ -686,5 +722,9 @@ describe("createPdfTool", () => {
|
||||
expect(props).toHaveProperty("password");
|
||||
expect(props).toHaveProperty("model");
|
||||
expect(props).toHaveProperty("maxBytesMb");
|
||||
expect(PdfToolSchema.properties.maxBytesMb).toMatchObject({
|
||||
type: "number",
|
||||
exclusiveMinimum: 0,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,7 +14,8 @@ import {
|
||||
} from "../../shared/string-coerce.js";
|
||||
import { resolveUserPath } from "../../utils.js";
|
||||
import type { AuthProfileStore } from "../auth-profiles/types.js";
|
||||
import { ToolInputError } from "./common.js";
|
||||
import { optionalFiniteNumberSchema } from "../schema/typebox.js";
|
||||
import { readFiniteNumberParam, ToolInputError } from "./common.js";
|
||||
import { coerceImageModelConfig, type ImageModelConfig } from "./image-tool.helpers.js";
|
||||
import {
|
||||
applyImageModelConfigDefaults,
|
||||
@@ -73,7 +74,7 @@ export const PdfToolSchema = Type.Object({
|
||||
),
|
||||
password: Type.Optional(Type.String({ description: "Password for encrypted PDFs." })),
|
||||
model: Type.Optional(Type.String()),
|
||||
maxBytesMb: Type.Optional(Type.Number()),
|
||||
maxBytesMb: optionalFiniteNumberSchema({ exclusiveMinimum: 0 }),
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -354,11 +355,12 @@ export function createPdfTool(options?: {
|
||||
record,
|
||||
DEFAULT_PROMPT,
|
||||
);
|
||||
const maxBytesMbRaw = typeof record.maxBytesMb === "number" ? record.maxBytesMb : undefined;
|
||||
const maxBytesMb =
|
||||
typeof maxBytesMbRaw === "number" && Number.isFinite(maxBytesMbRaw) && maxBytesMbRaw > 0
|
||||
? maxBytesMbRaw
|
||||
: configuredMaxBytesMb;
|
||||
readFiniteNumberParam(record, "maxBytesMb", {
|
||||
min: 0,
|
||||
minExclusive: true,
|
||||
message: "maxBytesMb must be greater than 0",
|
||||
}) ?? configuredMaxBytesMb;
|
||||
const maxBytes = Math.floor(maxBytesMb * 1024 * 1024);
|
||||
|
||||
// Parse page range
|
||||
|
||||
Reference in New Issue
Block a user