Skip to content

Commit 5e50aaf

Browse files
Validate keys after YAML removal
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent e3dfe17 commit 5e50aaf

4 files changed

Lines changed: 76 additions & 4 deletions

File tree

src/functions/private/Assert-YamlRemovalGraph.ps1

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ function Assert-YamlRemovalGraph {
143143
))
144144
}
145145
}
146+
146147
}
147148
for ($index = $node.Entries.Count - 1; $index -ge 0; $index--) {
148149
$pending.Push([pscustomobject]@{
@@ -155,4 +156,21 @@ function Assert-YamlRemovalGraph {
155156
})
156157
}
157158
}
159+
160+
$fingerprintCache = [System.Collections.Generic.Dictionary[int, string]]::new()
161+
$fingerprintHasher = [System.Security.Cryptography.SHA256]::Create()
162+
$fingerprintWorkState = [pscustomobject]@{
163+
Count = 0L
164+
MaxNodes = $MaxNodes
165+
}
166+
try {
167+
foreach ($document in $Documents) {
168+
Test-YamlNodeGraph -Node $document `
169+
-Visited ([System.Collections.Generic.HashSet[int]]::new()) `
170+
-FingerprintCache $fingerprintCache -FingerprintHasher $fingerprintHasher `
171+
-RemovalWorkState $fingerprintWorkState
172+
}
173+
} finally {
174+
$fingerprintHasher.Dispose()
175+
}
158176
}

src/functions/private/Get-YamlNodeFingerprint.ps1

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ function Get-YamlNodeFingerprint {
1818
[System.Collections.Generic.Dictionary[int, string]] $Cache,
1919

2020
[Parameter(Mandatory)]
21-
[System.Security.Cryptography.HashAlgorithm] $Hasher
21+
[System.Security.Cryptography.HashAlgorithm] $Hasher,
22+
23+
[Parameter()]
24+
[AllowNull()]
25+
[pscustomobject] $RemovalWorkState
2226
)
2327

2428
$root = [pscustomobject]@{ Value = '' }
@@ -48,6 +52,10 @@ function Get-YamlNodeFingerprint {
4852
[void] $stack.Pop()
4953
continue
5054
}
55+
if ($null -ne $RemovalWorkState) {
56+
Add-YamlRemovalWork -State $RemovalWorkState `
57+
-Operation 'duplicate-key fingerprint' -Node $effective
58+
}
5159
if (-not $Active.Add($effective.Id)) {
5260
throw (New-YamlException -Start $effective.Start -End $effective.End `
5361
-ErrorId 'YamlCyclicMappingKey' -Message (

src/functions/private/Test-YamlNodeGraph.ps1

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@ function Test-YamlNodeGraph {
1717
[System.Collections.Generic.Dictionary[int, string]] $FingerprintCache,
1818

1919
[Parameter(Mandatory)]
20-
[System.Security.Cryptography.HashAlgorithm] $FingerprintHasher
20+
[System.Security.Cryptography.HashAlgorithm] $FingerprintHasher,
21+
22+
[Parameter()]
23+
[AllowNull()]
24+
[pscustomobject] $RemovalWorkState
2125
)
2226

2327
$scalarTags = [System.Collections.Generic.HashSet[string]]::new(
@@ -94,7 +98,8 @@ function Test-YamlNodeGraph {
9498
$keyFingerprint = Get-YamlNodeFingerprint `
9599
-Node $entryNode.Entries[0].Key `
96100
-Active ([System.Collections.Generic.HashSet[int]]::new()) `
97-
-Cache $FingerprintCache -Hasher $FingerprintHasher
101+
-Cache $FingerprintCache -Hasher $FingerprintHasher `
102+
-RemovalWorkState $RemovalWorkState
98103
if (-not $orderedKeys.Add($keyFingerprint)) {
99104
$keyNode = $entryNode.Entries[0].Key
100105
throw (New-YamlException -Start $keyNode.Start -End $keyNode.End `
@@ -127,7 +132,8 @@ function Test-YamlNodeGraph {
127132
$entry = $current.Entries[$index]
128133
$fingerprint = Get-YamlNodeFingerprint -Node $entry.Key `
129134
-Active ([System.Collections.Generic.HashSet[int]]::new()) `
130-
-Cache $FingerprintCache -Hasher $FingerprintHasher
135+
-Cache $FingerprintCache -Hasher $FingerprintHasher `
136+
-RemovalWorkState $RemovalWorkState
131137
if (-not $keys.Add($fingerprint)) {
132138
throw (New-YamlException -Start $entry.Key.Start -End $entry.Key.End `
133139
-ErrorId 'YamlDuplicateKey' -Message (

tests/Remove-YamlEntry.Tests.ps1

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,46 @@ root: !item
613613
$failure.Exception.Data['YamlErrorId'] |
614614
Should -BeExactly 'YamlInvalidTaggedCollection'
615615
}
616+
617+
It 'rejects post-removal duplicate representation keys' -ForEach @(
618+
@{
619+
Yaml = @'
620+
cycle: &cycle { self: *cycle }
621+
shared: &shared { x: 1, y: 2 }
622+
? *shared
623+
: first
624+
? { x: 1 }
625+
: second
626+
'@
627+
}
628+
@{
629+
Yaml = @'
630+
shared: &shared { x: 1, y: 2 }
631+
set: !!set
632+
? *shared
633+
? { x: 1 }
634+
'@
635+
}
636+
@{
637+
Yaml = @'
638+
shared: &shared { x: 1, y: 2 }
639+
ordered: !!omap
640+
- ? *shared
641+
: first
642+
- ? { x: 1 }
643+
: second
644+
'@
645+
}
646+
) {
647+
$failure = Get-RemoveYamlFailure {
648+
Remove-YamlEntry $Yaml '/shared/y'
649+
}
650+
651+
$failure.Exception.Data['YamlErrorId'] |
652+
Should -BeExactly 'YamlDuplicateKey'
653+
$failure.FullyQualifiedErrorId |
654+
Should -Be 'YamlDuplicateKey,Remove-YamlEntry'
655+
}
616656
}
617657

618658
Context 'Validation and work limits' {

0 commit comments

Comments
 (0)