Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion scripts/deploy.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,16 @@ if ($DryRun) {
Write-Host "Updating CloudFront KeyValueStore" -ForegroundColor Cyan
Update-CloudFrontKVS

# A config-sync failure must not strand the content phases 1 and 2 already uploaded, so the
# error is held and rethrown after the invalidation and cleanup. The build still goes red.
Write-Host 'Syncing CloudFront configuration' -ForegroundColor Cyan
Sync-CloudFrontConfig
$configError = $null
try {
Sync-CloudFrontConfig
} catch {
$configError = $_
Write-Host " CloudFront config sync failed: $_" -ForegroundColor Red
}

if ($CloudFrontDistributionId) {
Write-Host "Invalidating CloudFront distribution $CloudFrontDistributionId" -ForegroundColor Cyan
Expand All @@ -276,6 +284,8 @@ if ($DryRun) {
--delete
if ($LASTEXITCODE) { throw 'aws s3 sync (hashed assets – cleanup) failed' }

if ($configError) { throw $configError }

}

Write-Host 'Deployment completed successfully.' -ForegroundColor Green
30 changes: 30 additions & 0 deletions scripts/lib/cloudfront-common.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,36 @@ function Invoke-CloudFrontInvalidation {
if ($LASTEXITCODE) { throw 'CloudFront invalidation failed' }
}

# get-response-headers-policy returns `{}` for headers that are not configured, but
# update-response-headers-policy refuses them: once XSSProtection, FrameOptions or ReferrerPolicy
# appears in the payload it requires its own fields. The read shape is therefore not a valid write
# shape, so empty objects are dropped before the config goes back. Empty arrays are left alone,
# since {Quantity: 0, Items: []} is valid on the way in.
function Remove-EmptyObjects {
param([AllowNull()] $Node)

if ($null -eq $Node) { return $null }

if ($Node -is [System.Collections.IEnumerable] -and $Node -isnot [string]) {
$items = [System.Collections.ArrayList]::new()
foreach ($item in $Node) { [void]$items.Add((Remove-EmptyObjects $item)) }
# Comma-prefix keeps a one-element array an array; returning it bare would unroll it to a
# scalar and turn {Items: ["x"]} into {Items: "x"}, which CloudFront rejects.
return , $items.ToArray()
}

if ($Node -isnot [System.Management.Automation.PSCustomObject]) { return $Node }

$kept = [ordered]@{}
foreach ($property in $Node.PSObject.Properties) {
$value = Remove-EmptyObjects $property.Value
$isEmptyObject = $value -is [System.Management.Automation.PSCustomObject] -and
@($value.PSObject.Properties).Count -eq 0
if (-not $isEmptyObject) { $kept[$property.Name] = $value }
}
return [pscustomobject]$kept
}

# Paths where $Candidate differs from $Reference: node kind, array length, added or dropped
# property, or scalar value. Proves a patch touched only the field it meant to.
function Get-JsonDifference {
Expand Down
5 changes: 3 additions & 2 deletions scripts/sync-csp.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ $current = aws cloudfront get-response-headers-policy --id $ResponseHeadersPolic
if ($LASTEXITCODE) { throw 'aws get-response-headers-policy failed' }

$etag = $current.ETag
$config = $current.ResponseHeadersPolicy.ResponseHeadersPolicyConfig
$config = Remove-EmptyObjects $current.ResponseHeadersPolicy.ResponseHeadersPolicyConfig

if (Compare-CloudFrontValue -Live $config.SecurityHeadersConfig.ContentSecurityPolicy.ContentSecurityPolicy -Local $csp -Noun 'policy') { return }

Expand Down Expand Up @@ -105,7 +105,8 @@ try {
$reread = aws cloudfront get-response-headers-policy --id $ResponseHeadersPolicyId | ConvertFrom-Json
if ($LASTEXITCODE) { throw 'aws get-response-headers-policy failed on re-read' }

$now = $reread.ResponseHeadersPolicy.ResponseHeadersPolicyConfig | ConvertTo-Json -Depth 30 -Compress | ConvertFrom-Json
$now = Remove-EmptyObjects $reread.ResponseHeadersPolicy.ResponseHeadersPolicyConfig |
ConvertTo-Json -Depth 30 -Compress | ConvertFrom-Json
$postDiffs = @(Get-JsonDifference -Reference $before -Candidate $now | Where-Object { $_ -notlike "$CspPath*" })
if ($postDiffs.Count) {
throw "The policy changed outside the CSP: $($postDiffs -join '; '). Restore the affected headers from the CloudFront console."
Expand Down
Loading