Repository navigation
Commit cc8a15f
authored
.Net: Bound element nesting depth in XML prompt parsing (#14504)
## Motivation and Context
`XmlPromptParser` converts chat prompts such as `<message
role="user">...</message>` into `PromptNode` trees that
`ChatPromptParser` turns into a `ChatHistory`. Each `PromptNode` stores
the serialized inner XML of its element, so the parser re-serializes and
decodes the whole remaining subtree at every nesting level. Parse work
therefore grows with nesting depth multiplied by prompt size, and the
recursive node builder had no depth bound.
Supported chat prompt structure is shallow: message elements with
optional `text`, `image`, `audio`, and `binary` children, plus encoded
or CDATA text. This change bounds element nesting so parsing cost stays
proportional to prompt size.
## Description
- Add `XmlPromptParser.MaxElementDepth` (internal, `64`, top-level
prompt elements count as depth 1, matching the `System.Text.Json`
default maximum depth).
- After the prompt is loaded and before any `PromptNode` is built, a
single non-recursive walk checks element depth. Prompts nested deeper
than the limit return `false` from `TryParse`.
- A rejected prompt takes the existing invalid-XML path: callers such as
`GetChatMessageContentsAsync(string)` send the whole prompt as one user
text message, exactly as they already do for malformed XML.
- Parse work for accepted prompts is bounded by the depth limit times
prompt size, and recursion in `GetPromptNode` is bounded.
- No public API change. Prompts within the limit, including content
items, CDATA, and encoded content, parse exactly as before.
Tests:
- `XmlPromptParserTests`: parses two prompts at exactly the maximum
depth; rejects one level over and far over the limit; rejects when only
a later top-level node exceeds the limit (covers sibling traversal after
a max-depth subtree).
- `ChatPromptParserTests`: a user message whose content exceeds the
limit makes `ChatPromptParser.TryParse` return `false`.
## Validation
All local runs use `-c Debug` on .NET SDK 10.0.401.
- Build with `--warnaserror`: `SemanticKernel.Abstractions` (`net10.0`,
`net8.0`, `netstandard2.0`), `SemanticKernel.UnitTests`,
`Extensions.UnitTests`, `PromptTemplates.Liquid.UnitTests`,
`Agents.UnitTests`, `IntegrationTests`: 0 warnings, 0 errors.
- `dotnet format --verify-no-changes` in
`mcr.microsoft.com/dotnet/sdk:10.0` for both changed projects: clean.
- `SemanticKernel.UnitTests`: 1614/1614 pass, including the 5 new tests.
The new over-limit tests fail without the parser change.
- `Extensions.UnitTests` (Handlebars) 90/90,
`PromptTemplates.Liquid.UnitTests` 16/16, `Agents.UnitTests` 504/504
executed tests pass, `IntegrationTests` CrossLanguage (offline, stubbed
HTTP request shape) 37/37.
- Normal chat prompts produce the same `ChatHistory` on `net10.0`,
`net8.0`, and .NET Framework 4.8 (`netstandard2.0` asset), and the
largest prompts still accepted parse in time proportional to their size.
## Contribution Checklist
- [x] The code builds clean without any errors or warnings
- [x] The PR follows the [SK Contribution
Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md)
and the [pre-submission formatting
script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts)
raises no violations
- [x] All unit tests pass, and I have added new tests where possible
- [x] I didn't break anyone 😄1 parent 64005c7 commit cc8a15f
3 files changed
Lines changed: 172 additions & 1 deletion
File tree
- dotnet/src
- SemanticKernel.Abstractions/AI
- SemanticKernel.UnitTests/Prompt
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
17 | 28 | | |
18 | 29 | | |
19 | 30 | | |
20 | 31 | | |
21 | 32 | | |
22 | | - | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
23 | 37 | | |
24 | 38 | | |
25 | 39 | | |
| |||
57 | 71 | | |
58 | 72 | | |
59 | 73 | | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
60 | 81 | | |
61 | 82 | | |
62 | 83 | | |
| |||
115 | 136 | | |
116 | 137 | | |
117 | 138 | | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
118 | 183 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
27 | 53 | | |
28 | 54 | | |
29 | 55 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
| 5 | + | |
5 | 6 | | |
6 | 7 | | |
7 | 8 | | |
| |||
77 | 78 | | |
78 | 79 | | |
79 | 80 | | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
80 | 160 | | |
81 | 161 | | |
82 | 162 | | |
| |||
0 commit comments