SSLLabsScanPS.psm1
#Region './Private/Initialisation.ps1' 0 $script:apiProperties = @( @{ ApiName = 'Info' TypeName = 'SSLLabsScan.Info' } @{ ApiName = 'Analyze' TypeName = 'SSLLabsScan.HostData' } @{ ApiName = 'GetEndpointData' TypeName = 'SSLLabsScan.EndPointData' } ) $script:baseEndpoint = 'https://api.ssllabs.com/api/v2/' $script:resourceDirectoryName = $ExecutionContext.SessionState.Module.ModuleBase + '\Resources' #EndRegion './Private/Initialisation.ps1' 18 #Region './Private/Invoke-SSLLabsScanApi.ps1' 0 Function Invoke-SSLLabsScanApi { <# .SYNOPSIS Invoke the SSLLabs Scan API .DESCRIPTION This function invokes the SSLLabs Scan API .PARAMETER ApiName Specifies the name of the API to invoke .PARAMETER QueryParameters Specifies the query parameters for the API .INPUTS None .OUTPUTS System.Management.Automation.PSCustomObject .EXAMPLE Invoke-SSLLabsScanApi -ApiName 'info' Invokes the SSLLabs Scan Info API. #> [CmdletBinding()] [OutputType([System.Management.Automation.PSCustomObject])] param( [Parameter( Mandatory, Position = 1)] [ValidateNotNullOrEmpty()] [System.String] $ApiName, [Parameter()] [System.String[]] $QueryParameters ) $endpoint = $script:baseEndpoint + $ApiName if ($QueryParameters) { $queryString = '?' + ($QueryParameters -join '&') $Uri = $endpoint + $queryString } else { $uri = $endpoint } Write-Debug -Message "Invoking RestMethod with URI $Uri" # Disable Write-Progress for Invoke-RestMethod to improve performance $ProgressPreference = 'SilentlyContinue' try { $result = Invoke-RestMethod -Uri $Uri } catch { throw $_ } $typeName = ($script:apiProperties | Where-Object -Property 'ApiName' -eq $ApiName).TypeName $result.PSTypeNames.Insert(0, $typeName) return $result } #EndRegion './Private/Invoke-SSLLabsScanApi.ps1' 72 #Region './Public/ConvertTo-SSLLabsScanHtml.ps1' 0 function ConvertTo-SSLLabsScanHtml { <# .SYNOPSIS Converts an SSL Labs Scan to an HTML report .DESCRIPTION This function converts an SSL Labs Scan to an HTML report .PARAMETER EndPointData Specifies the endpoint data to use for the report. .PARAMETER Path Specifies the output path for the report. If not specified, this defaults to the user's 'Documents' folder, with a file name of <hostName>-SSLLabsScanReport-<yyyyMMdd-HHmmss>.html'. .INPUTS None .OUTPUTS None .EXAMPLE ConvertTo-SSLLabsScanHtml Converts an SSL Labs Scan to an HTML report #> [CmdletBinding(PositionalBinding = $false)] [OutputType([System.String])] param( [Parameter(Mandatory)] [PSCustomObject[]] $EndPointData, [Parameter()] [System.String] $Path ) $hostName = $EndPointData[0].host $scanDate = $EndPointData[0].details.hostStartTime $htmlBody = [System.String]::Empty if (-not $PSBoundParameters.ContainsKey('Path')) { $fileName = "$hostName-SSLLabsScanReport-$($scanDate.ToString('yyyyMMdd-HHmmss')).html" $Path = Join-Path -Path ([Environment]::GetFolderPath('MyDocuments')) -ChildPath $fileName } $inlineStyleSheet = @( '<style>' (Get-Content -Path "$script:resourceDirectoryName\default.css") '</style>' ) $header = "SSLLabs Scan for $hostName" $preContent = "<h2>$header</h2>" foreach ($endpoint in $EndpointData) { Write-Verbose -Message "Converting scan for endpoint $($endpoint.ipAddress)" $protocols = @() foreach ($protocol in $endpoint.details.protocols) { $protocols += "$($protocol.name) v$($protocol.version)" } $cipherSuites = ($endpoint.details.suites.list.name | Out-String).Trim() -replace ('\r\n', '<br/>') $openSslCcsStatusMapping = @{ -1 = 'Test Failed' 0 = 'Unknown' 1 = 'Not Vulnerable' 2 = 'Possible Vulnerable, but not Exploitable' 3 = 'Vulnerable and Exploitable' } $openSslCcsStatus = $openSslCcsStatusMapping[$endpoint.details.openSslCcs] $openSslLuckyMinus20StatusMapping = @{ -1 = 'Test Failed' 0 = 'Unknown' 1 = 'Not Vulnerable' 2 = 'Vulnerable and Insecure' } $openSslLuckyMinus20Status = $openSslLuckyMinus20StatusMapping[$endpoint.details.openSSLLuckyMinus20] $poodleTlsStatusMapping = @{ -3 = 'Timeout' -2 = 'TLS Not Supported' -1 = 'Test Failed' 0 = 'Unknown' 1 = 'Not Vulnerable' 2 = 'Vulnerable' } $poodleTlsStatus = $poodleTlsStatusMapping[$endpoint.details.poodleTls] $reportData = [PSCustomObject][Ordered]@{ 'Server Name' = $endpoint.serverName 'Grade' = $endpoint.grade 'Grade Ignoring Trust' = $endpoint.gradeTrustIgnored 'Has Warnings' = $endpoint.hasWarnings 'Is Exceptional' = $endpoint.isExceptional 'Certificate Subject' = $endpoint.details.cert.subject 'Supported Protocols' = $protocols -join ', ' 'Supported Cipher Suites' = $cipherSuites 'BEAST Vulnerable' = $endpoint.details.vulnBeast 'Heartbleed Vulnerable' = $endpoint.details.Heartbleed 'Poodle Vulnerable' = $endpoint.details.poodle 'PoodleTLS Status' = $poodleTlsStatus 'FREAK Vulnerable' = $endpoint.details.freak 'Drown Vulnerable' = $endpoint.details.drownVulnerable 'OpenSSL CCS Status' = $openSslCcsStatus 'OpenSSL Lucky Minus 20 Status' = $openSslLuckyMinus20Status } $endpointPreContent = @( '<h3>' "IP Address: $($endpoint.ipAddress)" '</h3>' ) $htmlBody += $reportData | ConvertTo-Html -As List -PreContent $endpointPreContent -Fragment } $htmlReport = ConvertTo-Html -Head $inlineStyleSheet -PreContent $preContent -PostContent $htmlBody [System.Net.WebUtility]::HtmlDecode($htmlReport) | Out-File -FilePath $Path -Encoding ascii } #EndRegion './Public/ConvertTo-SSLLabsScanHtml.ps1' 134 #Region './Public/Get-SSLLabsScanEndpointData.ps1' 0 function Get-SSLLabsScanEndpointData { <# .SYNOPSIS Gets the endpoint data for a scan .DESCRIPTION This function gets the endpoint data for a scan .PARAMETER HostName Specifies the hostname of the scan. .PARAMETER IPAddress Specifies the ip address of the host in the scan. .PARAMETER HostData Specifies the HostData object containing details of the scan .PARAMETER FromCache Specifies whether to retrieve the scan from the cache. .INPUTS None .OUTPUTS SSLLabsScan.EndPointData .EXAMPLE Get-SSLLabsScanEndpointData -HostName www.bbc.co.uk -IPAddress 1.1.1.1 Gets the endpoint data for a scan on the bbc website for the specified IP address. #> [CmdletBinding(DefaultParameterSetName = 'Default')] [OutputType( { ($script:ApiPropertes | Where-Object -Property ApiName -eq 'getEndpointData').TypeName })] param( [Parameter( Mandatory, ParameterSetName = 'Default')] [System.String] $HostName, [Parameter( Mandatory, ParameterSetName = 'Default')] [System.String[]] $IPAddress, [Parameter( Mandatory, ParameterSetName = 'HostData')] [PSCustomObject] $HostData, [Parameter()] [System.Management.Automation.SwitchParameter] $FromCache ) $apiName = 'getEndpointData' if ($PSCmdlet.ParameterSetName -eq 'HostData') { $IPAddress = $HostData.endpoints.ipaddress $HostName = $HostData.host } $baseQueryParams = @() $baseQueryParams += "host=$HostName" if ($PSBoundParameters.ContainsKey('FromCache')) { $baseQueryParams += 'fromCache=on' } foreach ($ip in $IPAddress) { $queryParams = $baseQueryParams + "s=$ip" Write-Verbose "Getting SSL Labs Scan endpoint data on host $HostName, IP address $ip" $result = Invoke-SSLLabsScanApi -ApiName $apiName -QueryParameters $queryParams -Verbose:$false $result | Add-Member -Name 'host' -Value $HostName -MemberType NoteProperty $result.details.hostStartTime = ([System.DateTimeOffset]::FromUnixTimeMilliSeconds($result.details.hostStartTime)).UtcDateTime return $result } } #EndRegion './Public/Get-SSLLabsScanEndpointData.ps1' 88 #Region './Public/Get-SSLLabsScanInfo.ps1' 0 function Get-SSLLabsScanInfo { <# .SYNOPSIS Gets details of the SSLLabs Scan API .DESCRIPTION This function gets details of the SSLLabs Scan API .INPUTS None .OUTPUTS SSLLabsScan.Info .EXAMPLE Get-SSLLabsScanInfo Gets the SSLLabs Scan API info. #> [CmdletBinding()] [OutputType( { ($script:ApiPropertes | Where-Object -Property ApiName -eq 'info').TypeName })] param() $apiName = 'info' Write-Verbose 'Getting SSL Labs Scan API Info' $result = Invoke-SSLLabsScanApi -ApiName $apiName $result } #EndRegion './Public/Get-SSLLabsScanInfo.ps1' 32 #Region './Public/Invoke-SSLLabsScanAssessment.ps1' 0 function Invoke-SSLLabsScanAssessment { <# .SYNOPSIS Invokes an SSL Labs scan assessment of a website .DESCRIPTION This function invokes an SSL Labs scan assessment of a website .PARAMETER HostName Specifies the hostname for the scan .PARAMETER Publish Specifies whether to publish the scan results .PARAMETER StartNew Specifies whether to start a new scan .PARAMETER FromCache Specifies whether to retrieve a scan from the cache .PARAMETER MaxAge Specifies the maximum age in hours of a scan to retrieve from the cache .PARAMETER All If specified with a value of 'on', full information on individual endpoints will be returned. If specified with a value of 'done', full information on individual endpoints will only be returned if the assessment is complete. .PARAMETER IgnoreMismatch Specifies whether to ignore mismatches between the server certificate and the assessment hostname. .PARAMETER PollingInterval Specifies the polling interval in seconds between scan status checks. .INPUTS None .OUTPUTS SSLLabsScan.HostData .EXAMPLE Invoke-SSLLabsScanAssessment -HostName 'www.bbc.co.uk' -StartNew Invokes a new SSL Labs scan assessment of a the bbc web site #> [CmdletBinding(DefaultParameterSetName = 'Default')] [OutputType( { ($script:ApiPropertes | Where-Object -Property ApiName -eq 'Analyze').TypeName })] param( [Parameter(Mandatory)] [System.String] $HostName, [Parameter()] [System.Management.Automation.SwitchParameter] $Publish, [Parameter(ParameterSetName = 'StartNew')] [System.Management.Automation.SwitchParameter] $StartNew, [Parameter(ParameterSetName = 'FromCache')] [System.Management.Automation.SwitchParameter] $FromCache, [Parameter(ParameterSetName = 'FromCache')] [System.Int32] $MaxAge, [Parameter()] [ValidateSet('On', 'Done')] [System.String] $All, [Parameter()] [System.Management.Automation.SwitchParameter] $IgnoreMismatch, [Parameter()] [System.Int32] $PollingInterval = 10 ) $apiName = 'analyze' $queryParams = @() $queryParams += "host=$HostName" if ($PSBoundParameters.ContainsKey('Publish')) { $queryParams += 'publish=on' } if ($PSBoundParameters.ContainsKey('FromCache')) { $queryParams += 'fromCache=on' } if ($PSBoundParameters.ContainsKey('MaxAge')) { $queryParams += "maxAge=$MaxAge" } if ($PSBoundParameters.ContainsKey('All')) { $queryParams += "all=$All" } if ($PSBoundParameters.ContainsKey('IgnoreMismatch')) { $queryParams += 'ignoreMismatch=on' } $initialQueryParams = $queryParams # Only add the 'startNew' parameter on the initial query if ($PSBoundParameters.ContainsKey('StartNew')) { $initialQueryParams += 'startNew=on' } Write-Verbose "Invoking SSL Labs Scan API Analysis on host $HostName" $progressActivityMessage = "Checking SSL Labs Scan API Analysis on host $HostName" $result = Invoke-SSLLabsScanApi -ApiName $apiName -QueryParameters $initialQueryParams -Verbose:$false $retryCount = 0 while ($result.status -ne 'READY' -and $result.status -ne 'ERROR') { $retryCount++ Write-Progress -Activity $progressActivityMessage -Status "Status: $($result.status), $retryCount" Start-Sleep -Seconds $PollingInterval $result = Invoke-SSLLabsScanApi -ApiName $apiName -QueryParameters $queryParams -Verbose:$false } # Convert Unix time fields to PowerShell DateTime objects $result.startTime = ([System.DateTimeOffset]::FromUnixTimeMilliSeconds($result.startTime)).UtcDateTime $result.testTime = ([System.DateTimeOffset]::FromUnixTimeMilliSeconds($result.testTime)).UtcDateTime foreach ($endpoint in $result.endpoints) { $endpoint | Add-Member -Name 'host' -Value $HostName -MemberType NoteProperty $endpoint.details.hostStartTime = ([System.DateTimeOffset]::FromUnixTimeMilliSeconds($endpoint.details.hostStartTime)).UtcDateTime } Write-Progress -Activity $progressActivityMessage -Completed return $result } #EndRegion './Public/Invoke-SSLLabsScanAssessment.ps1' 156 |