#2922

fix(gateway): data contributions stop storing image and audio bytes

OpenDanielButler1wants to merge intofrom

https://entire.com/gh/phaseoteam/-/trails/154/changes/Phaseo/154

Data contributions (opt-in redacted request/response samples kept for 30 days) could still store user images. This PR makes them keep no image, audio or file bytes at all. It's the code side of phase 0 of the CSAM screening plan: before we screen anything, stop holding media we don't need.

What changed

  • Every data: URL is redacted, whatever its length. Before, only data URLs over 256 characters were removed, and only when they made up the whole string.
  • Data URLs embedded in text are redacted too, e.g. a markdown image inside a message.
  • Bare base64 is redacted. Any string of 256 or more base64 characters with no spaces becomes [REDACTED_BASE64]. This covers Anthropic source.data, Gemini inline_data.data, generated images (b64_json, image_generation_call.result) and any other format.
  • Known media fields are redacted at any size: b64_json, partial_image_b64, and data next to type: "base64", mime_type, mimeType, media_type or format.
  • DATA_CONTRIBUTION_REDACTION_VERSION goes to 2026-10-29-v2, so new rows can be told apart from older ones.

Why

The 10-16 CSAM review found three places where Phaseo stores image bytes: I/O logs, data contributions, and public avatars and logos. Contributions are the easiest to fix, because nothing reads the media back. Before this PR, raw base64 and short data URLs got past the redactor.

How

  • redactString now runs the data-URL and bare-base64 checks before the existing secret and PII rules.
  • sanitize checks each object key with its parent: isMediaBytesField catches short provider media fields that the length check would miss.
  • Image URLs are unchanged: they still have their query string, fragment and credentials stripped, but are kept. They point at third-party content rather than being stored bytes, and the existing test pins this.

Before and after

Input in a contributionBeforeAfter
data:image/png;base64,… (≤256 chars)stored[REDACTED_DATA_URL]
Data URL inside message textstored[REDACTED_DATA_URL]
Anthropic / Gemini / audio data fieldstored[REDACTED_BASE64]
b64_json, image_generation_call.resultstored[REDACTED_BASE64]
https://…/image.png?sig=…https://…/image.pngunchanged

Verification

  • vitest run src/pipeline/classification: 37 passed. That includes two new tests: one covering each provider's media format, and one showing that short ordinary strings such as data: "hello" and IDs are kept.
  • tsc --noEmit and eslint are clean on the gateway.
  • Existing contribution rows are not changed.

Not in this PR

  • I/O logs still store image bytes. "Open in Chat" (apps/web/src/lib/chat/logReplay.ts) rebuilds image parts from them, so stripping them is a product decision that's still open.
  • The rest of phase 0 isn't code: a terms clause, NCMEC registration, and enabling the Cloudflare CSAM tool for the avatar bucket.

Model: Claude Opus 5.5 (effort unknown) · Harness: Claude Code in T3 Code

🤖 Generated with Claude Code

Activity

6
vercel[bot]1d ago

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated
phaseo ReadyPreviewOct 29, 2026 3:17pm UTC

coderabbitai[bot]1d ago

WARNING

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 2 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 31594ce2-788a-426f-aa55-deecc12ac693
Commits

Reviewing files that changed from the base of the PR and between 401dbdf6b9b4cacad6be56f9adcb305a9f45081d and d90f5f2f398ff1c4a8be6bbc1202200b16b5df07.

Files selected for processing: 3
  • .changeset/data-contribution-media-bytes.md
  • apps/api/src/pipeline/classification/data-contribution.test.ts
  • apps/api/src/pipeline/classification/data-contribution.ts
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

chatgpt-codex-connector[bot]1d ago

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

ReviewStatusCommitReview trigger
📝 Code Review✅ Completed 2026-10-29T15:10:36.485148Zd90f5f2PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

github-actions[bot]1d ago

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None