Skip to content

[v6.0] fix(provider-utils): bound media-type sniffing decode for ID3-prefixed input - #17725

Merged
lgrammel merged 2 commits into
release-v6.0from
backport-v6.0/17709-1
Jul 22, 2026
Merged

[v6.0] fix(provider-utils): bound media-type sniffing decode for ID3-prefixed input#17725
lgrammel merged 2 commits into
release-v6.0from
backport-v6.0/17709-1

Conversation

@lgrammel

Copy link
Copy Markdown
Collaborator

Summary

Media-type sniffing is documented as an O(1) check on the first ~18 bytes (24 base64 chars). An ID3/SUQz-prefixed attachment bypassed that bound: stripID3TagsIfPresent ran before the truncation and decoded the entire base64 attachment (plus a full-size .slice() copy), then returned a Uint8Array so the 24-char cap was skipped. A 4-character prefix flipped intended O(1) sniffing into an O(N) decode + copy of the whole attachment, on every path including image detection.

Hardening

  • Decode a bounded prefix first, then strip ID3 within that bound, so no input can force a full decode.
  • Detection now runs on decoded bytes (ID3) instead of the base64 string (startsWith('SUQz')), so string and raw-byte inputs are checked uniformly and base64 alignment tricks cannot fool it.
  • The raw-Uint8Array path is bounded too, and stripID3 uses subarray (a view) instead of slice (a full copy), removing the second O(N) copy.
  • ID3 skip is capped at ID3_MAX_SCAN_BYTES (128 KB), large enough to cover typical tags with embedded cover art while keeping cost constant in attachment size.

Happy path

  • Non-ID3 attachments (PNG, JPEG, WEBP, PDF, WAV, and so on) decode only ~18 bytes exactly as before, so common detection is unchanged.
  • MP3s with an ID3v2 tag up to 128 KB, including typical embedded cover art, still auto-detect as audio/mpeg.
  • data: URLs, base64url, ArrayBuffer, Buffer, and text inputs are normalized upstream and behave the same.

Unhappy path

  • An ID3/SUQz-prefixed attachment (benign or crafted) now decodes at most ~128 KB regardless of attachment size, so sniffing stays constant-time instead of scaling with the input.
  • An ID3v2 tag larger than 128 KB is no longer scanned past, so such an MP3 is not auto-detected and falls back to the caller-supplied media type (resolveFullMediaType throws its existing "must specify subtype" error if none was provided). Raise ID3_MAX_SCAN_BYTES if larger tags must be detected; cost stays O(1) either way.
  • A crafted oversized id3Size cannot read out of bounds: subarray clamps to length and returns empty, so no signature matches and detection returns undefined.

Testing

  • Added a regression test that an ID3 tag larger than the scan bound is not detected (locks in the O(1) behavior).
  • Full detect-media-type suite passes (67 tests).

Fixes #17709

Related Issues

Backport of #17386

@lgrammel

Copy link
Copy Markdown
Collaborator Author

Bugfix review

Outcome: approved

Fixes issue

Status: fully-addresses

Detection now decodes only fixed-size prefixes, uses zero-copy Uint8Array views, preserves MP3 detection at the configured ID3 boundary, and returns undefined beyond that boundary.

Side effects

Risk: low

Normal signature detection remains limited to the existing 18-byte prefix; the only intentional behavior change is that ID3 tags beyond the fixed scan boundary no longer auto-detect.

Performance

Risk: low

Base64 work is capped at approximately 128 KiB for ID3 input and 18 bytes otherwise, while raw-byte handling uses subarray views rather than attachment-sized copies.

Backwards compatibility

Risk: low

No stored bytes are modified. Existing oversized ID3 attachments may require an explicitly supplied media type when reprocessed, which is the issue's specified fallback.

Architecture

Risk: none

The backport changes the existing media-detection utility in the owning ai package and consumes provider utilities through their declared package export, without cross-package src access or new coupling.

Change scope

Status: minimal

The three changed files are limited to the detection implementation, focused boundary regression tests, and the required patch changeset.

Security

Risk: none

The change reduces resource-exhaustion exposure, bounds malformed size-field handling, and introduces no new parsing, injection, or data-access surface.

Testing

Status: appropriate

Tests cover ordinary and ID3-tagged MP3s, raw/base64 parity at and beyond the scan boundary, and the existing image and audio signature controls.

Verification

Inspected every diff hunk and relevant caller; the 48-test detection suite passed in Node and Edge configurations, ai package type checking passed, repository formatting and lint checks passed, and a focused large-input probe confirmed bounded decoding and safe malformed-size handling. The linked ID3 specification was also reviewed.

Relevant Documentation

@lgrammel
lgrammel merged commit 1efdef8 into release-v6.0 Jul 22, 2026
54 of 55 checks passed
@lgrammel
lgrammel deleted the backport-v6.0/17709-1 branch July 22, 2026 13:18
@github-actions

Copy link
Copy Markdown
Contributor

🚀 Published in:

Package Version
ai 6.0.234 github npm
@ai-sdk/amazon-bedrock 4.0.139 github npm
@ai-sdk/angular 2.0.235 github npm
@ai-sdk/anthropic 3.0.100 github npm
@ai-sdk/anthropic-aws 1.0.22 github npm
@ai-sdk/azure 3.0.92 github npm
@ai-sdk/gateway 3.0.156 github npm
@ai-sdk/google 3.0.99 github npm
@ai-sdk/google-vertex 4.0.168 github npm
@ai-sdk/langchain 2.0.242 github npm
@ai-sdk/llamaindex 2.0.234 github npm
@ai-sdk/openai 3.0.87 github npm
@ai-sdk/react 3.0.236 github npm
@ai-sdk/rsc 2.0.234 github npm
@ai-sdk/svelte 4.0.234 github npm
@ai-sdk/vue 3.0.234 github npm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants