diff --git a/.gitignore b/.gitignore index e57eaa7..9ef9fee 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ # Don't check in the Output dir Output/ .DS_Store +launch.json diff --git a/CHANGELOG.md b/CHANGELOG.md index dba5d8f..f63f4a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Change Log +## [0.2.4] 2019-11-18 + +- Add pagination support, add support for more than 5000 records, and code cleanup for Get-PSTenableSeverity. Thanks [@AaronG1234](https://github.com/AaronG1234)! [#12](https://github.com/jwmoss/PSTenable/issues/12) + +## [0.2.3] 2019-09-01 + +- Code cleanup [#9](https://github.com/jwmoss/PSTenable/issues/9) + ## [0.2.2] 2019-07-13 - Fixed automatically retrieving token. diff --git a/PSTenable/PSTenable.psd1 b/PSTenable/PSTenable.psd1 index 4db917e..d471f93 100644 --- a/PSTenable/PSTenable.psd1 +++ b/PSTenable/PSTenable.psd1 @@ -12,7 +12,7 @@ RootModule = 'PSTenable.psm1' # Version number of this module. - ModuleVersion = '0.2.2' + ModuleVersion = '0.2.5' # Supported PSEditions # CompatiblePSEditions = @() @@ -77,7 +77,9 @@ 'Get-PSTenablePluginFamilyWindows', 'Get-PSTenableSeverity', 'Get-PSTenableWindowsServerJava' - 'Invoke-PSTenableRest' + 'Invoke-PSTenableRest', + 'Get-PSTenableQuery', + 'Get-PSTenableQueryAnalysis' ) # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. diff --git a/PSTenable/PSTenable.psm1 b/PSTenable/PSTenable.psm1 index 4a0f27e..35bd367 100644 --- a/PSTenable/PSTenable.psm1 +++ b/PSTenable/PSTenable.psm1 @@ -1,6 +1,12 @@ # Dot source public/private functions $public = @(Get-ChildItem -Path (Join-Path -Path $PSScriptRoot -ChildPath 'public/*.ps1') -Recurse -ErrorAction Stop) $private = @(Get-ChildItem -Path (Join-Path -Path $PSScriptRoot -ChildPath 'private/*.ps1') -Recurse -ErrorAction Stop) + +Set-PSFconfig -Module PSTenable -Name Credential -Value $null -Initialize -Validation credential -Description "The credentials used for authenticating to the Tenable server" +Set-PSFconfig -Module PSTenalbe -Name WebSession -Value $null -Initialize -Description "WebSession for Invoke Rest Method" +Set-PSFconfig -Module PSTenalbe -Name Token -Value $null -Initialize -Validation string -Description "Token used with Tenable" +Set-PSFconfig -Module PSTenalbe -Name Server -Value $null -Initialize -Validation string -Description "REST Tenable endpoint for Tenable Server" + foreach ($import in @($public + $private)) { try { . $import.FullName diff --git a/PSTenable/Private/Invoke-PSTenableRest.ps1 b/PSTenable/Private/Invoke-PSTenableRest.ps1 index 66a08d1..2bdacca 100644 --- a/PSTenable/Private/Invoke-PSTenableRest.ps1 +++ b/PSTenable/Private/Invoke-PSTenableRest.ps1 @@ -36,13 +36,35 @@ function Invoke-PSTenableRest { Begin { - $RestMethodParams = @{ - URI = $(Get-PSFConfigValue -FullName 'PSTenable.Server') + $Endpoint - Method = $Method - Headers = @{"X-SecurityCenter" = $(Get-PSFConfigValue -FullName 'PSTenable.Token') } - ContentType = "application/json" - ErrorAction = "Stop" - WebSession = $(Get-PSFConfigValue -FullName "PSTenable.WebSession") + if((Get-PSFConfigValue -FullName "PSTenable.ApiKey" )){ + #Check for API Key + $accessKey = Get-PSFConfigValue -FullName 'PSTenable.accesskey' + $secretkey= Get-PSFConfigValue -FullName 'PSTenable.secretkey' + + $authString ="accesskey = $accessKey;secretkey=$secretkey;" + + $headers = @{ + "x-apikey" = $authString + } + $RestMethodParams = @{ + URI = $(Get-PSFConfigValue -FullName 'PSTenable.Server') + '/rest'+ $Endpoint + Method = $Method + Headers = $headers + ContentType = "application/json" + ErrorAction = "Stop" + } + + }else{ + $headers = @{"X-SecurityCenter" = $(Get-PSFConfigValue -FullName 'PSTenable.Token') } + + $RestMethodParams = @{ + URI = $(Get-PSFConfigValue -FullName 'PSTenable.Server') + $Endpoint + Method = $Method + Headers = $headers + ContentType = "application/json" + ErrorAction = "Stop" + WebSession = $(Get-PSFConfigValue -FullName "PSTenable.WebSession") + } } if ($PSBoundParameters.ContainsKey('Body')) { @@ -62,6 +84,8 @@ function Invoke-PSTenableRest { [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor $_ } + Write-Debug $RestMethodParams.uri + Invoke-RestMethod @RestMethodParams } diff --git a/PSTenable/Private/Invoke-PSTenableTokenStatus.ps1 b/PSTenable/Private/Invoke-PSTenableTokenStatus.ps1 index 3385c53..cb8678b 100644 --- a/PSTenable/Private/Invoke-PSTenableTokenStatus.ps1 +++ b/PSTenable/Private/Invoke-PSTenableTokenStatus.ps1 @@ -19,28 +19,36 @@ Function Invoke-PSTenableTokenStatus { ) - # Credentials - $APICredential = @{ - username = (Get-PSFConfigValue -FullName 'PSTenable.Credential').UserName - password = (Get-PSFConfigValue -FullName 'PSTenable.Credential').GetNetworkCredential().Password - releaseSession = "FALSE" - } + $apiKeyStatus = Get-PSFConfigValue -FullName "PSTenable.ApiKey" - $SessionSplat = @{ - URI = "$(Get-PSFConfigValue -FullName 'PSTenable.Server')/token" - SessionVariable = "SCSession" - Method = "Post" - ContentType = "application/json" - Body = (ConvertTo-Json $APICredential) - ErrorAction = "Stop" - } + Write-Debug "$apikeystatus" + + if($apiKeyStatus -eq $false){ - $Session = Invoke-RestMethod @SessionSplat + # Credentials + $APICredential = @{ + username = (Get-PSFConfigValue -FullName 'PSTenable.Credential').UserName + password = (Get-PSFConfigValue -FullName 'PSTenable.Credential').GetNetworkCredential().Password + releaseSession = "FALSE" + } - if ($Session.response.releaseSession -eq $true) { - Write-Output $true - } else { + $SessionSplat = @{ + URI = "$(Get-PSFConfigValue -FullName 'PSTenable.Server')/token" + SessionVariable = "SCSession" + Method = "Post" + ContentType = "application/json" + Body = (ConvertTo-Json $APICredential) + ErrorAction = "Stop" + } + + $Session = Invoke-RestMethod @SessionSplat + + if ($Session.response.releaseSession -eq $true) { + Write-Output $true + } else { + Write-Output $false + } + }else{ Write-Output $false } } - diff --git a/PSTenable/Public/Connect-PSTenable.ps1 b/PSTenable/Public/Connect-PSTenable.ps1 index 6809e3d..30e9a5b 100755 --- a/PSTenable/Public/Connect-PSTenable.ps1 +++ b/PSTenable/Public/Connect-PSTenable.ps1 @@ -15,6 +15,8 @@ function Connect-PSTenable { Tenable Server Name, tenable.domain.com/rest .PARAMETER Register If specified, this will cache the Credential, TenableServer, Token, and Web Session. + .PARAMETER ApiKey + If specfied PSCredential Object will be treated as a API Key. .INPUTS None .OUTPUTS @@ -35,6 +37,10 @@ function Connect-PSTenable { [Parameter(Position = 2, mandatory = $false)] [switch] + $ApiKey, + + [Parameter(Position = 3, mandatory = $false)] + [switch] $Register ) begin { @@ -43,50 +49,76 @@ function Connect-PSTenable { process { - # Credentials - $APICredential = @{ - username = $Credential.UserName - password = $Credential.GetNetworkCredential().Password - releaseSession = "FALSE" - } + if($ApiKey){ - $SessionSplat = @{ - URI = "$TenableServer/token" - SessionVariable = "SCSession" - Method = "Post" - ContentType = "application/json" - Body = (ConvertTo-Json $APICredential) - ErrorAction = "Stop" - ErrorVariable = "TenableTokenError" - } + $accesskey = $Credential.UserName + $secretkey = $Credential.GetNetworkCredential().Password - $currentProgressPref = $ProgressPreference - $ProgressPreference = "SilentlyContinue" - $currentVersionTls = [Net.ServicePointManager]::SecurityProtocol - $currentSupportableTls = [Math]::Max($currentVersionTls.value__, [Net.SecurityProtocolType]::Tls.value__) - $availableTls = [enum]::GetValues('Net.SecurityProtocolType') | Where-Object { $_ -gt $currentSupportableTls } - $availableTls | ForEach-Object { - [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor $_ - } + }Else{ + + # Credentials + $APICredential = @{ + username = $Credential.UserName + password = $Credential.GetNetworkCredential().Password + releaseSession = "FALSE" + } - $Session = Invoke-RestMethod @SessionSplat + $SessionSplat = @{ + URI = "$TenableServer/token" + SessionVariable = "SCSession" + Method = "Post" + ContentType = "application/json" + Body = (ConvertTo-Json $APICredential) + ErrorAction = "Stop" + ErrorVariable = "TenableTokenError" + } - [Net.ServicePointManager]::SecurityProtocol = $currentVersionTls - $ProgressPreference = $currentProgressPref + $currentProgressPref = $ProgressPreference + $ProgressPreference = "SilentlyContinue" + $currentVersionTls = [Net.ServicePointManager]::SecurityProtocol + $currentSupportableTls = [Math]::Max($currentVersionTls.value__, [Net.SecurityProtocolType]::Tls.value__) + $availableTls = [enum]::GetValues('Net.SecurityProtocolType') | Where-Object { $_ -gt $currentSupportableTls } + $availableTls | ForEach-Object { + [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor $_ + } + + $Session = Invoke-RestMethod @SessionSplat + + [Net.ServicePointManager]::SecurityProtocol = $currentVersionTls + $ProgressPreference = $currentProgressPref + } } end { - Set-PSFconfig -FullName "PSTenable.WebSession" -Value $SCSession - Set-PSFconfig -FullName "PSTenable.Token" -Value $Session.response.token - Set-PSFConfig -FullName "PSTenable.Server" -Value $TenableServer - Set-PSFconfig -FullName "PSTenable.Credential" -Value $Credential + if($ApiKey){ + + Set-PSFconfig -FullName "PSTenable.accesskey" -Value $accesskey + Set-PSFconfig -FullName "PSTenable.secretkey" -Value $secretkey + Set-PSFConfig -FullName "PSTenable.Server" -Value $TenableServer + Set-PSFConfig -FullName "PSTenable.ApiKey" -Value $true + + if ($Register -eq $true) { + Register-PSFConfig -FullName "PSTenable.accesskey" + Register-PSFConfig -FullName "PSTenable.secretkey" + Register-PSFConfig -FullName "PSTenable.Server" + Register-PSFConfig -FullName "PSTenable.Apikey" + } + + }Else{ + Set-PSFconfig -FullName "PSTenable.WebSession" -Value $SCSession + Set-PSFconfig -FullName "PSTenable.Token" -Value $Session.response.token + Set-PSFConfig -FullName "PSTenable.Server" -Value $TenableServer + Set-PSFconfig -FullName "PSTenable.Credential" -Value $Credential + Set-PSFConfig -FullName "PSTenable.ApiKey" -Value $false - if ($PSBoundParameters.ContainsKey('Register')) { - Register-PSFConfig -FullName "PSTenable.WebSession" - Register-PSFConfig -FullName "PSTenable.Token" - Register-PSFConfig -FullName "PSTenable.Server" - Register-PSFConfig -FullName "PSTenable.Token" + if ($Register -eq $true) { + Register-PSFConfig -FullName "PSTenable.WebSession" + Register-PSFConfig -FullName "PSTenable.Token" + Register-PSFConfig -FullName "PSTenable.Server" + Register-PSFConfig -FullName "PSTenable.Token" + Set-PSFConfig -FullName "PSTenable.ApiKey" + } } } } diff --git a/PSTenable/Public/Get-PSTenableAssetAnalysis.ps1 b/PSTenable/Public/Get-PSTenableAssetAnalysis.ps1 index 4cd994c..145fc88 100755 --- a/PSTenable/Public/Get-PSTenableAssetAnalysis.ps1 +++ b/PSTenable/Public/Get-PSTenableAssetAnalysis.ps1 @@ -18,9 +18,8 @@ function Get-PSTenableAssetAnalysis { #> [CmdletBinding()] param ( - [String] [Parameter(Position = 0, Mandatory = $true)] - [string] + [PSFComputer] $ComputerName ) diff --git a/PSTenable/Public/Get-PSTenableCVE.ps1 b/PSTenable/Public/Get-PSTenableCVE.ps1 index 7836370..da15101 100755 --- a/PSTenable/Public/Get-PSTenableCVE.ps1 +++ b/PSTenable/Public/Get-PSTenableCVE.ps1 @@ -24,7 +24,7 @@ function Get-PSTenableCVE { [parameter(Position = 0, mandatory = $true, ValueFromPipeline = $true)] - [string] + [string[]] $CVE ) @@ -74,13 +74,13 @@ function Get-PSTenableCVE { } ## Get the pluginID and then call Get-TenablePlugin, and then output those results - $Results = Foreach ($Plugin in $output.response.results.pluginid) { + Foreach ($Plugin in $output.response.results.pluginid) { Get-PSTenablePlugin -ID $Plugin } } end { - $Results + } } diff --git a/PSTenable/Public/Get-PSTenablePluginFamilyWindows.ps1 b/PSTenable/Public/Get-PSTenablePluginFamilyWindows.ps1 index 5e027a5..834107c 100755 --- a/PSTenable/Public/Get-PSTenablePluginFamilyWindows.ps1 +++ b/PSTenable/Public/Get-PSTenablePluginFamilyWindows.ps1 @@ -34,7 +34,7 @@ function Get-PSTenablePluginFamilyWindows { '29' ) - $Output = Foreach ($plugin in $WindowsPlugins) { + Foreach ($plugin in $WindowsPlugins) { $query = @{ "tool" = "vulnipdetail" @@ -82,6 +82,6 @@ function Get-PSTenablePluginFamilyWindows { } end { - $output + } } diff --git a/PSTenable/Public/Get-PSTenableQuery.ps1 b/PSTenable/Public/Get-PSTenableQuery.ps1 new file mode 100644 index 0000000..6dab3b6 --- /dev/null +++ b/PSTenable/Public/Get-PSTenableQuery.ps1 @@ -0,0 +1,70 @@ +function Get-PSTenableQuery { + <# + .SYNOPSIS + Retrieves all Queries in Tenable.SC server. + .DESCRIPTION + This function provides a way to retrieve all queries. + .EXAMPLE + PS C:\> Get-PSTenableQuery -Type vuln + This requests all vuln queries currently saved. + + .PARAMETER QueryID + Get Specfic Query by ID + .PARAMETER Type + Get All Queryies of a Specfic type + .INPUTS + None + .OUTPUTS + None + .NOTES + You can pass one or multiple PluginID's in an array. + #> + [CmdletBinding()] + param ( + [parameter(Position = 0, + mandatory = $true, + ValueFromPipeline = $true)] + [string] + [ValidateSet("alert","all", "lce","mobile","ticket","user","vuln")] + $Type + ) + + begin { + $TokenExpiry = Invoke-PSTenableTokenStatus + if ($TokenExpiry -eq $True) {Invoke-PSTenableTokenRenewal} + } + + process { + + $output = + + $Endpoint = "/query" + + if($Type){ + + $Endpoint = $Endpoint + "?=$Type" + } + + $EndPoint = $Endpoint.ToLower() + + $Splat = @{ + Method = "Get" + Endpoint = $Endpoint + } + + $response = Invoke-PSTenableRest @Splat | Select-Object -ExpandProperty Response + + $managedOnly = $response.manageable|where{$_.canUse -eq $false} + + if(($managedOnly|measure).Sum -gt 0){ + + return $managedOnly + } + + return $response.usable + } + + end { + $Output + } +} diff --git a/PSTenable/Public/Get-PSTenableQueryAnalysis.ps1 b/PSTenable/Public/Get-PSTenableQueryAnalysis.ps1 new file mode 100644 index 0000000..1c613e6 --- /dev/null +++ b/PSTenable/Public/Get-PSTenableQueryAnalysis.ps1 @@ -0,0 +1,64 @@ +function Get-PSTenableQueryAnalysis { + <# + .SYNOPSIS + Retrieves all devices that are affected by PluginID. + .DESCRIPTION + This function provides a way to retrieve all devices affected by a specific PluginID that is passed to the function. + .EXAMPLE + PS C:\> Get-PSTenablePlugin -ID "20007" + This passes PluginID 20007 CVE's to the fucntion and returns and all devices affected by the PluginID 20007. + + PS C:\> @("20007","31705") Get-PSTenablePlugin + This passes PluginID 20007 CVE's to the fucntion and returns and all devices affected by the PluginID 20007. + .PARAMETER ID + PluginID from Tenable + .INPUTS + None + .OUTPUTS + None + .NOTES + You can pass one or multiple PluginID's in an array. + #> + [CmdletBinding()] + param ( + [parameter(Position = 0, + mandatory = $true, + ValueFromPipeline = $true)] + [string] + $ID + ) + + begin { + $TokenExpiry = Invoke-PSTenableTokenStatus + if ($TokenExpiry -eq $True) {Invoke-PSTenableTokenRenewal} + } + + process { + + $output = foreach ($queryID in $ID) { + $query = @{ + "type" = "vuln" + "sourceType" = "cumulative" + "query" = @{ + "tool" = "listvuln" + "type" = "vuln" + "id" = "$queryID" + } + } + + $Splat = @{ + Method = "Post" + Body = $(ConvertTo-Json $query -depth 5) + Endpoint = "/analysis" + } + + Invoke-PSTenableRest @Splat | Select-Object -ExpandProperty Response | Select-Object -ExpandProperty Results + + } + + } + + end { + $Output + } +} diff --git a/PSTenable/Public/Get-PSTenableSeverity.ps1 b/PSTenable/Public/Get-PSTenableSeverity.ps1 index ac2679b..a97b3cc 100755 --- a/PSTenable/Public/Get-PSTenableSeverity.ps1 +++ b/PSTenable/Public/Get-PSTenableSeverity.ps1 @@ -4,30 +4,57 @@ function Get-PSTenableSeverity { Retrieves all vulnerabilities that are Critical, High, Medium, or Low in Tenable. .DESCRIPTION This function provides a way to retrieve all vulnerabilities in Tenable that are Critical, High, - Meidum, or Low. - .EXAMPLE - PS C:\> Get-PSTenableSeverity -Severity "Critical" - Retrieves all criitcal vulnerabilities. + Medium, or Low. + Revision 0.1, Sept 2019, jwmoss + Revision 0.2, Nov 2019, aarong1234 .INPUTS None .PARAMETER Severity - Option for Critical, High, Medium or Low. + Option for any of "Critical", "High", "Medium", "Low", "All", "All with Info". Defaults to "Critical","High". All with Info will get ALL vuln data + .PARAMETER MaxRecords + Option for maximum records (rows of data) that should be requested (as a throttle), Default is 0 (all records) [note: sorted by score, descending] + .PARAMETER Detailed + Option to enable detailed data. Defaults to Summary Data. To determine how detailed the resulting data is by querying Tenable.sc "Vulnerability Summary" versus "Vulnerability Detail" .OUTPUTS - None + PSCustomObject .NOTES None + .EXAMPLE + Get-PSTenableSeverity -Severity "Critical" + Retrieves all critical vulnerabilities, Summary Data [sorted by vprscore (note:Very OLD plugins dont have a VPR Scores)] + .EXAMPLE + Get-PSTenableSeverity -Detailed + Retrieves all critical and high vulnerabilities, Detailed Data (return Tenable.sc Vulnerability Detail data instead of summary) [sorted by vprscore (note:Very OLD plugins dont have a VPR Scores)] + .EXAMPLE + Get-PSTenableSeverity -Severity "High","Medium" -Maxrecords 200 + Retrieves high and medium vulnerabilities, up to 200 records, Summary Data [sorted by vprscore (note:Very OLD plugins dont have a VPR Scores)] + .EXAMPLE + Get-PSTenableSeverity -Severity "All" -Detailed + Retrieves all non-info vulnerabilities, Detailed data [sorted by vprscore (note:Very OLD plugins dont have a VPR Scores)] #> [CmdletBinding()] param ( - [Parameter(Position = 0, Mandatory = $true)] + [Parameter(Position = 0, Mandatory = $false)] [ValidateSet( 'Critical', 'High', 'Medium', - 'Low' + 'Low', + 'All', + 'All with Info' + )] - [string] - $Severity + [string[]] + $Severity, + + [Parameter(Position = 1, Mandatory = $false)] + [int]$MaxRecords = 0, + + [Parameter(Mandatory = $false)] + [ValidateSet($true,$false)] + [switch] + $Detailed + ) begin { @@ -35,58 +62,85 @@ function Get-PSTenableSeverity { $TokenExpiry = Invoke-PSTenableTokenStatus if ($TokenExpiry -eq $True) {Invoke-PSTenableTokenRenewal} - switch ($Severity) { - "Critical" { $ID = "4" } - "High" { $ID = "3" } - "Medium" { $ID = "2" } - "Low" { $ID = "1" } + if (-not $Severity) { + $ID = @("4","3") #Magic Numbers for Critical & High + } elseif ($Severity -contains "All with Info") { + $ID = @("4","3","2","1","0") #all but info + } elseif ($Severity -contains "All") { #if $severity has both All and All with Info.. All with Info will take precedence + $ID = @("4","3","2","1") + } else { #if Severity has All or All with info, other values are ignored + $ID = @() + switch ($Severity) { + "Critical" { $ID += "4" } + "High" { $ID += "3" } + "Medium" { $ID += "2" } + "Low" { $ID += "1" } + } } + $ID = $ID | Sort-Object -Descending } process { + $APIresults = @() + + + $idCount = 0 - $query = @{ - "tool" = "vulnipdetail" - "sortField" = "cveID" - "sortDir" = "ASC" - "type" = "vuln" - "sourceType" = "cumulative" - "query" = @{ - "name" = "" - "description" = "" - "context" = "" - "status" = "-1" - "createdTime" = 0 - "modifiedtime" = 0 - "sourceType" = "cumulative" - "sortDir" = "desc" - "tool" = "listvuln" - "groups" = "[]" - "type" = "vuln" - "startOffset" = 0 - "endOffset" = 5000 - "filters" = [array]@{ - "id" = "severity" - "filterName" = "severity" - "operator" = "=" - "type" = "vuln" - "ispredefined" = $true - "value" = "$ID" + $ID | ForEach-Object { #because of lack of useful sorting data (scores, time) within a severity in summary data... we are going to query severities in order + $Sev = $_ + $CurrentStartOffset = 0 + + Do { + $idCount++ + if ($MaxRecords -ne 0 -and $MaxRecords -lt ($CurrentStartOffset + 2147483647)) { + $CurrentEndOffset = $MaxRecords + } else { + $CurrentEndOffset = ($CurrentStartOffset + 2147483647) + } + + $PreJSON = @{ + "type" = "vuln" + "sourceType" = "cumulative" + "sortField" = "basescore" + "sortDir" = "DESC" + "query" = @{ + "type" = "vuln" + "startOffset" = $CurrentStartOffset + "endOffset" = $CurrentEndOffset + "tool" = & {if ($Detailed) {"vulndetails"} else {"listvuln"}} + } + } + if ($ID) { + $PreJSON.query.add("filters",[array]@{ + "filterName" = "severity" + "operator" = "=" + "value" = "$Sev" + }) } - "vulntool" = "listvuln" - "sortField" = "severity" - } - } - $Splat = @{ - Method = "Post" - Body = $(ConvertTo-Json $query -depth 5) - Endpoint = "/analysis" + + + $Splat = @{ + Method = "Post" + Body = $(ConvertTo-Json $PreJSON -depth 5) + Endpoint = "/analysis" + } + #Note: initially I was paginating every 2000, but frankly it was super inefficient, now we paginate on sizeof(Int32) + $ThisResults = Invoke-PSTenableRest @Splat | Select-Object -ExpandProperty Response | Select-Object -ExpandProperty Results + if ($ThisResults) { #non zero records came back + $APIresults += $Thisresults + #move pagination line (if you it is ever hit) + $CurrentStartOffset = $CurrentEndOffset + if ($Maxrecords -and ($CurrentStartOffset -ge $MaxRecords)) {$ThisResults = $Null} # we don't need to loop anymore + } + } While ( (($id|measure).count - $idCount) -eq 0) } + + $APIresults | Sort-object vprscore,basescore -Descending + } end { - Invoke-PSTenableRest @Splat | Select-Object -ExpandProperty Response | Select-Object -ExpandProperty Results } } diff --git a/PSTenable/Public/Get-PSTenableWindowsServerJava.ps1 b/PSTenable/Public/Get-PSTenableWindowsServerJava.ps1 index abe62ff..264351f 100644 --- a/PSTenable/Public/Get-PSTenableWindowsServerJava.ps1 +++ b/PSTenable/Public/Get-PSTenableWindowsServerJava.ps1 @@ -85,10 +85,12 @@ function Get-PSTenableWindowsServerJava { Body = $(ConvertTo-Json $query -depth 50) Endpoint = "/analysis" } + + Invoke-PSTenableRest @Splat | Select-Object -ExpandProperty Response | Select-Object -ExpandProperty Results + } End { - Invoke-PSTenableRest @Splat | Select-Object -ExpandProperty Response | Select-Object -ExpandProperty Results } } diff --git a/README.md b/README.md index 2e76fb4..f81fe92 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Install-Module -Name PSTenable -Scope CurrentUser -Repository PSGallery ```powershell ## Cache credentials, tenable server, web session, and token. $Credential = Get-Credential -Connect-PSTenable -Credential $Credential -TenableServer "server.domain.com/rest" -Register +Connect-PSTenable -Credential $Credential -TenableServer "http(s)://server.domain.com/rest" -Register ## Get all devices affected by CVE-2019-0708 Get-PSTenableCVE -CVE "CVE-2019-0708" @@ -34,4 +34,10 @@ Get-PSTenablePlugin -ID "125877" ## Get all vulnerabilities related to patch family Windows, Windows : Microsoft Bulletins, and Windows : User management Get-PSTenablePluginFamilyWindows + +## Retrieves all non-info vulnerabilities, Detailed data +Get-PSTenableSeverity -Severity "All" -Detailed + +## Retrieves high and medium vulnerabilities, up to 200 records, Summary Data +Get-PSTenableSeverity -Severity "High","Medium" -Maxrecords 200 ```