From 1ab256cc98489aa030d1359a1c8e23657d55e730 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 26 May 2026 15:11:34 +0100 Subject: [PATCH] fix: harden tool arg decoding and codeql path --- ...rocess-tool-boundary-critical-security.yml | 2 +- .../tool-call-argument-decoding.test.ts | 24 +++++++++++++++++++ .../tool-call-argument-decoding.ts | 11 +++++++-- 3 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 src/agents/embedded-agent-runner/tool-call-argument-decoding.test.ts diff --git a/.github/codeql/codeql-mcp-process-tool-boundary-critical-security.yml b/.github/codeql/codeql-mcp-process-tool-boundary-critical-security.yml index b72485da2c9..eab6f460181 100644 --- a/.github/codeql/codeql-mcp-process-tool-boundary-critical-security.yml +++ b/.github/codeql/codeql-mcp-process-tool-boundary-critical-security.yml @@ -27,7 +27,7 @@ paths: - src/agents/agent-tool-definition-adapter.ts - src/agents/agent-tools.abort.ts - src/agents/agent-tools.before-tool-call*.ts - - src/agents/agent-tools.host-edit.ts + - src/agents/agent-tools.read.ts - src/agents/agent-tools-parameter-schema.ts - src/agents/embedded-agent-runner/effective-tool-policy.ts - src/agents/embedded-agent-runner/tool-name-allowlist.ts diff --git a/src/agents/embedded-agent-runner/tool-call-argument-decoding.test.ts b/src/agents/embedded-agent-runner/tool-call-argument-decoding.test.ts new file mode 100644 index 00000000000..87411cf0537 --- /dev/null +++ b/src/agents/embedded-agent-runner/tool-call-argument-decoding.test.ts @@ -0,0 +1,24 @@ +import { describe, expect, it } from "vitest"; +import { decodeHtmlEntitiesInObject } from "./tool-call-argument-decoding.js"; + +describe("decodeHtmlEntitiesInObject", () => { + it("decodes valid HTML entities in nested tool arguments", () => { + expect( + decodeHtmlEntitiesInObject({ + query: "Rock & Roll A 'ok'", + }), + ).toEqual({ + query: "Rock & Roll A 'ok'", + }); + }); + + it("preserves invalid numeric HTML entities", () => { + expect( + decodeHtmlEntitiesInObject({ + query: "bad � and �", + }), + ).toEqual({ + query: "bad � and �", + }); + }); +}); diff --git a/src/agents/embedded-agent-runner/tool-call-argument-decoding.ts b/src/agents/embedded-agent-runner/tool-call-argument-decoding.ts index 9a98091e95a..96536ebd9de 100644 --- a/src/agents/embedded-agent-runner/tool-call-argument-decoding.ts +++ b/src/agents/embedded-agent-runner/tool-call-argument-decoding.ts @@ -6,6 +6,13 @@ import type { MutableAssistantMessageEventStream } from "../stream-compat.js"; const HTML_ENTITY_RE = /&(?:amp|lt|gt|quot|apos|#39|#x[0-9a-f]+|#\d+);/i; function decodeHtmlEntities(value: string): string { + const decodeNumericEntity = (raw: string, radix: 10 | 16): string => { + const codePoint = Number.parseInt(raw, radix); + return Number.isFinite(codePoint) && codePoint >= 0 && codePoint <= 0x10ffff + ? String.fromCodePoint(codePoint) + : `&#${radix === 16 ? "x" : ""}${raw};`; + }; + return value .replace(/&/gi, "&") .replace(/"/gi, '"') @@ -13,8 +20,8 @@ function decodeHtmlEntities(value: string): string { .replace(/'/gi, "'") .replace(/</gi, "<") .replace(/>/gi, ">") - .replace(/&#x([0-9a-f]+);/gi, (_, hex) => String.fromCodePoint(Number.parseInt(hex, 16))) - .replace(/&#(\d+);/gi, (_, dec) => String.fromCodePoint(Number.parseInt(dec, 10))); + .replace(/&#x([0-9a-f]+);/gi, (_, hex: string) => decodeNumericEntity(hex, 16)) + .replace(/&#(\d+);/gi, (_, dec: string) => decodeNumericEntity(dec, 10)); } export function decodeHtmlEntitiesInObject(value: unknown): unknown {