Fix x:Key values not escaped in source-generated C# string literals#34727
Fix x:Key values not escaped in source-generated C# string literals#34727jfversluis merged 1 commit intomainfrom
Conversation
Escape x:Key values using CSharpExpressionHelpers.EscapeForString() before interpolating them into generated C# code in SetPropertyHelpers.AddToResourceDictionary. Previously, keys containing double quotes, backslashes, or control characters would produce syntactically invalid generated C#. Fixes #34726 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 34727Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 34727" |
There was a problem hiding this comment.
Pull request overview
This PR fixes an XAML source generator correctness/security issue where x:Key values were emitted into generated C# string literals without escaping, which could produce syntactically invalid generated code for keys containing quotes, backslashes, or control characters.
Changes:
- Escape
x:Keyvalues when emitting ResourceDictionary indexer assignments in sourcegen. - Escape
DynamicResourcekeys when emittingnew DynamicResource("...")in sourcegen. - Reuse the escaping helper by widening
EscapeForStringvisibility and add a regression XAML unit test for quote/backslash keys.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/Controls/src/SourceGen/SetPropertyHelpers.cs | Escapes resource dictionary keys before writing them into generated C# string literals. |
| src/Controls/src/SourceGen/KnownMarkups.cs | Escapes DynamicResource keys before emitting generated constructor code. |
| src/Controls/src/SourceGen/CSharpExpressionHelpers.cs | Makes EscapeForString reusable outside the defining type. |
| src/Controls/tests/Xaml.UnitTests/Issues/Maui34726.xaml | Adds a repro XAML with x:Key values containing quotes/backslashes. |
| src/Controls/tests/Xaml.UnitTests/Issues/Maui34726.xaml.cs | Adds a regression test covering SourceGen + Runtime/XamlC verification for special-character keys. |
| internal static string EscapeForString(string value) | ||
| { | ||
| var sb = new StringBuilder(); | ||
| foreach (var c in value) | ||
| { | ||
| switch (c) | ||
| { | ||
| case '"': sb.Append("\\\""); break; | ||
| case '\\': sb.Append("\\\\"); break; | ||
| case '\n': sb.Append("\\n"); break; | ||
| case '\r': sb.Append("\\r"); break; | ||
| case '\t': sb.Append("\\t"); break; | ||
| default: sb.Append(c); break; | ||
| case '"': | ||
| sb.Append("\\\""); | ||
| break; | ||
| case '\\': | ||
| sb.Append("\\\\"); | ||
| break; | ||
| case '\n': | ||
| sb.Append("\\n"); | ||
| break; | ||
| case '\r': | ||
| sb.Append("\\r"); | ||
| break; | ||
| case '\t': | ||
| sb.Append("\\t"); | ||
| break; | ||
| default: | ||
| sb.Append(c); | ||
| break; | ||
| } | ||
| } | ||
| return sb.ToString(); |
There was a problem hiding this comment.
EscapeForString currently only handles ", \, \n, \r, and \t. Other valid control characters (e.g., \0, \b, \f, \v, or arbitrary chars where char.IsControl(c) is true) will still be emitted verbatim into generated source, which can produce invalid/fragile C# and leaves some of the injection surface described in the issue. Consider escaping all control chars (e.g., use SymbolDisplay.FormatLiteral(value, quote: false) or fall back to \uXXXX for remaining controls).
There was a problem hiding this comment.
I do not think these are a problem in C# string literals.
🧪 PR Test EvaluationOverall Verdict: The XAML test covers the
📊 Expand Full EvaluationPR Test Evaluation ReportPR: #34727 — Fix x:Key values not escaped in source-generated C# string literals Overview of the FixThe PR applies
Additionally, 1. Fix Coverage —
|
| Fix site | What it does | Tested? |
|---|---|---|
SetPropertyHelpers.cs |
Escapes key in rd["key"] = value |
✅ Directly tested |
KnownMarkups.cs |
Escapes key in new DynamicResource("key") |
❌ Not tested |
CSharpExpressionHelpers.cs |
Made EscapeForString internal |
✅ (implicitly, as both fixes call it) |
Recommendations
-
Add a
{DynamicResource}reference with a special-char key — Add e.g.(Label TextColor="{DynamicResource Key"Quote}" /)toMaui34726.xamland assert that the resultingTextColorequalsColors.Red. This directly covers theKnownMarkups.csfix and ensures it can't be silently reverted. -
Strengthen the SourceGen assertion — After
Assert.Empty(result.Diagnostics), optionally addAssert.Contains("\\\"Key\\\\\\\"Quote\\\"\\\"", result.GeneratedInitializeComponent())or similar to verify the escaped string literal appears in the generated code (though this is optional given the compile-check is already a meaningful guard).
Warning
⚠️ Firewall blocked 1 domain
The following domain was blocked by the firewall during workflow execution:
dc.services.visualstudio.com
To allow these domains, add them to the network.allowed list in your workflow frontmatter:
network:
allowed:
- defaults
- "dc.services.visualstudio.com"See Network Configuration for more information.
Note
🔒 Integrity filtering filtered 2 items
Integrity filtering activated and filtered the following items during workflow execution.
This happens when a tool call accesses a resource that does not meet the required integrity or secrecy level of the workflow.
- pr:Fix x:Key values not escaped in source-generated C# string literals #34727 (
pull_request_read: Resource 'pr:Fix x:Key values not escaped in source-generated C# string literals #34727' has lower integrity than agent requires. Agent would need to drop integrity tags [unapproved:all approved:all] to trust this resource.) - pr:Fix x:Key values not escaped in source-generated C# string literals #34727 (
pull_request_read: Resource 'pr:Fix x:Key values not escaped in source-generated C# string literals #34727' has lower integrity than agent requires. Agent would need to drop integrity tags [approved:all unapproved:all] to trust this resource.)
🧪 Test evaluation by Evaluate PR Tests
|
/azp run maui-pr-uitests, maui-pr-devicetests |
|
Azure Pipelines successfully started running 2 pipeline(s). |
The EscapeForString fix from PR #34727 only escaped x:Key in the resources["..."] path but missed the AddFactory("...") path, causing generated C# with unescaped quotes/backslashes in keys. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Replace non-existent PR numbers (#34000, #33500, #33000, #34100) with real merged PRs (#34024, #34727, #31202, #28713, #34723) - Add "in dotnet/maui" to all prompts to prevent agent asking for repo - All PRs verified as real merged PRs with actual code changes Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Description
Fixes #34726
The XAML source generator interpolates
x:Keyvalues directly into generated C# string literals without escaping special characters. If anx:Keycontains a double quote ("), backslash (\), or control character, the generated C# is syntactically invalid.Changes
SetPropertyHelpers.cs— Escape thex:Keyvalue viaCSharpExpressionHelpers.EscapeForString()before interpolating into the generatedresources["..."] = ...assignment.KnownMarkups.cs— Same fix forDynamicResourcekey emission (new DynamicResource("...")).CSharpExpressionHelpers.cs— ChangedEscapeForStringfromprivate statictointernal staticso it can be reused fromSetPropertyHelpersandKnownMarkups.Test
Added
Maui34726.xaml/.xaml.csXAML unit test withx:Keyvalues containing double quotes and backslashes: