Skip to content

Gateway: normalize HEIC input_image sources#38122

Merged
vincentkoc merged 7 commits intomainfrom
vincentkoc-code/telegram-media-max-config
Mar 6, 2026
Merged

Gateway: normalize HEIC input_image sources#38122
vincentkoc merged 7 commits intomainfrom
vincentkoc-code/telegram-media-max-config

Conversation

@vincentkoc
Copy link
Copy Markdown
Member

Summary

  • Problem: OpenClaw already handled HEIC in channel/web media paths, but Gateway HTTP input_image handling only accepted jpeg/png/gif/webp and passed HEIC through inconsistently.
  • Why it matters: HEIC/HEIF images from iPhone/macOS workflows could be rejected by schema/default allowlists or reach providers in a format they do not reliably support.
  • What changed: input_image sources now accept HEIC/HEIF, normalize them to JPEG before provider delivery, and ship with matching schema/docs/tests.
  • What did NOT change (scope boundary): this does not broaden unrelated file MIME handling or change non-image upload security policy.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

User-visible / Behavior Changes

  • Gateway HTTP input_image now accepts image/heic and image/heif in the default image MIME allowlist.
  • HEIC/HEIF inputs are normalized to JPEG before provider delivery.

Security Impact (required)

  • New permissions/capabilities? (Yes/No) No
  • Secrets/tokens handling changed? (Yes/No) No
  • New/changed network calls? (Yes/No) No
  • Command/tool execution surface changed? (Yes/No) No
  • Data access scope changed? (Yes/No) No
  • If any Yes, explain risk + mitigation:

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: Node 22 / pnpm
  • Model/provider: N/A
  • Integration/channel (if any): Gateway HTTP input_image
  • Relevant config (redacted): default gateway.http.endpoints.responses.images.allowedMimes

Steps

  1. Send a Gateway HTTP input_image request using HEIC/HEIF base64 or URL input.
  2. Observe schema validation and image extraction.
  3. Verify the normalized image passed downstream is JPEG.

Expected

  • HEIC/HEIF images are accepted and converted to JPEG inside the input-image pipeline.

Actual

  • HEIC/HEIF images were not in the default allowlist/schema and could be rejected or passed downstream without normalization.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios: HEIC base64 input normalizes to JPEG; HEIC URL input normalizes to JPEG; OpenResponses schema accepts HEIC base64 media types.
  • Edge cases checked: default MIME allowlist expansion remains image-only; URL fetch path still uses the existing SSRF guard and byte cap.
  • What you did not verify: live provider calls with a real HEIC image file.

Compatibility / Migration

  • Backward compatible? (Yes/No) Yes
  • Config/env changes? (Yes/No) No
  • Migration needed? (Yes/No) No
  • If yes, exact upgrade steps:

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: revert this PR or override gateway.http.endpoints.responses.images.allowedMimes to exclude image/heic / image/heif.
  • Files/config to restore: src/media/input-files.ts, src/gateway/open-responses.schema.ts
  • Known bad symptoms reviewers should watch for: HEIC inputs rejected unexpectedly, or provider-facing image parts staying in HEIC instead of JPEG.

Risks and Mitigations

  • Risk: HEIC inputs that convert to JPEG larger than the configured image byte cap will now fail explicitly after normalization.
    • Mitigation: the code keeps existing byte limits and returns a clear size error after conversion instead of silently sending oversized data.

@vincentkoc vincentkoc self-assigned this Mar 6, 2026
@openclaw-barnacle openclaw-barnacle bot added docs Improvements or additions to documentation gateway Gateway runtime size: S maintainer Maintainer-authored PR labels Mar 6, 2026
@vincentkoc vincentkoc marked this pull request as ready for review March 6, 2026 16:17
@aisle-research-bot
Copy link
Copy Markdown

aisle-research-bot bot commented Mar 6, 2026

🔒 Aisle Security Analysis

We found 3 potential security issue(s) in this PR:

# Severity Title
1 🟡 Medium Potential image decompression bomb / CPU & memory exhaustion via HEIC→JPEG normalization (no pixel limits, conversion before size check)
2 🟡 Medium MIME allowlist bypass via fallback to user-/server-claimed Content-Type when sniffing fails
3 🔵 Low MIME allowlist bypass via HEIC/HEIF normalization returning JPEG without re-checking allowedMimes

1. 🟡 Potential image decompression bomb / CPU & memory exhaustion via HEIC→JPEG normalization (no pixel limits, conversion before size check)

Property Value
Severity Medium
CWE CWE-400
Location src/media/input-files.ts:232-258

Description

The new HEIC support normalizes image/heic/image/heif inputs by converting them to JPEG, but does not enforce any pixel/dimension limits before decoding/encoding. This creates a DoS vector:

  • Input size is limited by limits.maxBytes, but a small HEIC can declare extremely large dimensions and trigger huge allocations during decode/encode ("decompression bomb").
  • The code checks normalizedBuffer.byteLength > maxBytes after conversion; the expensive part (decode/encode) happens first and can OOM or consume CPU.
  • The conversion uses native backends (sharp/libvips or /usr/bin/sips), which will perform full image decoding and can be CPU-intensive; repeated requests can exhaust CPU/process resources.

Vulnerable code (conversion performed without preflight dimension/pixel checks):

const normalizedBuffer = await convertHeicToJpeg(params.buffer);
if (normalizedBuffer.byteLength > params.limits.maxBytes) {
  throw new Error(`Image too large after HEIC conversion: ...`);
}

Related backend code shows sharp(buffer) is instantiated without limitInputPixels or other pixel caps:

return (buffer) => sharp(buffer, { failOnError: false });

Recommendation

Add pixel/dimension limits and fail fast before conversion.

Suggested approach:

  1. Extend InputImageLimits with a maxPixels (and optionally maxWidth/maxHeight) similar to PDFs.
  2. Before calling convertHeicToJpeg, read metadata and reject large images.
  3. Configure sharp to enforce pixel limits during decode.
  4. Consider concurrency limiting (queue) for expensive conversions.

Example (sharp backend):

// image-ops.ts
const MAX_IMAGE_PIXELS = 20_000_000; // example

async function loadSharp(): Promise<(buffer: Buffer) => ReturnType<Sharp>> {
  const mod = (await import("sharp")) as unknown as { default?: Sharp };
  const sharp = mod.default ?? (mod as unknown as Sharp);
  return (buffer) => sharp(buffer, {
    failOnError: true,
    limitInputPixels: MAX_IMAGE_PIXELS,
  });
}

Example (call site preflight):

const meta = await getImageMetadata(params.buffer);
if (!meta || meta.width * meta.height > params.limits.maxPixels) {
  throw new Error("Image dimensions exceed pixel limit");
}
const normalizedBuffer = await convertHeicToJpeg(params.buffer);

For the /usr/bin/sips path, run sips -g pixelWidth -g pixelHeight first (already implemented as sipsMetadataFromBuffer) and reject over-limit images before converting.


2. 🟡 MIME allowlist bypass via fallback to user-/server-claimed Content-Type when sniffing fails

Property Value
Severity Medium
CWE CWE-345
Location src/media/input-files.ts:237-243

Description

normalizeInputImage() attempts to determine an image's MIME type using detectMime() (magic-number sniffing via file-type), but falls back to the caller-provided mimeType when sniffing returns undefined.

This creates a content-type spoofing path:

  • Inputs:
    • Base64 sources: source.mediaType is user-controlled and defaults to "image/png".
    • URL sources: result.mimeType is derived from the remote server's Content-Type header.
  • Failure mode: detectMime() returns undefined for content it cannot recognize (e.g., plain text or arbitrary bytes).
  • Bypass: The code then uses the claimed MIME (params.mimeType) and only checks it against allowedMimes, allowing non-image data to be accepted as an allowed image type.

Concrete example (base64 spoofing):

  • Send source.type="base64", omit mediaType (defaults to image/png), and set data to base64 of a non-image payload like "hello world".
  • detectMime({buffer}) returns undefined (text has no magic signature), so sourceMime becomes "image/png" and passes the allowlist.

Security impact depends on downstream handling, but this breaks the intended guarantee that input_image actually contains an image. It can also unexpectedly route non-image payloads into image-processing paths (e.g., HEIC conversion) based solely on a spoofed MIME.

Recommendation

Do not trust the claimed/header MIME when magic-number detection fails for input_image.

Recommended: require successful sniffing and (optionally) enforce consistency with the claimed type.

Example fix:

const detected = normalizeMimeType(
  await detectMime({ buffer: params.buffer, headerMime: params.mimeType })
);
if (!detected) {
  throw new Error("Unsupported/unknown image format");
}
if (!params.limits.allowedMimes.has(detected)) {
  throw new Error(`Unsupported image MIME type: ${detected}`);
}// Optional: if a caller-supplied mimeType exists and is non-generic, require match
const claimed = normalizeMimeType(params.mimeType);
if (claimed && claimed !== detected) {
  throw new Error(`MIME mismatch: claimed ${claimed} but detected ${detected}`);
}

Additionally:

  • For URL sources, consider passing the URL/path to detectMime({ filePath }) to improve accuracy.
  • If you need a compatibility escape hatch, only allow fallback for a narrow set of known-safe cases, and/or validate by actually decoding the image with a safe library before acceptance.

3. 🔵 MIME allowlist bypass via HEIC/HEIF normalization returning JPEG without re-checking allowedMimes

Property Value
Severity Low
CWE CWE-693
Location src/media/input-files.ts:237-263

Description

normalizeInputImage() enforces limits.allowedMimes against the source detected MIME type (sourceMime), but when the source is HEIC/HEIF it always converts and returns a JPEG payload (mimeType: "image/jpeg") without verifying that JPEG is also allowed.

This creates a policy bypass when operators configure a custom allowlist that permits HEIC/HEIF but forbids JPEG:

  • Input check: allowedMimes is only checked for sourceMime (e.g., image/heic).
  • Transformation: HEIC/HEIF is converted to JPEG.
  • Output: returned InputImageContent.mimeType becomes image/jpeg even if allowedMimes does not include JPEG.

Vulnerable code:

const sourceMime = ...;
if (!params.limits.allowedMimes.has(sourceMime)) {
  throw new Error(`Unsupported image MIME type: ${sourceMime}`);
}
...
if (HEIC_INPUT_IMAGE_MIMES.has(sourceMime)) {
  const normalizedBuffer = await convertHeicToJpeg(params.buffer);
  return {
    type: "image",
    data: normalizedBuffer.toString("base64"),
    mimeType: "image/jpeg",
  };
}

Call path showing this can affect operator-configured policies:

  • gateway.http.endpoints.responses.images.allowedMimes -> resolveResponsesLimits() -> extractImageContentFromSource() -> normalizeInputImage() -> image delivered to providers as { data, mimeType }.
    • src/gateway/openresponses-http.ts calls extractImageContentFromSource(imageSource, limits.images).
  • Same pattern for OpenAI-compatible endpoint:
    • src/gateway/openai-http.ts calls extractImageContentFromSource(source, limits.images).

Because downstream code uses image.mimeType to construct provider requests and sometimes to infer filenames/extensions, this can violate MIME/type policy expectations and routing logic.

Recommendation

Enforce the allowlist against the final delivered MIME type (post-normalization), or split configuration into separate allowlists for source vs delivered types.

Minimal fix (validate output mime after normalization):

const sourceMime = ...;
if (!params.limits.allowedMimes.has(sourceMime)) {
  throw new Error(`Unsupported image MIME type: ${sourceMime}`);
}

if (HEIC_INPUT_IMAGE_MIMES.has(sourceMime)) {
  const normalizedMime = NORMALIZED_INPUT_IMAGE_MIME; // image/jpeg
  if (!params.limits.allowedMimes.has(normalizedMime)) {
    throw new Error(`Unsupported normalized image MIME type: ${normalizedMime}`);
  }

  const normalizedBuffer = await convertHeicToJpeg(params.buffer);
  ...
  return { type: "image", data: normalizedBuffer.toString("base64"), mimeType: normalizedMime };
}

Also update docs/config help to clarify whether images.allowedMimes applies to:

  • the source MIME type,
  • the delivered MIME type,
  • or both (recommended).

If the intent is "allow HEIC but always normalize to JPEG", consider automatically requiring/adding image/jpeg when image/heic/image/heif is allowed, but document this behavior explicitly to avoid surprising policy broadening.


Analyzed PR: #38122 at commit 9d14504

Last updated on: 2026-03-06T16:34:04Z

@vincentkoc vincentkoc merged commit 9aceb51 into main Mar 6, 2026
8 checks passed
@vincentkoc vincentkoc deleted the vincentkoc-code/telegram-media-max-config branch March 6, 2026 16:19
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 6, 2026

Greptile Summary

This PR adds HEIC/HEIF support to the Gateway HTTP input_image pipeline: the default MIME allowlist, Zod schema, docs, and tests are all extended consistently, and a new normalizeInputImage helper converts HEIC/HEIF buffers to JPEG via the existing convertHeicToJpeg primitive before they reach downstream providers.

Key observations:

  • Scope creep in MIME detection: normalizeInputImage calls detectMime (magic-byte sniffing via fileTypeFromBuffer) on every image type, not only HEIC/HEIF. Previously the declared mediaType was used as-is; now the sniffed type takes precedence. For non-HEIC images whose declared type disagrees with their actual magic bytes, the behaviour changes — the sniffed type is used for the allowlist check and is returned downstream. Constraining the detectMime call to the HEIC branch only would keep the change within its stated scope.
  • Non-hermetic HEIC tests: The new tests in input-files.fetch-guard.test.ts mock ./image-ops.js but leave ./mime.js unmocked, so the real fileTypeFromBuffer is invoked against synthetic test buffers. The tests pass because the fake data isn't recognised by file-type, but adding an explicit detectMime mock would make the unit tests properly isolated.
  • Size guards are correctly ordered: pre-conversion byte cap prevents oversized inputs, post-conversion check catches cases where JPEG expansion exceeds the limit.
  • Schema, docs, and CHANGELOG are all updated in sync with the runtime changes.

Confidence Score: 3/5

  • Mostly safe to merge, but the unintended MIME-sniffing side-effect on non-HEIC images warrants attention before shipping.
  • The HEIC conversion logic and size guards are correct, and all touched areas (schema, docs, tests, allowlist) are updated consistently. However, normalizeInputImage silently changes how MIME types are resolved for all image types — not just HEIC — because detectMime is called unconditionally. This is an unannounced behavior change that could cause legitimate non-HEIC images to be rejected or delivered with a different MIME if their declared type doesn't match their magic bytes. The missing detectMime mock in tests means this path isn't exercised in isolation.
  • src/media/input-files.ts — specifically the normalizeInputImage function and its unconditional detectMime call for non-HEIC images.

Last reviewed commit: 9d14504

Comment on lines +232 to +264
async function normalizeInputImage(params: {
buffer: Buffer;
mimeType?: string;
limits: InputImageLimits;
}): Promise<InputImageContent> {
const sourceMime =
normalizeMimeType(await detectMime({ buffer: params.buffer, headerMime: params.mimeType })) ??
normalizeMimeType(params.mimeType) ??
"application/octet-stream";
if (!params.limits.allowedMimes.has(sourceMime)) {
throw new Error(`Unsupported image MIME type: ${sourceMime}`);
}

if (!HEIC_INPUT_IMAGE_MIMES.has(sourceMime)) {
return {
type: "image",
data: params.buffer.toString("base64"),
mimeType: sourceMime,
};
}

const normalizedBuffer = await convertHeicToJpeg(params.buffer);
if (normalizedBuffer.byteLength > params.limits.maxBytes) {
throw new Error(
`Image too large after HEIC conversion: ${normalizedBuffer.byteLength} bytes (limit: ${params.limits.maxBytes} bytes)`,
);
}
return {
type: "image",
data: normalizedBuffer.toString("base64"),
mimeType: NORMALIZED_INPUT_IMAGE_MIME,
};
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

detectMime sniffing now applies to all image types, not just HEIC

normalizeInputImage calls detectMime (which runs fileTypeFromBuffer magic-byte sniffing) for every image processed, not only for HEIC/HEIF. Previously the declared mediaType was trusted at face value; now the sniffed type takes precedence.

This creates a subtle behavioral change for non-HEIC images: if fileTypeFromBuffer returns a MIME type that differs from the declared one (e.g. a real PNG sent with mediaType: "image/webp"), the sniffed "image/png" will be used for the allowlist check and for the mimeType returned downstream. In the worst case, an existing caller whose declared type disagrees with the actual magic bytes could start receiving rejections.

The PR description explicitly scopes the change to HEIC handling. Consider guarding the detectMime call behind the HEIC check so that non-HEIC images continue to use the declared type:

async function normalizeInputImage(params: {
  buffer: Buffer;
  mimeType?: string;
  limits: InputImageLimits;
}): Promise<InputImageContent> {
  const declaredMime =
    normalizeMimeType(params.mimeType) ?? "application/octet-stream";

  // Only sniff for HEIC/HEIF to confirm actual format before conversion.
  const sourceMime = HEIC_INPUT_IMAGE_MIMES.has(declaredMime)
    ? (normalizeMimeType(
        await detectMime({ buffer: params.buffer, headerMime: params.mimeType }),
      ) ?? declaredMime)
    : declaredMime;

  if (!params.limits.allowedMimes.has(sourceMime)) {
    throw new Error(`Unsupported image MIME type: ${sourceMime}`);
  }

  if (!HEIC_INPUT_IMAGE_MIMES.has(sourceMime)) {
    return {
      type: "image",
      data: params.buffer.toString("base64"),
      mimeType: sourceMime,
    };
  }

  const normalizedBuffer = await convertHeicToJpeg(params.buffer);
  if (normalizedBuffer.byteLength > params.limits.maxBytes) {
    throw new Error(
      `Image too large after HEIC conversion: ${normalizedBuffer.byteLength} bytes (limit: ${params.limits.maxBytes} bytes)`,
    );
  }
  return {
    type: "image",
    data: normalizedBuffer.toString("base64"),
    mimeType: NORMALIZED_INPUT_IMAGE_MIME,
  };
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/media/input-files.ts
Line: 232-264

Comment:
**`detectMime` sniffing now applies to all image types, not just HEIC**

`normalizeInputImage` calls `detectMime` (which runs `fileTypeFromBuffer` magic-byte sniffing) for every image processed, not only for HEIC/HEIF. Previously the declared `mediaType` was trusted at face value; now the sniffed type takes precedence.

This creates a subtle behavioral change for non-HEIC images: if `fileTypeFromBuffer` returns a MIME type that differs from the declared one (e.g. a real PNG sent with `mediaType: "image/webp"`), the sniffed `"image/png"` will be used for the allowlist check and for the `mimeType` returned downstream. In the worst case, an existing caller whose declared type disagrees with the actual magic bytes could start receiving rejections.

The PR description explicitly scopes the change to HEIC handling. Consider guarding the `detectMime` call behind the HEIC check so that non-HEIC images continue to use the declared type:

```
async function normalizeInputImage(params: {
  buffer: Buffer;
  mimeType?: string;
  limits: InputImageLimits;
}): Promise<InputImageContent> {
  const declaredMime =
    normalizeMimeType(params.mimeType) ?? "application/octet-stream";

  // Only sniff for HEIC/HEIF to confirm actual format before conversion.
  const sourceMime = HEIC_INPUT_IMAGE_MIMES.has(declaredMime)
    ? (normalizeMimeType(
        await detectMime({ buffer: params.buffer, headerMime: params.mimeType }),
      ) ?? declaredMime)
    : declaredMime;

  if (!params.limits.allowedMimes.has(sourceMime)) {
    throw new Error(`Unsupported image MIME type: ${sourceMime}`);
  }

  if (!HEIC_INPUT_IMAGE_MIMES.has(sourceMime)) {
    return {
      type: "image",
      data: params.buffer.toString("base64"),
      mimeType: sourceMime,
    };
  }

  const normalizedBuffer = await convertHeicToJpeg(params.buffer);
  if (normalizedBuffer.byteLength > params.limits.maxBytes) {
    throw new Error(
      `Image too large after HEIC conversion: ${normalizedBuffer.byteLength} bytes (limit: ${params.limits.maxBytes} bytes)`,
    );
  }
  return {
    type: "image",
    data: normalizedBuffer.toString("base64"),
    mimeType: NORMALIZED_INPUT_IMAGE_MIME,
  };
}
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +10 to +12
vi.mock("./image-ops.js", () => ({
convertHeicToJpeg: (...args: unknown[]) => convertHeicToJpegMock(...args),
}));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing mock for detectMime / mime.js

normalizeInputImage (called by extractImageContentFromSource) now imports and calls detectMime from ./mime.js, which in turn invokes fileTypeFromBuffer from the file-type package. The test file mocks ./image-ops.js but leaves ./mime.js unmocked, so the real fileTypeFromBuffer runs against the fake test buffers (Buffer.from("heic-source"), Buffer.from("heic-url-source")).

The tests pass today because fileTypeFromBuffer returns undefined for unrecognised byte sequences, allowing detectMime to fall back to the supplied headerMime. However:

  • If a future version of file-type happens to recognise the fake bytes as a known format, the MIME used in the allowlist check would change and the test would fail silently.
  • The unit under test (extractImageContentFromSource) is no longer truly isolated from the MIME detection layer.

Adding a mock keeps the test hermetic:

const detectMimeMock = vi.fn();

vi.mock("./mime.js", () => ({
  detectMime: (...args: unknown[]) => detectMimeMock(...args),
}));

Then in the HEIC tests:

detectMimeMock.mockResolvedValue("image/heic");
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/media/input-files.fetch-guard.test.ts
Line: 10-12

Comment:
**Missing mock for `detectMime` / `mime.js`**

`normalizeInputImage` (called by `extractImageContentFromSource`) now imports and calls `detectMime` from `./mime.js`, which in turn invokes `fileTypeFromBuffer` from the `file-type` package. The test file mocks `./image-ops.js` but leaves `./mime.js` unmocked, so the real `fileTypeFromBuffer` runs against the fake test buffers (`Buffer.from("heic-source")`, `Buffer.from("heic-url-source")`).

The tests pass today because `fileTypeFromBuffer` returns `undefined` for unrecognised byte sequences, allowing `detectMime` to fall back to the supplied `headerMime`. However:

- If a future version of `file-type` happens to recognise the fake bytes as a known format, the MIME used in the allowlist check would change and the test would fail silently.
- The unit under test (`extractImageContentFromSource`) is no longer truly isolated from the MIME detection layer.

Adding a mock keeps the test hermetic:

```ts
const detectMimeMock = vi.fn();

vi.mock("./mime.js", () => ({
  detectMime: (...args: unknown[]) => detectMimeMock(...args),
}));
```

Then in the HEIC tests:
```ts
detectMimeMock.mockResolvedValue("image/heic");
```

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9d14504ded

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +261 to +262
data: normalizedBuffer.toString("base64"),
mimeType: NORMALIZED_INPUT_IMAGE_MIME,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Recount normalized HEIC bytes against total image budget

extractImageContentFromSource now rewrites HEIC/HEIF base64 inputs to JPEG (normalizedBuffer.toString("base64")), but OpenAI request accounting still adds estimateBase64DecodedBytes(source.data) for base64 sources before extraction and never replaces that with the normalized size (src/gateway/openai-http.ts:302-314). This means maxTotalImageBytes can be bypassed whenever HEIC compresses much smaller than the converted JPEG (for example multiple small HEICs that expand after conversion), so requests that should be rejected can proceed with a much larger effective image payload.

Useful? React with 👍 / 👎.

ant1eicher pushed a commit to ant1eicher/openclaw that referenced this pull request Mar 6, 2026
* Media: normalize HEIC input images

* Gateway: accept HEIC image input schema

* Media: add HEIC input normalization tests

* Gateway: cover HEIC input schema parity

* Docs: document HEIC input image support

* Changelog: note HEIC input image fix
Saitop pushed a commit to NomiciAI/openclaw that referenced this pull request Mar 8, 2026
* Media: normalize HEIC input images

* Gateway: accept HEIC image input schema

* Media: add HEIC input normalization tests

* Gateway: cover HEIC input schema parity

* Docs: document HEIC input image support

* Changelog: note HEIC input image fix
jenawant pushed a commit to jenawant/openclaw that referenced this pull request Mar 10, 2026
* Media: normalize HEIC input images

* Gateway: accept HEIC image input schema

* Media: add HEIC input normalization tests

* Gateway: cover HEIC input schema parity

* Docs: document HEIC input image support

* Changelog: note HEIC input image fix
dhoman pushed a commit to dhoman/chrono-claw that referenced this pull request Mar 11, 2026
* Media: normalize HEIC input images

* Gateway: accept HEIC image input schema

* Media: add HEIC input normalization tests

* Gateway: cover HEIC input schema parity

* Docs: document HEIC input image support

* Changelog: note HEIC input image fix
senw-developers pushed a commit to senw-developers/va-openclaw that referenced this pull request Mar 17, 2026
* Media: normalize HEIC input images

* Gateway: accept HEIC image input schema

* Media: add HEIC input normalization tests

* Gateway: cover HEIC input schema parity

* Docs: document HEIC input image support

* Changelog: note HEIC input image fix
V-Gutierrez pushed a commit to V-Gutierrez/openclaw-vendor that referenced this pull request Mar 17, 2026
* Media: normalize HEIC input images

* Gateway: accept HEIC image input schema

* Media: add HEIC input normalization tests

* Gateway: cover HEIC input schema parity

* Docs: document HEIC input image support

* Changelog: note HEIC input image fix
alexey-pelykh pushed a commit to remoteclaw/remoteclaw that referenced this pull request Mar 20, 2026
* Media: normalize HEIC input images

* Gateway: accept HEIC image input schema

* Media: add HEIC input normalization tests

* Gateway: cover HEIC input schema parity

* Docs: document HEIC input image support

* Changelog: note HEIC input image fix

(cherry picked from commit 9aceb51)
alexey-pelykh pushed a commit to remoteclaw/remoteclaw that referenced this pull request Mar 20, 2026
* Media: normalize HEIC input images

* Gateway: accept HEIC image input schema

* Media: add HEIC input normalization tests

* Gateway: cover HEIC input schema parity

* Docs: document HEIC input image support

* Changelog: note HEIC input image fix

(cherry picked from commit 9aceb51)
alexey-pelykh pushed a commit to remoteclaw/remoteclaw that referenced this pull request Mar 20, 2026
* Media: normalize HEIC input images

* Gateway: accept HEIC image input schema

* Media: add HEIC input normalization tests

* Gateway: cover HEIC input schema parity

* Docs: document HEIC input image support

* Changelog: note HEIC input image fix

(cherry picked from commit 9aceb51)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Improvements or additions to documentation gateway Gateway runtime maintainer Maintainer-authored PR size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant