Skip to content

fix(ai): harden getMediaTypeFromUrl against prototype-property collision#14751

Merged
lgrammel merged 3 commits into
vercel:mainfrom
etairl:fix/url-media-type-prototype-confusion
Apr 27, 2026
Merged

fix(ai): harden getMediaTypeFromUrl against prototype-property collision#14751
lgrammel merged 3 commits into
vercel:mainfrom
etairl:fix/url-media-type-prototype-confusion

Conversation

@etairl

@etairl etairl commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Background

getMediaTypeFromUrl in convert-to-language-model-prompt.ts does an ext in URL_EXTENSION_TO_MEDIA_TYPE check on a plain object literal. Because plain objects inherit from Object.prototype, the in operator returns true for inherited keys like constructor, toString, hasOwnProperty, etc. A URL ending in .constructor (or any other Object.prototype member) therefore takes the lookup branch and returns the inherited value — for .constructor, that's the Object constructor function, which is then forwarded as mediaType to provider adapters.

This is a low-severity correctness/typing bug rather than an exploit path, but it's worth fixing: the helper's return type is string | undefined and a non-string slipping through can break downstream code paths that assume a string mediaType.

Splitting this out per maintainer feedback on #14749 that the media-type fix should be its own PR.

Summary

  • packages/ai/src/prompt/convert-to-language-model-prompt.ts: replace ext in URL_EXTENSION_TO_MEDIA_TYPE with Object.hasOwn(URL_EXTENSION_TO_MEDIA_TYPE, ext) so only own-property extensions are matched.
  • packages/ai/src/prompt/convert-to-language-model-prompt.test.ts: regression test for a URL ending in .constructor — asserts the helper falls back to the no-extension behaviour instead of returning a non-string value from the prototype chain.
  • Patch changeset.

The change is one line of production code; the table is treated as a closed lookup, which is what the original code intended.

Manual Verification

  • pnpm --filter ai test:node -- src/prompt/convert-to-language-model-prompt.test.ts — passes, including the new prototype-collision regression test.

Checklist

  • Tests have been added / updated (for bug fixes / features)
  • Documentation has been added / updated (for bug fixes / features)
  • A patch changeset for relevant packages has been added (for bug fixes / features - run pnpm changeset in the project root)
  • I have reviewed this pull request (self-review)

No documentation change — getMediaTypeFromUrl is internal.

Future Work

None. If other helpers in the prompt layer use in against plain object literals indexed by user input, the same prototype-confusion fix would apply, but I didn't spot any others while looking at this one.

Related Issues

Detected automatically by https://github.com/etairl/Probus

Comment on lines +745 to +747
// Use `Object.hasOwn` instead of `in` so attacker-controlled extensions
// like `constructor` cannot resolve to inherited `Object.prototype`
// members and leak a non-string value through this `: string` helper.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

comment not needed (there is a regression test)

@lgrammel

lgrammel commented Apr 27, 2026

Copy link
Copy Markdown
Collaborator

Leaving to @felixarntz to confirm since he added the code.

@lgrammel lgrammel requested a review from felixarntz April 27, 2026 12:39
@lgrammel lgrammel changed the title fix(ai): harden getMediaTypeFromUrl against prototype-property collis… fix(ai): harden getMediaTypeFromUrl against prototype-property collision Apr 27, 2026
etairl added a commit to etairl/ai that referenced this pull request Apr 27, 2026
Per review on vercel#14751: the regression test for the prototype-property
collision case is enough; the inline comment is redundant.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@lgrammel

Copy link
Copy Markdown
Collaborator

@etairl can you reopen with signed commits?

@etairl

etairl commented Apr 27, 2026

Copy link
Copy Markdown
Contributor Author

@etairl can you reopen with signed commits?

Sure, will do in a few hours.

@felixarntz felixarntz left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@etairl Thank you for opening the PR!

We have many cases of prop in obj in our codebase - how much of a concern is this really? If it is a concern, we should probably revisit the other places too. If not, then changing it here seems a bit arbitrary.

Comment thread packages/ai/src/prompt/convert-to-language-model-prompt.ts

@felixarntz felixarntz left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@etairl I briefly chatted about this with @lgrammel, the fix looks good. Can you please recreate this with signed commits?

Afterwards, we should review our codebase for other usage of prop in obj and replace as applicable too.

etairl and others added 2 commits April 27, 2026 20:38
…ions

`getMediaTypeFromUrl` (used to infer media types for `file-url` /
`image-url` parts) used `ext in URL_EXTENSION_TO_MEDIA_TYPE` against a
plain object literal. A URL ending in `.constructor` therefore resolved
through the prototype chain and returned the `Object` constructor
function, violating the helper's `: string` return type and forwarding
a non-string value to provider adapters.

Switch to `Object.hasOwn(...)` so attacker-controlled extensions like
`.constructor` cannot resolve to inherited `Object.prototype` keys.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Per review on vercel#14751: the regression test for the prototype-property
collision case is enough; the inline comment is redundant.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@etairl etairl force-pushed the fix/url-media-type-prototype-confusion branch from 7d131c5 to bc4694e Compare April 27, 2026 17:38
@etairl

etairl commented Apr 27, 2026

Copy link
Copy Markdown
Contributor Author

@etairl I briefly chatted about this with @lgrammel, the fix looks good. Can you please recreate this with signed commits?

Afterwards, we should review our codebase for other usage of prop in obj and replace as applicable too.

Done, rebased with signed commits.

@lgrammel lgrammel merged commit 4181cfe into vercel:main Apr 27, 2026
17 of 18 checks passed
gr2m pushed a commit that referenced this pull request Jun 5, 2026
…ion (#14751)

## Background

`getMediaTypeFromUrl` in `convert-to-language-model-prompt.ts` does an
`ext in URL_EXTENSION_TO_MEDIA_TYPE` check on a plain object literal.
Because plain objects inherit from `Object.prototype`, the `in` operator
returns `true` for inherited keys like `constructor`, `toString`,
`hasOwnProperty`, etc. A URL ending in `.constructor` (or any other
`Object.prototype` member) therefore takes the lookup branch and returns
the inherited value — for `.constructor`, that's the `Object`
constructor function, which is then forwarded as `mediaType` to provider
adapters.

This is a low-severity correctness/typing bug rather than an exploit
path, but it's worth fixing: the helper's return type is `string |
undefined` and a non-string slipping through can break downstream code
paths that assume a string `mediaType`.

Splitting this out per maintainer feedback on #14749 that the media-type
fix should be its own PR.

## Summary

- `packages/ai/src/prompt/convert-to-language-model-prompt.ts`: replace
`ext in URL_EXTENSION_TO_MEDIA_TYPE` with
`Object.hasOwn(URL_EXTENSION_TO_MEDIA_TYPE, ext)` so only own-property
extensions are matched.
- `packages/ai/src/prompt/convert-to-language-model-prompt.test.ts`:
regression test for a URL ending in `.constructor` — asserts the helper
falls back to the no-extension behaviour instead of returning a
non-string value from the prototype chain.
- Patch changeset.

The change is one line of production code; the table is treated as a
closed lookup, which is what the original code intended.

## Manual Verification

- `pnpm --filter ai test:node --
src/prompt/convert-to-language-model-prompt.test.ts` — passes, including
the new prototype-collision regression test.

## Checklist

- [x] Tests have been added / updated (for bug fixes / features)
- [ ] Documentation has been added / updated (for bug fixes / features)
- [x] A _patch_ changeset for relevant packages has been added (for bug
fixes / features - run `pnpm changeset` in the project root)
- [x] I have reviewed this pull request (self-review)

No documentation change — `getMediaTypeFromUrl` is internal.

## Future Work

None. If other helpers in the prompt layer use `in` against plain object
literals indexed by user input, the same prototype-confusion fix would
apply, but I didn't spot any others while looking at this one.

## Related Issues

- Split out from #14749 (closed) per maintainer feedback.

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Lars Grammel <lars.grammel@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants