diff --git a/Nessus.psm1 b/Nessus.psm1 deleted file mode 100644 index 8a7d106..0000000 --- a/Nessus.psm1 +++ /dev/null @@ -1,227 +0,0 @@ -Set-StrictMode -Version latest - -function Send-NessusRequest() { - [CmdletBinding()] - param( - [Parameter(Mandatory=$true, Position=0, valuefromPipeline=$true)] - [string] $method, - [Parameter(Mandatory=$true, Position=1, valuefromPipeline=$true)] - [string] $resource, - [Parameter(Mandatory=$false, Position=2, valuefromPipeline=$true)] - [hashtable] $data = @{} - ) - - $header = @{"X-Cookie" = "token=$token"} - $url = $base + $resource - - # Use an empty dictionary for the body on GET requests - if ($method -eq "Get"){ - $body = @{} - } else { - $body = ConvertTo-Json $data - } - - $resp = Invoke-RestMethod -Uri $url -ContentType "application/json" -Method $method -Headers $header -Body $body -verbose - - return $resp -} - - -function Connect-Nessus() { - [CmdletBinding()] - param( - [Parameter(Mandatory=$true, Position=0, valuefromPipeline=$true)] - [string] $username, - [Parameter(Mandatory=$true, Position=1, valuefromPipeline=$true)] - [string] $password - ) - $data = @{"username" = $username; "password" = $password} - $resp = Send-NessusRequest "Post" "/session" $data - - return $resp.token -} - - -function Disconnect-Nessus{ - $resp = Send-NessusRequest "Delete" "/session" -} - - -function Get-NessusPolicies{ - $pols = @{} - $resp = Send-NessusRequest "Get" "/editor/policy/templates" - - foreach ($pol in $resp.templates) - { - $pols.Add($pol.title, $pol.uuid) - } - - return $pols -} - - -function Get-NessusHistoryIds() { - [CmdletBinding()] - param( - [Parameter(Mandatory=$true, Position=0, valuefromPipeline=$true)] - [string] $sid - ) - $hids = @() - $resp = Send-NessusRequest "Get" "/scans/$sid" - - foreach ($hist in $resp.history) - { - [pscustomobject]@{ - uuid = $hist.uuid - history_id = $hist.history_id - status = $hist.status - creation_date = [TimeZone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').AddSeconds($hist.creation_date)) - last_modification_date = [TimeZone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').AddSeconds($hist.last_modification_date)) - } - } -} - - -function Get-NessusScanHistory() { - [CmdletBinding()] - param( - [Parameter(Mandatory=$true, Position=0, valuefromPipeline=$true)] - [string] $sid, - [Parameter(Mandatory=$true, Position=1, valuefromPipeline=$true)] - [string] $hid - ) - $data = @{"history_id" = $hid} - $resp = Send-NessusRequest "GET" "/scans/$sid" $data - - return $resp.info -} - - -function Add() { - [CmdletBinding()] - param( - [Parameter(Mandatory=$true, Position=0, valuefromPipeline=$true)] - [string] $name, - [Parameter(Mandatory=$true, Position=1, valuefromPipeline=$true)] - [string] $desc, - [Parameter(Mandatory=$true, Position=2, valuefromPipeline=$true)] - [string] $targets, - [Parameter(Mandatory=$true, Position=3, valuefromPipeline=$true)] - [string] $policy - ) - $settings = @{} - $settings.Add("name", $name) - $settings.Add("description", $desc) - $settings.Add("text_targets", $targets) - - $data = @{} - $data.Add("uuid", $policy) - $data.Add("settings", $settings) - - $resp = Send-NessusRequest "Post" "/scans" $data - - return $resp.scan -} - - -function Start-NessusScan() { - [CmdletBinding()] - param( - [Parameter(Mandatory=$true, Position=0, valuefromPipeline=$true)] - [string] $sid - ) - $resp = Send-NessusRequest "Post" "/scans/$sid/launch" - - return $resp.scan_uuid -} - - -function Get-NessusStatus() { - [CmdletBinding()] - param( - [Parameter(Mandatory=$true, Position=0, valuefromPipeline=$true)] - [string] $sid, - [Parameter(Mandatory=$true, Position=0, valuefromPipeline=$true)] - [string] $hid - ) - - $resp = Get-NessusScanHistory $sid $hid - - return $resp.status -} - - -function Export-NessusStatus() { - [CmdletBinding()] - param( - [Parameter(Mandatory=$true, Position=0, valuefromPipeline=$true)] - [string] $sid, - [Parameter(Mandatory=$true, Position=1, valuefromPipeline=$true)] - [string] $fid - ) - - $resp = Send-NessusRequest "Get" "/scans/$sid/export/$fid/status" - - return $resp.status -} - - -function Export-NessusHistory() { - [CmdletBinding()] - param( - [Parameter(Mandatory=$true, Position=0, valuefromPipeline=$true)] - [string] $sid, - [Parameter(Mandatory=$true, Position=1, valuefromPipeline=$true)] - [string] $hid - ) - - $data = @{} - $data.Add("history_id", $hid) - $data.Add("format", "nessus") - - $resp = Send-NessusRequest "Post" "/scans/$sid/export" $data - $fid = $resp.file - - do { - Start-Sleep -Seconds 5 - $status = Export-NessusStatus -sid $sid -fid $fid - } while ($status -ne "ready") - - return $fid -} - - -function Get-NessusExportFile() { - [CmdletBinding()] - param( - [Parameter(Mandatory=$true, Position=0, valuefromPipeline=$true)] - [string] $sid, - [Parameter(Mandatory=$true, Position=1, valuefromPipeline=$true)] - [string] $fid - ) - - $resp = Send-NessusRequest "Get" "/scans/$sid/export/$fid/download" - - $file = "nessus-$sid-$fid.nessus" - Write-Verbose "Saving report to $file" - $resp.OuterXml | Out-File $file -Encoding ascii - - Get-ChildItem $file -} - - -function Get-NessusScans { - $scans = @() - $resp = Send-NessusRequest "Get" "/scans" - - foreach ($scan in $resp.scans){ - $scans += [pscustomobject]@{ - id = $scan.id - name = $scan.name - starttime = $scan.starttime - } - } - - $scans -} - diff --git a/Nessus.psd1 b/PSNessus/PSNessus.psd1 similarity index 62% rename from Nessus.psd1 rename to PSNessus/PSNessus.psd1 index cd3af6b..ff119e9 100644 --- a/Nessus.psd1 +++ b/PSNessus/PSNessus.psd1 @@ -1,5 +1,5 @@ # -# Module manifest for module 'Nessus' +# Module manifest for module 'PSNessus' # # Generated by: David F. Severski # @@ -9,7 +9,7 @@ @{ # Script module or binary module file associated with this manifest. -RootModule = 'Nessus.psm1' +RootModule = 'PSNessus.psm1' # Version number of this module. ModuleVersion = '1.0' @@ -21,13 +21,13 @@ GUID = 'fcbc8e15-c80d-4658-92f4-d57d35a6348b' Author = 'David F. Severski' # Company or vendor of this module -CompanyName = "Seattle Children's" +CompanyName = "NA" # Copyright statement for this module -Copyright = "(c) 2015 Seattle Children's. All rights reserved." +Copyright = "(c) 2016 David F. Severski. All rights reserved." # Description of the functionality provided by this module -# Description = '' +Description = 'PowerShell interface for the Tenable Nessus scanner.' # Minimum version of the Windows PowerShell engine required by this module # PowerShellVersion = '2.0' @@ -66,16 +66,19 @@ Copyright = "(c) 2015 Seattle Children's. All rights reserved." # NestedModules = @() # Functions to export from this module -FunctionsToExport = '*' +FunctionsToExport = 'Connect-Nessus', 'Disconnect-Nessus', 'Export-NessusHistory', + 'Export-NessusStatus', 'Get-NessusExportFile', 'Get-NessusHistoryIds', + 'Get-NessusPolicies', 'Get-NessusScanHistory', 'Get-NessuScans', + 'Send-NessusRequest', 'Start-NessusScan' # Cmdlets to export from this module -CmdletsToExport = '*' +CmdletsToExport = '' # Variables to export from this module -VariablesToExport = '*' +VariablesToExport = '' # Aliases to export from this module -AliasesToExport = '*' +AliasesToExport = '' # List of all modules packaged with this module. # ModuleList = @() @@ -84,7 +87,29 @@ AliasesToExport = '*' # FileList = @() # Private data to pass to the module specified in RootModule/ModuleToProcess -# PrivateData = '' +PrivateData = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + Tags = 'tenable', 'nessus', 'security', 'network', 'api', 'vulnerabilty' + + # A URL to the license for this module. + LicenseUri = 'https://github.com/davidski/PSNessus/LICENSE' + + # A URL to the main website for this project. + ProjectUri = 'https://github.com/davidski/PSNessus' + + # A URL to an icon representing this module. + # IconUri = '' + + # ReleaseNotes of this module + # ReleaseNotes = '' + + } # End of PSData hashtable + +} # End of PrivateData hashtable + # HelpInfo URI of this module # HelpInfoURI = '' diff --git a/PSNessus/PSNessus.psm1 b/PSNessus/PSNessus.psm1 new file mode 100644 index 0000000..a91e9b5 --- /dev/null +++ b/PSNessus/PSNessus.psm1 @@ -0,0 +1,20 @@ +#Get public and private function definition files. +$Public = @( Get-ChildItem -Path $PSScriptRoot\public\*.ps1 -ErrorAction SilentlyContinue ) +$Private = @( Get-ChildItem -Path $PSScriptRoot\private\*.ps1 -ErrorAction SilentlyContinue ) + +#Dot source the files +Foreach($import in @($Public + $Private)) +{ + Try + { + . $import.fullname + } + Catch + { + Write-Error -Message "Failed to import function $($import.fullname): $_" + } +} + +#$script:servername = "localhost" + +Export-ModuleMember -Function $Public.Basename diff --git a/PSNessus/PSScriptAnalyzerSettings.psd1 b/PSNessus/PSScriptAnalyzerSettings.psd1 new file mode 100644 index 0000000..b454018 --- /dev/null +++ b/PSNessus/PSScriptAnalyzerSettings.psd1 @@ -0,0 +1,10 @@ +# PSScriptAnalyzerSettings.psd1 +@{ + Severity=@('Error','Warning') + ExcludeRules=@('PSAvoidUsingCmdletAliases', + 'PSAvoidUsingUserNameAndPassWordParams', + 'PSShouldProcess', + 'PSAvoidUsingPlainTextForPassword', + 'PSUseShouldProcessForStateChangingFunctions', + 'PSUseSingularNouns') +} \ No newline at end of file diff --git a/PSNessus/private/Add.ps1 b/PSNessus/private/Add.ps1 new file mode 100644 index 0000000..42bb8b5 --- /dev/null +++ b/PSNessus/private/Add.ps1 @@ -0,0 +1,25 @@ +function Add() { + [CmdletBinding()] + param( + [Parameter(Mandatory=$true, Position=0, valuefromPipeline=$true)] + [string] $name, + [Parameter(Mandatory=$true, Position=1, valuefromPipeline=$true)] + [string] $desc, + [Parameter(Mandatory=$true, Position=2, valuefromPipeline=$true)] + [string] $targets, + [Parameter(Mandatory=$true, Position=3, valuefromPipeline=$true)] + [string] $policy + ) + $settings = @{} + $settings.Add("name", $name) + $settings.Add("description", $desc) + $settings.Add("text_targets", $targets) + + $data = @{} + $data.Add("uuid", $policy) + $data.Add("settings", $settings) + + $resp = Send-NessusRequest "Post" "/scans" $data + + return $resp.scan +} diff --git a/PSNessus/public/Connect-Nessus.ps1 b/PSNessus/public/Connect-Nessus.ps1 new file mode 100644 index 0000000..b8fe0ec --- /dev/null +++ b/PSNessus/public/Connect-Nessus.ps1 @@ -0,0 +1,13 @@ +function Connect-Nessus() { + [CmdletBinding()] + param( + [Parameter(Mandatory=$true, Position=0, valuefromPipeline=$true)] + [string] $username, + [Parameter(Mandatory=$true, Position=1, valuefromPipeline=$true)] + [string] $password + ) + $data = @{"username" = $username; "password" = $password} + $resp = Send-NessusRequest "Post" "/session" $data + + return $resp.token +} diff --git a/PSNessus/public/Disconnect-Nessus.ps1 b/PSNessus/public/Disconnect-Nessus.ps1 new file mode 100644 index 0000000..602dd8f --- /dev/null +++ b/PSNessus/public/Disconnect-Nessus.ps1 @@ -0,0 +1,4 @@ +function Disconnect-Nessus{ + $resp = Send-NessusRequest "Delete" "/session" + $resp +} diff --git a/PSNessus/public/Export-NessusHistory.ps1 b/PSNessus/public/Export-NessusHistory.ps1 new file mode 100644 index 0000000..62add4b --- /dev/null +++ b/PSNessus/public/Export-NessusHistory.ps1 @@ -0,0 +1,23 @@ +function Export-NessusHistory() { + [CmdletBinding()] + param( + [Parameter(Mandatory=$true, Position=0, valuefromPipeline=$true)] + [string] $sid, + [Parameter(Mandatory=$true, Position=1, valuefromPipeline=$true)] + [string] $hid + ) + + $data = @{} + $data.Add("history_id", $hid) + $data.Add("format", "nessus") + + $resp = Send-NessusRequest "Post" "/scans/$sid/export" $data + $fid = $resp.file + + do { + Start-Sleep -Seconds 5 + $status = Export-NessusStatus -sid $sid -fid $fid + } while ($status -ne "ready") + + return $fid +} diff --git a/PSNessus/public/Export-NessusStatus.ps1 b/PSNessus/public/Export-NessusStatus.ps1 new file mode 100644 index 0000000..6da250e --- /dev/null +++ b/PSNessus/public/Export-NessusStatus.ps1 @@ -0,0 +1,13 @@ +function Export-NessusStatus() { + [CmdletBinding()] + param( + [Parameter(Mandatory=$true, Position=0, valuefromPipeline=$true)] + [string] $sid, + [Parameter(Mandatory=$true, Position=1, valuefromPipeline=$true)] + [string] $fid + ) + + $resp = Send-NessusRequest "Get" "/scans/$sid/export/$fid/status" + + return $resp.status +} diff --git a/PSNessus/public/Get-NessusExportFile.ps1 b/PSNessus/public/Get-NessusExportFile.ps1 new file mode 100644 index 0000000..c924b07 --- /dev/null +++ b/PSNessus/public/Get-NessusExportFile.ps1 @@ -0,0 +1,17 @@ +function Get-NessusExportFile() { + [CmdletBinding()] + param( + [Parameter(Mandatory=$true, Position=0, valuefromPipeline=$true)] + [string] $sid, + [Parameter(Mandatory=$true, Position=1, valuefromPipeline=$true)] + [string] $fid + ) + + $resp = Send-NessusRequest "Get" "/scans/$sid/export/$fid/download" + + $file = "nessus-$sid-$fid.nessus" + Write-Verbose "Saving report to $file" + $resp.OuterXml | Out-File $file -Encoding ascii + + Get-ChildItem $file +} diff --git a/PSNessus/public/Get-NessusHistoryIds.ps1 b/PSNessus/public/Get-NessusHistoryIds.ps1 new file mode 100644 index 0000000..8c27e9e --- /dev/null +++ b/PSNessus/public/Get-NessusHistoryIds.ps1 @@ -0,0 +1,20 @@ +function Get-NessusHistoryIds() { + [CmdletBinding()] + param( + [Parameter(Mandatory=$true, Position=0, valuefromPipeline=$true)] + [string] $sid + ) + #$hids = @() + $resp = Send-NessusRequest "Get" "/scans/$sid" + + foreach ($hist in $resp.history) + { + [pscustomobject]@{ + uuid = $hist.uuid + history_id = $hist.history_id + status = $hist.status + creation_date = [TimeZone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').AddSeconds($hist.creation_date)) + last_modification_date = [TimeZone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').AddSeconds($hist.last_modification_date)) + } + } +} diff --git a/PSNessus/public/Get-NessusPolicies.ps1 b/PSNessus/public/Get-NessusPolicies.ps1 new file mode 100644 index 0000000..7dd5a1e --- /dev/null +++ b/PSNessus/public/Get-NessusPolicies.ps1 @@ -0,0 +1,11 @@ +function Get-NessusPolicies{ + $pols = @{} + $resp = Send-NessusRequest "Get" "/editor/policy/templates" + + foreach ($pol in $resp.templates) + { + $pols.Add($pol.title, $pol.uuid) + } + + return $pols +} diff --git a/PSNessus/public/Get-NessusScanHistory.ps1 b/PSNessus/public/Get-NessusScanHistory.ps1 new file mode 100644 index 0000000..7d25651 --- /dev/null +++ b/PSNessus/public/Get-NessusScanHistory.ps1 @@ -0,0 +1,13 @@ +function Get-NessusScanHistory() { + [CmdletBinding()] + param( + [Parameter(Mandatory=$true, Position=0, valuefromPipeline=$true)] + [string] $sid, + [Parameter(Mandatory=$true, Position=1, valuefromPipeline=$true)] + [string] $hid + ) + $data = @{"history_id" = $hid} + $resp = Send-NessusRequest "GET" "/scans/$sid" $data + + return $resp.info +} diff --git a/PSNessus/public/Get-NessusScans.ps1 b/PSNessus/public/Get-NessusScans.ps1 new file mode 100644 index 0000000..ebcbd2b --- /dev/null +++ b/PSNessus/public/Get-NessusScans.ps1 @@ -0,0 +1,14 @@ +function Get-NessusScans { + $scans = @() + $resp = Send-NessusRequest "Get" "/scans" + + foreach ($scan in $resp.scans){ + $scans += [pscustomobject]@{ + id = $scan.id + name = $scan.name + starttime = $scan.starttime + } + } + + $scans +} diff --git a/PSNessus/public/Send-NessusRequest.ps1 b/PSNessus/public/Send-NessusRequest.ps1 new file mode 100644 index 0000000..7ae14ad --- /dev/null +++ b/PSNessus/public/Send-NessusRequest.ps1 @@ -0,0 +1,27 @@ +Set-StrictMode -Version latest + +function Send-NessusRequest() { + [CmdletBinding()] + param( + [Parameter(Mandatory=$true, Position=0, valuefromPipeline=$true)] + [string] $method, + [Parameter(Mandatory=$true, Position=1, valuefromPipeline=$true)] + [string] $resource, + [Parameter(Mandatory=$false, Position=2, valuefromPipeline=$true)] + [hashtable] $data = @{} + ) + + $header = @{"X-Cookie" = "token=$token"} + $url = $base + $resource + + # Use an empty dictionary for the body on GET requests + if ($method -eq "Get"){ + $body = @{} + } else { + $body = ConvertTo-Json $data + } + + $resp = Invoke-RestMethod -Uri $url -ContentType "application/json" -Method $method -Headers $header -Body $body -verbose + + return $resp +} diff --git a/PSNessus/public/Start-NessusScan.ps1 b/PSNessus/public/Start-NessusScan.ps1 new file mode 100644 index 0000000..688e308 --- /dev/null +++ b/PSNessus/public/Start-NessusScan.ps1 @@ -0,0 +1,10 @@ +function Start-NessusScan() { + [CmdletBinding()] + param( + [Parameter(Mandatory=$true, Position=0, valuefromPipeline=$true)] + [string] $sid + ) + $resp = Send-NessusRequest "Post" "/scans/$sid/launch" + + return $resp.scan_uuid +} diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..cc34a7e --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,71 @@ +skip_commits: + files: + - appveyor.yml + - README.md +init: + - ps: | + if ($env:APPVEYOR_REPO_TAG -eq "true") + { + Update-AppveyorBuild -Version "$($env:APPVEYOR_REPO_TAG_NAME.TrimStart("v"))" + } + else + { + Update-AppveyorBuild -Version "0.0.$($env:APPVEYOR_BUILD_NUMBER)" + } + +environment: + nuget_api_key: + secure: 4HmcYasXmCps4URyA53dat5hyqyvIQzTE3IS7GcVFuJgad2Bn1hkKoE8qUH9fpso + +install: + - ps: Install-PackageProvider Nuget –Force + - ps: Set-PSRepository -Name PSGallery -InstallationPolicy Trusted + - ps: Install-Module -Name PSScriptAnalyzer -Force + +build_script: + - ps: | + $versionParts = ($env:APPVEYOR_BUILD_VERSION).split('.') + Import-Module .\PSNessus + $moduleInfo = Get-Module -Name PSNessus + $newVersion = New-Object -TypeName 'System.Version' -ArgumentList @($versionParts[0],$versionParts[1],$versionParts[2],$versionParts[3]) + $FunctionsToExport = @() + foreach($key in $moduleInfo.ExportedFunctions.Keys) { + $FunctionsToExport += $key + } + $projectUri = [uri]'https://github.com/davidski/PSNessus' + $projectLicense = [uri]'https://github.com/davidski/PSNessus/LICENSE' + $tags = @('tenable', 'nessus', 'security', 'network', 'vulnerability', 'api') + New-ModuleManifest -Path .\PSNessus\PSNessus.psd1 -Guid $moduleInfo.Guid -Author $moduleInfo.Author -CompanyName $moduleInfo.CompanyName ` + -Copyright $moduleInfo.Copyright -RootModule $moduleInfo.RootModule -ModuleVersion $newVersion -Description $moduleInfo.Description ` + -FunctionsToExport $FunctionsToExport -CmdletsToExport '' ` + -AliasesToExport '' -VariablesToExport '' -tags $tags -LicenseUri $projectLicense ` + -ProjectUri $projectUri + - 7z a PSNessus.zip %APPVEYOR_BUILD_FOLDER%\PSNessus\* + - ps: Get-ChildItem .\PSNessus.zip | % {Push-AppveyorArtifact $_.FullName -FileName $_.Name } + +test_script: + - ps: | + $res = Invoke-ScriptAnalyzer -Path $ENV:APPVEYOR_BUILD_FOLDER\PSNessus\* -Settings $ENV:APPVEYOR_BUILD_FOLDER\PSNessus\PSScriptAnalyzerSettings.psd1 + # Format the results + $header = "" + $body = $results | ForEach-Object {"$($_.Message)"} + $footer = "" + $header + $body + $footer | out-file .\TestsResults.xml + # Upload results + $wc = New-Object 'System.Net.WebClient' + $wc.UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\TestsResults.xml)) + # Fail if there are issues + if ($res.Count -gt 0) { throw "$($res.Count) tests failed."} + # If on a GH tag, push to PS Gallery + if ($env:APPVEYOR_REPO_TAG -eq "true") { + Publish-Module -Path $ENV:APPVEYOR_BUILD_FOLDER\PSNessus -NuGetApiKey $Env:NUGET_API_KEY + } + +deploy: + - provider: GitHub + draft: true + description: 'AppVeyor generated build' + on: + appveyor_repo_tag: true + auth_token: + secure: ANstD90CPeaEG/JTUq4pw8/s0UaGDmIomjvVt3f2tPqnFdD2VhPF8DOlP5AAP2lX