Skip to content

Follow-up: strip marker for non-image base64 payloads should record the real mediatype (from PR #719) #722

Description

@philcunliffe

Deferred from PR #719 (which fixed #718's 124.5 MB/day regression). Not a blocker for that PR: the marker is truthful about the presence of a stripped payload even when wrong about its type, and no downstream consumer of content_text currently parses or branches on the marker's mime field.

What is wrong

stripBase64DataUris in hypaware-core/plugins-workspace/ai-gateway/src/message_projector.js replaces every data:<mime>;base64,<payload> in content_text with a fixed sentinel:

// message_projector.js:1151-1152
const BASE64_DATA_URI = /data:[^\s,]{0,255}?;base64,[A-Za-z0-9+/=_-]+/g
const STRIPPED_DATA_URI = 'data:image;base64,<stripped>'

The marker always says image, even when the stripped payload was application/pdf, audio/*, or anything else. The row then asserts something false about what was captured: a search for data:application/pdf over content_text silently misses rows where such a payload was present, because it has been rewritten to claim it was an image.

Why it was deferred rather than fixed

The fixed-constant marker is the suggested snippet from #718 itself, and #718 lists "the mime allowlist" as a point worth deciding rather than assuming. Review rounds 1 and 2 on PR #719 both flagged it and left it deliberately; test/plugins/ai-gateway-content-data-uri.test.js pins the current behaviour as intentional.

Reachability today is thin. The only production trigger on record is Codex view_image, which is PNG-only by construction, and nothing in the current codex or claude plugin source emits an input_file / application/pdf / input_audio data URI through this string path. The surrounding JSON ("type":"input_image","image_url":"data:image;base64,<stripped>") is left untouched by the regex, so the structural context a human or the n-gram index relies on for "was there an attachment here" survives intact.

Suggested fix

Verify against current line numbers before landing:

const BASE64_DATA_URI = /data:([^\s,]{0,255}?);base64,[A-Za-z0-9+/=_-]+/g
return text.replace(BASE64_DATA_URI, (_m, mime) => `data:${mime || 'application/octet-stream'};base64,<stripped>`)

This captures the matched mediatype and echoes it back, falling back to application/octet-stream for the empty-mediatype case the existing bare: data:;base64,... test already covers.

The decision needed before landing

Echo the mediatype verbatim (simplest, matches what was on the wire), or apply an allowlist/normalization (collapse image/png, image/jpeg and friends to a known set, bucket everything else as application/octet-stream) to avoid storing an attacker-influenced or malformed mediatype string in content_text. The regex already caps the mediatype at 255 chars, so an allowlist is defense in depth, not a correctness requirement.

Test to update

test/plugins/ai-gateway-content-data-uri.test.js, the case a non-image mime type is stripped as well: its expected value and its "deliberate" comment both need updating to the new intent.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    neutral:fixDelegate this issue to neutral for an autonomous fix attempt (reproduce -> fix -> PR)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions