fix(Prepare-KHDBStorage): reject manifest shard names outside shard root

In -UploadOnly mode, entry.name from the parsed manifest.json was
used directly in Join-Path (local read) and as the remote object key
with no path-traversal check. A tampered local manifest.json
(requires prior local write access to the shard directory) with a
name like '..\..\secrets.txt' could make the upload step read and
upload an arbitrary local file under an attacker-chosen remote key.
Now resolves each shard's full path via GetFullPath and rejects any
entry that resolves outside the shard root, before it's added to the
list actually used for reads/uploads. Verified the resolved-path
containment check against normal relative names (allowed) and '..'
traversal attempts (blocked).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-29 12:04:43 +02:00
parent 03ceec9d5e
commit 2f0ff9085a
+9
View File
@@ -892,12 +892,21 @@ if ($UploadOnly) {
$manifestHash = (Get-FileHash -Path $manifestPath -Algorithm SHA256).Hash.ToLowerInvariant()
$resolvedLocalShardRoot = [System.IO.Path]::GetFullPath($localShardRoot).TrimEnd([System.IO.Path]::DirectorySeparatorChar, [System.IO.Path]::AltDirectorySeparatorChar) + [System.IO.Path]::DirectorySeparatorChar
$manifestShards = @()
$totalSizeBytes = 0L
foreach ($entry in ($manifestObject.shards | Sort-Object name)) {
$name = [string]$entry.name
if ([string]::IsNullOrWhiteSpace($name)) { continue }
$localPath = Join-Path -Path $localShardRoot -ChildPath $name
# Manifest shard names are attacker-controllable if manifest.json was tampered with (requires
# prior local write access to the shard directory). Reject anything that resolves outside the
# shard root instead of trusting Join-Path to keep '..'/rooted paths contained.
$resolvedLocalPath = [System.IO.Path]::GetFullPath($localPath)
if (-not $resolvedLocalPath.StartsWith($resolvedLocalShardRoot, [System.StringComparison]::OrdinalIgnoreCase)) {
throw "Manifest shard name '$name' resolves outside the shard directory '$localShardRoot'."
}
if (-not (Test-Path -LiteralPath $localPath)) {
throw "Shard file '$name' listed in manifest was not found under '$localShardRoot'."
}