fix(media): add shared media MIME validation helpers#76566
fix(media): add shared media MIME validation helpers#76566masatohoshino wants to merge 3 commits into
Conversation
|
Codex review: needs maintainer review before merge. Summary Reproducibility: not applicable. This PR adds helper/API surface and intentionally has no runtime caller. Source inspection and the PR body show the current branch adds unit coverage plus terminal import proof for the new SDK subpath exports. Real behavior proof Next step before merge Security Review detailsBest possible solution: Land the helper-only SDK addition if maintainers approve the API shape, keeping channel/adopter migrations in separate focused PRs with call-site tests. Do we have a high-confidence way to reproduce the issue? Not applicable: this PR adds helper/API surface and intentionally has no runtime caller. Source inspection and the PR body show the current branch adds unit coverage plus terminal import proof for the new SDK subpath exports. Is this the best way to solve the issue? Mostly yes: the helper-only split matches the related maintainer direction, and the latest branch includes the narrow SDK export plus generated API hash update. The final decision is whether this exact public SDK helper shape should be supported. Acceptance criteria:
What I checked:
Likely related people:
Remaining risk / open question:
Codex review notes: model gpt-5.5, reasoning high; reviewed against 4aa671b71a1a. |
…ia media-mime subpath The two new helpers added to src/media/mime.ts in the first commit on this branch are only consumable from internal core paths. Bundled plugins consume the narrow subpath openclaw/plugin-sdk/media-mime, which re-exports a fixed list of MIME helpers from src/media/mime.js but did not include the two new ones. Without this entry plugins cannot import them from the public SDK surface — addressing the Codex P2 finding on this PR.
Two helpers added to openclaw/plugin-sdk/media-mime in the prior commit (isVerifiedAudioSource, sanitizeMediaMime) intentionally extend the public SDK surface. Update docs/.generated/plugin-sdk-api-baseline.sha256 so the drift gate (pnpm plugin-sdk:api:check) reflects the new exports. Addresses Codex P2 finding on PR openclaw#76566.
9fbe30b to
eab493b
Compare
|
Closing this PR to reduce maintainer review load. This PR adds shared media MIME validation helpers and a plugin-SDK export, but it has no runtime caller in this PR. The intended follow-up adapter migrations have not materialized, and the PR has since accumulated conflict / review-staleness. Given the current OpenClaw review shape, an unused helper-only surface is not the strongest PR form. I’d rather re-submit this bundled with its first concrete adapter caller if an outbound MIME-validation gap appears in a specific channel. Nothing is lost here; the code remains available in git history. Closing to reduce P3 shelf noise. |
Summary
src/media/exposes MIME and audio-classification primitives, but there is no shared seam that distinguishes the caller-provided classifiedkindof a media payload from filename/URL hints, and no shared seam that normalizes a free-form MIME string before it reaches an outbound header. Adapter and core paths inline ad hoc checks that drift over time.src/media/mime.tsand re-exported via the existingsrc/plugin-sdk/media-runtime.tswildcard barrel:isVerifiedAudioSource(media: { kind?, contentType? })andsanitizeMediaMime(input, options?). Tests added insrc/media/mime.test.ts. Brief CHANGELOG entry added under### Changesfor plugin-author visibility.expectedPrefixoption, noisVerifiedMediaSource(media, kind)shape), no cross-channel adapter changes, no docs.Supporting context: the helper-only / adopter-PR split direction was previously affirmed by the maintainer in the close note on #68744.
Trust model
isVerifiedAudioSourcereads only the caller-provided classifiedkindfield. It does not infer audio from filename or URL hints, and acontentTypestarting withaudio/is not enough on its own (a remote sender can set the HTTPContent-Type). ThecontentTypefield is kept on the parameter shape as a seam for a later sniffed-MIME extension viadetectMime, but it is not read today.sanitizeMediaMimevalidates a MIME string against the RFC 2045 token character set (broader than RFC 6838's restricted-name, to admit valid vendor types such asapplication/vnd.x~v1+json), strips ASCII control characters and DEL, lowercases the type/subtype, and by default strips all parameters.preserveCodecsParam: truepreserves common single-codec, multi-codec, and quotedcodecs=...parameters when present (e.g.audio/ogg; codecs=opus,audio/mp4; codecs=mp4a.40.2,opus,video/mp4; codecs="avc1.42e01e,mp4a.40.2"); it remains the only parameter the helper round-trips, and malformed or injection-shaped values fall through to the strip-all-parameters path.Non-goals
expectedPrefix, noisVerifiedMediaSource(media, kind)generalization, no header-level helper.Follow-up candidates
Adopter PRs (outbound paths and core audio-source classification call sites) will land separately, each small and individually revertable.
Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
Root Cause (if applicable)
N/A — additive helper surface, no behavior fixed.
Regression Test Plan (if applicable)
N/A — no regression; helpers have no caller in this PR.
src/media/mime.test.tsUser-visible / Behavior Changes
None.
Diagram (if applicable)
N/A.
Security Impact (required)
NoNoNoNoNoYes, explain risk + mitigation: N/ANo runtime security impact; this PR adds security-relevant helper surface only, and nothing calls it.
Repro + Verification
Environment
Steps
pnpm installpnpm check:changed(covers tsgo:core, tsgo:core:test, oxlint, vitest changed lanes)pnpm test src/media/mime.test.tspnpm exec oxfmt --check --threads=1 src/media/mime.ts src/media/mime.test.tsExpected
pnpm check:changedpasses; targeted vitest run reports the new helper assertions pass; oxfmt check is clean.Actual
Evidence
## Real behavior proofsection below.Real behavior proof
Captured 2026-05-17 against branch
pr/openclaw-media-helper-bundlehead9fbe30b0a3(= basefb83e6a03f+ new commit9fbe30b0a3 fix(plugin-sdk): expose isVerifiedAudioSource and sanitizeMediaMime via media-mime subpath).Behavior or issue addressed: the two new helpers (
isVerifiedAudioSource,sanitizeMediaMime) added insrc/media/mime.tsare intended for plugin code to consume through the narrow public aliasopenclaw/plugin-sdk/media-mime(the same alias that already re-exportsdetectMime,extensionForMime,getFileExtension,mediaKindFromMime,normalizeMimeType). The original commit on this PR only added the helpers tosrc/media/mime.tsand did not extend the re-export list, so plugin code could not reach them from the public SDK surface — addressing the Codex P2 finding about the missing SDK export.Real environment tested: Linux x86_64 local dev workspace (Node 22, pnpm 10.33.2) running against this branch's actual source tree. The proof script resolves the import alias
openclaw/plugin-sdk/media-mimethrough the real plugin-sdk path map (the same resolver bundled plugins use at runtime), and the script is run via tsx — no mocks, no test framework, no stubs.Exact steps or command run after this patch:
isVerifiedAudioSource,sanitizeMediaMime) to the re-export list insrc/plugin-sdk/media-mime.ts.openclaw/plugin-sdk/media-mime(scripts/proof-media-helper-narrow-subpath.ts, not committed).Concrete commands run on the rebased branch:
Evidence after fix:
Terminal transcript from the standalone SDK-subpath probe (actual production helpers resolved via the real plugin-sdk alias and invoked against fixed inputs, no test framework involved):
Observed result after fix:
openclaw/plugin-sdk/media-mime(the import-shape probe lists both alongside the previously-exposeddetectMime/extensionForMime/getFileExtension/mediaKindFromMime/normalizeMimeType).isVerifiedAudioSourcehonors the documented trust model:{kind:"audio"}→ true; raw{contentType:"audio/ogg"}(nokind) → false; mismatched{kind:"image", contentType:"audio/ogg"}→ false;{}→ false. URL/extension hints cannot promote a payload to "audio".sanitizeMediaMimestrips control characters (CRLF, NUL, DEL) by returning null, accepts RFC 2045 tokens including vendor types likeapplication/vnd.x~v1+json, normalizes case (Audio/OGG→audio/ogg), strips unrelated parameters by default, and preservescodecs=...(single, comma-separated multi-codec, and matched-quote forms) whenpreserveCodecsParam:trueis passed. Malformed inputs and non-MIME strings return null.Against the prior commit
fb83e6a03fthe same import statement would fail with a TS / module-resolution error because the two helpers were absent from the re-export list — the public SDK surface had no path to them.What was not tested:
B-4throughB-9per the PR scope split rule).dist/artifact — the proof imports from the alias-resolved source.pnpm check:changedrantsgo-typecheck-all on the changed surface so the public type shape is validated against consumers.Human Verification (required)
isVerifiedAudioSourcereturns true only forkind === "audio"; explicit negative cases foraudio/-prefixedcontentTypealone, missingkind, and unrelated kinds.sanitizeMediaMimerejects ASCII control characters (\r\n,\n,\t,\x00,\x7f), accepts the RFC 2045 token character set including~,%,*for valid vendor types, and preserves common single-codec, multi-codec, and quotedcodecs=...parameters whenpreserveCodecsParam: trueis passed (e.g.codecs=opus,codecs=mp4a.40.2,opus,codecs="avc1.42e01e,mp4a.40.2"); malformed or injection-shaped values (mismatched quotes, trailing comma, semicolon inside a quoted value) fall through to the strip-all-parameters path.pnpm check:changed(core + core test lanes) was run and reported clean.contentType: "audio/ogg"alone is not enough to be a verified audio source.application/vnd.example~v1+jsonis not falsely rejected by the MIME validation regex.audio/ogg; charset=utf-8is normalized toaudio/ogg(parameters stripped by default).Review Conversations
Compatibility / Migration
Yes(additive surface only)NoNoRisks and Mitigations