Skip to content

Commit eb23a1c

Browse files
Merge updated conversion foundation
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2 parents 79a26c0 + e339f1f commit eb23a1c

9 files changed

Lines changed: 124 additions & 101 deletions
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
function ConvertFrom-YamlTagUriEscape {
2+
<#
3+
.SYNOPSIS
4+
Decodes YAML tag URI %xx escapes as UTF-8.
5+
#>
6+
[CmdletBinding()]
7+
[OutputType([string])]
8+
param (
9+
[Parameter(Mandatory)]
10+
[string] $Text,
11+
12+
[Parameter(Mandatory)]
13+
[pscustomobject] $Mark,
14+
15+
[Parameter(Mandatory)]
16+
[string] $Token
17+
)
18+
19+
$builder = [System.Text.StringBuilder]::new()
20+
$escapedBytes = [System.Collections.Generic.List[byte]]::new()
21+
$utf8 = [System.Text.UTF8Encoding]::new($false, $true)
22+
23+
for ($index = 0; $index -lt $Text.Length; $index++) {
24+
$character = $Text[$index]
25+
if ($character -ne '%') {
26+
if ($escapedBytes.Count -gt 0) {
27+
try {
28+
[void] $builder.Append($utf8.GetString($escapedBytes.ToArray()))
29+
} catch [System.Text.DecoderFallbackException] {
30+
throw (New-YamlException -Start $Mark -End $Mark -ErrorId 'YamlInvalidTag' -Message (
31+
"The tag token '$Token' contains an invalid UTF-8 escape sequence."
32+
))
33+
}
34+
$escapedBytes.Clear()
35+
}
36+
[void] $builder.Append($character)
37+
continue
38+
}
39+
40+
if ($index + 2 -ge $Text.Length) {
41+
throw (New-YamlException -Start $Mark -End $Mark -ErrorId 'YamlInvalidTag' -Message (
42+
"The tag token '$Token' contains a malformed percent escape."
43+
))
44+
}
45+
$pair = '{0}{1}' -f $Text[$index + 1], $Text[$index + 2]
46+
if ($pair -cnotmatch '^[0-9A-Fa-f]{2}$') {
47+
throw (New-YamlException -Start $Mark -End $Mark -ErrorId 'YamlInvalidTag' -Message (
48+
"The tag token '$Token' contains a malformed percent escape."
49+
))
50+
}
51+
$escapedBytes.Add([System.Convert]::ToByte($pair, 16))
52+
$index += 2
53+
}
54+
55+
if ($escapedBytes.Count -gt 0) {
56+
try {
57+
[void] $builder.Append($utf8.GetString($escapedBytes.ToArray()))
58+
} catch [System.Text.DecoderFallbackException] {
59+
throw (New-YamlException -Start $Mark -End $Mark -ErrorId 'YamlInvalidTag' -Message (
60+
"The tag token '$Token' contains an invalid UTF-8 escape sequence."
61+
))
62+
}
63+
}
64+
65+
$builder.ToString()
66+
}

src/functions/private/ConvertFrom-YamlTagUriText.ps1

Lines changed: 0 additions & 44 deletions
This file was deleted.

src/functions/private/Resolve-YamlTag.ps1

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -67,18 +67,7 @@ function Resolve-YamlTag {
6767
}
6868
}
6969

70-
try {
71-
$expanded = ConvertFrom-YamlTagUriText -Text ($prefix + $suffix)
72-
} catch [System.FormatException] {
73-
throw (New-YamlException -Start $Mark -End $Mark -ErrorId 'YamlInvalidTag' -Message (
74-
"The tag token '$Token' contains a malformed URI escape."
75-
))
76-
} catch [System.Text.DecoderFallbackException] {
77-
throw (New-YamlException -Start $Mark -End $Mark -ErrorId 'YamlInvalidTag' -Message (
78-
"The tag token '$Token' contains URI escapes that are not valid UTF-8."
79-
))
80-
}
81-
70+
$expanded = ConvertFrom-YamlTagUriEscape -Text ($prefix + $suffix) -Mark $Mark -Token $Token
8271
$expandedLength = $expanded.Length
8372
if ($expandedLength -gt $Context.MaxTagLength) {
8473
throw (New-YamlException -Start $Mark -End $Mark -ErrorId 'YamlTagLimitExceeded' -Message (

tests/Conformance.Tests.ps1

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,14 +128,15 @@ Describe 'Released yaml-test-suite corpus accounting' {
128128
}
129129

130130
It 'keeps the previously failing multi-document JSON cases green' {
131-
@(
131+
$jsonRegressions = @(
132132
$suiteResults |
133133
Where-Object Case -In @(
134134
'35KP', '6XDY', '6ZKB', '7Z25', '9DXL',
135135
'9KAX', 'JHB9', 'KSS4', 'L383', 'M7A3',
136136
'PUW8', 'RZT7', 'U9NS', 'UT92', 'W4TN'
137137
) |
138138
Where-Object JsonResult -NE 'Pass'
139-
).Count | Should -Be 0
139+
)
140+
$jsonRegressions.Count | Should -Be 0
140141
}
141142
}

tests/ConvertFrom-Yaml.Tests.ps1

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -187,22 +187,6 @@ mapping: !<tag:example.test,2026:object>
187187
$result.mapping.name | Should -Be 'safe'
188188
}
189189

190-
It 'decodes percent escapes in expanded representation tags' {
191-
$escaped = Get-TestYamlRepresentationRoot -Yaml (
192-
"%TAG !e! tag:example.com,2000:app/`n--- !e!tag%21 value"
193-
)
194-
$multibyte = Get-TestYamlRepresentationRoot -Yaml (
195-
"%TAG !e! tag:example.com,2000:app/`n--- !e!caf%C3%A9 value"
196-
)
197-
198-
$escaped.Tag | Should -Be 'tag:example.com,2000:app/tag!'
199-
$escaped.HasUnknownTag | Should -BeTrue
200-
$multibyte.Tag | Should -Be (
201-
'tag:example.com,2000:app/caf{0}' -f [char] 0x00E9
202-
)
203-
$multibyte.HasUnknownTag | Should -BeTrue
204-
}
205-
206190
It 'retains unknown local and global tags before neutral value projection' {
207191
$local = Get-TestYamlRepresentationRoot -Yaml '!local value'
208192
$global = Get-TestYamlRepresentationRoot -Yaml (
@@ -394,6 +378,49 @@ date: !!timestamp 2001-12-14
394378
{ $largeInteger | ConvertFrom-Yaml -MaxNumericLength 4096 } | Should -Throw
395379
}
396380

381+
It 'decodes percent escapes in expanded tags using UTF-8' {
382+
$yaml = @'
383+
%TAG !e! tag:example.com,2000:app/
384+
---
385+
first: !e!tag%21 value
386+
second: !e!currency%E2%82%AC amount
387+
'@
388+
$representation = Read-YamlStreamCore -Yaml $yaml -Depth 32 -MaxNodes 64 -MaxAliases 16 `
389+
-MaxScalarLength 4096 -MaxTagLength 1024 -MaxTotalTagLength 4096 `
390+
-MaxNumericLength 64
391+
392+
$representation.Value.Count | Should -Be 1
393+
$entries = $representation.Value[0].Entries
394+
$entries.Count | Should -Be 2
395+
$entries[0].Value.Tag | Should -Be 'tag:example.com,2000:app/tag!'
396+
$entries[1].Value.Tag | Should -Be ('tag:example.com,2000:app/currency' + [char] 0x20AC)
397+
}
398+
399+
It 'rejects malformed or non-UTF8 tag percent escapes' {
400+
$truncated = @'
401+
%TAG !e! tag:example.com,2000:app/
402+
---
403+
!e!tag%2 value
404+
'@
405+
$invalidHex = @'
406+
%TAG !e! tag:example.com,2000:app/
407+
---
408+
!e!tag%ZZ value
409+
'@
410+
$invalidUtf8 = @'
411+
%TAG !e! tag:example.com,2000:app/
412+
---
413+
!e!tag%E2%28%A1 value
414+
'@
415+
416+
($truncated | Test-Yaml) | Should -BeFalse
417+
($invalidHex | Test-Yaml) | Should -BeFalse
418+
($invalidUtf8 | Test-Yaml) | Should -BeFalse
419+
{ $truncated | ConvertFrom-Yaml } | Should -Throw
420+
{ $invalidHex | ConvertFrom-Yaml } | Should -Throw
421+
{ $invalidUtf8 | ConvertFrom-Yaml } | Should -Throw
422+
}
423+
397424
It 'bounds expanded-tag storage and rejects huge numerics promptly' {
398425
$prefix = 'x' * 20000
399426
$taggedItems = 1..500 | ForEach-Object { '- !e!value item' }

tests/Packaging.Tests.ps1

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ BeforeAll {
3030
$command = Get-Command -Name ConvertFrom-Yaml -ErrorAction SilentlyContinue
3131
if ($null -ne $command -and $command.ModuleName -eq 'Yaml') {
3232
$command.Module
33+
} else {
34+
$null
3335
}
3436
}
3537
$artifactManifestPath = if ($null -ne $loadedYamlModule) {
@@ -93,8 +95,8 @@ Describe 'Dependency-free package source' {
9395
'ConvertTo-YamlNode.ps1',
9496
'Write-YamlNodeText.ps1'
9597
) | ForEach-Object {
96-
Test-Path -LiteralPath (Join-Path $privatePath $_) |
97-
Should -BeTrue -Because "$_ defines a required processor layer"
98+
$isPresent = Test-Path -LiteralPath (Join-Path $privatePath $_)
99+
$isPresent | Should -BeTrue -Because "$_ defines a required processor layer"
98100
}
99101
}
100102

tests/Test-Yaml.Tests.ps1

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -48,17 +48,6 @@ Describe 'Test-Yaml' {
4848
("a: &a value`nb: &b *a" | Test-Yaml) | Should -BeFalse
4949
}
5050

51-
It 'rejects malformed or invalid UTF-8 tag URI escapes: <Tag>' -ForEach @(
52-
@{ Tag = '!value%' }
53-
@{ Tag = '!value%2' }
54-
@{ Tag = '!value%GG' }
55-
@{ Tag = '!value%C3' }
56-
@{ Tag = '!value%C3%28' }
57-
) {
58-
("$Tag value" | Test-Yaml) | Should -BeFalse
59-
{ "$Tag value" | ConvertFrom-Yaml } | Should -Throw
60-
}
61-
6251
It 'recognizes document markers only at column zero' {
6352
("key:`n ---" | Test-Yaml) | Should -BeTrue
6453
("key:`n ..." | Test-Yaml) | Should -BeTrue

tests/TestBootstrap.ps1

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ function Get-TestYamlFingerprintLength {
5252
} finally {
5353
$hasher.Dispose()
5454
}
55-
5655
@($cache.Values | ForEach-Object Length)
5756
}
5857

tests/tools/Invoke-YamlTestSuite.ps1

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
[Diagnostics.CodeAnalysis.SuppressMessageAttribute(
2+
'PSProvideCommentHelp', '',
3+
Justification = 'Internal test helper functions in this tooling script.'
4+
)]
15
[CmdletBinding()]
26
param (
37
[Parameter(Mandatory)]
@@ -23,7 +27,6 @@ if (-not $PSBoundParameters.ContainsKey('CompareJson') -and
2327
}
2428

2529
function Invoke-InYamlModule {
26-
[CmdletBinding()]
2730
param (
2831
[Parameter(Mandatory)]
2932
[scriptblock] $ScriptBlock,
@@ -40,7 +43,6 @@ function Invoke-InYamlModule {
4043
}
4144

4245
function Split-YamlSuiteJsonDocument {
43-
[CmdletBinding()]
4446
param (
4547
[Parameter(Mandatory)]
4648
[AllowEmptyString()]
@@ -111,8 +113,6 @@ function Split-YamlSuiteJsonDocument {
111113
}
112114

113115
function ConvertTo-YamlSuiteCanonicalValue {
114-
[CmdletBinding()]
115-
[OutputType([string])]
116116
param (
117117
[AllowNull()]
118118
[object] $Value
@@ -173,7 +173,6 @@ function ConvertTo-YamlSuiteCanonicalValue {
173173
}
174174

175175
function ConvertTo-YamlSuiteReferenceSignature {
176-
[CmdletBinding()]
177176
[OutputType([string])]
178177
param (
179178
[AllowNull()]
@@ -244,7 +243,6 @@ function ConvertTo-YamlSuiteReferenceSignature {
244243
}
245244

246245
function Get-YamlSuiteJsonPolicyReason {
247-
[CmdletBinding()]
248246
[OutputType([string])]
249247
param (
250248
[Parameter(Mandatory)]
@@ -259,7 +257,6 @@ function Get-YamlSuiteJsonPolicyReason {
259257
}
260258

261259
function ConvertFrom-YamlSuiteEventText {
262-
[CmdletBinding()]
263260
[OutputType([string[]])]
264261
param (
265262
[Parameter(Mandatory)]
@@ -284,7 +281,7 @@ function ConvertFrom-YamlSuiteEventText {
284281
$builder.ToString()
285282
}
286283

287-
function ConvertFrom-YamlSuiteEventEscapes {
284+
function ConvertFrom-YamlSuiteEventEscape {
288285
param ([AllowNull()][string] $Value)
289286
if ($null -eq $Value) {
290287
return ''
@@ -320,7 +317,7 @@ function ConvertFrom-YamlSuiteEventText {
320317
$lines = $Text -split '\r?\n'
321318

322319
foreach ($rawLine in $lines) {
323-
$line = $rawLine
320+
$line = $rawLine
324321
if ([string]::IsNullOrWhiteSpace($line)) {
325322
continue
326323
}
@@ -385,7 +382,7 @@ function ConvertFrom-YamlSuiteEventText {
385382
$value = ''
386383
}
387384
$value = ConvertTo-YamlSuiteEventEscapedText -Value (
388-
ConvertFrom-YamlSuiteEventEscapes -Value $value
385+
ConvertFrom-YamlSuiteEventEscape -Value $value
389386
)
390387
}
391388

@@ -426,7 +423,6 @@ function ConvertFrom-YamlSuiteEventText {
426423
}
427424

428425
function ConvertTo-YamlSuiteActualEvent {
429-
[CmdletBinding()]
430426
[OutputType([string[]])]
431427
param (
432428
[Parameter(Mandatory)]
@@ -533,7 +529,6 @@ function ConvertTo-YamlSuiteActualEvent {
533529
}
534530

535531
function Compare-YamlSuiteCanonicalList {
536-
[CmdletBinding()]
537532
[OutputType([bool])]
538533
param (
539534
[string[]] $Left,
@@ -552,7 +547,6 @@ function Compare-YamlSuiteCanonicalList {
552547
}
553548

554549
function Get-YamlSuiteAnchorToken {
555-
[CmdletBinding()]
556550
[OutputType([string])]
557551
param (
558552
[Parameter(Mandatory)]

0 commit comments

Comments
 (0)