rules/Azure.Deployment.Rule.ps1
# Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # # Validation rules for Azure deployments # #region Rules # Synopsis: Avoid outputting sensitive deployment values. Rule 'Azure.Deployment.OutputSecretValue' -Ref 'AZR-000279' -Type 'Microsoft.Resources/deployments' -Tag @{ release = 'GA'; ruleSet = '2022_06'; 'Azure.WAF/pillar' = 'Security'; } { $Assert.Create($PSRule.Issue.Get('PSRule.Rules.Azure.Template.OutputSecretValue')); } # Synopsis: Ensure all properties named used for setting a username within a deployment are expressions (e.g. an ARM function not a string) Rule 'Azure.Deployment.AdminUsername' -Ref 'AZR-000284' -Type 'Microsoft.Resources/deployments' -Tag @{ release = 'GA'; ruleSet = '2022_09'; 'Azure.WAF/pillar' = 'Security'; } { RecurseDeploymentSensitive -Deployment $TargetObject } # Synopsis: Use secure parameters for any parameter that contains sensitive information. Rule 'Azure.Deployment.SecureParameter' -Ref 'AZR-000408' -Type 'Microsoft.Resources/deployments' -Tag @{ release = 'GA'; ruleSet = '2023_12'; 'Azure.WAF/pillar' = 'Security'; } { GetSecureParameter -Deployment $TargetObject } # Synopsis: Use secure parameters for setting properties of resources that contain sensitive information. Rule 'Azure.Deployment.SecureValue' -Ref 'AZR-000316' -Type 'Microsoft.Resources/deployments' -Tag @{ release = 'GA'; ruleSet = '2024_12'; 'Azure.WAF/pillar' = 'Security'; } { RecurseSecureValue -Deployment $TargetObject } # Synopsis: Ensure Outer scope deployments aren't using SecureString or SecureObject Parameters Rule 'Azure.Deployment.OuterSecret' -Ref 'AZR-000331' -Type 'Microsoft.Resources/deployments' -If { IsParentDeployment } -Tag @{ release = 'GA'; ruleSet = '2022_12'; 'Azure.WAF/pillar' = 'Security'; } { $template = @($TargetObject.properties.template); if ($template.resources.Length -eq 0) { return $Assert.Pass(); } $secureParameters = @($template.parameters.PSObject.properties | Where-Object { $_.Value.type -eq 'secureString' -or $_.Value.type -eq 'secureObject' } | ForEach-Object { $_.Name }); foreach ($deployments in $template.resources) { if ($deployments.properties.expressionEvaluationOptions.scope -eq 'outer') { foreach ($outerDeployment in $deployments.properties.template.resources) { foreach ($property in $outerDeployment.properties) { RecursivePropertiesSecretEvaluation -Resource $outerDeployment -SecureParameters $secureParameters -ShouldUseSecret $False -Property $property } } } else { $Assert.Pass() } } } # Synopsis: The deployment parameter leaks sensitive information. Rule 'Azure.Deployment.SecretLeak' -Ref 'AZR-000459' -Type 'Microsoft.Resources/deployments' -Tag @{ release = 'GA'; ruleSet = '2025_06'; 'Azure.WAF/pillar' = 'Security'; } { $Assert.Create($PSRule.Issue.Get('PSRule.Rules.Azure.Template.ParameterSecureAssignment')); } #endregion Rules #region Helpers function global:GetSecureParameter { param ( [Parameter(Mandatory = $True)] [PSObject]$Deployment ) process { $count = 0; if ($Null -ne $Deployment.properties.template.parameters.PSObject.properties) { foreach ($parameter in $Deployment.properties.template.parameters.PSObject.properties.GetEnumerator()) { if ( $Assert.Like($parameter, 'Name', @( '*password*' '*secret*' '*token*' '*key' '*keys' )).Result -and $parameter.Name -ne 'customerManagedKey' -and $parameter.Name -notLike '*name' -and $parameter.Name -notLike '*uri' -and $parameter.Name -notLike '*url' -and $parameter.Name -notLike '*path' -and $parameter.Name -notLike '*type' -and $parameter.Name -notLike '*id' -and $parameter.Name -notLike '*options' -and $parameter.Name -notLike '*publickey' -and $parameter.Name -notLike '*publickeys' -and $parameter.Name -notLike '*secretname*' -and $parameter.Name -notLike '*secreturl*' -and $parameter.Name -notLike '*secreturi*' -and $parameter.Name -notLike '*secrettype*' -and $parameter.Name -notLike '*secretrotation*' -and $parameter.Name -notLike '*tokenname*' -and $parameter.Name -notLike '*tokentype*' -and $parameter.Name -notLike '*interval*' -and $parameter.Name -notLike '*length*' -and $parameter.Name -notLike '*secretprovider*' -and $parameter.Name -notLike '*secretsprovider*' -and $parameter.Name -notLike '*secretref*' -and $parameter.Name -notLike '*secretid*' -and $parameter.Name -notLike '*disablepassword*' -and $parameter.Name -notLike '*sync*passwords*' -and $parameter.Name -notLike '*keyvaultpath*' -and $parameter.Name -notLike '*keyvaultname*' -and $parameter.Name -notLike '*keyvaulturi*' -and $Assert.NotIn($parameter, 'Name', $Configuration.GetStringValues('AZURE_DEPLOYMENT_NONSENSITIVE_PARAMETER_NAMES')).Result -and $Null -ne $parameter.Value.type -and $parameter.Value.type -ne 'bool' -and $parameter.Value.type -ne 'int' ) { $count++ # If the parameter is a custom type, recurse the type properties marked as secure. if ($Assert.HasField($parameter.Value, '$ref').Result) { $result = DefinitionHasSecureProperty -Definitions $Deployment.properties.template.definitions -Name $parameter.Value.'$ref'; $Assert.Create($result -eq $True, $LocalizedData.InsecureParameterType, $parameter.Name, $parameter.Value.'$ref'); } elseif ($Assert.HasField($parameter.Value, 'items.$ref').Result) { # Array of custom types $result = DefinitionHasSecureProperty -Definitions $Deployment.properties.template.definitions -Name $parameter.Value.items.'$ref'; $Assert.Create($result -eq $True, $LocalizedData.InsecureParameterType, $parameter.Name, $parameter.Value.items.'$ref'); } elseif ($Assert.HasField($parameter.Value, 'items.type').Result) { # Array parameters $Assert.In($parameter.Value.items.type, '.', @('secureString', 'secureObject')).ReasonFrom($parameter.Name, $LocalizedData.InsecureParameterType, $parameter.Name, $parameter.Value.items.type); } else { # Normal parameters $Assert.In($parameter.Value.type, '.', @('secureString', 'secureObject')).ReasonFrom($parameter.Name, $LocalizedData.InsecureParameterType, $parameter.Name, $parameter.Value.type); } } } } if ($count -eq 0) { return $Assert.Pass(); } } } # Check a definition for secure properties. # If the property is a custom type, recurse the definition to check these properties are secure. function global:DefinitionHasSecureProperty { param ( [Parameter(Mandatory = $True)] [PSObject]$Definitions, [Parameter(Mandatory = $True)] [String]$Name ) process { $result = $False; $shortName = $Name.Split('/')[-1]; Write-Verbose -Message "Checking definition: $shortName"; foreach ($definition in $Definitions.PSObject.Properties.GetEnumerator()) { if ($definition.Name -eq $shortName) { if ($Assert.HasField($definition.Value, 'properties').Result) { foreach ($property in $definition.Value.properties.PSObject.Properties.GetEnumerator()) { if ($Assert.HasField($property.Value, '$ref').Result) { $child = DefinitionHasSecureProperty -Definitions $Definitions -Name $property.Value.'$ref'; if ($child) { $result = $True; } } elseif ($Assert.HasField($property.Value, 'items.$ref').Result) { $child = DefinitionHasSecureProperty -Definitions $Definitions -Name $property.Value.items.'$ref'; if ($child) { $result = $True; } } elseif ($Assert.HasField($property.Value, 'items.type').Result) { $child = $Assert.In($property.Value.items.type, '.', @('secureString', 'secureObject')).Result; if ($child) { $result = $True; } } elseif ($Assert.In($property.Value.type, '.', @('secureString', 'secureObject')).Result) { $result = $True; } } } } } if ($result -eq $False) { Write-Verbose -Message "No secure properties found in definition: $shortName"; } else { Write-Verbose -Message "Secure properties found in definition: $shortName"; } return $result; } } function global:RecurseDeploymentSensitive { param ( [Parameter(Mandatory = $True)] [PSObject]$Deployment ) process { Write-Debug "Deployment is: $($Deployment.name)"; $propertyNames = $Configuration.GetStringValues('AZURE_DEPLOYMENT_SENSITIVE_PROPERTY_NAMES'); # Resources could be an object or an array. Check if it is an object and enumerate properties instead. $resources = @($Deployment.properties.template.resources); if ($Deployment.properties.template.resources -is [System.Management.Automation.PSObject]) { $resources = @($Deployment.properties.template.resources.PSObject.Properties.GetEnumerator() | ForEach-Object { $_.Value }); } if ($resources.Length -eq 0) { return $Assert.Pass(); } foreach ($resource in $resources) { if ($resource.type -eq 'Microsoft.Resources/deployments') { RecurseDeploymentSensitive -Deployment $resource; } else { foreach ($propertyName in $propertyNames) { $found = $PSRule.GetPath($resource, "$..$propertyName"); if ($Null -eq $found -or $found.Length -eq 0) { $Assert.Pass(); } else { Write-Debug "Found property name: $propertyName, value: $found"; foreach ($value in $found) { $Assert.Create(![PSRule.Rules.Azure.Runtime.Helper]::HasLiteralValue($value), $LocalizedData.LiteralSensitiveProperty, $propertyName); } } } } } } } function global:RecursivePropertiesSecretEvaluation { param ( [Parameter(Mandatory = $True)] [PSObject]$Resource, [Parameter(Mandatory = $True)] [PSObject]$Property, [Parameter(Mandatory = $True)] [AllowEmptyCollection()] [PSObject]$SecureParameters, [Parameter(Mandatory = $False)] [Bool]$ShouldUseSecret = $True ) process { $PropertyName = $Property.PSObject.properties.Name foreach ($NestedProperty in $Property.PSObject.Properties.Value.PSObject.Properties) { if($NestedProperty.MemberType -eq 'NoteProperty') { RecursivePropertiesSecretEvaluation -Resource $Resource -SecureParameters $SecureParameters -Property $NestedProperty -ShouldUseSecret $ShouldUseSecret } else { CheckPropertyUsesSecureParameter -Resource $Resource -SecureParameters $SecureParameters -PropertyPath "properties.$($PropertyName)" -ShouldUseSecret $ShouldUseSecret } } } } function global:CheckPropertyUsesSecureParameter { param ( [Parameter(Mandatory = $True)] [PSObject]$Resource, [Parameter(Mandatory = $True)] [AllowEmptyCollection()] [PSObject]$SecureParameters, [Parameter(Mandatory = $True)] [String]$PropertyPath, [Parameter(Mandatory = $False)] [Bool]$ShouldUseSecret = $True ) process { $propertyValues = $PSRule.GetPath($Resource, $PropertyPath); if ($propertyValues.Length -eq 0) { return $Assert.Pass(); } foreach ($propertyValue in $propertyValues) { $hasSecureParam = [PSRule.Rules.Azure.Runtime.Helper]::HasSecureValue($propertyValue, $SecureParameters); $Assert.Create($hasSecureParam -eq $ShouldUseSecret, $LocalizedData.SecureParameterRequired, $PropertyPath); } } } # Check resource properties that should be set by secure parameters. function global:RecurseSecureValue { param ( [Parameter(Mandatory = $True)] [PSObject]$Deployment ) process { $resources = @($Deployment.properties.template.resources); if ($resources.Length -eq 0) { return $Assert.Pass(); } $secureParameters = @($Deployment.properties.template.parameters.PSObject.properties | Where-Object { $_.Value.type -eq 'secureString' -or $_.Value.type -eq 'secureObject' } | ForEach-Object { $_.Name }); Write-Debug -Message "Secure parameters are: $($secureParameters -join ', ')"; foreach ($resource in $resources) { if ($resource.type -eq 'Microsoft.Resources/Deployments') { RecurseSecureValue -Deployment $resource; } else { $properties = [PSRule.Rules.Azure.Runtime.Helper]::GetSecretProperty($PSRule.GetService('Azure.Context'), $resource.type); if ($Null -eq $properties -or $properties.Length -eq 0) { $Assert.Pass(); } else { foreach ($property in $properties) { CheckPropertyUsesSecureParameter -Resource $resource -SecureParameters $secureParameters -PropertyPath $property } } } } } } # Check if the TargetObject is a parent deployment, with scoped deployments or a rendered deployment function global:IsParentDeployment { [CmdletBinding()] [OutputType([System.Boolean])] param () process { foreach ($deployment in $TargetObject.properties.template.resources){ return $Assert.HasField($deployment, 'properties.expressionEvaluationOptions.scope').Result; } } } #endregion Helpers # SIG # Begin signature block # MIIoQwYJKoZIhvcNAQcCoIIoNDCCKDACAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCmr/Qqu6ImHYEE # BjojLoYLZjHZAjt64oicllPnX5/K8aCCDXYwggX0MIID3KADAgECAhMzAAAEBGx0 # Bv9XKydyAAAAAAQEMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjQwOTEyMjAxMTE0WhcNMjUwOTExMjAxMTE0WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQC0KDfaY50MDqsEGdlIzDHBd6CqIMRQWW9Af1LHDDTuFjfDsvna0nEuDSYJmNyz # NB10jpbg0lhvkT1AzfX2TLITSXwS8D+mBzGCWMM/wTpciWBV/pbjSazbzoKvRrNo # DV/u9omOM2Eawyo5JJJdNkM2d8qzkQ0bRuRd4HarmGunSouyb9NY7egWN5E5lUc3 # a2AROzAdHdYpObpCOdeAY2P5XqtJkk79aROpzw16wCjdSn8qMzCBzR7rvH2WVkvF # HLIxZQET1yhPb6lRmpgBQNnzidHV2Ocxjc8wNiIDzgbDkmlx54QPfw7RwQi8p1fy # 4byhBrTjv568x8NGv3gwb0RbAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQU8huhNbETDU+ZWllL4DNMPCijEU4w # RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW # MBQGA1UEBRMNMjMwMDEyKzUwMjkyMzAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci # tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG # CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu # Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0 # MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAIjmD9IpQVvfB1QehvpC # Ge7QeTQkKQ7j3bmDMjwSqFL4ri6ae9IFTdpywn5smmtSIyKYDn3/nHtaEn0X1NBj # L5oP0BjAy1sqxD+uy35B+V8wv5GrxhMDJP8l2QjLtH/UglSTIhLqyt8bUAqVfyfp # h4COMRvwwjTvChtCnUXXACuCXYHWalOoc0OU2oGN+mPJIJJxaNQc1sjBsMbGIWv3 # cmgSHkCEmrMv7yaidpePt6V+yPMik+eXw3IfZ5eNOiNgL1rZzgSJfTnvUqiaEQ0X # dG1HbkDv9fv6CTq6m4Ty3IzLiwGSXYxRIXTxT4TYs5VxHy2uFjFXWVSL0J2ARTYL # E4Oyl1wXDF1PX4bxg1yDMfKPHcE1Ijic5lx1KdK1SkaEJdto4hd++05J9Bf9TAmi # u6EK6C9Oe5vRadroJCK26uCUI4zIjL/qG7mswW+qT0CW0gnR9JHkXCWNbo8ccMk1 # sJatmRoSAifbgzaYbUz8+lv+IXy5GFuAmLnNbGjacB3IMGpa+lbFgih57/fIhamq # 5VhxgaEmn/UjWyr+cPiAFWuTVIpfsOjbEAww75wURNM1Imp9NJKye1O24EspEHmb # DmqCUcq7NqkOKIG4PVm3hDDED/WQpzJDkvu4FrIbvyTGVU01vKsg4UfcdiZ0fQ+/ # V0hf8yrtq9CkB8iIuk5bBxuPMIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq # hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 # IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG # EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG # A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg # Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC # CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03 # a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr # rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg # OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy # 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9 # sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh # dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k # A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB # w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn # Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90 # lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w # ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o # ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD # VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa # BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny # bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG # AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t # L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV # HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3 # dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG # AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl # AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb # C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l # hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6 # I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0 # wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560 # STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam # ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa # J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah # XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA # 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt # Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr # /Xmfwb1tbWrJUnMTDXpQzTGCGiMwghofAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp # Z25pbmcgUENBIDIwMTECEzMAAAQEbHQG/1crJ3IAAAAABAQwDQYJYIZIAWUDBAIB # BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO # MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIJL7uyN/2wpbihuf1orwTmEt # bjYJElBtWOB2F+YH3p00MEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A # cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB # BQAEggEABIQx7PzcDKOgITTOHVNthzipejV0LaUFILYa6PGTNwnG+2h6jQRO/GEM # q18sslsRM9QZGMzpL5Zix5OGU9pWDivEWcINrvzESwNCtulHdJFQIedipFYrxdKd # TekLp7y+d+cVCdu+WZYlCXtvFqrVI1gpHqt28dZyCv7ybgrv4wBcnqvLFFhC6ad7 # sXfo5+Nt1NyG9BAHorVp2rwgVT5YzLYI4jQ5VaEauSjAvanarcdMzkupK37sUZX+ # jxED/analhZMPp99X7HUOXJ7X4UiKji+u9YjRyErOuoKMdl+q7h9O08qNgbCxr1U # CvHEtL1LwON4J2NTRTIuXR6YqYtqhKGCF60wghepBgorBgEEAYI3AwMBMYIXmTCC # F5UGCSqGSIb3DQEHAqCCF4YwgheCAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFaBgsq # hkiG9w0BCRABBKCCAUkEggFFMIIBQQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl # AwQCAQUABCC4Kok1TpDo591SEqlQkrORR3t+aA/bYxGKa70Gn+4WRgIGaFLWeMOB # GBMyMDI1MDYyMDE2MTEzOC41NjhaMASAAgH0oIHZpIHWMIHTMQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJl # bGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVT # Tjo0QzFBLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAg # U2VydmljZaCCEfswggcoMIIFEKADAgECAhMzAAAB/xI4fPfBZdahAAEAAAH/MA0G # CSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u # MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp # b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMB4XDTI0 # MDcyNTE4MzExOVoXDTI1MTAyMjE4MzExOVowgdMxCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9w # ZXJhdGlvbnMgTGltaXRlZDEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjRDMUEt # MDVFMC1EOTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNl # MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyeiV0pB7bg8/qc/mkiDd # JXnzJWPYgk9mTGeI3pzQpsyrRJREWcKYHd/9db+g3z4dU4VCkAZEXqvkxP5QNTtB # G5Ipexpph4PhbiJKwvX+US4KkSFhf1wflDAY1tu9CQqhhxfHFV7vhtmqHLCCmDxh # ZPmCBh9/XfFJQIUwVZR8RtUkgzmN9bmWiYgfX0R+bDAnncUdtp1xjGmCpdBMygk/ # K0h3bUTUzQHb4kPf2ylkKPoWFYn2GNYgWw8PGBUO0vTMKjYD6pLeBP0hZDh5P3f4 # xhGLm6x98xuIQp/RFnzBbgthySXGl+NT1cZAqGyEhT7L0SdR7qQlv5pwDNerbK3Y # SEDKk3sDh9S60hLJNqP71iHKkG175HAyg6zmE5p3fONr9/fIEpPAlC8YisxXaGX4 # RpDBYVKpGj0FCZwisiZsxm0X9w6ZSk8OOXf8JxTYWIqfRuWzdUir0Z3jiOOtaDq7 # XdypB4gZrhr90KcPTDRwvy60zrQca/1D1J7PQJAJObbiaboi12usV8axtlT/dCeP # C4ndcFcar1v+fnClhs9u3Fn6LkHDRZfNzhXgLDEwb6dA4y3s6G+gQ35o90j2i6am # aa8JsV/cCF+iDSGzAxZY1sQ1mrdMmzxfWzXN6sPJMy49tdsWTIgZWVOSS9uUHhSY # kbgMxnLeiKXeB5MB9QMcOScCAwEAAaOCAUkwggFFMB0GA1UdDgQWBBTD+pXk/rT/ # d7E/0QE7hH0wz+6UYTAfBgNVHSMEGDAWgBSfpxVdAF5iXYP05dJlpxtTNRnpcjBf # BgNVHR8EWDBWMFSgUqBQhk5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3Bz # L2NybC9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcmww # bAYIKwYBBQUHAQEEYDBeMFwGCCsGAQUFBzAChlBodHRwOi8vd3d3Lm1pY3Jvc29m # dC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0El # MjAyMDEwKDEpLmNydDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUF # BwMIMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAgEAOSNN5MpLiyun # m866frWIi0hdazKNLgRp3WZPfhYgPC3K/DNMzLliYQUAp6WtgolIrativXjOG1lI # jayG9r6ew4H1n5XZdDfJ12DLjopap5e1iU/Yk0eutPyfOievfbsIzTk/G51+uiUJ # k772nVzau6hI2KGyGBJOvAbAVFR0g8ppZwLghT4z3mkGZjq/O4Z/PcmVGtjGps2T # CtI4rZjPNW8O4c/4aJRmYQ/NdW91JRrOXRpyXrTKUPe3kN8N56jpl9kotLhdvd89 # RbOsJNf2XzqbAV7XjV4caCglA2btzDxcyffwXhLu9HMU3dLYTAI91gTNUF7BA9q1 # EvSlCKKlN8N10Y4iU0nyIkfpRxYyAbRyq5QPYPJHGA0Ty0PD83aCt79Ra0IdDIMS # uwXlpUnyIyxwrDylgfOGyysWBwQ/js249bqQOYPdpyOdgRe8tXdGrgDoBeuVOK+c # RClXpimNYwr61oZ2/kPMzVrzRUYMkBXe9WqdSezh8tytuulYYcRK95qihF0irQs6 # /WOQJltQX79lzFXE9FFln9Mix0as+C4HPzd+S0bBN3A3XRROwAv016ICuT8hY1In # yW7jwVmN+OkQ1zei66LrU5RtAz0nTxx5OePyjnTaItTSY4OGuGU1SXaH49JSP3t8 # yGYA/vorbW4VneeD721FgwaJToHFkOIwggdxMIIFWaADAgECAhMzAAAAFcXna54C # m0mZAAAAAAAVMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UE # CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z # b2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZp # Y2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0yMTA5MzAxODIyMjVaFw0zMDA5MzAxODMy # MjVaMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQH # EwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNV # BAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMIICIjANBgkqhkiG9w0B # AQEFAAOCAg8AMIICCgKCAgEA5OGmTOe0ciELeaLL1yR5vQ7VgtP97pwHB9KpbE51 # yMo1V/YBf2xK4OK9uT4XYDP/XE/HZveVU3Fa4n5KWv64NmeFRiMMtY0Tz3cywBAY # 6GB9alKDRLemjkZrBxTzxXb1hlDcwUTIcVxRMTegCjhuje3XD9gmU3w5YQJ6xKr9 # cmmvHaus9ja+NSZk2pg7uhp7M62AW36MEBydUv626GIl3GoPz130/o5Tz9bshVZN # 7928jaTjkY+yOSxRnOlwaQ3KNi1wjjHINSi947SHJMPgyY9+tVSP3PoFVZhtaDua # Rr3tpK56KTesy+uDRedGbsoy1cCGMFxPLOJiss254o2I5JasAUq7vnGpF1tnYN74 # kpEeHT39IM9zfUGaRnXNxF803RKJ1v2lIH1+/NmeRd+2ci/bfV+AutuqfjbsNkz2 # K26oElHovwUDo9Fzpk03dJQcNIIP8BDyt0cY7afomXw/TNuvXsLz1dhzPUNOwTM5 # TI4CvEJoLhDqhFFG4tG9ahhaYQFzymeiXtcodgLiMxhy16cg8ML6EgrXY28MyTZk # i1ugpoMhXV8wdJGUlNi5UPkLiWHzNgY1GIRH29wb0f2y1BzFa/ZcUlFdEtsluq9Q # BXpsxREdcu+N+VLEhReTwDwV2xo3xwgVGD94q0W29R6HXtqPnhZyacaue7e3Pmri # Lq0CAwEAAaOCAd0wggHZMBIGCSsGAQQBgjcVAQQFAgMBAAEwIwYJKwYBBAGCNxUC # BBYEFCqnUv5kxJq+gpE8RjUpzxD/LwTuMB0GA1UdDgQWBBSfpxVdAF5iXYP05dJl # pxtTNRnpcjBcBgNVHSAEVTBTMFEGDCsGAQQBgjdMg30BATBBMD8GCCsGAQUFBwIB # FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL0RvY3MvUmVwb3NpdG9y # eS5odG0wEwYDVR0lBAwwCgYIKwYBBQUHAwgwGQYJKwYBBAGCNxQCBAweCgBTAHUA # YgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU # 1fZWy4/oolxiaNE9lJBb186aGMQwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDovL2Ny # bC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0XzIw # MTAtMDYtMjMuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0cDov # L3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXRfMjAxMC0w # Ni0yMy5jcnQwDQYJKoZIhvcNAQELBQADggIBAJ1VffwqreEsH2cBMSRb4Z5yS/yp # b+pcFLY+TkdkeLEGk5c9MTO1OdfCcTY/2mRsfNB1OW27DzHkwo/7bNGhlBgi7ulm # ZzpTTd2YurYeeNg2LpypglYAA7AFvonoaeC6Ce5732pvvinLbtg/SHUB2RjebYIM # 9W0jVOR4U3UkV7ndn/OOPcbzaN9l9qRWqveVtihVJ9AkvUCgvxm2EhIRXT0n4ECW # OKz3+SmJw7wXsFSFQrP8DJ6LGYnn8AtqgcKBGUIZUnWKNsIdw2FzLixre24/LAl4 # FOmRsqlb30mjdAy87JGA0j3mSj5mO0+7hvoyGtmW9I/2kQH2zsZ0/fZMcm8Qq3Uw # xTSwethQ/gpY3UA8x1RtnWN0SCyxTkctwRQEcb9k+SS+c23Kjgm9swFXSVRk2XPX # fx5bRAGOWhmRaw2fpCjcZxkoJLo4S5pu+yFUa2pFEUep8beuyOiJXk+d0tBMdrVX # VAmxaQFEfnyhYWxz/gq77EFmPWn9y8FBSX5+k77L+DvktxW/tM4+pTFRhLy/AsGC # onsXHRWJjXD+57XQKBqJC4822rpM+Zv/Cuk0+CQ1ZyvgDbjmjJnW4SLq8CdCPSWU # 5nR0W2rRnj7tfqAxM328y+l7vzhwRNGQ8cirOoo6CGJ/2XBjU02N7oJtpQUQwXEG # ahC0HVUzWLOhcGbyoYIDVjCCAj4CAQEwggEBoYHZpIHWMIHTMQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJl # bGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVT # Tjo0QzFBLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAg # U2VydmljZaIjCgEBMAcGBSsOAwIaAxUAqROMbMS8JcUlcnPkwRLFRPXFspmggYMw # gYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD # VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQsF # AAIFAOv/98owIhgPMjAyNTA2MjAxNTA3NTRaGA8yMDI1MDYyMTE1MDc1NFowdDA6 # BgorBgEEAYRZCgQBMSwwKjAKAgUA6//3ygIBADAHAgEAAgIA7DAHAgEAAgISYzAK # AgUA7AFJSgIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIB # AAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBCwUAA4IBAQCCS3g/P0h8pXcp # ZHM+ympt8/Fq6BzGCmHXtbTRcWdP+UiwSdEABHpl8KRygO9PEtbIF46h9SNf1QrD # Pv9E9t8efyNxOe44w8uXFVFYYErf6gpTs6S1Qi0FPGktYzxBA0F7E8SH4kc3dY+p # LeLUVQ2KpngdgTvnpHIiUJo7gr0/ent0nnUNEbzHW/imFZkGucDSm+55+4lsPg4w # +0nB1U8aj5GloaPr1MfU4TxhtsCfG4/dRRm9XkZ2Ofy5nsY8wHNxfOIOok23RSWf # 0AuFQGyQg+RL2560HMFISuVxm0PdbMKsBMGgjkZpusVAE1LTp79uTKxy8D+p9BQF # JfSmYKuFMYIEDTCCBAkCAQEwgZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh # c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD # b3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIw # MTACEzMAAAH/Ejh898Fl1qEAAQAAAf8wDQYJYIZIAWUDBAIBBQCgggFKMBoGCSqG # SIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQxIgQgRs2hJ0/cTXo9 # k8x1QK0N7LsI+JaisEZBS9rWYp/v/cwwgfoGCyqGSIb3DQEJEAIvMYHqMIHnMIHk # MIG9BCDkMu++yQJ3aaycIuMT6vA7JNuMaVOI3qDjSEV8upyn/TCBmDCBgKR+MHwx # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1p # Y3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB/xI4fPfBZdahAAEAAAH/ # MCIEIDYxqCX8EwhzYfbP+cP+2QMqNy3af+lhy7P3MtGbNwYYMA0GCSqGSIb3DQEB # CwUABIICAKKIJyi52FKOX2JXR7WiVrbtvuL/UBVfDBQm1ygp4tz057nWUQrQcgZv # XaEmwnHhNjQ/MEvD10AJjQC8GuZi1bZ902HsWsVo2st6iHgKVWoMpPIc5CFdrc6R # con95hFw192nJrj4KqGIozVTmMdoBT9ApJl17TK8+eQsvjxLp6v+2j6/093jnjil # fR3e9IZENJFhXfUS0Fgtzc8gHJySQrHTj17G/QqBeRj+oWD4W4U5AqLCuzI52Bd7 # /4I9b8KFnwrNqUvYLOtAMGjHNtM453gAcXCCQyJK61EvLJiL+CMz32aJdSR5zZNR # WVj7WSKFNKfOu0z2f66o5LcJ+ed9BklDBmyq7mlknmNxZktKbOq24N5IkO3NwqQn # AVfJLdawuyCR5BT80bmScOBAQaLBmoZ5KqlaEzPONRfqZAotFnsnfHlsO0Yb4Tqc # Mm6/u59GK89YK0EoN9ifgw/xGBP5dMugkLCYKsxMNQhNgg/5WUoeHqubaLEuKXAm # meQoPEIYKGkc3z9yiyWalObg7BO8Ry7LlO4K5t7MgiYheG2CY0V3gwz8O49N3PeH # ARDCQuUxBfV8w1YV9Ok6NiY/e2bJJ314aaTxUADweZrtU9fJgP51FuJmEsqPd7mG # UQy4RZ3dqMUU6T81CDZm1vd1sLAz9BkqoKXEUlH6ta6O+8/6Uy1w # SIG # End signature block |