Skip to content

Python: Support structured MIME subtypes in data URIs - #14304

Open
Zhewen Tan (tandede) wants to merge 9 commits into
microsoft:mainfrom
tandede:fix-data-uri-mime-subtypes
Open

Zhewen Tan (tandede) wants to merge 9 commits into
microsoft:mainfrom
tandede:fix-data-uri-mime-subtypes

Conversation

@tandede

Copy link
Copy Markdown

Motivation and Context

DataUri.from_data_uri() rejects valid MIME subtypes containing structured syntax suffixes or vendor facets because its parser only accepts letters and hyphens. This prevents common values such as image/svg+xml and application/vnd.api+json from being used in data URIs.

Description

Expand the MIME subtype character class to accept digits and the punctuation allowed in registered subtype names. Add regression cases covering SVG and vendor JSON data URIs, including MIME type preservation and payload decoding.

Validation:

  • 4,086 non-Dapr unit tests passed, with 2 skipped and 5 expected failures
  • 82 Dapr unit tests passed in the repository's dedicated Dapr environment
  • Full Python pre-commit suite passed

Contribution Checklist

@tandede
Zhewen Tan (tandede) marked this pull request as ready for review August 19, 2026 11:29
Copilot AI lite review requested due to automatic review settings August 19, 2026 11:29
@tandede
Zhewen Tan (tandede) requested a review from a team as a code owner August 19, 2026 11:29

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Expands the Data URI parsing logic to accept MIME subtypes containing structured syntax suffixes and vendor-tree subtypes (e.g., image/svg+xml, application/vnd.api+json).

Changes:

  • Added unit tests covering MIME subtypes with +suffix and vendor subtypes.
  • Relaxed the MIME subtype regex to allow additional valid characters.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
python/tests/unit/contents/test_data_uri.py Adds regression tests for structured-suffix and vendor-subtype Data URIs.
python/semantic_kernel/contents/utils/data_uri.py Updates the MIME type regex to accept richer subtype formats.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +138 to 139
pattern = "(((?P<mime_type>[a-zA-Z]+/[a-zA-Z0-9!#$&^_.+-]+)(?P<parameters>(;[a-zA-Z0-9]+=+[a-zA-Z0-9]+)*))?(;+(?P<data_format>.*)))?(,(?P<data_str>.*))" # noqa: E501
match = re.match(pattern, data)
Comment on lines +138 to 139
pattern = "(((?P<mime_type>[a-zA-Z]+/[a-zA-Z0-9!#$&^_.+-]+)(?P<parameters>(;[a-zA-Z0-9]+=+[a-zA-Z0-9]+)*))?(;+(?P<data_format>.*)))?(,(?P<data_str>.*))" # noqa: E501
match = re.match(pattern, data)

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

MAF Automated Review — Iteration 1

Result: No findings
Scope: full PR (1 commit(s)): 861112f49281
Model: claude-opus-4.8

Overview

This PR makes a single-line change at python/semantic_kernel/contents/utils/data_uri.py:138, widening the
MIME subtype regex character class from [a-zA-Z-]+ to [a-zA-Z0-9!#$&^_.+-]+ so registered structured
subtypes like image/svg+xml and application/vnd.api+json parse, plus two matching regression tests. The
change is strictly additive: the new class excludes the structural delimiters / ; , =, so no previously
accepted input parses differently and no new backtracking ambiguity is created. All nine existing negative
tests still raise, the full test_data_uri.py suite passes, and downstream consumers (BinaryContent,
ImageContent, AudioContent) receive mime_type through the unchanged to_string()/to_dict() paths.
The residual ReDoS and RFC-token-completeness concerns are pre-existing, not worsened by this PR, and are
already captured in the open reviewer feedback threads; no publishable defect remains.

Reviewed the supplied pull-request change set across correctness, security/reliability, architecture, and failure behavior.
No publishable findings remained after source verification for this scope.

@tandede

Copy link
Copy Markdown
Author

I’ve synced this branch with the latest main. There are no merge conflicts, and the current CLA and labeling checks are passing. This should be ready for review. Thanks!

Mangesh Raut (mangeshraut712) pushed a commit to mangeshraut712/semantic-kernel that referenced this pull request Oct 28, 2026
…oding is set (microsoft#14464)

### What this fixes

`ChatMessageContent.to_prompt()` is annotated `-> str` but returns
`bytes` for any message that carries an `encoding`.

```python
root = self.to_element()
return ElementTree.tostring(root, encoding=self.encoding or "unicode", short_empty_elements=False)
```

`encoding` is a real field on the content, so the `or` never protects
it: setting `encoding="utf-8"` makes `tostring` take its bytes branch.
Measured on `ca40aa72`:

```
encoding='utf-8'  ->  type=bytes  value=b'<message role="user" encoding="utf-8">...'
encoding='ascii'  ->  b'<?xml ...?>\n<message ...'   # XML declaration too
```

This is user-visible rather than merely untidy:
`jinja2_system_helpers._message_to_prompt` and its handlebars twin
render a context variable via `str(context.to_prompt())`, so the prompt
the model receives literally contains `b'<message role="user" ...>'` —
bytes repr plus quote noise — whenever the message had an encoding set.

Every other `tostring` call site in the same package already hardcodes
`encoding="unicode"`: `chat_history.py:285`, `:317`, `:362`,
`chat_message_content.py:275`, `handlebars_system_helpers.py:37`, `:55`
and `jinja2_system_helpers.py:29`. This one line was the outlier, so the
change aligns it rather than introducing a new convention.

The message's own encoding is still preserved: it is an attribute on the
element and round-trips through `to_element`/`from_element`; it
describes the text content, not how we serialize a prompt string.

### Change

One line, plus a docstring note, and a parametrized regression test over
`utf-8`, `ascii` and `latin-1` asserting the prompt is a `str`, has no
XML declaration, keeps the non-ASCII text, and re-parses to the same
content.

### How this was verified

`cd /private/tmp/sk/tree/python`, `PYTHONPATH=.`, the repo's own test
file `tests/unit/contents/test_chat_message_content.py`.

Base control — `semantic_kernel/contents/chat_message_content.py`
restored from `ca40aa7226531d28a721d0ca0e451d0aaf86dafc` with `git diff
-- <that file>` printing nothing (so the run measures upstream code with
the new tests kept), sha re-echoed:

```
FAILED tests/unit/contents/test_chat_message_content.py::test_cmc_to_prompt_with_encoding[utf8]
FAILED tests/unit/contents/test_chat_message_content.py::test_cmc_to_prompt_with_encoding[ascii]
FAILED tests/unit/contents/test_chat_message_content.py::test_cmc_to_prompt_with_encoding[latin1]
3 failed, 43 passed
```

With the patch applied: `46 passed`.

Enclosing directory, both revisions, same command (`pytest
tests/unit/contents`): the `test_chat_history.py::test_to_from_file` /
`::test_chat_history_serialize` collection errors appear **identically
at base and at head** — they come from optional extras missing in my
environment, not from this diff. I did not run the integration or dotnet
suites.

`ruff check` on the two files reports only a pre-existing `D` finding at
`chat_message_content.py:201`, present at base too; `ruff format
--check` is clean. (`mypy` was not installed in this environment, so the
CI type gate is unverified here.)

Dedup: searched issues and PRs (all states) for `to_prompt`,
`message_to_prompt` and `encoding unicode prompt`; the only hits are
merged history (microsoft#13738, microsoft#5550). The nearby `data_uri` MIME-subtype
defect I found while investigating is already claimed by open microsoft#14304, so
it is deliberately not part of this PR.

This branch had an error being deployed

1 failed deployment
github-app-auth — 8049ec8c Deployed Sep 28, 2026 by tandede via add_label #29049
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.

2 participants