fix(Extract-NTHashes): remove mismatched blob after checksum failure

On upload checksum mismatch, the script warned and preserved the
local encrypted file but left the already-uploaded, corrupt blob live
in remote storage under its normal name - discoverable only via an
easy-to-miss console warning, and fetchable as if it were good data
by any downstream consumer. Added Invoke-S3DeleteFile (SigV4-signed,
mirrors the existing Put/Get helpers) and now delete the mismatched
blob from S3 (AWS Tools or native HTTP path) or Azure Blob Storage
right after detecting the mismatch.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-29 11:54:21 +02:00
parent 867fb6427d
commit d155276366
+36
View File
@@ -93,6 +93,23 @@ function Invoke-S3GetToFile([string]$endpointUrl, [string]$bucket, [string]$key,
} finally { if ($req) { $req.Dispose() }; $client.Dispose() }
}
function Invoke-S3DeleteFile([string]$endpointUrl, [string]$bucket, [string]$key, [string]$region, [string]$ak, [string]$sk, [bool]$forcePathStyle) {
$uri = BuildS3Uri -endpointUrl $endpointUrl -bucket $bucket -key $key -forcePathStyle $forcePathStyle
$payloadHash = (Get-HashHex (Get-Bytes ''))
Add-Type -AssemblyName System.Net.Http -ErrorAction SilentlyContinue
$client = [System.Net.Http.HttpClient]::new()
try {
$req = [System.Net.Http.HttpRequestMessage]::new([System.Net.Http.HttpMethod]::Delete, $uri)
$hdrs = BuildAuthHeaders -method 'DELETE' -uri $uri -region $region -accessKey $ak -secretKey $sk -payloadHash $payloadHash
$req.Headers.TryAddWithoutValidation('x-amz-date', $hdrs['x-amz-date']) | Out-Null
$req.Headers.TryAddWithoutValidation('Authorization', $hdrs['Authorization']) | Out-Null
$req.Headers.TryAddWithoutValidation('x-amz-content-sha256', $hdrs['x-amz-content-sha256']) | Out-Null
$resp = $client.SendAsync($req).Result
# S3 DELETE is idempotent and returns 204 even if the key never existed; anything else is a real failure.
if (-not $resp.IsSuccessStatusCode) { throw "S3 DELETE failed: $([int]$resp.StatusCode) $($resp.ReasonPhrase)" }
} finally { if ($req) { $req.Dispose() }; $client.Dispose() }
}
function Protect-FileWithAES {
param (
[Parameter(Mandatory = $true)]
@@ -355,6 +372,25 @@ try {
if ($tempDownloadPath -and (Test-Path $tempDownloadPath)) {
Remove-Item -Path $tempDownloadPath -Force -ErrorAction SilentlyContinue
}
# A checksum mismatch means the blob already sitting in remote storage under $blobName
# is corrupt/incomplete. Leaving it live under its normal name would let a downstream
# consumer silently fetch bad data, so remove it rather than only warning locally.
try {
if ($storageProvider -ieq 'S3') {
if ($usedAwsTools -and $s3Client) {
$delReq = New-Object Amazon.S3.Model.DeleteObjectRequest -Property @{ BucketName = $s3BucketName; Key = $blobName }
$null = $s3Client.DeleteObject($delReq)
} else {
Invoke-S3DeleteFile -endpointUrl $s3EndpointUrl -bucket $s3BucketName -key $blobName -region $s3Region -ak $s3AccessKeyId -sk $s3SecretAccessKey -forcePathStyle:$s3ForcePathStyle
}
} else {
Remove-AzStorageBlob -Blob $blobName -Container $containerName -Context $storageContext -Force -ErrorAction Stop
}
Write-Warning "Removed the mismatched blob '$blobName' from remote storage."
} catch {
Write-Warning "Could not remove the mismatched blob '$blobName' from remote storage - remove it manually: $($_.Exception.Message)"
}
}
} finally {
# Always delete plaintext hashes and compressed archive regardless of outcome.