diff --git a/dist/ModuleInfo.json b/dist/ModuleInfo.json index 59757c32..942cfdcc 100644 --- a/dist/ModuleInfo.json +++ b/dist/ModuleInfo.json @@ -1,5 +1,5 @@ { - "Version": "1.3.2", + "Version": "1.4.0", "ReleaseTag": "Interested Siamese Fighting Fish", "Name": "LogRhythm.Tools", "Psm1": "LogRhythm.Tools.psm1", diff --git a/dist/common/LogRhythm.Tools.json b/dist/common/LogRhythm.Tools.json index a36c7069..a2c6328f 100644 --- a/dist/common/LogRhythm.Tools.json +++ b/dist/common/LogRhythm.Tools.json @@ -12,11 +12,17 @@ }, "LogRhythm": { - "Version":"7.7.0", + "Version":"7.11.0", "BaseUrl": "https://[NOT_SET]:8501", "ApiKey": "" }, + "Exabeam": { + "BaseUrl": "https://api.us-east.exabeam.cloud/", + "ApiKey": "", + "Token": "" + }, + "LogRhythmEcho": { "BaseUrl": "https://[NOT_SET]:33333/api" }, @@ -58,7 +64,7 @@ }, "RecordedFuture": { - "BaseUrl": "https://api.recordedfuture.com/v2/", + "BaseUrl": "https://api.recordedfuture.com/", "ApiKey": "" }, diff --git a/dist/installer/config/Lrt.Config.Input.json b/dist/installer/config/Lrt.Config.Input.json index 59ffcf90..dea50352 100644 --- a/dist/installer/config/Lrt.Config.Input.json +++ b/dist/installer/config/Lrt.Config.Input.json @@ -76,6 +76,18 @@ } }, + "Exabeam": { + "Name": "Exabeam", + "Optional": true, + "Message": "Use of Exabeam requires an API key.", + "HasKey": true, + "HasClientId": true, + + "Fields": {} + }, + + + "LogRhythmEcho": { "Name": "LogRhythm Echo", "Optional": true, diff --git a/dist/installer/include/input/Get-InputVersion.ps1 b/dist/installer/include/input/Get-InputVersion.ps1 index 5d13b184..ed8834bf 100644 --- a/dist/installer/include/input/Get-InputVersion.ps1 +++ b/dist/installer/include/input/Get-InputVersion.ps1 @@ -25,7 +25,7 @@ Function Get-InputVersion { ) # Validation Regexes - $ValidRegex = [regex]::new("^[1-9]\.[0-9](\.[0-9]([0-9])?)?$") + $ValidRegex = [regex]::new("^[1-9]\.\d+\.\d+?$") $Return = [PSCustomObject]@{ diff --git a/docs/examples/LR.Tools_Installer.gif b/docs/examples/LR.Tools_Installer.gif index d70ca836..67705216 100644 Binary files a/docs/examples/LR.Tools_Installer.gif and b/docs/examples/LR.Tools_Installer.gif differ diff --git a/examples/LogRhythm/SIEM/Admin/LogSources/Invoke-ManageWinLocalSources.ps1 b/examples/LogRhythm/SIEM/Admin/LogSources/Invoke-ManageWinLocalSources.ps1 new file mode 100644 index 00000000..5c408331 --- /dev/null +++ b/examples/LogRhythm/SIEM/Admin/LogSources/Invoke-ManageWinLocalSources.ps1 @@ -0,0 +1,176 @@ +using namespace System.Collections.Generic + +## Manual Config Begin +# Array of Log Sources Names we want to Automatically Add +$Lists = get-lrlists +$LogSourceAdds = [list[object]]::new() +$LogSourceAdds.add([PSCustomObject]@{ + Name = "MS Windows Event Logging - Firewall With Advanced Security" + Path = 'Microsoft-Windows-Windows Firewall With Advanced Security/Firewall' +}) + +$LogSourceAdds.add([PSCustomObject]@{ + Name = "MS Windows Event Logging XML - Windows Defender" + Path = 'Microsoft-Windows-Windows Defender/Operational' +}) + +$LogSourceAdds.add([PSCustomObject]@{ + Name = "MS Windows Event Logging XML - Security" + Path = 'Security' +}) + +$LogSourceAdds.add([PSCustomObject]@{ + Name = "MS Windows Event Logging XML - System" + Path = 'System' +}) + +$LogSourceAdds.add([PSCustomObject]@{ + Name = "MS Windows Event Logging XML - Application" + Path = 'Application' +}) + +$LogSourceAdds.add([PSCustomObject]@{ + Name = "MS Windows Event Logging XML - PowerShell" + Path = 'Windows PowerShell' +}) + +$LogSourceRemoves = @("MS Windows Event Logging - System", "MS Windows Event Logging - Security", "MS Windows Event Logging - Application") + +# Defines the amount of messages the LR Agent will retrieve per cycle +$MaxMsgCount = 2000 + +# Set log sources to LSO 2.0 policy where applicable +$MPEv2 = $true +## Manual Config End + +# Cmdlet to get past a potential local issue with firewall/network connection +Get-LrEntities + +## Automation Begin +# Generate the Log Source IDs based on the LogSourceReqs defined +$LogSourceAddIds = [list[object]]::new() +ForEach ($LogSourceReq in $LogSourceAdds) { + $LogSourceReqDetails = Get-LrLogSourceTypes -Name $($LogSourceReq.name) + if (($LogSourceAddIds -notcontains $LogSourceReqDetails) -and ($null -ne $LogSourceReqDetails) -and ($null -eq $LogSourceDetails.Error)) { + $LogSourceReqDetails | Add-Member -MemberType NoteProperty -Name 'Path' -Value $($LogSourceReq.Path) + Write-Host "$(Get-TimeStamp) | Add Log Sources | Adding Request | Log Source Name: $($LogSourceReqDetails.name)" + $LogSourceAddIds.add($LogSourceReqDetails) + } else { + if ($null -eq $LogSourceDetails.Error) { + Write-Host "$(Get-TimeStamp) | Add Log Sources | Skipped Request | Log Source Name: $($LogSourceReq.name)" + } else { + Write-Host "$(Get-TimeStamp) | Add Log Sources | Request Error | Log Source Name: $($LogSourceReq.name)" + write-host $LogSourceReqDetails.Error + } + } +} + +$LogSourceRemIds = [list[object]]::new() +ForEach ($LogSourceReq in $LogSourceRemoves) { + $LogSourceReqDetails = Get-LrLogSourceTypes -Name $LogSourceReq + if (($LogSourceRemIds -notcontains $LogSourceReqDetails) -and ($null -ne $LogSourceReqDetails) -and ($null -eq $LogSourceDetails.Error)) { + Write-Host "$(Get-TimeStamp) | Remove Log Sources | Adding Request | Log Source Name: $($LogSourceReqDetails.name)" + $LogSourceRemIds.add($LogSourceReqDetails) + } else { + if ($null -eq $LogSourceDetails.Error) { + Write-Host "$(Get-TimeStamp) | Remove Log Sources | Skipped Request | Log Source Name: $($LogSourceReq)" + } else { + Write-Host "$(Get-TimeStamp) | Remove Log Sources | Request Error | Log Source Name: $($LogSourceReq)" + write-host $LogSourceReqDetails.Error + } + } +} + +# Get Log Source IDs for the Log Source Types I want to automatically add +Write-Host "$(Get-TimeStamp) | Retrieving Active Agents | Begin" +$Agents = Get-LrAgentsAccepted -RecordStatus 'active' -AgentType 'Windows' +Write-Host "$(Get-TimeStamp) | Retrieving Active Agents | End" +$Counters = [PSCustomObject]@{ + StartTime = Get-Date + EndTime = 0 + Duration = 0 + TotalAgents = 0 + Add = 0 + Remove = 0 + AddError = 0 + RemoveError = 0 + AddSkip = 0 + RemoveSkip = 0 +} +ForEach ($Agent in $Agents) { + Write-Host "$(Get-TimeStamp) | Automation Runtime | Begin | Agent: $($Agent.hostName)" + $Counters.TotalAgents += 1 + $LogSources = Get-LrAgentLogSources -Id $Agent.Id -RecordStatus active + + # Adds + ForEach ($LogSourceAddId in $LogSourceAddIds) { + if ($LogSources.logSourceType.id -notcontains $LogSourceAddId.id) { + $AgentHost = Get-LrHostDetails -Id $Agent.hostId + $LogSourceMPEPolicies = Get-LrMpePolicies -msgSourceTypeId $LogSourceAddId.id + + if ($MPEv2 -eq $true -and $LogSourceMPEPolicies.name -match ".*?V2\.0") { + $MPEPolicy = $LogSourceMPEPolicies | Where-Object -FilterScript {$_.name -match ".*?V2\.0"} + } else { + $MPEPolicy = $LogSourceMPEPolicies | Where-Object -FilterScript {$_.name -match "LogRhythm Default"} + } + + # Define $LogFilePath + $LogFilePath = "$($Agent.hostname):$($LogSourceAddId.path)" + $RID = $(Get-Random -Maximum 999 -Minimum 100) + $AddResult = Add-LrLogSource -systemMonitorId $Agent.id -name "$($Agent.hostName) | $($RID) | $($LogSourceAddId.abbreviation)" -hostId $($Agent.hostId) -entityId $($AgentHost.entity.id) -logSourceTypeId $($LogSourceAddId.id) -mpePolicyId $($MPEPolicy.id) -mpeProcessingMode 'EventForwardingEnabled' -maxMsgCount $MaxMsgCount -longDescription "$(Get-TimeStamp) | Log Source Added through automation from $($env:computername)" -filePath $LogFilePath -RecordStatus Active -Status Enabled + if (($null -ne $AddResult.Error) -and ($AddResult.Error -eq $true)) { + Write-Host "$(Get-TimeStamp) | Automation Runtime | Adding Logsource | Error | Agent: $($Agent.hostName) Log Source: $($LogSourceAddId.name)" + write-host $AddResult + $Counters.AddError += 1 + } else { + $Counters.Add += 1 + Write-Host "$(Get-TimeStamp) | Automation Runtime | Adding Logsource | Success | Agent: $($Agent.hostName) Log Source: $($LogSourceAddId.name)" + } + # Add it + } else { + $Counters.AddSkip += 1 + Write-Host "$(Get-TimeStamp) | Automation Runtime | Adding Logsource | Skip | Agent: $($Agent.hostName) Log Source: $($LogSourceAddId.name)" + } + } + + # Removes + ForEach ($LogSource in $LogSources) { + # Removals + if ($LogSourceRemIds.id -contains $LogSource.logSourceType.id) { + if ($LogSource.systemMonitorName -like $LogSource.host.name) { + #Update-LrLogSource -Id $LogSource.id + $UpdateResult = Update-LrLogSource -Id $($LogSource.id) -RecordStatus Retired + if (($null -ne $UpdateResult.Error) -and ($UpdateResult.Error -eq $true)) { + write-host $UpdateResult + $Counters.RemoveError += 1 + } else { + $Counters.Remove += 1 + Write-Host "$(Get-TimeStamp) | Automation Runtime | Retire Logsource | Success | Agent: $($Agent.hostName) Log Source: $($LogSource.name)" + } + # Remove it + } else { + $Counters.RemoveSkip += 1 + Write-Host "$(Get-TimeStamp) | Automation Runtime | Retire Logsource | Skip | Agent: $($Agent.hostName) Log Source: $($LogSource.name) | Remote Collection" + } + } else { + $Counters.RemoveSkip += 1 + Write-Host "$(Get-TimeStamp) | Automation Runtime | Retire Logsource | Skip | Agent: $($Agent.hostName) Log Source: $($LogSource.name) | Criteria Miss" + } + } + Write-Host "$(Get-TimeStamp) | Automation Runtime | End | Agent: $($Agent.hostName)" +} +$Counters.EndTime = $(Get-Date) +$Counters.Duration = New-TimeSpan -Start $Counters.StartTime -End $Counters.EndTime +write-host "$(Get-TimeStamp) | Automation Complete | Duration | Start: $($Counters.StartTime) End: $($Counters.EndTime) Duration: $($Counters.Duration.Days)d $($Counters.Duration.Hours)h $($Counters.Duration.Minutes)m $($Counters.Duration.Seconds)s" +write-host "$(Get-TimeStamp) | Automation Complete | Summary | Agent Total: $($Counters.TotalAgents) Added Sources: $($Counters.Add) Add Errors: $($Counters.AddError) Removes: $($Counters.Remove) Remove Errors: $($Counters.RemoveError)" + +# Set all Windows Security Event Logs to same Policy +$WinSecLogSources = $(get-lrlogsources -RecordStatus 'active' -MessageSourceTypeId $(Get-LrLogSourceTypes -Name "MS Windows Event Logging XML - Security" | Select-Object -ExpandProperty id)) | Where-Object -FilterScript {$_.mpePolicy.name -notlike 'LogRhythm Default v2.0'} +ForEach ($WinSecSource in $WinSecLogSources) { + $Results = Update-LrLogSource -Id $WinSecSource.id -MpePolicyId -1000000020 -PassThru + if (($null -ne $Results.Error) -and ($Results.Error -eq $true)) { + write-host $Results + } + write-host "$(Get-TimeStamp) | MPE Set | Log Source: $($Results.name) Policy: $($Results.mpePolicy.Name) Log Source: $($Results.logSourceType.Name)" +} +## Automation End \ No newline at end of file diff --git a/src/Public/Exabeam/Agents/Get-ExaSiteAgentInstallCommand.ps1 b/src/Public/Exabeam/Agents/Get-ExaSiteAgentInstallCommand.ps1 new file mode 100644 index 00000000..e1d0e691 --- /dev/null +++ b/src/Public/Exabeam/Agents/Get-ExaSiteAgentInstallCommand.ps1 @@ -0,0 +1,140 @@ +using namespace System +using namespace System.IO +using namespace System.Collections.Generic + +Function Get-ExaSiteAgentInstallCommand { + <# + .SYNOPSIS + Get a Site Collector agent installation command. + .DESCRIPTION + Install command to install the Site Collector agent. The command is encoded in base64, necessitating a decoding step. + This encoding ensures the command's integrity during transmission, maintaining its format and preventing alterations. + + For example, after you receive the response, you can decode the string using the command + base64 -d <<< "c3Vkb...............". + + After decoding the command from base64 to its original string format, you'll have the necessary shell commands to + install the Site Collector agent. The install command varies depending on the type of Site Collector agent as + defined in the Site Collector template. The command sequence downloads and runs a script to configure the agent + and passes in parameters (deploymentHosts, templateIds, and optionally fetchStartDate and fetchHistoricalData) that + affect how the script configures the Site Collector agent. + .PARAMETER Type + Type of Site Collector agent for which the template applies. + .PARAMETER DeploymentHosts + Hostname or IP address of the Site Collector Core for which you want to install the agent. + .PARAMETER StartLogDate + (Windows collectors only) The date after which you want the Site Collector agent to receive logs (ISO-8601 format). + .PARAMETER FetchHistoricalData + (FileWindows and FileLinux only) This flag is only applicable for Core version 2.3.0 or higher. + For Core versions lower than 2.3.0, this flag will always be set to true. + .PARAMETER TemplateIDs + You can assign multiple Template IDs to any collector. + .PARAMETER Uninstall + This changes the API call to the commands/uninstallation endpoint. + + Provide parameters: type, deploymentHosts. + .PARAMETER Exact + Switch to force PARAMETER Name to be matched explicitly. + .PARAMETER Credential + PSCredential containing an API Token in the Password field. + .INPUTS + The Name parameter can be provided via the PowerShell pipeline. + .OUTPUTS + Base64 representing the install/uninstall command. + .NOTES + Exabeam-API + .LINK + https://github.com/LogRhythm-Tools/LogRhythm.Tools + #> + + [CmdletBinding()] + Param( + [Parameter(Mandatory = $true, Position = 0)] + [ValidateSet( + 'ArchiveLinux', + 'ArchiveWindows', + 'FileLinux', + 'FileWindows', + 'Windows', + ignorecase=$true + )] + [string] $Type, + + + [Parameter(Mandatory = $true, Position = 1)] + [string] $DeploymentHosts, + + + [Parameter(Mandatory = $false, Position = 2)] + [switch] $Exact, + + + [Parameter(Mandatory = $false, Position = 3)] + [datetime] $StartLogDate, + + + [Parameter(Mandatory = $false, Position = 4)] + [bool] $FetchHistoricalData = $true, + + [Parameter(Mandatory = $true, Position = 5)] + [string] $TemplateIDs, + + [Parameter(Mandatory = $false, Position = 6)] + [switch] $Uninstall, + + [Parameter(Mandatory = $false, Position = 7)] + [ValidateNotNull()] + [pscredential] $Credential = $LrtConfig.Exabeam.ApiKey + ) + + Begin { + $Me = $MyInvocation.MyCommand.Name + Set-LrtExaToken + # Request Setup + $BaseUrl = $LrtConfig.Exabeam.BaseUrl + $Token = $LrtConfig.Exabeam.Token.access_token + + + # Define HTTP Headers + $Headers = [Dictionary[string,string]]::new() + $Headers.Add("Authorization", "Bearer $Token") + $Headers.Add("content-type", "application/json") + $Headers.Add("accept", "application/json") + + # Define HTTP Method + $Method = $HttpMethod.Post + + # Define HTTP URI + if ($Uninstall) { + $RequestUrl = $BaseUrl + "site-collectors/v1/collectors/commands/uninstallation" + } else { + $RequestUrl = $BaseUrl + "site-collectors/v1/collectors/commands/installation" + } + + + # Check preference requirements for self-signed certificates and set enforcement for Tls1.2 + Enable-TrustAllCertsPolicy + } + + Process { + Write-Verbose "[$Me]: Request URL: $RequestUrl" + + $Body = [PSCustomObject]@{ + type = $Type + fetchHistoricalData = $FetchHistoricalData + templateIds = @($TemplateIDs) + startLogDate = $StartLogDate + deploymentHosts = @($DeploymentHosts) + } | ConvertTo-Json + + # Send Request + $Response = Invoke-RestAPIMethod -Uri $RequestUrl -Body $Body -Headers $Headers -Method $Method -Origin $Me + if (($null -ne $Response.Error) -and ($Response.Error -eq $true)) { + return $Response + } + + return $Response + } + + End { } +} \ No newline at end of file diff --git a/src/Public/Exabeam/Agents/Get-ExaSiteAgents.ps1 b/src/Public/Exabeam/Agents/Get-ExaSiteAgents.ps1 new file mode 100644 index 00000000..0f640100 --- /dev/null +++ b/src/Public/Exabeam/Agents/Get-ExaSiteAgents.ps1 @@ -0,0 +1,113 @@ +using namespace System +using namespace System.IO +using namespace System.Collections.Generic + +Function Get-ExaSiteAgents { + <# + .SYNOPSIS + Get a list of Site Collector agents. + .DESCRIPTION + To monitor configuration and management changes, you can retrieve a list of all Site Collector + agents with relevant details such as status. You can obtain these details for Site Collector + agents collectively or separately. Results are paginated. + .PARAMETER Credential + PSCredential containing an API Token in the Password field. + .INPUTS + The Name parameter can be provided via the PowerShell pipeline. + .OUTPUTS + PSCustomObject representing the specified LogRhythm List and its contents. + + If parameter ListItemsOnly is specified, a string collection is returned containing the + list's item values. + .EXAMPLE + PS C:\> Get-ExaSiteAgents + --- + id : 82a96a97-90f8-4b32-8734-039edf7d54e6 + name : Example + type : Ldap + status : RUNNING + statusMessage : Collector hasn't been receiving heartbeats more than 10 minutes + settings : @{primaryHost=examplehost.example.com; secondaryHosts=System.Object[]; port=636; ssl=True; globalCatalog=False; baseDn=DC=example, DC=com; bindDn=exacct; pullFullContext=False; pollingInterval=3600} + coreId : e6696714-5555-5555-5555-6f68c675b2ea + coreName : Example Prod1 + heartbeat : @{timestamp=2025-05-30T14:49:47Z} + .NOTES + Exabeam-API + .LINK + https://github.com/LogRhythm-Tools/LogRhythm.Tools + #> + + [CmdletBinding()] + Param( + [Parameter(Mandatory = $false, Position = 0)] + [ValidateNotNull()] + [pscredential] $Credential = $LrtConfig.Exabeam.ApiKey + ) + + Begin { + $Me = $MyInvocation.MyCommand.Name + Set-LrtExaToken + # Request Setup + $BaseUrl = $LrtConfig.Exabeam.BaseUrl + $Token = $LrtConfig.Exabeam.Token.access_token + + # Define HTTP Headers + $Headers = [Dictionary[string,string]]::new() + $Headers.Add("Authorization", "Bearer $Token") + + # Define HTTP Method + $Method = $HttpMethod.Get + + # Define HTTP URI + $RequestUrl = $BaseUrl + "site-collectors/v1/collectors" + + # Check preference requirements for self-signed certificates and set enforcement for Tls1.2 + Enable-TrustAllCertsPolicy + } + + Process { + Write-Verbose "[$Me]: Request URL: $RequestUrl" + + # Send Request + $Response = Invoke-RestAPIMethod -Uri $RequestUrl -Headers $Headers -Method $Method -Origin $Me + if (($null -ne $Response.Error) -and ($Response.Error -eq $true)) { + return $Response + } + + if ($Response.paging.next) { + $Results = [list[object]]::new() + ForEach($Item in $Response.items) { + $Results.add($Item) + } + $Counter = 0 + DO { + if ($Counter -eq 0) { + $RequestUrl = $Response.paging.next[0] -replace "^http:", "https:" + } else { + $RequestUrl = $PaginationResults.paging.next[0] -replace "^http:", "https:" + } + Write-Verbose "[$Me]: Request URL: $RequestUrl" + # Retrieve Query Results + $PaginationResults = Invoke-RestAPIMethod -Uri $RequestUrl -Headers $Headers -Method $Method -Origin $Me + if (($null -ne $PaginationResults.Error) -and ($PaginationResults.Error -eq $true)) { + return $PaginationResults + } + + # Append results to Response + ForEach ($Item in $PaginationResults.items) { + $Results.add($Item) + } + $Counter += 1 + } While ($PaginationResults.paging.next) + return $Results + } + + if ($Response.items) { + return $Response.items + } else { + return $Response + } + } + + End { } +} \ No newline at end of file diff --git a/src/Public/Exabeam/Context/Add-ExaContextRecords.ps1 b/src/Public/Exabeam/Context/Add-ExaContextRecords.ps1 new file mode 100644 index 00000000..3d2853ac --- /dev/null +++ b/src/Public/Exabeam/Context/Add-ExaContextRecords.ps1 @@ -0,0 +1,99 @@ +using namespace System +using namespace System.IO +using namespace System.Collections.Generic + +Function Add-ExaContextRecords { + <# + .NOTES + Exabeam-API + .LINK + https://github.com/LogRhythm-Tools/LogRhythm.Tools + #> + + [CmdletBinding()] + Param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)] + [ValidateNotNull()] + [string] $ContextId, + + + [Parameter(Mandatory = $true, Position = 1)] + [PSCustomObject[]] $Data = @(), + + [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 2)] + [ValidateSet( + 'append', + 'replace', + ignorecase=$true + )] + [string] $Operation, + + + [Parameter(Mandatory = $false, Position = 3)] + [ValidateNotNull()] + [pscredential] $Credential = $LrtConfig.Exabeam.ApiKey + ) + + Begin { + $Me = $MyInvocation.MyCommand.Name + Set-LrtExaToken + # Request Setup + $BaseUrl = $LrtConfig.Exabeam.BaseUrl + $Token = $LrtConfig.Exabeam.Token.access_token + + + # Define HTTP Headers + $Headers = [Dictionary[string,string]]::new() + $Headers.Add("Authorization", "Bearer $Token") + + # Define HTTP Method + $Method = $HttpMethod.Post + + # Define HTTP URI + $RequestUrl = $BaseUrl + "context-management/v1/tables/$ContextId/addRecords" + + # Check preference requirements for self-signed certificates and set enforcement for Tls1.2 + Enable-TrustAllCertsPolicy + + $ErrorObject = [PSCustomObject]@{ + Error = $false + Data = $Data + Note = $null + Value = $null + } + } + + Process { + Write-Verbose "[$Me]: Request URL: $RequestUrl" + + $SegmentCount = ([Math]::Round(($($Data.Count) / 20000), 0)) +1 + $SegmentedAddList = Create-LrPsArraySegments -InputArray $Data -Segments $SegmentCount + $Segment = 0 + foreach ($AddArray in $SegmentedAddList) { + $Segment += 1 + Write-Verbose "[$Me]: $(Get-TimeStamp) - Submitting $($AddArray.count) - Segment: $Segment/$SegmentCount" + Try { + # Build the JSON Body + $Body = @{ + operation = $Operation + data = $AddArray + } | ConvertTo-Json -Depth 5 -Compress + + # Send Request + $Response = Invoke-RestAPIMethod -Uri $RequestUrl -Headers $Headers -Method $Method -Body $Body -Origin $Me + } Catch { + $ErrorObject.Error = $true + $ErrorObject.Note = "Failed to submit addition entries." + $ErrorObject.Value = $AddArray + } + } + + if (($null -ne $Response.Error) -and ($Response.Error -eq $true)) { + return $Response + } + + return $Response + } + + End { } +} diff --git a/src/Public/Exabeam/Context/Get-ExaContextAddStatus.ps1 b/src/Public/Exabeam/Context/Get-ExaContextAddStatus.ps1 new file mode 100644 index 00000000..d4a3e88a --- /dev/null +++ b/src/Public/Exabeam/Context/Get-ExaContextAddStatus.ps1 @@ -0,0 +1,57 @@ +using namespace System +using namespace System.IO +using namespace System.Collections.Generic + +Function Get-ExaContextAddStatus { + <# + .NOTES + Exabeam-API + .LINK + https://github.com/LogRhythm-Tools/LogRhythm.Tools + #> + + [CmdletBinding()] + Param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)] + [ValidateNotNull()] + [string] $id, + + [Parameter(Mandatory = $false, Position = 3)] + [ValidateNotNull()] + [pscredential] $Credential = $LrtConfig.Exabeam.ApiKey + ) + + Begin { + $Me = $MyInvocation.MyCommand.Name + Set-LrtExaToken + # Request Setup + $BaseUrl = $LrtConfig.Exabeam.BaseUrl + $Token = $LrtConfig.Exabeam.Token.access_token + + # Define HTTP Headers + $Headers = [Dictionary[string,string]]::new() + $Headers.Add("accept", "application/json") + $Headers.Add("Authorization", "Bearer $Token") + + # Define HTTP Method + $Method = $HttpMethod.Get + $RequestUrl = $BaseUrl + "context-management/v1/tables/uploadStatus/" + $id + + # Check preference requirements for self-signed certificates and set enforcement for Tls1.2 + Enable-TrustAllCertsPolicy + } + + Process { + Write-Verbose "[$Me]: Request URL: $RequestUrl" + + # Send Request + $Response = Invoke-RestAPIMethod -Uri $RequestUrl -Headers $Headers -Method $Method -Origin $Me + if (($null -ne $Response.Error) -and ($Response.Error -eq $true)) { + return $Response + } + + return $Response + } + + End { } +} \ No newline at end of file diff --git a/src/Public/Exabeam/Context/Get-ExaContextAttributes.ps1 b/src/Public/Exabeam/Context/Get-ExaContextAttributes.ps1 new file mode 100644 index 00000000..9a8e4662 --- /dev/null +++ b/src/Public/Exabeam/Context/Get-ExaContextAttributes.ps1 @@ -0,0 +1,72 @@ +using namespace System +using namespace System.IO +using namespace System.Collections.Generic + +Function Get-ExaContextAttributes { + <# + .NOTES + Exabeam-API + .LINK + https://github.com/LogRhythm-Tools/LogRhythm.Tools + #> + + [CmdletBinding()] + Param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)] + [ValidateSet('Other','User', 'TI_ips', 'TI_domains', ignorecase=$true)] + [string] $contextType, + + [Parameter(Mandatory = $false, Position = 6)] + [ValidateNotNull()] + [pscredential] $Credential = $LrtConfig.Exabeam.ApiKey + ) + + Begin { + $Me = $MyInvocation.MyCommand.Name + Set-LrtExaToken + # Request Setup + $BaseUrl = $LrtConfig.Exabeam.BaseUrl + $Token = $LrtConfig.Exabeam.Token.access_token + + + # Define HTTP Headers + $Headers = [Dictionary[string,string]]::new() + $Headers.Add("Authorization", "Bearer $Token") + + # Define HTTP Method + $Method = $HttpMethod.Get + + if ($contextType -like 'user') { + $Type = 'User' + } + if ($contextType -like 'other') { + $Type = 'Other' + } + if ($contextType -like 'ti_ips') { + $Type = 'TI_ips' + } + if ($contextType -like 'ti_domains') { + $Type = 'TI_domains' + } + + # Define HTTP URI + $RequestUrl = $BaseUrl + "context-management/v1/attributes/" + $Type + + # Check preference requirements for self-signed certificates and set enforcement for Tls1.2 + Enable-TrustAllCertsPolicy + } + + Process { + Write-Verbose "[$Me]: Request URL: $RequestUrl" + + # Send Request + $Response = Invoke-RestAPIMethod -Uri $RequestUrl -Headers $Headers -Method $Method -Origin $Me + if (($null -ne $Response.Error) -and ($Response.Error -eq $true)) { + return $Response + } + + return $Response + } + + End { } +} \ No newline at end of file diff --git a/src/Public/Exabeam/Context/Get-ExaContextRecords.ps1 b/src/Public/Exabeam/Context/Get-ExaContextRecords.ps1 new file mode 100644 index 00000000..9d9332ad --- /dev/null +++ b/src/Public/Exabeam/Context/Get-ExaContextRecords.ps1 @@ -0,0 +1,96 @@ +using namespace System +using namespace System.IO +using namespace System.Collections.Generic + +Function Get-ExaContextRecords { + <# + .NOTES + Exabeam-API + .LINK + https://github.com/LogRhythm-Tools/LogRhythm.Tools + #> + + [CmdletBinding()] + Param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)] + [ValidateNotNull()] + [string] $id, + + [Parameter(Mandatory = $false, ValueFromPipeline = $true, Position = 1)] + [ValidateNotNull()] + [int32] $limit, + + [Parameter(Mandatory = $false, ValueFromPipeline = $true, Position = 2)] + [ValidateNotNull()] + [int32] $offset, + + [Parameter(Mandatory = $false, Position = 3)] + [ValidateNotNull()] + [pscredential] $Credential = $LrtConfig.Exabeam.ApiKey + ) + + Begin { + $Me = $MyInvocation.MyCommand.Name + Set-LrtExaToken + # Request Setup + $BaseUrl = $LrtConfig.Exabeam.BaseUrl + $Token = $LrtConfig.Exabeam.Token.access_token + + # Define HTTP Headers + $Headers = [Dictionary[string,string]]::new() + $Headers.Add("accept", "application/json") + $Headers.Add("Authorization", "Bearer $Token") + + # Define HTTP Method + $Method = $HttpMethod.Get + + $RequestUrl = $BaseUrl + "context-management/v1/tables/" + $id + "/records" + + # Check preference requirements for self-signed certificates and set enforcement for Tls1.2 + Enable-TrustAllCertsPolicy + } + + Process { + Write-Verbose "[$Me]: Request URL: $RequestUrl" + + # Send Request + $Response = Invoke-RestAPIMethod -Uri $RequestUrl -Headers $Headers -Method $Method -Origin $Me + if (($null -ne $Response.Error) -and ($Response.Error -eq $true)) { + return $Response + } + + if ($Response.paging.next) { + $Results = [list[object]]::new() + ForEach($Record in $Response.records) { + $Results.add($Record) + } + $Counter = 0 + DO { + if ($Counter -eq 0) { + $RequestUrl = $Response.paging.next[0] -replace "^http:", "https:" + } else { + $RequestUrl = $PaginationResults.paging.next[0] -replace "^http:", "https:" + } + Write-Verbose "[$Me]: Request URL: $RequestUrl" + # Retrieve Query Results + $PaginationResults = Invoke-RestAPIMethod -Uri $RequestUrl -Headers $Headers -Method $Method -Origin $Me + if (($null -ne $PaginationResults.Error) -and ($PaginationResults.Error -eq $true)) { + return $PaginationResults + } + + # Append results to Response + ForEach($Record in $PaginationResults.records) { + $Results.add($Record) + } + $Counter += 1 + } While ($PaginationResults.paging.next) + $PaginationResults.records = $Results + + return $PaginationResults + } + + return $Response + } + + End { } +} \ No newline at end of file diff --git a/src/Public/Exabeam/Context/Get-ExaContextTableAttributes.ps1 b/src/Public/Exabeam/Context/Get-ExaContextTableAttributes.ps1 new file mode 100644 index 00000000..087524b4 --- /dev/null +++ b/src/Public/Exabeam/Context/Get-ExaContextTableAttributes.ps1 @@ -0,0 +1,59 @@ +using namespace System +using namespace System.IO +using namespace System.Collections.Generic + +Function Get-ExaContextTableAttributes { + <# + .NOTES + Exabeam-API + .LINK + https://github.com/LogRhythm-Tools/LogRhythm.Tools + #> + + [CmdletBinding()] + Param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)] + [ValidateNotNull()] + [string] $id, + + [Parameter(Mandatory = $false, Position = 1)] + [ValidateNotNull()] + [pscredential] $Credential = $LrtConfig.Exabeam.ApiKey + ) + + Begin { + $Me = $MyInvocation.MyCommand.Name + Set-LrtExaToken + # Request Setup + $BaseUrl = $LrtConfig.Exabeam.BaseUrl + $Token = $LrtConfig.Exabeam.Token.access_token + + + # Define HTTP Headers + $Headers = [Dictionary[string,string]]::new() + $Headers.Add("Authorization", "Bearer $Token") + + # Define HTTP Method + $Method = $HttpMethod.Get + + # Define HTTP URI + $RequestUrl = $BaseUrl + "context-management/v1/tables/" + $id + + # Check preference requirements for self-signed certificates and set enforcement for Tls1.2 + Enable-TrustAllCertsPolicy + } + + Process { + Write-Verbose "[$Me]: Request URL: $RequestUrl" + + # Send Request + $Response = Invoke-RestAPIMethod -Uri $RequestUrl -Headers $Headers -Method $Method -Origin $Me + if (($null -ne $Response.Error) -and ($Response.Error -eq $true)) { + return $Response + } + + return $Response + } + + End { } +} \ No newline at end of file diff --git a/src/Public/Exabeam/Context/Get-ExaContextTables.ps1 b/src/Public/Exabeam/Context/Get-ExaContextTables.ps1 new file mode 100644 index 00000000..ec60bc0b --- /dev/null +++ b/src/Public/Exabeam/Context/Get-ExaContextTables.ps1 @@ -0,0 +1,81 @@ +using namespace System +using namespace System.IO +using namespace System.Collections.Generic + +Function Get-ExaContextTables { + <# + .NOTES + Exabeam-API + .LINK + https://github.com/LogRhythm-Tools/LogRhythm.Tools + #> + + [CmdletBinding()] + Param( + [Parameter(Mandatory = $false, ValueFromPipeline = $true, Position = 0)] + [ValidateNotNull()] + [string] $Name, + + [Parameter(Mandatory = $false, Position = 3)] + [switch] $Exact, + + [Parameter(Mandatory = $false, Position = 6)] + [ValidateNotNull()] + [pscredential] $Credential = $LrtConfig.Exabeam.ApiKey + ) + + Begin { + $Me = $MyInvocation.MyCommand.Name + Set-LrtExaToken + # Request Setup + $BaseUrl = $LrtConfig.Exabeam.BaseUrl + $Token = $LrtConfig.Exabeam.Token.access_token + + + # Define HTTP Headers + $Headers = [Dictionary[string,string]]::new() + $Headers.Add("Authorization", "Bearer $Token") + + # Define HTTP Method + $Method = $HttpMethod.Get + + # Define HTTP URI + $RequestUrl = $BaseUrl + "context-management/v1/tables" + + # Check preference requirements for self-signed certificates and set enforcement for Tls1.2 + Enable-TrustAllCertsPolicy + } + + Process { + Write-Verbose "[$Me]: Request URL: $RequestUrl" + + # Send Request + $Response = Invoke-RestAPIMethod -Uri $RequestUrl -Headers $Headers -Method $Method -Origin $Me + if (($null -ne $Response.Error) -and ($Response.Error -eq $true)) { + return $Response + } + + if ($Name.length -gt 0) { + $Results = [list[object]]::new() + ForEach ($List in $Response) { + if ($Exact) { + if ($List.name -like $Name) { + return $List + } + } else { + if ($List.name -match "$Name.*") { + $Results.add($List) + } + } + } + if ($Results) { + return $Results + } else { + return + } + } + return $Response + } + + End { } +} \ No newline at end of file diff --git a/src/Public/Exabeam/Context/New-ExaContextTable.ps1 b/src/Public/Exabeam/Context/New-ExaContextTable.ps1 new file mode 100644 index 00000000..ae9f1cbf --- /dev/null +++ b/src/Public/Exabeam/Context/New-ExaContextTable.ps1 @@ -0,0 +1,98 @@ +using namespace System +using namespace System.IO +using namespace System.Collections.Generic + +Function New-ExaContextTable { + <# + .NOTES + LogRhythm-API + .LINK + https://github.com/LogRhythm-Tools/LogRhythm.Tools + #> + + [CmdletBinding()] + Param( + [Parameter(Mandatory = $false, ValueFromPipeline = $true, Position = 0)] + [ValidateNotNull()] + [string] $Name, + + + [Parameter(Mandatory = $false, Position = 1)] + [ValidateSet( + 'Other', + 'User', + ignorecase=$true + )] + [string] $ContextType = 'Other', + + + [Parameter(Mandatory = $false, Position = 2)] + [ValidateSet( + 'Custom', + ignorecase=$true + )] + [string] $Source, + + + [Parameter(Mandatory = $false, Position = 3)] + [PSCustomObject[]] $Attributes = @(), + + + [Parameter(Mandatory = $false, Position = 4)] + [ValidateNotNull()] + [pscredential] $Credential = $LrtConfig.Exabeam.ApiKey + ) + + Begin { + $Me = $MyInvocation.MyCommand.Name + Set-LrtExaToken + # Request Setup + $BaseUrl = $LrtConfig.Exabeam.BaseUrl + $Token = $LrtConfig.Exabeam.Token.access_token + + + # Define HTTP Headers + $Headers = [Dictionary[string,string]]::new() + $Headers.Add("Authorization", "Bearer $Token") + + # Define HTTP Method + $Method = $HttpMethod.Post + + # Define HTTP URI + $RequestUrl = $BaseUrl + "context-management/v1/tables" + + # Check preference requirements for self-signed certificates and set enforcement for Tls1.2 + Enable-TrustAllCertsPolicy + } + + Process { + Write-Verbose "[$Me]: Request URL: $RequestUrl" + + # Validate Attributes + foreach ($Attribute in $Attributes) { + if (-not (Test-ExaAttributeSchema $Attribute)) { + throw "Invalid attribute schema detected: $($Attribute | ConvertTo-Json -Depth 10 -Compress)" + } + } + + + # Build the JSON Body + $Body = @{ + name = $Name + contextType = $ContextType + source = $Source + attributes = $Attributes + } | ConvertTo-Json -Depth 3 -Compress + + # Send Request + $Response = Invoke-RestAPIMethod -Uri $RequestUrl -Headers $Headers -Method $Method -Body $Body -Origin $Me + + if (($null -ne $Response.Error) -and ($Response.Error -eq $true)) { + return $Response + } + + return $Response + } + + End { } +} \ No newline at end of file diff --git a/src/Public/Exabeam/Context/Remove-ExaContextTable.ps1 b/src/Public/Exabeam/Context/Remove-ExaContextTable.ps1 new file mode 100644 index 00000000..662fe021 --- /dev/null +++ b/src/Public/Exabeam/Context/Remove-ExaContextTable.ps1 @@ -0,0 +1,61 @@ +using namespace System +using namespace System.IO +using namespace System.Collections.Generic + +Function Remove-ExaContextTable { + <# + .NOTES + LogRhythm-API + .LINK + https://github.com/LogRhythm-Tools/LogRhythm.Tools + #> + + [CmdletBinding()] + Param( + [Parameter(Mandatory = $false, ValueFromPipeline = $true, Position = 0)] + [ValidateNotNull()] + [string] $id, + + + [Parameter(Mandatory = $false, Position = 4)] + [ValidateNotNull()] + [pscredential] $Credential = $LrtConfig.Exabeam.ApiKey + ) + + Begin { + $Me = $MyInvocation.MyCommand.Name + Set-LrtExaToken + # Request Setup + $BaseUrl = $LrtConfig.Exabeam.BaseUrl + $Token = $LrtConfig.Exabeam.Token.access_token + + + # Define HTTP Headers + $Headers = [Dictionary[string,string]]::new() + $Headers.Add("Authorization", "Bearer $Token") + + # Define HTTP Method + $Method = $HttpMethod.Delete + + # Define HTTP URI + $RequestUrl = $BaseUrl + "context-management/v1/tables/$($id)?deleteUnusedCustomAttributes=false" + + # Check preference requirements for self-signed certificates and set enforcement for Tls1.2 + Enable-TrustAllCertsPolicy + } + + Process { + Write-Verbose "[$Me]: Request URL: $RequestUrl" + + # Send Request + $Response = Invoke-RestAPIMethod -Uri $RequestUrl -Headers $Headers -Method $Method -Origin $Me + + if (($null -ne $Response.Error) -and ($Response.Error -eq $true)) { + return $Response + } + + return $Response + } + + End { } +} \ No newline at end of file diff --git a/src/Public/Exabeam/Context/Test-ExaAttributeSchema.ps1 b/src/Public/Exabeam/Context/Test-ExaAttributeSchema.ps1 new file mode 100644 index 00000000..307595d8 --- /dev/null +++ b/src/Public/Exabeam/Context/Test-ExaAttributeSchema.ps1 @@ -0,0 +1,59 @@ +Function Test-ExaAttributeSchema { + <# + .SYNOPSIS + Tests if a given attribute conforms to one of the two allowed schemas. + + .DESCRIPTION + This function validates a PSCustomObject to ensure it matches one of the two schemas: + Schema 1: { id, isKey } + Schema 2: { displayName, isKey } + + The `isKey` property, if present, must match the string "true" or "false" (case-insensitive). + + .PARAMETER Attribute + The attribute object to validate. + + .INPUTS + [PSCustomObject] + + .OUTPUTS + [bool] + Returns $true if the attribute matches a schema; otherwise, $false. + + .NOTES + Approved verb "Test" is used in the function name to comply with PowerShell best practices. + #> + Param( + [PSCustomObject]$Attribute + ) + + # Schema 1: { id, isKey } + if ($Attribute.PSObject.Properties["id"]) { + if (-not $Attribute.PSObject.Properties["id"].Value -is [string]) { + throw "The 'id' property must be a string." + } + if ($Attribute.PSObject.Properties["isKey"]) { + $isKeyValue = $Attribute.PSObject.Properties["isKey"].Value.ToString() + if (-not ($isKeyValue -match '^(?i:true|false)$')) { + throw "The 'isKey' property must be 'true' or 'false' (case-insensitive)." + } + } + return $true + } + + # Schema 2: { displayName, isKey } + if ($Attribute.PSObject.Properties["displayName"]) { + if (-not $Attribute.PSObject.Properties["displayName"].Value -is [string]) { + throw "The 'displayName' property must be a string." + } + if ($Attribute.PSObject.Properties["isKey"]) { + $isKeyValue = $Attribute.PSObject.Properties["isKey"].Value.ToString() + if (-not ($isKeyValue -match '^(?i:true|false)$')) { + throw "The 'isKey' property must be 'true' or 'false' (case-insensitive)." + } + } + return $true + } + + throw "Invalid schema. Object must contain 'id' or 'displayName' and optionally 'isKey'." +} diff --git a/src/Public/Exabeam/Cores/Get-ExaSiteCollectorCerts.ps1 b/src/Public/Exabeam/Cores/Get-ExaSiteCollectorCerts.ps1 new file mode 100644 index 00000000..3157d91f --- /dev/null +++ b/src/Public/Exabeam/Cores/Get-ExaSiteCollectorCerts.ps1 @@ -0,0 +1,71 @@ +using namespace System +using namespace System.IO +using namespace System.Collections.Generic + +Function Get-ExaSiteCollectorCerts { + <# + .SYNOPSIS + Download a Site Collector Core certificate + .DESCRIPTION + Download the certificate for a Site Collector Core identified by its Core ID. + .PARAMETER Path + Location where certificates will be downloaded to. Must be in format of full path plus file name. + .PARAMETER CoreID + Site Collector Core ID. To retrieve the ID, see Get Site Collector Core details. + .EXAMPLE + PS C:\> Get-ExaSiteCollectorCerts -Path "C:\TEMP\certs.zip" -CoreID e6696714-5555-5555-5555-6f68c675b2ea + .NOTES + Exabeam-API + .LINK + https://github.com/LogRhythm-Tools/LogRhythm.Tools + #> + + [CmdletBinding()] + Param( + [Parameter(Mandatory = $false, Position = 0)] + [string] $Path = '.\ngsc_certs.zip', + + + [Parameter(Mandatory = $false, Position = 1)] + [string] $CoreID, + + + [Parameter(Mandatory = $false, Position = 2)] + [ValidateNotNull()] + [pscredential] $Credential = $LrtConfig.Exabeam.ApiKey + ) + + Begin { + $Me = $MyInvocation.MyCommand.Name + Set-LrtExaToken + # Request Setup + $BaseUrl = $LrtConfig.Exabeam.BaseUrl + $Token = $LrtConfig.Exabeam.Token.access_token + + # Define HTTP Headers + $Headers = [Dictionary[string,string]]::new() + $Headers.Add("Authorization", "Bearer $Token") + $Headers.Add("accept", "application/json") + + # Define HTTP Method + $Method = $HttpMethod.Get + + # Define HTTP URI + $RequestUrl = $BaseUrl + "site-collectors/v1/cores/$CoreID/certificates/download" + + # Check preference requirements for self-signed certificates and set enforcement for Tls1.2 + Enable-TrustAllCertsPolicy + } + + Process { + Write-Verbose "[$Me]: Request URL: $RequestUrl" + + # Send Request + $Response = Invoke-WebRequest -OutFile $Path -Uri $RequestUrl -Headers $Headers -Method $Method + if (($null -ne $Response.Error) -and ($Response.Error -eq $true)) { + return $Response + } + } + + End { } +} \ No newline at end of file diff --git a/src/Public/Exabeam/Cores/Get-ExaSiteCollectors.ps1 b/src/Public/Exabeam/Cores/Get-ExaSiteCollectors.ps1 new file mode 100644 index 00000000..52da9407 --- /dev/null +++ b/src/Public/Exabeam/Cores/Get-ExaSiteCollectors.ps1 @@ -0,0 +1,119 @@ +using namespace System +using namespace System.IO +using namespace System.Collections.Generic + +Function Get-ExaSiteCollectors { + <# + .SYNOPSIS + Get Site Collector list. + .DESCRIPTION + To monitor configuration and management changes, you can retrieve a list of all + Site Collector Core IDs with relevant details such as status, version, OS version, + hostname, and proxy hostname and port. You can obtain these details for Site + Collectors collectively or separately. + .PARAMETER Credential + PSCredential containing an API Token in the Password field. + .INPUTS + The Name parameter can be provided via the PowerShell pipeline. + .EXAMPLE + PS C:\> Get-LrList -Name "LR Threat List : URL : Attack" + --- + id : 83045d2e-5555-5555-5555-b293be7810e8 + name : Example + deploymentHosts : {host.example.com} + egressFilter : + proxy : + status : RUNNING + collectorsCount : 19 + createdDate : 2024-12-05T16:40:10Z + modifiedDate : 2025-05-27T19:48:16Z + heartbeat : @{timestamp=2025-05-30T14:56:40Z; caExpirationDate=2048949684; certExpirationDate=1796488885} + metadata : @{timezoneOffset=-0500; timezoneName=New York City, Brooklyn, Queens, Philadelphia} + currentVersion : 2.9.1 + latestVersion : 2.9.1 + coreVersions : @{latestVersion=2.9.1; stableVersion=2.8.1} + latestVersions : @{msi=2.9.0; windowsFile=2.9.0; linuxFile=2.9.0; windowsArchive=2.9.0; linuxArchive=2.9.0} + operationSystem : @{ipAddress=10.1.1.55; name=Red Hat Enterprise Linux; version=9.4 (Plow)} + systemConfiguration : @{cpu=4; memory=15GB} + installationPath : /opt + extractionPath : /tmp + ngscdStatus : RUNNING + ngscdHeartbeat : @{timestamp=1748617083} + .NOTES + Exabeam-API + .LINK + https://github.com/LogRhythm-Tools/LogRhythm.Tools + #> + + [CmdletBinding()] + Param( + [Parameter(Mandatory = $false, Position = 0)] + [ValidateNotNull()] + [pscredential] $Credential = $LrtConfig.Exabeam.ApiKey + ) + + Begin { + $Me = $MyInvocation.MyCommand.Name + Set-LrtExaToken + # Request Setup + $BaseUrl = $LrtConfig.Exabeam.BaseUrl + $Token = $LrtConfig.Exabeam.Token.access_token + + + # Define HTTP Headers + $Headers = [Dictionary[string,string]]::new() + $Headers.Add("Authorization", "Bearer $Token") + $Headers.Add("accept", "application/json") + + # Define HTTP Method + $Method = $HttpMethod.Get + + # Define HTTP URI + $RequestUrl = $BaseUrl + "site-collectors/v1/cores" + + # Check preference requirements for self-signed certificates and set enforcement for Tls1.2 + Enable-TrustAllCertsPolicy + } + + Process { + Write-Verbose "[$Me]: Request URL: $RequestUrl" + + # Send Request + $Response = Invoke-RestAPIMethod -Uri $RequestUrl -Headers $Headers -Method $Method -Origin $Me + if (($null -ne $Response.Error) -and ($Response.Error -eq $true)) { + return $Response + } + + if ($Response.paging.next) { + $Results = [list[object]]::new() + $Results.add($Response.items) + $Counter = 0 + DO { + if ($Counter -eq 0) { + $RequestUrl = $Response.paging.next[0] -replace "^http:", "https:" + } else { + $RequestUrl = $PaginationResults.paging.next[0] -replace "^http:", "https:" + } + Write-Verbose "[$Me]: Request URL: $RequestUrl" + # Retrieve Query Results + $PaginationResults = Invoke-RestAPIMethod -Uri $RequestUrl -Headers $Headers -Method $Method -Origin $Me + if (($null -ne $PaginationResults.Error) -and ($PaginationResults.Error -eq $true)) { + return $PaginationResults + } + + # Append results to Response + $Results.add($PaginationResults.items) + $Counter += 1 + } While ($PaginationResults.paging.next) + return $Results + } + + if ($Response.items) { + return $Response.items + } else { + return $Response + } + } + + End { } +} \ No newline at end of file diff --git a/src/Public/Exabeam/General/Get-LrtExaToken.ps1 b/src/Public/Exabeam/General/Get-LrtExaToken.ps1 new file mode 100644 index 00000000..ce7fd6a4 --- /dev/null +++ b/src/Public/Exabeam/General/Get-LrtExaToken.ps1 @@ -0,0 +1,87 @@ +using namespace System +using namespace System.Net +using namespace System.Collections.Generic +using namespace System.Web + +Function Get-LrtExaToken { + <# + .SYNOPSIS + Get an access token to access Exabeam API resources. + + .DESCRIPTION + Retrieves an access token from Exabeam by authenticating with provided client ID and secret. + The token is essential for making authenticated requests to Exabeam's APIs. This function uses the OAuth 2.0 + client credentials grant type to authenticate and fetch the token. The function constructs the request URI + using configuration settings from $LrtConfig. + .PARAMETER Credential + PSCredential containing an API Token in the Password field. + .OUTPUTS + [System.Object] representing an Exabeam resource access token. + + Object Properties + ----------------- + token_type = Type of token (e.g., "Bearer"). + expires_in = Expiry time span in seconds. + ext_expires_in = Extended expiry time span in seconds. + expires_on = Expiry timestamp as UTC datetime. + not_before = Creation timestamp as UTC datetime. + resource = Resource URL for which the token is generated. + access_token = The actual bearer token string. + .NOTES + Exabeam-API + .LINK + https://github.com/LogRhythm-Tools/LogRhythm.Tools + #> + + [CmdletBinding()] + Param( + [Parameter(Mandatory = $false, Position = 1)] + [ValidateNotNull()] + [pscredential] $Credential + ) + + Begin { + $Me = $MyInvocation.MyCommand.Name + + # Credentials + $ClientId = $LrtConfig.Exabeam.ApiKey.Username + $ClientSecret = $LrtConfig.Exabeam.ApiKey.GetNetworkCredential().Password + + $ResourceUri = $LrtConfig.Exabeam.BaseUrl + 'auth/v1/token' + } + + + Process { + $BodyContents = [PSCustomObject]@{ + "grant_type" = 'client_credentials' + client_id = $ClientId + client_secret = $ClientSecret + } + + # Establish Body Contents + $Body = $BodyContents | ConvertTo-Json + Write-Verbose $Body + + # Headers + $Headers = [Dictionary[string,string]]::new() + $Headers.Add("accept",'application/json') + $Headers.Add("content-type",'application/json') + + + # Request + try { + $Token = Invoke-RestMethod -Uri $ResourceUri -Headers $Headers -Method $HttpMethod.Post -Body $Body + } + catch [WebException] { + $Err = Get-RestErrorMessage $_ + throw [Exception] "[$Me] [$($Err.error)]: $($Err.error_description)`n" + } + + $Token | Add-Member -MemberType NoteProperty -Name expires_on -Value $((get-date).AddSeconds($Token.expires_in)) + + return $Token + } + + + End { } +} \ No newline at end of file diff --git a/src/Public/Exabeam/General/Set-LrtExaToken.ps1 b/src/Public/Exabeam/General/Set-LrtExaToken.ps1 new file mode 100644 index 00000000..e61733bf --- /dev/null +++ b/src/Public/Exabeam/General/Set-LrtExaToken.ps1 @@ -0,0 +1,53 @@ +using namespace System + +Function Set-LrtExaToken { + <# + .SYNOPSIS + Sets an Exabeam Access Token for the current PowerShell scope. + .DESCRIPTION + The Set-LrtAzToken cmdlet will update the environment variable + $LrtConfig.[ResourceName].Token with a new access token if one + is not already set. + + Tokens are not written to disk, and are only cached in memory + for the current PowerShell application context. + .OUTPUTS + None + .LINK + https://github.com/LogRhythm-Tools/LogRhythm.Tools + #> + + [CmdletBinding()] + Param( + ) + + + Begin { + # For error/info reporting + $Me = $MyInvocation.MyCommand.Name + # Set our reference + $CurrentToken = $LrtConfig.Exabeam.Token + } + + + Process { + # Check remaining token time if one is set + if ($CurrentToken) { + # Get Token time remaining and now, in UTC + if ($(Get-Date) -lt $LrtConfig.Exabeam.Token.expires_on) { + Write-Verbose "[$Me]: New token not required." + return + } + } + + # Get / save token + try { + Write-Verbose "[$Me]: Getting new token for Exabeam." + $LrtConfig.Exabeam.Token = Get-LrtExaToken + } catch { + $PSCmdlet.ThrowTerminatingError($PSItem) + } + } + + End { } +} \ No newline at end of file diff --git a/src/Public/Exabeam/Search/Get-ExaSearch.ps1 b/src/Public/Exabeam/Search/Get-ExaSearch.ps1 new file mode 100644 index 00000000..63687956 --- /dev/null +++ b/src/Public/Exabeam/Search/Get-ExaSearch.ps1 @@ -0,0 +1,155 @@ +using namespace System +using namespace System.IO +using namespace System.Collections.Generic + +Function Get-ExaSearch { + <# + .SYNOPSIS + + .DESCRIPTION + + .PARAMETER Credential + PSCredential containing an API Token in the Password field. + .INPUTS + The Name parameter can be provided via the PowerShell pipeline. + .OUTPUTS + + .EXAMPLE + + .NOTES + LogRhythm-API + .LINK + https://github.com/LogRhythm-Tools/LogRhythm.Tools + #> + [CmdletBinding()] + Param( + [Parameter(Mandatory = $false, Position = 0)] + [ValidateNotNull()] + [int] $StartHour = 0, + + [Parameter(Mandatory = $false, Position = 1)] + [ValidateNotNull()] + [int] $EndHour = 23, + + [Parameter(Mandatory = $false, Position = 2)] + [ValidateNotNull()] + [DateTime] $SearchDate = (Get-Date), + + [Parameter(Mandatory = $true, Position = 3)] + [ValidateNotNull()] + [string] $Filter, + + [Parameter(Mandatory = $true, Position = 4)] + [ValidateNotNull()] + [string[]] $Fields, + + [Parameter(Mandatory = $false, Position = 5)] + [ValidateNotNull()] + [string[]] $ShaFields, + + [Parameter(Mandatory = $false, Position = 6)] + [ValidateNotNull()] + [bool] $Distinct = $false, + + [Parameter(Mandatory = $false, Position = 7)] + [ValidateNotNull()] + [pscredential] $Credential = $LrtConfig.Exabeam.ApiKey + ) + + Begin { + $Me = $MyInvocation.MyCommand.Name + Set-LrtExaToken + # Request Setup + $BaseUrl = $LrtConfig.Exabeam.BaseUrl + $Token = $LrtConfig.Exabeam.Token.access_token + + + # Define HTTP Headers + $Headers = [Dictionary[string,string]]::new() + $Headers.Add("Authorization", "Bearer $Token") + $Headers.Add("content-type", "application/json") + + # Define HTTP Method + $Method = $HttpMethod.Post + + # Define HTTP URI + $RequestUrl = $BaseUrl + "search/v2/events" + + # Use SearchDate parameter (defaults to today if not provided) + $QueryDate = $SearchDate.Date + Write-Verbose "[$Me]: Using search date: $QueryDate" + + # Validate hour parameters (between 0-23) + $ValidatedStartHour = [Math]::Max(0, [Math]::Min(23, $StartHour)) + $ValidatedEndHour = [Math]::Max(0, [Math]::Min(23, $EndHour)) + + # Ensure EndHour isn't less than StartHour + if ($ValidatedEndHour -lt $ValidatedStartHour) { + $ValidatedEndHour = $ValidatedStartHour + } + + # Create precise time range for this query with guaranteed non-overlapping time windows + $startTime = $QueryDate.AddHours($ValidatedStartHour).ToString("yyyy-MM-ddTHH:00:00.000Z") + + # Create precise end time for the time block (last second of the end hour) + # End time is the last second of the specified end hour (HH:59:59) + $endTime = $QueryDate.AddHours($ValidatedEndHour).AddMinutes(59).AddSeconds(59).ToString("yyyy-MM-ddTHH:mm:ss.000Z") + Write-Verbose "[$Me]: Precise time window: $startTime to $endTime" + + + # Check preference requirements for self-signed certificates and set enforcement for Tls1.2 + Enable-TrustAllCertsPolicy + } + + Process { + Write-Verbose "[$Me]: Request URL: $RequestUrl" + + if ($Fields -notcontains 'approxLogTime') { + $Fields += 'approxLogTime' + } + + $body = [PSCustomObject]@{ + limit = 1000000 + distinct = $Distinct + filter = $Filter + startTime = $startTime + endTime = $endTime + fields = $Fields + } | ConvertTo-Json -Compress + + Write-Verbose $Body + + # Send Request + $Response = Invoke-RestAPIMethod -Uri $RequestUrl -Headers $Headers -Method $Method -Origin $Me -Body $body + if (($null -ne $Response.Error) -and ($Response.Error -eq $true)) { + return $Response + } + + if ($ShaFields -and $ShaFields.Count -gt 0) { + $sha1 = New-Object System.Security.Cryptography.SHA1CryptoServiceProvider + + if ($Response.rows) { + $AddRows = [list[object]]::new() + ForEach($Row in $Response.rows) { + $timestamp = $(ConvertFrom-UnixEpoch -UnixTime $($row.approxLogTime / 1000000)) + $Row | Add-Member -MemberType NoteProperty -Name 'timestamp' -Value $timestamp.ToString("M/d/yyyy h:mm:ss tt") + $hashBytes = $sha1.ComputeHash([System.Text.Encoding]::UTF8.GetBytes("$($Row.approxLogTime)$($Row.$($ShaFields[0]))$($Row.$($ShaFields[1]))")) + $Row | Add-Member -MemberType NoteProperty -Name 'sha1' -Value $([BitConverter]::ToString($hashBytes) -replace '-', '') + if ($AddRows.sha1 -notcontains $Row.sha1) { + $AddRows.add($Row) + } + } + $Response.rows = @($AddRows) + } + } + + + + + + + return $Response + } + + End { } +} \ No newline at end of file diff --git a/src/Public/General/Test-IPv4AddressInRange.ps1 b/src/Public/General/Test-IPv4AddressInRange.ps1 index acf90cf0..973ca36e 100644 --- a/src/Public/General/Test-IPv4AddressInRange.ps1 +++ b/src/Public/General/Test-IPv4AddressInRange.ps1 @@ -21,7 +21,7 @@ param( [array]::Reverse($ipAddress) $ipAddress = [system.BitConverter]::ToUInt32($ipAddress, 0) } else { - return $IPValid + return $false } if ($BIPValid.IsValid) { @@ -29,7 +29,7 @@ param( [array]::Reverse($bipAddress) $bipAddress = [system.BitConverter]::ToUInt32($bipAddress, 0) } else { - return $BIPValid + return $false } if ($EIPValid.IsValid) { @@ -37,7 +37,7 @@ param( [array]::Reverse($eipAddress) $eipAddress = [system.BitConverter]::ToUInt32($eipAddress, 0) } else { - return $EIPValid + return $false } diff --git a/src/Public/General/Test-ValidIPv4Address.ps1 b/src/Public/General/Test-ValidIPv4Address.ps1 index 7892eb6f..8570c665 100644 --- a/src/Public/General/Test-ValidIPv4Address.ps1 +++ b/src/Public/General/Test-ValidIPv4Address.ps1 @@ -36,16 +36,20 @@ Function Test-ValidIPv4Address { IsPrivate = $false } - # Check if ID value is an integer - if ($IP -as [ipaddress]) { + # Check if IP value is a valid IP address, and is IPv4 by parsing it as an [ipaddress] + if (($IP -as [ipaddress]) -and ($IP -as [ipaddress]).AddressFamily -eq 'InterNetwork') { $OutObject.Value = $IP.ToString() $OutObject.IsValid = $true if ($IP -Match '(^127\.)|(^192\.168\.)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)') { - $OutObject.IsPrivate =$true + $OutObject.IsPrivate = $true } else { $OutObject.IsPrivate = $false } + + if ($IP -match '(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))') { + $OutObject.IsValid = $false + } } else { $OutObject.IsValid = $false } diff --git a/src/Public/LogRhythm/Admin/Hosts/New-LrHost.ps1 b/src/Public/LogRhythm/Admin/Hosts/New-LrHost.ps1 index d7a3de90..a472be96 100644 --- a/src/Public/LogRhythm/Admin/Hosts/New-LrHost.ps1 +++ b/src/Public/LogRhythm/Admin/Hosts/New-LrHost.ps1 @@ -414,7 +414,7 @@ Function New-LrHost { id = -1 entity = [PSCustomObject]@{ id = $($_entity.Id) - name = $($_entity.Name) + name = $($_entity.fullName) } name = $Name shortDesc = $shortDesc diff --git a/src/Public/LogRhythm/Admin/Identities/Merge-LrIdentities.ps1 b/src/Public/LogRhythm/Admin/Identities/Merge-LrIdentities.ps1 index 01d06c15..58ca5002 100644 --- a/src/Public/LogRhythm/Admin/Identities/Merge-LrIdentities.ps1 +++ b/src/Public/LogRhythm/Admin/Identities/Merge-LrIdentities.ps1 @@ -38,13 +38,9 @@ Function Merge-LrIdentities { Required integer The IdentityId of the TrueIdentity which will be retired after merging All Identifiers will be moved from the Secondary TrueIdentity to the Primary TrueIdentity - .PARAMETER TestMode - Enabled by default. Disabling "TestMode" will perform the TrueIdentity migration. - - With TestMode on the cmdlet will check for errors but not make any changes to the TrueIdentities .EXAMPLE - C:\> Merge-LrIdentities -PrimaryIdentity 8 -SecondaryIdentity 1 -TestMode $false - Merge-LrIdentities -PrimaryIdentityId 8 -SecondaryIdentityId 1 -TestMode $false + C:\> Merge-LrIdentities -PrimaryIdentity 8 -SecondaryIdentity 1 + Merge-LrIdentities -PrimaryIdentityId 8 -SecondaryIdentityId 1 Primary Identity: 'Eric Hart (Eric.Hart)' Secondary Identity: 'Eric Hart (Eric.Hart)' Moving Identifiers: @@ -52,19 +48,6 @@ Function Merge-LrIdentities { Identifier 'eric.hart@logrhythm.com' type 'Email' already exists in the Primary Identity Successfully moved Identifier 'eric23hart@gmail.com' type 'Email' @{identityID=1; nameFirst=Eric; nameMiddle=W; nameLast=Hart; displayIdentifier=Eric.Hart; company=LogRhythm; department=Customer Success; title=; manager=Chuck Talley; addressCity=; domainName=; entity=; dateUpdated=2020-06-19T14:25:33.883Z; recordStatus=Retired; identifiers=System.Object[]; groups=System.Object[]} - .EXAMPLE - C:\> Merge-LrIdentities -IdentityObject @(8,1) - --- - Running in Preview mode; no changes to TrueIdentities will be made - Primary Identity: 'Eric Hart (Eric.Hart)' - Secondary Identity: 'Eric Hart (Eric.Hart)' - Moving Identifiers: - Identifier 'eric.hart@logrhythm.com' type 'Login' already exists in the Primary Identity - Identifier 'eric.hart@logrhythm.com' type 'Email' already exists in the Primary Identity - Successfully moved Identifier 'eric23hart@gmail.com' type 'Email' - Test Mode: Disable-LrIdentity -IdentityId 1 - identityID : 1 - status : Retired .LINK https://github.com/LogRhythm-Tools/LogRhythm.Tools #> diff --git a/src/Public/LogRhythm/Admin/Lists/Add-LrListItem.ps1 b/src/Public/LogRhythm/Admin/Lists/Add-LrListItem.ps1 index 264d7bd5..fb9c1961 100644 --- a/src/Public/LogRhythm/Admin/Lists/Add-LrListItem.ps1 +++ b/src/Public/LogRhythm/Admin/Lists/Add-LrListItem.ps1 @@ -514,6 +514,12 @@ Function Add-LrListItem { $ListItemDataType = "Int32" $ListItemType = "MsgSourceType" } + Network { + # Entity Network + # Only accepts NetworkIDs + $ListItemDataType = "Int32" + $ListItemType = "Network" + } Default {} } diff --git a/src/Public/LogRhythm/Admin/Lists/New-LrList.ps1 b/src/Public/LogRhythm/Admin/Lists/New-LrList.ps1 index 59406f1d..7610d90f 100644 --- a/src/Public/LogRhythm/Admin/Lists/New-LrList.ps1 +++ b/src/Public/LogRhythm/Admin/Lists/New-LrList.ps1 @@ -322,7 +322,7 @@ Function New-LrList { $ErrorObject.Type = "Input.Validation" $ErrorObject.Note = "Does expire is set to true, requires input parameter TimeToLiveSeconds to be provided." $ErrorObject.FieldType = $ListType - } else { + } elseif ($DoesExpire) { $BodyContents | Add-Member -MemberType NoteProperty -Name 'timeToLiveSeconds' -Value $TimeToLiveSeconds } diff --git a/src/Public/LogRhythm/Admin/Lists/Remove-LrListItem.ps1 b/src/Public/LogRhythm/Admin/Lists/Remove-LrListItem.ps1 index a1271937..7be3f974 100644 --- a/src/Public/LogRhythm/Admin/Lists/Remove-LrListItem.ps1 +++ b/src/Public/LogRhythm/Admin/Lists/Remove-LrListItem.ps1 @@ -494,6 +494,12 @@ Function Remove-LrListItem { $ListItemDataType = "Int32" $ListItemType = "MsgSourceType" } + Network { + # Entity Network + # Only accepts NetworkIDs + $ListItemDataType = "Int32" + $ListItemType = "Network" + } Default {} } diff --git a/src/Public/LogRhythm/Admin/Lists/Update-LrList.ps1 b/src/Public/LogRhythm/Admin/Lists/Update-LrList.ps1 index 280042eb..079b479d 100644 --- a/src/Public/LogRhythm/Admin/Lists/Update-LrList.ps1 +++ b/src/Public/LogRhythm/Admin/Lists/Update-LrList.ps1 @@ -428,7 +428,7 @@ Function Update-LrList { $ErrorObject.Type = "Input.Validation" $ErrorObject.Note = "Does expire is set to true, requires input parameter TimeToLiveSeconds to be provided." $ErrorObject.FieldType = $ListType - } else { + } elseif ($DoesExpire) { $BodyContents | Add-Member -MemberType NoteProperty -Name 'timeToLiveSeconds' -Value $TimeToLiveSeconds } diff --git a/src/Public/LogRhythm/Admin/LogSources/Add-LrLogSource.ps1 b/src/Public/LogRhythm/Admin/LogSources/Add-LrLogSource.ps1 new file mode 100644 index 00000000..3da0eb47 --- /dev/null +++ b/src/Public/LogRhythm/Admin/LogSources/Add-LrLogSource.ps1 @@ -0,0 +1,283 @@ +using namespace System +using namespace System.IO +using namespace System.Collections.Generic + +Function Add-LrLogSource { + <# + .SYNOPSIS + Add the Log Source to an existing System Monitor Agent host. + .DESCRIPTION + This cmdlet currently only supports a limited number of parameters. + .PARAMETER Credential + PSCredential containing an API Token in the Password field. + .PARAMETER Id + [int] + Specifies a LogRhythm Log Source object by providing one of the following property values: + + List Int (as System.Int), e.g. 2657 + + Can be passed as ValueFromPipeline but does not support Arrays. + .PARAMETER Name + [string] Represents the updated name value to apply to the log source record. + .PARAMETER RecordStatus + [string] Represents the record status value to apply to the log source record. + .PARAMETER Status + [string] Represents the status value to apply to the log source record. + .PARAMETER FilePath + [string] Represents the FilePath value to apply to the log source record. + .PARAMETER maxMsgCount + [int] Represents the maxMsgCount value to apply to the log source record. + .PARAMETER PassThru + Switch paramater that will enable the return of the output object from the cmdlet. + .OUTPUTS + Success: No Output + Error: PSCustomObject representing error details. + PassThru: PSCustomObject representing LogRhythm Log Source record and its updated contents. + .EXAMPLE + + .NOTES + LogRhythm-API + .LINK + https://github.com/LogRhythm-Tools/LogRhythm.Tools + #> + + [CmdletBinding()] + Param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 0)] + [ValidateNotNull()] + [int32] $SystemMonitorId, + + [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 1)] + [ValidateNotNull()] + [int32] $HostId, + + [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 2)] + [ValidateNotNull()] + [int32] $EntityId, + + [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 3)] + [ValidateNotNull()] + [int32] $LogSourceTypeId, + + [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 4)] + [ValidateNotNull()] + [int32] $MpePolicyId, + + [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 5)] + [ValidateSet('NoRulesProcessing','EventForwardingEnabled', 'EventForwardingEnabled', ignorecase=$true)] + [string] $MpeProcessingMode, + + [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 6)] + [string] $Name, + + [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 7)] + [ValidateSet('Active','Retired', ignorecase=$true)] + [string] $RecordStatus, + + [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 8)] + [ValidateSet('Enabled','Disabled', 'Unregistered', ignorecase=$true)] + [string] $Status, + + [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 9)] + [string] $FilePath, + + + [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 10)] + [ValidateRange(1, 10000)] + [int32] $maxMsgCount, + + [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 11)] + [string] $ShortDescription, + + [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 12)] + [string] $LongDescription, + + [Parameter(Mandatory = $false, Position = 13)] + [switch] $PassThru, + + [Parameter(Mandatory = $false, Position = 14)] + [ValidateNotNull()] + [pscredential] $Credential = $LrtConfig.LogRhythm.ApiKey + ) + + Begin { + $Me = $MyInvocation.MyCommand.Name + + # Request Setup + $BaseUrl = $LrtConfig.LogRhythm.BaseUrl + $Token = $Credential.GetNetworkCredential().Password + + # Define HTTP Headers + $Headers = [Dictionary[string,string]]::new() + $Headers.Add("Authorization", "Bearer $Token") + + + # Define HTTP Method + $Method = $HttpMethod.Post + + + # Check preference requirements for self-signed certificates and set enforcement for Tls1.2 + Enable-TrustAllCertsPolicy + } + + Process { + # Establish General Error object Output + $ErrorObject = [PSCustomObject]@{ + Code = $null + Error = $false + Type = $null + Note = $null + Value = $Id + Raw = $null + } + + # Verify version + if ($LrtConfig.LogRhythm.Version -match '7\.[0-7]\.\d+') { + $ErrorObject.Error = $true + $ErrorObject.Code = "404" + $ErrorObject.Type = "Cmdlet not supported." + $ErrorObject.Note = "This cmdlet is available in LogRhythm version 7.8.0 and greater." + return $ErrorObject + } + + $AgentInfo = Get-LrAgentDetails -Id $SystemMonitorId + if (($null -ne $AgentInfo.Error) -and ($AgentInfo.Error -eq $true)) { + return $AgentInfo + } + + $HostInfo = Get-LrHostDetails -Id $HostId + if (($null -ne $HostInfo.Error) -and ($HostInfo.Error -eq $true)) { + return $HostInfo + } + + if ($EntityId) { + $EntityInfo = Get-LrEntityDetails -Id $EntityId + if (($null -ne $EntityInfo.Error) -and ($EntityInfo.Error -eq $true)) { + return $EntityInfo + } + } else { + $EntityInfo = $HostInfo.entity + } + + $LogTypeInfo = Get-LrLogSourceTypeDetails -Id $LogSourceTypeId + if (($null -ne $LogTypeInfo.Error) -and ($LogTypeInfo.Error -eq $true)) { + return $LogTypeInfo + } + + $MpeInfo = Get-LrMpePolicy -Id $MpePolicyId + if (($null -ne $MpeInfo.Error) -and ($MpeInfo.Error -eq $true)) { + return $MpeInfo + } + + if ($ShortDescription) { + $_shortDescription = $ShortDescription + } else { + $_shortDescription = "" + } + + if ($LongDescription) { + $_longDescription = $LongDescription + } else { + $_longDescription = "" + } + + $Body = [PSCustomObject]@{ + id = -1 + systemMonitorId = $AgentInfo.id + systemMonitorName = $AgentInfo.name + name = $Name + host = [PSCustomObject]@{ + id = $HostInfo.id + name = $HostInfo.name + } + entity = [PSCustomObject]@{ + id = $EntityInfo.id + name = $EntityInfo.name + } + logSourceType = [PSCustomObject]@{ + id = $LogTypeInfo.id + name = $LogTypeInfo.name + } + mpePolicy = [PSCustomObject]@{ + id = $MpeInfo.id + name = $MpeInfo.name + } + shortDescription = $_shortDescription + longDescription = $_longDescription + recordStatus = $RecordStatus + status = $Status + isVirtual = $false + logMartMode = 0 + isLoadBalanced = $false + mpeProcessingMode = $MpeProcessingMode + isArchivingEnabled = $true + maxMsgCount = $maxMsgCount + defMsgTTLValue = 0 +# dateUpdated = "2023-09-21T14:14:10Z" + isSilentLogSourceEnabled = $false + msgSourceDateFormatID = 0 + filePath = $FilePath + cryptoMode = 0 + signMode = 0 +# monitorStart = "2023-09-21T14:14:10Z" +# monitorStop = "2023-09-21T14:14:10Z" + defMsgTTL = 0 + msgPerCycle = $maxMsgCount + collectionDepth = 0 +# udlaConnectionString = "string" +# udlaStateField = "string" +# udlaStateFieldType = "Increment" +# udlaStateFieldConversion = "string" +# udlaQueryStatement = "string" +# udlaOutputFormat = "string" +# udlaUniqueIdentifier = "string" +# udlaMsgDateField = "string" +# udlaGetUTCDateStatement = "string" + parameter1 = 0 + parameter2 = 0 + parameter3 = 0 + parameter4 = 0 +# parameter5 = 0 +# parameter6 = "string" +# parameter7 = "string" +# parameter8 = "string" +# parameter9 = "string" +# parameter10 = "string" +# msgRegexStart = "string" +# msgRegexDelimeter = "string" +# msgRegexEnd = "string" +# recursionDepth = 0 + isDirectory = $false +# inclusions = "string" +# exclusions = "string" + compressionType = "none" +# udlaConnectionType = 0 + collectionThreadTimeout = 120 +# virtualSourceRegex = "string" +# virtualSourceSortOrder = 0 +# virtualSourceCatchAllID = 0 + persistentConnection = $false + autoAcceptanceRuleId = "Manual" +# maxLogDate = "2023-09-21T14:14:10Z" +# virtualLogSourceParentID = 0 +# virtualLogSourceName = "string" + watchFileRenameOnRollover = $true + } + + + $RequestUrl = $BaseUrl + "/lr-admin-api/logsources/" + Write-Verbose "[$Me]: Request URL: $RequestUrl" + Write-Verbose "[$Me]: Request Body:`n$Body" + + # Send Request + $Response = Invoke-RestAPIMethod -Uri $RequestUrl -Headers $Headers -Method $Method -Body $($Body | ConvertTo-Json) -Origin $Me + if (($null -ne $Response.Error) -and ($Response.Error -eq $true)) { + return $Response + } + + if ($PassThru) { + return $Response + } + } + + End { } +} \ No newline at end of file diff --git a/src/Public/LogRhythm/Admin/LogSources/Get-LrLogSourceTypeDetails.ps1 b/src/Public/LogRhythm/Admin/LogSources/Get-LrLogSourceTypeDetails.ps1 new file mode 100644 index 00000000..cd79d9f3 --- /dev/null +++ b/src/Public/LogRhythm/Admin/LogSources/Get-LrLogSourceTypeDetails.ps1 @@ -0,0 +1,68 @@ +function Get-LrLogSourceTypeDetails +{ + [CmdletBinding()] + Param( + [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, Position = 0)] + [string] $Id, + + [Parameter(Mandatory = $false, Position = 1)] + [ValidateNotNull()] + [pscredential] $Credential = $LrtConfig.LogRhythm.ApiKey + ) + + Begin { + $Me = $MyInvocation.MyCommand.Name + + # Request Setup + $BaseUrl = $LrtConfig.LogRhythm.BaseUrl + $Token = $Credential.GetNetworkCredential().Password + + # Define HTTP Headers + $Headers = [Dictionary[string,string]]::new() + $Headers.Add("Authorization", "Bearer $Token") + + + # Define HTTP Method + $Method = $HttpMethod.Get + + # Check preference requirements for self-signed certificates and set enforcement for Tls1.2 + Enable-TrustAllCertsPolicy + } + + Process { + # Define ErrorObject + $ErrorObject = [PSCustomObject]@{ + Code = $null + Error = $false + Type = $null + Note = $null + Raw = $null + } + + # Verify version + if ($LrtConfig.LogRhythm.Version -match '7\.[0-7]\.\d+') { + $ErrorObject.Error = $true + $ErrorObject.Code = "404" + $ErrorObject.Type = "Cmdlet not supported." + $ErrorObject.Note = "This cmdlet is available in LogRhythm version 7.5.0 and greater." + + return $ErrorObject + } + + # Request URL + $RequestUrl = $BaseUrl + "/lr-admin-api/messagesourcetypes/" + $Id + "/" + + Write-Verbose "[$Me]: Request URL: $RequestUrl" + + # Send Request + $Response = Invoke-RestAPIMethod -Uri $RequestUrl -Headers $Headers -Method $Method -Origin $Me + if (($null -ne $Response.Error) -and ($Response.Error -eq $true)) { + return $Response + } + + return $Response + } + + End { } +} + \ No newline at end of file diff --git a/src/Public/LogRhythm/Admin/LogSources/Get-LrLogSourceTypes.ps1 b/src/Public/LogRhythm/Admin/LogSources/Get-LrLogSourceTypes.ps1 index acd12032..02774244 100644 --- a/src/Public/LogRhythm/Admin/LogSources/Get-LrLogSourceTypes.ps1 +++ b/src/Public/LogRhythm/Admin/LogSources/Get-LrLogSourceTypes.ps1 @@ -5,15 +5,12 @@ function Get-LrLogSourceTypes [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, Position = 0)] [string] $Name, - [Parameter(Mandatory = $false, Position = 1)] [int] $PageValuesCount = 1000, - [Parameter(Mandatory = $false, Position = 2)] [int] $PageCount = 1, - [Parameter(Mandatory = $false, Position = 3)] [ValidateNotNull()] [pscredential] $Credential = $LrtConfig.LogRhythm.ApiKey diff --git a/src/Public/LogRhythm/Admin/LogSources/Update-LrLogSource.ps1 b/src/Public/LogRhythm/Admin/LogSources/Update-LrLogSource.ps1 index 3108c2e6..2d43f27c 100644 --- a/src/Public/LogRhythm/Admin/LogSources/Update-LrLogSource.ps1 +++ b/src/Public/LogRhythm/Admin/LogSources/Update-LrLogSource.ps1 @@ -89,31 +89,28 @@ Function Update-LrLogSource { [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 1)] [string] $Name, - [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 2)] [ValidateSet('Active','Retired', ignorecase=$true)] [string] $RecordStatus, - [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 3)] [ValidateSet('Enabled','Disabled', 'Unregistered', ignorecase=$true)] [string] $Status, - [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 4)] [string] $FilePath, - [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 5)] [ValidateRange(1, 10000)] [int32] $maxMsgCount, + [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 6)] + [int32] $MpePolicyId, - [Parameter(Mandatory = $false, Position = 6)] + [Parameter(Mandatory = $false, Position = 7)] [switch] $PassThru, - - [Parameter(Mandatory = $false, Position = 7)] + [Parameter(Mandatory = $false, Position = 8)] [ValidateNotNull()] [pscredential] $Credential = $LrtConfig.LogRhythm.ApiKey ) @@ -189,6 +186,9 @@ Function Update-LrLogSource { } if ($FilePath) { + if ($null -eq $Body.filePath) { + $Body | Add-Member -MemberType NoteProperty -Name 'filePath' -Value 'Bogus' -Force + } $Body.filePath = $FilePath } @@ -196,6 +196,16 @@ Function Update-LrLogSource { $Body.maxMsgCount = $maxMsgCount $Body.msgPerCycle = $maxMsgCount } + + if ($MpePolicyId) { + $MpePolicy = Get-LrMpePolicy -Id $MpePolicyId + if (($null -ne $MpePolicy.Error) -and ($MpePolicy.Error -eq $true)) { + return $MpePolicy + } else { + $Body.mpePolicy.id = $MpePolicy.id + $Body.mpePolicy.name = $MpePolicy.name + } + } $RequestUrl = $BaseUrl + "/lr-admin-api/logsources/" + $Guid + "/" diff --git a/src/Public/LogRhythm/Admin/MPERules/Get-LrMpePolicies.ps1 b/src/Public/LogRhythm/Admin/MPERules/Get-LrMpePolicies.ps1 new file mode 100644 index 00000000..2845315c --- /dev/null +++ b/src/Public/LogRhythm/Admin/MPERules/Get-LrMpePolicies.ps1 @@ -0,0 +1,160 @@ +using namespace System +using namespace System.IO +using namespace System.Collections.Generic + +Function Get-LrMpePolicies { + <# + .SYNOPSIS + Retrieve a list of MPE Policies from the LogRhythm. + .DESCRIPTION + Get-LrMpePolicies returns a list of accepted Log Sources, including details. + .PARAMETER Id + Filters results for a specific Log Source Type Id in resources. + .PARAMETER Credential + PSCredential containing an API Token in the Password field. + .PARAMETER PageCount + Integer representing number of pages to return. Default is maximum, 1000. + .OUTPUTS + PSCustomObject representing LogRhythm MPE Rules and their contents. + .EXAMPLE + PS C:\> Get-LrMpeRules + ---- + .NOTES + LogRhythm-API + .LINK + https://github.com/LogRhythm-Tools/LogRhythm.Tools + #> + + [CmdletBinding()] + Param( + [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, Position = 0)] + [int32] $msgSourceTypeId, + + + [Parameter(Mandatory = $false, Position = 1)] + [int] $PageValuesCount = 1000, + + + [Parameter(Mandatory = $false, Position = 2)] + [int] $PageCount = 1, + + + [Parameter(Mandatory = $false, Position = 3)] + [ValidateNotNull()] + [pscredential] $Credential = $LrtConfig.LogRhythm.ApiKey + ) + + Begin { + $Me = $MyInvocation.MyCommand.Name + + # Request Setup + $BaseUrl = $LrtConfig.LogRhythm.BaseUrl + $Token = $Credential.GetNetworkCredential().Password + + # Define HTTP Headers + $Headers = [Dictionary[string,string]]::new() + $Headers.Add("Authorization", "Bearer $Token") + + + # Define HTTP Method + $Method = $HttpMethod.Get + + + # Check preference requirements for self-signed certificates and set enforcement for Tls1.2 + Enable-TrustAllCertsPolicy + } + + Process { + # Establish General Error object Output + $ErrorObject = [PSCustomObject]@{ + Error = $false + Type = $null + Code = $null + Note = $null + Raw = $null + } + + # Verify version + if ($LrtConfig.LogRhythm.Version -match '7\.[0-8]\.\d+') { + $ErrorObject.Error = $true + $ErrorObject.Code = "404" + $ErrorObject.Type = "Cmdlet not supported." + $ErrorObject.Note = "This cmdlet is available in LogRhythm version 7.5.0 and greater." + + return $ErrorObject + } + + #region: Process Query Parameters____________________________________________________ + $QueryParams = [Dictionary[string,string]]::new() + + # PageCount + if ($PageValuesCount) { + $_pageValueCount = $PageValuesCount + } else { + $_pageValueCount = 1000 + } + # PageValuesCount - Amount of Values per Page + $QueryParams.Add("count", $_pageValueCount) + + # Query Offset - PageCount + $Offset = ($PageCount -1) * $_pageValueCount + $QueryParams.Add("offset", $Offset) + + # Filter by Object Name + if ($msgSourceTypeId) { + $_id = $msgSourceTypeId + $QueryParams.Add("messageSourceTypeId", $_id) + } + + # Build QueryString + if ($QueryParams.Count -gt 0) { + $QueryString = $QueryParams | ConvertTo-QueryString + Write-Verbose "[$Me]: QueryString is [$QueryString]" + } + + # Request URL + $RequestUrl = $BaseUrl + "/lr-admin-api/mpepolicies/" + $QueryString + + Write-Verbose "[$Me]: Request URL: $RequestUrl" + + # Send Request + $Response = Invoke-RestAPIMethod -Uri $RequestUrl -Headers $Headers -Method $Method -Origin $Me + if (($null -ne $Response.Error) -and ($Response.Error -eq $true)) { + return $Response + } + + # Check if pagination is required, if so - paginate! + if ($Response.data.Count -eq $PageValuesCount) { + Write-Verbose "[$Me]: Begin Pagination" + + DO { + # Increment Page Count / Offset + $PageCount = $PageCount + 1 + $Offset = ($PageCount -1) * $PageValuesCount + # Update Query Paramater + $QueryParams.offset = $Offset + # Apply to Query String + $QueryString = $QueryParams | ConvertTo-QueryString + # Update Query URL + $RequestUrl = $BaseUrl + "/lr-admin-api/mpepolicies/" + $QueryString + Write-Verbose "[$Me]: Request URL: $RequestUrl" + # Retrieve Query Results + $PaginationResults = Invoke-RestAPIMethod -Uri $RequestUrl -Headers $Headers -Method $Method -Origin $Me + if (($null -ne $PaginationResults.Error) -and ($PaginationResults.Error -eq $true)) { + return $PaginationResults + } + + # Append results to Response + $Results = $Results + $PaginationResults + } While ($($PaginationResults.data.Count) -eq $PageValuesCount) + + $Response = $Response | Sort-Object -Property id -Unique + Write-Verbose "[$Me]: End Pagination" + } + + return $Response + } + + End { + } +} \ No newline at end of file diff --git a/src/Public/LogRhythm/Admin/MPERules/Get-LrMpePolicy.ps1 b/src/Public/LogRhythm/Admin/MPERules/Get-LrMpePolicy.ps1 new file mode 100644 index 00000000..cd9728fd --- /dev/null +++ b/src/Public/LogRhythm/Admin/MPERules/Get-LrMpePolicy.ps1 @@ -0,0 +1,94 @@ +using namespace System +using namespace System.IO +using namespace System.Collections.Generic + +Function Get-LrMpePolicy { + <# + .SYNOPSIS + Retrieve a list of MPE Policy by ID from the LogRhythm. + .DESCRIPTION + Get-LrMpePolicy returns a list of accepted Log Sources, including details. + .PARAMETER Id + Filters results for a specific MPE Policy Id in resources. + .PARAMETER Credential + PSCredential containing an API Token in the Password field. + .OUTPUTS + PSCustomObject representing LogRhythm MPE Rules and their contents. + .EXAMPLE + PS C:\> Get-LrMpePolicy + ---- + .NOTES + LogRhythm-API + .LINK + https://github.com/LogRhythm-Tools/LogRhythm.Tools + #> + + [CmdletBinding()] + Param( + [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, Position = 0)] + [int32] $Id, + + + [Parameter(Mandatory = $false, Position = 1)] + [ValidateNotNull()] + [pscredential] $Credential = $LrtConfig.LogRhythm.ApiKey + ) + + Begin { + $Me = $MyInvocation.MyCommand.Name + + # Request Setup + $BaseUrl = $LrtConfig.LogRhythm.BaseUrl + $Token = $Credential.GetNetworkCredential().Password + + # Define HTTP Headers + $Headers = [Dictionary[string,string]]::new() + $Headers.Add("Authorization", "Bearer $Token") + + + # Define HTTP Method + $Method = $HttpMethod.Get + + + # Check preference requirements for self-signed certificates and set enforcement for Tls1.2 + Enable-TrustAllCertsPolicy + } + + Process { + # Establish General Error object Output + $ErrorObject = [PSCustomObject]@{ + Error = $false + Type = $null + Code = $null + Note = $null + Raw = $null + } + + # Verify version + if ($LrtConfig.LogRhythm.Version -match '7\.[0-8]\.\d+') { + $ErrorObject.Error = $true + $ErrorObject.Code = "404" + $ErrorObject.Type = "Cmdlet not supported." + $ErrorObject.Note = "This cmdlet is available in LogRhythm version 7.5.0 and greater." + + return $ErrorObject + } + + + # Request URL + $RequestUrl = $BaseUrl + "/lr-admin-api/mpepolicies/" + $Id + "/" + + Write-Verbose "[$Me]: Request URL: $RequestUrl" + + # Send Request + $Response = Invoke-RestAPIMethod -Uri $RequestUrl -Headers $Headers -Method $Method -Origin $Me + if (($null -ne $Response.Error) -and ($Response.Error -eq $true)) { + return $Response + } + + return $Response + } + + End { + } +} \ No newline at end of file diff --git a/src/Public/LogRhythm/Admin/MPERules/Get-LrMpeRules.ps1 b/src/Public/LogRhythm/Admin/MPERules/Get-LrMpeRules.ps1 new file mode 100644 index 00000000..06038d85 --- /dev/null +++ b/src/Public/LogRhythm/Admin/MPERules/Get-LrMpeRules.ps1 @@ -0,0 +1,176 @@ +using namespace System +using namespace System.IO +using namespace System.Collections.Generic + +Function Get-LrMpeRules { + <# + .SYNOPSIS + Retrieve a list of accepted MPE Rules from the LogRhythm. + .DESCRIPTION + Get-LrLogSources returns a list of accepted Log Sources, including details. + .PARAMETER Id + Filters results for a specific Log Source Type Id in resources. + .PARAMETER Credential + PSCredential containing an API Token in the Password field. + .PARAMETER PageCount + Integer representing number of pages to return. Default is maximum, 1000. + .OUTPUTS + PSCustomObject representing LogRhythm MPE Rules and their contents. + .EXAMPLE + PS C:\> Get-LrMpeRules + ---- + .NOTES + LogRhythm-API + .LINK + https://github.com/LogRhythm-Tools/LogRhythm.Tools + #> + + [CmdletBinding()] + Param( + [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, Position = 0)] + [int32] $msgSourceTypeId, + + + [Parameter(Mandatory = $false, Position = 1)] + [int] $PageValuesCount = 1000, + + + [Parameter(Mandatory = $false, Position = 2)] + [int] $PageCount = 1, + + + [Parameter(Mandatory = $false, Position = 3)] + [ValidateNotNull()] + [pscredential] $Credential = $LrtConfig.LogRhythm.ApiKey + ) + + Begin { + $Me = $MyInvocation.MyCommand.Name + + # Request Setup + $BaseUrl = $LrtConfig.LogRhythm.BaseUrl + $Token = $Credential.GetNetworkCredential().Password + + # Define HTTP Headers + $Headers = [Dictionary[string,string]]::new() + $Headers.Add("Authorization", "Bearer $Token") + + + # Define HTTP Method + $Method = $HttpMethod.Get + + + # Check preference requirements for self-signed certificates and set enforcement for Tls1.2 + Enable-TrustAllCertsPolicy + } + + Process { + # Establish General Error object Output + $ErrorObject = [PSCustomObject]@{ + Error = $false + Type = $null + Code = $null + Note = $null + Raw = $null + } + + # Verify version + if ($LrtConfig.LogRhythm.Version -match '7\.[0-8]\.\d+') { + $ErrorObject.Error = $true + $ErrorObject.Code = "404" + $ErrorObject.Type = "Cmdlet not supported." + $ErrorObject.Note = "This cmdlet is available in LogRhythm version 7.5.0 and greater." + + return $ErrorObject + } + + #region: Process Query Parameters____________________________________________________ + $QueryParams = [Dictionary[string,string]]::new() + + # PageCount + if ($PageValuesCount) { + $_pageValueCount = $PageValuesCount + } else { + $_pageValueCount = 1000 + } + # PageValuesCount - Amount of Values per Page + $QueryParams.Add("count", $_pageValueCount) + + # Query Offset - PageCount + $Offset = ($PageCount -1) * $_pageValueCount + $QueryParams.Add("offset", $Offset) + + # Filter by Object Name + if ($msgSourceTypeId) { + $_id = $msgSourceTypeId + $QueryParams.Add("msgSourceTypeId", $_id) + } + + # Build QueryString + if ($QueryParams.Count -gt 0) { + $QueryString = $QueryParams | ConvertTo-QueryString + Write-Verbose "[$Me]: QueryString is [$QueryString]" + } + + # Request URL + $RequestUrl = $BaseUrl + "/lr-admin-api/mperules/" + $QueryString + + Write-Verbose "[$Me]: Request URL: $RequestUrl" + + # Send Request + $Response = Invoke-RestAPIMethod -Uri $RequestUrl -Headers $Headers -Method $Method -Origin $Me + if (($null -ne $Response.Error) -and ($Response.Error -eq $true)) { + return $Response + } + + # Check if pagination is required, if so - paginate! + if ($Response.data.Count -eq $PageValuesCount) { + Write-Verbose "[$Me]: Begin Pagination" + $MpeResults = [list[object]]::new() + ForEach ($MpeData in $Response.data) { + if ($MpeResults.mpeRuleId -notcontains $MpeData.mpeRuleId) { + $MpeResults.Add($MpeData) + } + } + + DO { + # Increment Page Count / Offset + $PageCount = $PageCount + 1 + $Offset = ($PageCount -1) * $PageValuesCount + # Update Query Paramater + $QueryParams.offset = $Offset + # Apply to Query String + $QueryString = $QueryParams | ConvertTo-QueryString + # Update Query URL + $RequestUrl = $BaseUrl + "/lr-admin-api/mperules/" + $QueryString + Write-Verbose "[$Me]: Request URL: $RequestUrl" + # Retrieve Query Results + $PaginationResults = Invoke-RestAPIMethod -Uri $RequestUrl -Headers $Headers -Method $Method -Origin $Me + if (($null -ne $PaginationResults.Error) -and ($PaginationResults.Error -eq $true)) { + return $PaginationResults + } + + # Append results to Response + ForEach ($MpeData in $PaginationResults.data) { + if ($MpeResults.mpeRuleId -notcontains $MpeData.mpeRuleId) { + $MpeResults.Add($MpeData) + } + } + } While ($($PaginationResults.data.Count) -eq $PageValuesCount) + + $Response = [PSCustomObject]@{ + alarmsSearchDetails = $MpeResults + alarmsCount = $MpeResults.Count + statusCode = $PaginationResults.statusCode + statusMessage = $PaginationResults.statusMessage + responseMessage = $PaginationResults.responseMessage + } + Write-Verbose "[$Me]: End Pagination" + } + + return $Response + } + + End { + } +} \ No newline at end of file diff --git a/src/Public/LogRhythm/Admin/Networks/Get-LrNetworks.ps1 b/src/Public/LogRhythm/Admin/Networks/Get-LrNetworks.ps1 index a43129c8..63056f53 100644 --- a/src/Public/LogRhythm/Admin/Networks/Get-LrNetworks.ps1 +++ b/src/Public/LogRhythm/Admin/Networks/Get-LrNetworks.ps1 @@ -130,11 +130,11 @@ Function Get-LrNetworks { [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, Position = 4)] - [string] $BIP, + [ipaddress] $BIP, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, Position = 5)] - [string] $EIP, + [ipaddress] $EIP, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, Position = 6)] @@ -285,7 +285,7 @@ Function Get-LrNetworks { DO { # Increment Page Count / Offset #$PageCount = $PageCount + 1 - $Offset = $Offset + 1 + $Offset += $PageValuesCount # Update Query Paramater $QueryParams.offset = $Offset # Apply to Query String diff --git a/src/Public/RecordedFuture/Alert/Get-RfAlert.ps1 b/src/Public/RecordedFuture/Alert/Get-RfAlert.ps1 index 04669049..3a855b56 100644 --- a/src/Public/RecordedFuture/Alert/Get-RfAlert.ps1 +++ b/src/Public/RecordedFuture/Alert/Get-RfAlert.ps1 @@ -4,9 +4,9 @@ using namespace System.Collections.Generic Function Get-RfAlert { <# .SYNOPSIS - Get RecordedFuture Alert details. + Get RecordedFuture Alert detail for a specified alert. .DESCRIPTION - Get RecordedFuture Alert details based on Alert ID. + Get RecordedFuture Alert allows for retrieving the details for a specific alert. .PARAMETER Credential PSCredential containing an API Token in the Password field. .PARAMETER Id @@ -14,19 +14,19 @@ Function Get-RfAlert { .INPUTS .NOTES - RecordedFuture-API + RecordedFuture-API v3 .LINK https://github.com/LogRhythm-Tools/LogRhythm.Tools #> [CmdletBinding()] Param( - [Parameter(Mandatory = $false, Position = 0)] + [Parameter(Mandatory = $true, Position = 0)] [ValidateNotNullOrEmpty()] - [string] $Id, + [string] $AlertId, - [Parameter(Mandatory = $false, Position = 1)] + [Parameter(Mandatory = $false, Position = 2)] [ValidateNotNull()] [pscredential] $Credential = $LrtConfig.RecordedFuture.ApiKey ) @@ -49,15 +49,15 @@ Function Get-RfAlert { } Process { + # Define Search URL - $RequestUrl = $BaseUrl + "alert/" + $Id + $RequestUrl = $BaseUrl + "v3/alerts/" + $AlertId Write-Verbose "[$Me]: Request URL: $RequestUrl" Try { $Results = Invoke-RestMethod $RequestUrl -Method $Method -Headers $Headers - } - catch { + } catch { If ($_.Exception.Response.StatusCode.value__) { $HTTPCode = ($_.Exception.Response.StatusCode.value__ ).ToString().Trim() Write-Verbose "HTTP Code: $HTTPCode" diff --git a/src/Public/RecordedFuture/Alert/Get-RfAlertRules.ps1 b/src/Public/RecordedFuture/Alert/Get-RfAlertRules.ps1 index 30c68458..55178b2e 100644 --- a/src/Public/RecordedFuture/Alert/Get-RfAlertRules.ps1 +++ b/src/Public/RecordedFuture/Alert/Get-RfAlertRules.ps1 @@ -71,7 +71,7 @@ Function Get-RfAlertRules { # Define Search URL - $RequestUrl = $BaseUrl + "alert/rule" + $QueryString + $RequestUrl = $BaseUrl + "v2/alert/rule" + $QueryString Write-Verbose "[$Me]: Request URL: $RequestUrl" diff --git a/src/Public/RecordedFuture/Alert/Get-RfAlerts.ps1 b/src/Public/RecordedFuture/Alert/Get-RfAlerts.ps1 new file mode 100644 index 00000000..1b9ea227 --- /dev/null +++ b/src/Public/RecordedFuture/Alert/Get-RfAlerts.ps1 @@ -0,0 +1,107 @@ +using namespace System +using namespace System.Collections.Generic + +Function Get-RfAlerts { + <# + .SYNOPSIS + Get RecordedFuture Alerts detail. + .DESCRIPTION + Get RecordedFuture Alerts allows for searching for triggered alerts. + .PARAMETER Credential + PSCredential containing an API Token in the Password field. + .PARAMETER Id + Id value for Recorded Future Alert retrieval. + .INPUTS + + .NOTES + RecordedFuture-API v3 + .LINK + https://github.com/LogRhythm-Tools/LogRhythm.Tools + #> + + [CmdletBinding()] + Param( + [Parameter(Mandatory = $false, Position = 0)] + [ValidateNotNullOrEmpty()] + [string] $AlertRuleId, + + + [Parameter(Mandatory = $false, Position = 1)] + [ValidateSet('New','Resolved', 'Pending', 'Flag for Tuning', ignorecase=$true)] + [string] $Status, + + + [Parameter(Mandatory = $false, Position = 1)] + [string] $Triggered, + + + [Parameter(Mandatory = $false, Position = 2)] + [ValidateNotNull()] + [pscredential] $Credential = $LrtConfig.RecordedFuture.ApiKey + ) + + Begin { + $BaseUrl = $LrtConfig.RecordedFuture.BaseUrl + $Token = $Credential.GetNetworkCredential().Password + + # Request Headers + $Headers = [Dictionary[string,string]]::new() + $Headers.Add("X-RFToken", $Token) + + Write-Verbose "$($Headers | Out-String)" + + # Request Setup + $Method = $HttpMethod.Get + + # Check preference requirements for self-signed certificates and set enforcement for Tls1.2 + Enable-TrustAllCertsPolicy + } + + Process { + # Establish Query Parameters object + $QueryParams = [Dictionary[string,string]]::new() + + if ($AlertRuleId) { + $QueryParams.Add("alertRule", $AlertRuleId) + } + + if ($Status) { + $QueryParams.Add("statusInPortal", $Status) + } + + + if ($Triggered) { + $QueryParams.Add("triggered", "["+$Triggered+",]") + } + + if ($QueryParams.Count -gt 0) { + $QueryString = $QueryParams | ConvertTo-QueryString + Write-Verbose "[$Me]: QueryString is [$QueryString]" + } + + # Define Search URL + $RequestUrl = $BaseUrl + "v3/alerts/" + $QueryString + Write-Verbose "[$Me]: Request URL: $RequestUrl" + + + Try { + $Results = Invoke-RestMethod $RequestUrl -Method $Method -Headers $Headers + } catch { + If ($_.Exception.Response.StatusCode.value__) { + $HTTPCode = ($_.Exception.Response.StatusCode.value__ ).ToString().Trim() + Write-Verbose "HTTP Code: $HTTPCode" + } + If ($_.Exception.Message) { + $ExceptionMessage = ($_.Exception.Message).ToString().Trim() + Write-Verbose "Exception Message: $ExceptionMessage" + return $ExceptionMessage + } + } + + + # Return Values only as an array or all results as object + Return $Results.data + } + + End { } +} \ No newline at end of file diff --git a/src/Public/RecordedFuture/Alert/Update-RfAlert.ps1 b/src/Public/RecordedFuture/Alert/Update-RfAlert.ps1 new file mode 100644 index 00000000..bc17f29b --- /dev/null +++ b/src/Public/RecordedFuture/Alert/Update-RfAlert.ps1 @@ -0,0 +1,106 @@ +using namespace System +using namespace System.Collections.Generic + +Function Update-RfAlert { + <# + .SYNOPSIS + Update RecordedFuture Alert details for a specified alert. + .DESCRIPTION + Update RecordedFuture Alert allows for updating the details for a specific alert. + .PARAMETER Credential + PSCredential containing an API Token in the Password field. + .PARAMETER Id + Id value for Recorded Future Alert retrieval. + .INPUTS + + .NOTES + RecordedFuture-API v2 + .LINK + https://api.recordedfuture.com/v2/#!/Alerts/Alert_Notification_Update + #> + + [CmdletBinding()] + Param( + [Parameter(Mandatory = $true, Position = 0)] + [ValidateNotNullOrEmpty()] + [string] $AlertId, + + + [Parameter(Mandatory = $false, Position = 1)] + [ValidateSet('unassigned','assigned', 'pending', 'dismiss', 'no-action', 'actionable', 'tuning', ignorecase=$true)] + [string] $Status, + + + [Parameter(Mandatory = $false, Position = 2)] + [string] $Note, + + + [Parameter(Mandatory = $false, Position = 3)] + [ValidateNotNull()] + [pscredential] $Credential = $LrtConfig.RecordedFuture.ApiKey + ) + + Begin { + $BaseUrl = $LrtConfig.RecordedFuture.BaseUrl + $Token = $Credential.GetNetworkCredential().Password + + # Request Headers + $Headers = [Dictionary[string,string]]::new() + $Headers.Add("X-RFToken", $Token) + $Headers.Add("Content-Type", "application/json") + + Write-Verbose "$($Headers | Out-String)" + + # Request Setup + $Method = $HttpMethod.Post + + # Check preference requirements for self-signed certificates and set enforcement for Tls1.2 + Enable-TrustAllCertsPolicy + } + + Process { + + # Define Search URL + $RequestUrl = $BaseUrl + "v2/alert/update" + Write-Verbose "[$Me]: Request URL: $RequestUrl" + + # Establish JSON Body contents + $BodyContents = [PSCustomObject]@{ + id = $AlertId + } + + if ($Status) { + $BodyContents | Add-Member -MemberType NoteProperty -Name 'status' -Value $Status + } + + if ($Note) { + $BodyContents | Add-Member -MemberType NoteProperty -Name 'note' -Value $Note + } + + + + # Establish Body Contents + $Body = '[' + $(@($BodyContents) | ConvertTo-Json) + ']' + Write-Verbose "[$Me]: Request Body:`n$Body" + + Try { + $Results = Invoke-RestMethod $RequestUrl -Method $Method -Headers $Headers -Body $Body + } catch { + If ($_.Exception.Response.StatusCode.value__) { + $HTTPCode = ($_.Exception.Response.StatusCode.value__ ).ToString().Trim() + Write-Verbose "HTTP Code: $HTTPCode" + } + If ($_.Exception.Message) { + $ExceptionMessage = ($_.Exception.Message).ToString().Trim() + Write-Verbose "Exception Message: $ExceptionMessage" + return $ExceptionMessage + } + } + + + # Return Values only as an array or all results as object + Return $Results.data + } + + End { } +} \ No newline at end of file diff --git a/src/Public/RecordedFuture/Domain/Get-RfDomainRiskList.ps1 b/src/Public/RecordedFuture/Domain/Get-RfDomainRiskList.ps1 index 54b089f7..c864334e 100644 --- a/src/Public/RecordedFuture/Domain/Get-RfDomainRiskList.ps1 +++ b/src/Public/RecordedFuture/Domain/Get-RfDomainRiskList.ps1 @@ -137,7 +137,7 @@ Function Get-RfDomainRiskList { # Define Search URL - $RequestUrl = $BaseUrl + "domain/risklist" + $QueryString + $RequestUrl = $BaseUrl + "v2/domain/risklist" + $QueryString Write-Verbose "[$Me]: Request URL: $RequestUrl" Try { diff --git a/src/Public/RecordedFuture/Domain/Get-RfDomainRiskLists.ps1 b/src/Public/RecordedFuture/Domain/Get-RfDomainRiskLists.ps1 index fa0dfe87..ee9d0cbd 100644 --- a/src/Public/RecordedFuture/Domain/Get-RfDomainRiskLists.ps1 +++ b/src/Public/RecordedFuture/Domain/Get-RfDomainRiskLists.ps1 @@ -111,7 +111,7 @@ Function Get-RfDomainRiskLists { # Define Search URL - $RequestUrl = $BaseUrl + "domain/riskrules" + $RequestUrl = $BaseUrl + "v2/domain/riskrules" Write-Verbose "[$Me]: Request URL: $RequestUrl" Try { diff --git a/src/Public/RecordedFuture/General/Get-RfSoarEnrichment.ps1 b/src/Public/RecordedFuture/General/Get-RfSoarEnrichment.ps1 index d7fd1858..1720c4a7 100644 --- a/src/Public/RecordedFuture/General/Get-RfSoarEnrichment.ps1 +++ b/src/Public/RecordedFuture/General/Get-RfSoarEnrichment.ps1 @@ -64,7 +64,7 @@ function Get-RfSoarEnrichment { Process { # Request URI - $RequestUrl = $BaseUrl + "/soar/enrichment?metadata=false" + $RequestUrl = $BaseUrl + "v2//soar/enrichment?metadata=false" $Body = $IoCList | ConvertTo-Json -Depth 5 -Compress Write-Verbose "[$Me]: Request URL: $RequestUrl" diff --git a/src/Public/RecordedFuture/General/Invoke-RfExaSync.ps1 b/src/Public/RecordedFuture/General/Invoke-RfExaSync.ps1 new file mode 100644 index 00000000..c692c815 --- /dev/null +++ b/src/Public/RecordedFuture/General/Invoke-RfExaSync.ps1 @@ -0,0 +1,777 @@ +using namespace System.Collections.Generic +Function Invoke-RfExaSync { + <# + .SYNOPSIS + RfSync serves as an application-like service to enable synchronizing Recorded Future Threat Lists into the LogRhythm SIEM + environment without need to directly interact with PowerShell. + + LogRhythm List's are established to support the complete control of which threat lists are synchronized and determining + Confidence High as compared to Confidence Low thresholds as related to the Recorded Future Risk Level assigned to each entry. + .DESCRIPTION + Upon first invocation the Invoke-RfSync function will establish configuration lists to enable the control of Recorded Future + to LogRhythm SIEM Lists. There are four risk types supported: Hash, IP, Url, Domain. For each risk type there are three + configuration files: + + RF : Conf : (RiskType) : Available Risk Lists + RF : Conf : (RiskType) : Enabled Risk Lists + RF : Conf : (RiskType) : Confidence Threshold + + All lists created by Invoke-RfSync follow a naming convention. + Configuration Lists: + ListPrefix : Conf : (RiskType) : Configuration Name + + ListPrefex - Manually set by the PowerShell variable $ListPrefix. Default value to "RF :" + Conf - Designates the list is a configuration List. + Configuration Name - Designates the list's configuration purpose. + + ShortDescription - Contains details associated with the list, including instructions on how to use. + + Threat Lists: + ListPrefix : RiskType : ConfLevel : RecordedFutureRiskListName + + ListPrefex - Manually set by the PowerShell variable $ListPrefix. Default value to "RF :" + RiskType - Auto populated based on the Risk List Type: Hash, IP, Url, Domain + ConfLevel - Auto set to ConfHi or ConfLo. Each ThreatList Value's Risk Value is compared against the configured RiskType's Confidence Threshold. + If the ThreatList Value's Risk Value < Confidence Threshold the value is populated in the ConfLo LogRhythm List. + If the ThreatList Value's Risk Value >= Confidence Threshold the value is populated in the ConfHi LogRhythm list. + RecordedFutureRiskListName - Auto populated with the original Threat List name as provided by Recorded Future. + + ShortDescription - Contains details associated with the list. List will always contain a timestamp for last synchronization. + .PARAMETER SyncScope + This parameter is utilized as part of the manual control/invocation through LogRhythm via SmartResponse. The default Synchronization Scope is All list types. + + Valid Synchronization Scopes: + All - Default + Vulnerability + IP + HASH + URL + Domain + + This value supports submission of multiple types as an array. + .EXAMPLE + This script is intended to be established on a dedicated system with the LogRhyhtm Powershell Module installed. A scheduled task should be established + that appropriately imports the LogRhythm PowerShell Module. Once imported the Invoke-RfSync function can be called. + + Invoke-RfSync will syncrhonize contente between Recorded Future and the LogRhyhtm SIEM based on the LogRhythm Lists: RF : Conf : (RiskType) : Enabled Risk Lists. + .NOTES + RecordedFuture-API + .LINK + https://github.com/LogRhythm-Tools/LogRhythm.Tools + #> + [CmdletBinding()] + Param( + [Parameter(Mandatory = $false, Position = 0)] + [ValidateSet('all','vulnerability','ip','url','domain', 'hash', ignorecase=$true)] + [ValidateNotNull()] + [string[]] $SyncScope = "all", + + [Parameter(Mandatory = $false, Position = 1)] + [ValidateNotNull()] + [string] $ListPrefix = "RF :" + ) + + # End Section - General Setup + #--------------------------------------- + # Begin Section - Hash Setup & Control + # Determine if LR List exists + # TODO FIX + $ListNameHash = 'Recorded Future - Hash Lists' + $ListStatusHash = Get-ExaContextTables -Name $ListNameHash -Exact + + # Create the list if it does not exist + if (!$ListStatusHash) { + $Attributes = [list[object]]::new() + $Attributes.add([PSCustomObject]@{ + id = 'value' + isKey = $true + }) + $Attributes.add([PSCustomObject]@{ + id = 'enabled' + isKey = $false + }) + $Results = New-ExaContextTable -Name $ListNameHash -ContextType 'Other' -Source 'Custom' -Attributes $Attributes + $ListStatusHash = Get-ExaContextTables -Name $ListNameHash -Exact + } else { + Write-Verbose "$(Get-TimeStamp) - List Verification: $ListNameHash exists. Synchronizing contents between Recorded Future and this Exabeam list." + } + + # Sync Items + Try { + $RfHashRiskLists = Get-RfHashRiskLists + } Catch { + Write-Host "$(Get-TimeStamp) - Unable to retrieve Recorded Future Hash Threat Lists. See Get-RfHashRiskLists" + } + + $RfHashRiskDescriptions = [list[object]]::new() + $NonFunctionalHashLists = @('linkedToMalware', 'positiveMalwareVerdict') + foreach ($RfHashRiskList in $RfHashRiskLists) { + if (($NonFunctionalHashLists -notcontains $($RfHashRiskList.name)) -and ($RfHashRiskList.criticality -ge 2)) { + $RfHashRiskDescriptions.add([PSCustomObject]@{ + Value = $RfHashRiskList.Description + Enabled = 'true' + }) + } + } + + $Results = Add-ExaContextRecords -ContextId $ListStatusHash.id -Data $RfHashRiskDescriptions -Operation 'append' + + Start-Sleep -Seconds 30 + # User Enabled Hash List + $ListStatusHashEnabled = Get-ExaContextRecords -Id $ListStatusHash.id + + + # End Section - Setup & Control - Hash + # ----------------------------------- + # Begin Section - Setup & Control - URL + # Establish LR List of available URL Threat Lists + $ListNameUrl = 'Recorded Future - Url Lists' + $ListStatusUrl = Get-ExaContextTables -Name $ListNameUrl -Exact + + + # Create the list if it does not exist + if (!$ListStatusUrl) { + $Attributes = [list[object]]::new() + $Attributes.add([PSCustomObject]@{ + id = 'value' + isKey = $true + }) + $Attributes.add([PSCustomObject]@{ + id = 'enabled' + isKey = $false + }) + New-ExaContextTable -Name $ListNameUrl -ContextType 'Other' -Source 'Custom' -Attributes $Attributes + + $ListStatusUrl = Get-ExaContextTables -Name $ListNameUrl -Exact + } else { + Write-Verbose "$(Get-TimeStamp) - List Verification: $ListNameUrl exists. Synchronizing contents between Recorded Future and this Exabeam list." + } + + # Sync Items + Try { + $RfUrlRiskLists = Get-RfUrlRiskLists + } Catch { + Write-Host "$(Get-TimeStamp) - Unable to retrieve Recorded Future Url Threat Lists. See Get-RfUrlRiskLists" + } + + $RfUrlRiskDescriptions = [list[object]]::new() + $NonFunctionalUrlLists = @() + foreach ($RfUrlRiskList in $RfUrlRiskLists) { + if (($NonFunctionalUrlLists -notcontains $($RfUrlRiskList.name)) -and ($RfUrlRiskList.criticality -ge 2)) { + $RfUrlRiskDescriptions.add([PSCustomObject]@{ + Value = $RfUrlRiskList.Description + Enabled = 'true' + }) + } + } + + $Results = Add-ExaContextRecords -ContextId $ListStatusUrl.id -Data $RfUrlRiskDescriptions -Operation 'append' + + Start-Sleep -Seconds 30 + # User Enabled URL List + $ListStatusUrlEnabled = Get-ExaContextRecords -Id $ListStatusUrl.id + + + # End Section - Setup & Control - URL + # ----------------------------------- + # Begin Section - Setup & Control - Domain + # Establish LR List of available Domain Threat Lists + $ListNameDomain = 'Recorded Future - Domain Lists' + $ListStatusDomain = Get-ExaContextTables -Name $ListNameDomain -Exact + + + # Create the list if it does not exist + if (!$ListStatusDomain) { + $Attributes = [list[object]]::new() + $Attributes.add([PSCustomObject]@{ + id = 'value' + isKey = $true + }) + $Attributes.add([PSCustomObject]@{ + id = 'enabled' + isKey = $false + }) + $Results = New-ExaContextTable -Name $ListNameDomain -ContextType 'Other' -Source 'Custom' -Attributes $Attributes + + $ListStatusDomain = Get-ExaContextTables -Name $ListNameDomain -Exact + } else { + Write-Verbose "$(Get-TimeStamp) - List Verification: $ListNameDomain exists. Synchronizing contents between Recorded Future and this Exabeam list." + } + + # Sync Items + Try { + $RfDomainRiskLists = Get-RfDomainRiskLists + } Catch { + Write-Host "$(Get-TimeStamp) - Unable to retrieve Recorded Future Domain Threat Lists. See Get-RfDomainRiskLists" + } + + $RfDomainRiskDescriptions = [list[object]]::new() + $NonFunctionalDomainLists = @() + foreach ($RfDomainRiskList in $RfDomainRiskLists) { + if (($NonFunctionalDomainLists -notcontains $($RfDomainRiskList.name)) -and ($RfDomainRiskList.criticality -ge 2)) { + $RfDomainRiskDescriptions.add([PSCustomObject]@{ + Value = $RfDomainRiskList.Description + Enabled = 'true' + }) + } + } + + $Results = Add-ExaContextRecords -ContextId $ListStatusDomain.id -Data $RfDomainRiskDescriptions -Operation 'append' + + Start-Sleep -Seconds 30 + # User Enabled URL List + $ListStatusDomainEnabled = Get-ExaContextRecords -Id $ListStatusDomain.id + + + # End Section - Setup & Control - Domain + #--------------------------------------- + # Begin Section - Setup & Control - IP + # Establish LR List of available IP Threat Lists + $ListNameIP = 'Recorded Future - IP Lists' + $ListStatusIP = Get-ExaContextTables -Name $ListNameIP -Exact + + + # Create the list if it does not exist + if (!$ListStatusIP) { + $Attributes = [list[object]]::new() + $Attributes.add([PSCustomObject]@{ + id = 'value' + isKey = $true + }) + $Attributes.add([PSCustomObject]@{ + id = 'enabled' + isKey = $false + }) + $Results = New-ExaContextTable -Name $ListNameIP -ContextType 'Other' -Source 'Custom' -Attributes $Attributes + + $ListStatusIP = Get-ExaContextTables -Name $ListNameIP -Exact + } else { + Write-Verbose "$(Get-TimeStamp) - List Verification: $ListNameIP exists. Synchronizing contents between Recorded Future and this Exabeam list." + } + + # Sync Items + Try { + $RfIPRiskLists = Get-RfIPRiskLists + } Catch { + Write-Host "$(Get-TimeStamp) - Unable to retrieve Recorded Future IP Threat Lists. See Get-RfIPRiskLists" + } + + $RfIPRiskDescriptions = [list[object]]::new() + $NonFunctionalIPLists = @() + foreach ($RfIPRiskList in $RfIPRiskLists) { + if (($NonFunctionalIPLists -notcontains $($RfIPRiskList.name)) -and ($RfIPRiskList.criticality -ge 2)) { + $RfIPRiskDescriptions.add([PSCustomObject]@{ + Value = $RfIPRiskList.Description + Enabled = 'true' + }) + } + } + + $Results = Add-ExaContextRecords -ContextId $ListStatusIP.id -Data $RfIPRiskDescriptions -Operation 'append' + + Start-Sleep -Seconds 30 + # User Enabled URL List + $ListStatusIPEnabled = Get-ExaContextRecords -Id $ListStatusIP.id + + + # End Section - Setup & Control - IP + # Begin Section - Vulnerability Setup & Control + # Establish LR List of available Vulnerability Threat Lists + $ListNameVuln = 'Recorded Future - Vulnerability Lists' + $ListStatusVuln = Get-ExaContextTables -Name $ListNameVuln -Exact + + + # Create the list if it does not exist + if (!$ListStatusVuln) { + $Attributes = [list[object]]::new() + $Attributes.add([PSCustomObject]@{ + id = 'value' + isKey = $true + }) + $Attributes.add([PSCustomObject]@{ + id = 'enabled' + isKey = $false + }) + $Results = New-ExaContextTable -Name $ListNameVuln -ContextType 'Other' -Source 'Custom' -Attributes $Attributes + + $ListStatusVuln = Get-ExaContextTables -Name $ListNameVuln -Exact + } else { + Write-Verbose "$(Get-TimeStamp) - List Verification: $ListNameVuln exists. Synchronizing contents between Recorded Future and this Exabeam list." + } + + # Sync Items + Try { + $RfVulnRiskLists = Get-RfVulnerabilityRiskLists + } Catch { + Write-Host "$(Get-TimeStamp) - Unable to retrieve Recorded Future Vulnerability Threat Lists. See Get-RfVulnerabilityRiskLists" + } + + $RfVulnRiskDescriptions = [list[object]]::new() + $NonFunctionalVulnLists = @() + foreach ($RfVulnRiskList in $RfVulnRiskLists) { + if (($NonFunctionalVulnLists -notcontains $($RfVulnRiskList.name)) -and ($RfVulnRiskList.criticality -ge 2)) { + $RfVulnRiskDescriptions.add([PSCustomObject]@{ + Value = $RfVulnRiskList.Description + Enabled = 'true' + }) + } + } + + $Results = Add-ExaContextRecords -ContextId $ListStatusVuln.id -Data $RfVulnRiskDescriptions -Operation 'append' + + Start-Sleep -Seconds 30 + # User Enabled URL List + $ListStatusVulnEnabled = Get-ExaContextRecords -Id $ListStatusVuln.id + + + # End Section - Setup & Control - Vulnerability + #----------------------------- + # Begin Section - Value Sync - Hash + # Create Hash Threat Lists based on enabled Threat List(s) + if (($SyncScope -contains "all") -or ($SyncScope -contains "hash")) { + if ($ListStatusHashEnabled) { + Write-Host "$(Get-TimeStamp) - Begin - Recorded Future Hash Threat List Sync" + ForEach ($ThreatListHash in $ListStatusHashEnabled.records) { + if ($ThreatListHash.enabled -like 'False') { + continue + } + # Fork each RiskList into two Lists + Write-Host "$(Get-TimeStamp) - Working: $($ThreatListHash.value)" + + # Map list Description to List Name + Try { + Write-Host "$(Get-TimeStamp) - Mapping RecordedFuture Threat List Description to Name" + $HashListName = $RfHashRiskLists.Where({($_.description -like $($ThreatListHash.value))}).name + $HashListResultQuantity = $($RfHashRiskLists.Where({($_.description -like $($ThreatListHash.value))}) | Select-Object -ExpandProperty count) + } Catch { + Write-Host "$(Get-TimeStamp) - Pulled list: $($ThreatListHash.value) is not a valid list." + } + + # Update capitilization for RiskList Value + $HashThreatListName = "RF Hash: $((Get-Culture).TextInfo.ToTitleCase($ThreatListHash.value))" + + + # Check if list exists - Change to Get-LRListGuidByName + Write-Host "$(Get-TimeStamp) - Testing List Status" + $HashListStatus = Get-ExaContextTables -name $HashThreatListName -Exact + + # If the list exists then update it. Else create it. + if ($HashListStatus) { + Write-Host "$(Get-TimeStamp) - Updating List: $HashThreatListName" + } else { + Write-Host "$(Get-TimeStamp) - Creating List: $HashThreatListName" + $Attributes = [list[object]]::new() + $Attributes.add([PSCustomObject]@{ + id = 'value' + isKey = $true + }) + $Attributes.add([PSCustomObject]@{ + id = 'risk_level' + isKey = $false + }) + $HashListStatus = New-ExaContextTable -Name $HashThreatListName -ContextType 'Other' -Source 'Custom' -Attributes $Attributes + # If successful, reset the status to reflect the same result schema as a Get-ExaContextTables + if ($HashListStatus.table) { + $HashListStatus = $HashListStatus.table + } + } + + + # Pull list values + Write-Host "$(Get-TimeStamp) - Running: Get-RfHashRiskList -List $HashThreatListName" + + Write-Host "$(Get-TimeStamp) - Retrieving List to process. List: $HashThreatListName RecordCount: $HashListResultQuantity" + $ListResults = Get-RfHashRiskList -List $HashListName + + # Determin lowest confidence score provided in list. + if ($ListResults.Risk) { + $Data = [list[object]]::new() + ForEach ($Entry in $ListResults) { + if ($Entry.Risk -ge 85) { + $rlevel = 'High' + } elseif ($Entry.Risk -ge 60) { + $rlevel = 'Medium' + } else { + $rlevel = 'Low' + } + $Data.add([PSCustomObject]@{ + value = $Entry.name + risk_level = $rlevel + }) + } + + $Results = Add-ExaContextRecords -ContextId $HashListStatus.id -Data $($Data | Sort-Object risk_level ) -Operation 'append' + } + + Write-Host "$(Get-TimeStamp) - Clearing Variables: Hash*" + Clear-Variable -Name Hash* -ErrorAction SilentlyContinue + Clear-Variable -Name ListResults -ErrorAction SilentlyContinue + Clear-Variable -Name Data -ErrorAction SilentlyContinue + Clear-Variable -Name MinimumConfidenceScore -ErrorAction SilentlyContinue + [GC]::Collect() + } + Write-Host "$(Get-TimeStamp) - End - Recorded Future Hash Risk List Sync" + } + } + # End Section - Value Sync - Hash + # ----------------------------------- + # Begin Section - Value Sync - Url + # Create URL Threat Lists based on RfUrlEnabledThreatList values + if (($SyncScope -contains "all") -or ($SyncScope -contains "url")) { + + if ($ListStatusUrlEnabled) { + Write-Host "$(Get-TimeStamp) - Begin - Recorded Future URL Threat List Sync" + ForEach ($ThreatListUrl in $ListStatusUrlEnabled.records) { + if ($ThreatListUrl.enabled -like 'False') { + continue + } + # Fork each RiskList into two Lists + Write-Host "$(Get-TimeStamp) - Working: $($ThreatListUrl.value)" + + # Map list Description to List Name + Try { + Write-Host "$(Get-TimeStamp) - Mapping RecordedFuture Threat List Description to Name" + $UrlListName = $RfUrlRiskLists.Where({($_.description -like $($ThreatListUrl.value))}).name + $UrlListResultQuantity = $($RfUrlRiskLists.Where({($_.description -like $($ThreatListUrl.value))}) | Select-Object -ExpandProperty count) + } Catch { + Write-Host "$(Get-TimeStamp) - Pulled list: $($ThreatListUrl.value) is not a valid list." + } + + # Update capitilization for RiskList Value + $UrlThreatListName = "RF URL: $((Get-Culture).TextInfo.ToTitleCase($ThreatListUrl.value))" + + # Check if list exists - Change to Get-ExaContextTables + Write-Host "$(Get-TimeStamp) - Testing List Status" + $UrlListStatus = Get-ExaContextTables -name $UrlThreatListName -Exact + + + # If the list exists then update it. Else create it. + if ($UrlListStatus) { + Write-Host "$(Get-TimeStamp) - Updating List: $UrlThreatListName" + } else { + Write-Host "$(Get-TimeStamp) - Creating List: $UrlThreatListName" + $Attributes = [list[object]]::new() + $Attributes.add([PSCustomObject]@{ + id = 'value' + isKey = $true + }) + $Attributes.add([PSCustomObject]@{ + id = 'risk_level' + isKey = $false + }) + $UrlListStatus = New-ExaContextTable -Name $UrlThreatListName -ContextType 'Other' -Source 'Custom' -Attributes $Attributes + # If successful, reset the status to reflect the same result schema as a Get-ExaContextTables + if ($UrlListStatus.table) { + $UrlListStatus = $UrlListStatus.table + } + } + + # Pull list values + Write-Host "$(Get-TimeStamp) - Running: Get-RfUrlRiskList -List $UrlThreatListName" + + Write-Host "$(Get-TimeStamp) - Retrieving List to process. List: $UrlThreatListName RecordCount: $UrlListResultQuantity" + $ListResults = Get-RfUrlRiskList -List $UrlListName + + if ($ListResults.Risk) { + $Data = [list[object]]::new() + ForEach ($Entry in $ListResults) { + if ($Entry.Risk -ge 85) { + $rlevel = 'High' + } elseif ($Entry.Risk -ge 60) { + $rlevel = 'Medium' + } else { + $rlevel = 'Low' + } + $Data.add([PSCustomObject]@{ + value = $Entry.name + risk_level = $rlevel + }) + } + + $Results = Add-ExaContextRecords -ContextId $UrlListStatus.id -Data $($Data | Sort-Object risk_level ) -Operation 'append' + } + Write-Host "$(Get-TimeStamp) - Clearing Variables: Url*" + Clear-Variable -Name Url* -ErrorAction SilentlyContinue + Clear-Variable -Name ListResults -ErrorAction SilentlyContinue + Clear-Variable -Name Data -ErrorAction SilentlyContinue + Clear-Variable -Name MinimumConfidenceScore -ErrorAction SilentlyContinue + [GC]::Collect() + } + Write-Host "$(Get-TimeStamp) - End - Recorded Future URL Risk List Sync" + } + } + # End Section - Value Sync - Url + # ----------------------------------- + # Begin Section - Value Sync - Domain + # Create Domain Threat Lists based on RfDomainEnabledThreatList values + if (($SyncScope -contains "all") -or ($SyncScope -contains "domain")) { + if ($ListStatusDomainEnabled) { + Write-Host "$(Get-TimeStamp) - Begin - Recorded Future Domain Threat List Sync" + ForEach ($ThreatList in $ListStatusDomainEnabled.records) { + if ($ThreatList.enabled -like 'False') { + continue + } + # Fork each RiskList into two Lists + Write-Host "$(Get-TimeStamp) - Working: $($ThreatList.value)" + + # Map list Description to List Name + Try { + Write-Host "$(Get-TimeStamp) - Mapping RecordedFuture Threat List Description to Name" + $DomainListName = $RfDomainRiskLists.Where({($_.description -like $($ThreatList.value))}).name + $DomainListResultQuantity = $($RfDomainRiskLists.Where({($_.description -like $($ThreatList.value))}) | Select-Object -ExpandProperty count) + } Catch { + Write-Host "$(Get-TimeStamp) - Pulled list: $($ThreatList.value) is not a valid list." + } + + + # Update capitilization for RiskList Value + $DomainThreatListName = "RF Domain: $((Get-Culture).TextInfo.ToTitleCase($ThreatList.value))" + + + # Check if list exists - Change to Get-LRListGuidByName + Write-Host "$(Get-TimeStamp) - Testing List Status" + $DomainListStatus = Get-ExaContextTables -name $DomainThreatListName -Exact + + # If the list exists then update it. Else create it. + if ($DomainListStatus) { + Write-Host "$(Get-TimeStamp) - Updating List: $DomainThreatListName" + } else { + Write-Host "$(Get-TimeStamp) - Creating List: $DomainThreatListName" + $Attributes = [list[object]]::new() + $Attributes.add([PSCustomObject]@{ + id = 'value' + isKey = $true + }) + $Attributes.add([PSCustomObject]@{ + id = 'risk_level' + isKey = $false + }) + $DomainListStatus = New-ExaContextTable -Name $DomainThreatListName -ContextType 'Other' -Source 'Custom' -Attributes $Attributes + # If successful, reset the status to reflect the same result schema as a Get-ExaContextTables + if ($DomainListStatus.table) { + $DomainListStatus = $DomainListStatus.table + } + } + + # Pull list values + Write-Host "$(Get-TimeStamp) - Running: Get-RfDomainRiskList -List $($ThreatList.value)" + + Write-Host "$(Get-TimeStamp) - Retrieving List to process. List: $($ThreatList.value) RecordCount: $DomainListResultQuantity" + $ListResults = Get-RfDomainRiskList -List $DomainListName + + # Determin lowest confidence score provided in list. + if ($ListResults.Risk) { + $Data = [list[object]]::new() + ForEach ($Entry in $ListResults) { + if ($Entry.Risk -ge 85) { + $rlevel = 'High' + } elseif ($Entry.Risk -ge 60) { + $rlevel = 'Medium' + } else { + $rlevel = 'Low' + } + $Data.add([PSCustomObject]@{ + value = $Entry.name + risk_level = $rlevel + }) + } + + $Results = Add-ExaContextRecords -ContextId $DomainListStatus.id -Data $($Data | Sort-Object risk_level ) -Operation 'append' + } + + Write-Host "$(Get-TimeStamp) - Clearing Variables: Domain*" + Clear-Variable -Name Hash* -ErrorAction SilentlyContinue + Clear-Variable -Name ListResults -ErrorAction SilentlyContinue + Clear-Variable -Name Data -ErrorAction SilentlyContinue + Clear-Variable -Name MinimumConfidenceScore -ErrorAction SilentlyContinue + [GC]::Collect() + } + Write-Host "$(Get-TimeStamp) - End - Recorded Future Domain Risk List Sync" + } + + } + # End Section - Value Sync - Domain + # ----------------------------------- + # Begin Section - Value Sync - IP + # Create IP Threat Lists based on RfIPEnabledThreatList values + if (($SyncScope -contains "all") -or ($SyncScope -contains "ip")) { + if ($ListStatusIPEnabled) { + Write-Host "$(Get-TimeStamp) - Begin - Recorded Future IP Threat List Sync" + ForEach ($ThreatList in $ListStatusIPEnabled.records) { + if ($ThreatList.enabled -like 'False') { + continue + } + # Fork each RiskList into two Lists + Write-Host "$(Get-TimeStamp) - Working: $($ThreatList.value)" + + # Map list Description to List Name + Try { + Write-Host "$(Get-TimeStamp) - Mapping RecordedFuture Threat List Description to Name" + $IPListName = $RfIPRiskLists.Where({($_.description -like $($ThreatList.value))}).name + $IPListResultQuantity = $($RfIPRiskLists.Where({($_.description -like $($ThreatList.value))}) | Select-Object -ExpandProperty count) + } Catch { + Write-Host "$(Get-TimeStamp) - Pulled list: $($ThreatList.value) is not a valid list." + } + + + # Update capitilization for RiskList Value + $IPThreatListName = "RF IP: $((Get-Culture).TextInfo.ToTitleCase($ThreatList.value))" + + + # Check if list exists - Change to Get-LRListGuidByName + Write-Host "$(Get-TimeStamp) - Testing List Status" + $IPListStatus = Get-ExaContextTables -name $IPThreatListName -Exact + + # If the list exists then update it. Else create it. + if ($IPListStatus) { + Write-Host "$(Get-TimeStamp) - Updating List: $IPThreatListName" + } else { + Write-Host "$(Get-TimeStamp) - Creating List: $IPThreatListName" + $Attributes = [list[object]]::new() + $Attributes.add([PSCustomObject]@{ + id = 'value' + isKey = $true + }) + $Attributes.add([PSCustomObject]@{ + id = 'risk_level' + isKey = $false + }) + $IPListStatus = New-ExaContextTable -Name $IPThreatListName -ContextType 'Other' -Source 'Custom' -Attributes $Attributes + # If successful, reset the status to reflect the same result schema as a Get-ExaContextTables + if ($IPListStatus.table) { + $IPListStatus = $IPListStatus.table + } + } + + # Pull list values + Write-Host "$(Get-TimeStamp) - Running: Get-RfIPRiskList -List $IPListName" + + Write-Host "$(Get-TimeStamp) - Retrieving List to process. List: $IPListName RecordCount: $IPListResultQuantity" + $ListResults = Get-RfIPRiskList -List $IPListName + + # Determin lowest confidence score provided in list. + if ($ListResults.Risk) { + $Data = [list[object]]::new() + ForEach ($Entry in $ListResults) { + if ($Entry.Risk -ge 85) { + $rlevel = 'High' + } elseif ($Entry.Risk -ge 60) { + $rlevel = 'Medium' + } else { + $rlevel = 'Low' + } + $Data.add([PSCustomObject]@{ + value = $Entry.name + risk_level = $rlevel + }) + } + + $Results = Add-ExaContextRecords -ContextId $IPListStatus.id -Data $($Data | Sort-Object risk_level ) -Operation 'append' + } + + Write-Host "$(Get-TimeStamp) - Clearing Variables: IP*" + Clear-Variable -Name Hash* -ErrorAction SilentlyContinue + Clear-Variable -Name ListResults -ErrorAction SilentlyContinue + Clear-Variable -Name Data -ErrorAction SilentlyContinue + Clear-Variable -Name MinimumConfidenceScore -ErrorAction SilentlyContinue + [GC]::Collect() + } + Write-Host "$(Get-TimeStamp) - End - Recorded Future IP Risk List Sync" + } + } + # End Section - Value Sync - IP + # ----------------------------------- + # Begin Section - Value Sync - Vulnerability + # Create Vulnerability Threat Lists based on RfVulnerabilityEnabledThreatList values + + + + if (($SyncScope -contains "all") -or ($SyncScope -contains "vulnerability")) { + if ($ListStatusVulnEnabled) { + Write-Host "$(Get-TimeStamp) - Begin - Recorded Future Vuln Threat List Sync" + ForEach ($ThreatList in $ListStatusVulnEnabled.records) { + if ($ThreatList.enabled -like 'False') { + continue + } + # Fork each RiskList into two Lists + Write-Host "$(Get-TimeStamp) - Working: $($ThreatList.value)" + + # Map list Description to List Name + Try { + Write-Host "$(Get-TimeStamp) - Mapping RecordedFuture Threat List Description to Name" + $VulnListName = $RfVulnRiskLists.Where({($_.description -like $($ThreatList.value))}).name + $VulnListResultQuantity = $($RfVulnRiskLists.Where({($_.description -like $($ThreatList.value))}) | Select-Object -ExpandProperty count) + } Catch { + Write-Host "$(Get-TimeStamp) - Pulled list: $($ThreatList.value) is not a valid list." + } + + + # Update capitilization for RiskList Value + $VulnThreatListName = "RF Vuln: $((Get-Culture).TextInfo.ToTitleCase($ThreatList.value))" + + + # Check if list exists - Change to Get-LRListGuidByName + Write-Host "$(Get-TimeStamp) - Testing List Status" + $VulnListStatus = Get-ExaContextTables -name $VulnThreatListName -Exact + + # If the list exists then update it. Else create it. + if ($VulnListStatus) { + Write-Host "$(Get-TimeStamp) - Updating List: $VulnThreatListName" + } else { + Write-Host "$(Get-TimeStamp) - Creating List: $VulnThreatListName" + $Attributes = [list[object]]::new() + $Attributes.add([PSCustomObject]@{ + id = 'value' + isKey = $true + }) + $Attributes.add([PSCustomObject]@{ + id = 'risk_level' + isKey = $false + }) + $VulnListStatus = New-ExaContextTable -Name $VulnThreatListName -ContextType 'Other' -Source 'Custom' -Attributes $Attributes + # If successful, reset the status to reflect the same result schema as a Get-ExaContextTables + if ($VulnListStatus.table) { + $VulnListStatus = $VulnListStatus.table + } + } + + # Pull list values + Write-Host "$(Get-TimeStamp) - Running: Get-RfIPRiskList -List $VulnListName" + + Write-Host "$(Get-TimeStamp) - Retrieving List to process. List: $VulnListName RecordCount: $VulnListResultQuantity" + $ListResults = Get-RfVulnerabilityRiskList -List $VulnListName + + # Determin lowest confidence score provided in list. + if ($ListResults.Risk) { + $Data = [list[object]]::new() + ForEach ($Entry in $ListResults) { + if ($Entry.Risk -ge 85) { + $rlevel = 'High' + } elseif ($Entry.Risk -ge 60) { + $rlevel = 'Medium' + } else { + $rlevel = 'Low' + } + $Data.add([PSCustomObject]@{ + value = $Entry.name + risk_level = $rlevel + }) + } + + $Results = Add-ExaContextRecords -ContextId $VulnListStatus.id -Data $($Data | Sort-Object risk_level ) -Operation 'append' + } + + Write-Host "$(Get-TimeStamp) - Clearing Variables: Vuln*" + Clear-Variable -Name Hash* -ErrorAction SilentlyContinue + Clear-Variable -Name ListResults -ErrorAction SilentlyContinue + Clear-Variable -Name Data -ErrorAction SilentlyContinue + Clear-Variable -Name MinimumConfidenceScore -ErrorAction SilentlyContinue + [GC]::Collect() + } + Write-Host "$(Get-TimeStamp) - End - Recorded Future Vuln Risk List Sync" + } + } + # Begin Section - Value Sync - Vulnerability + # Cleanup memory. + [GC]::Collect() +} \ No newline at end of file diff --git a/src/Public/RecordedFuture/Hash/Get-RfHashRiskList.ps1 b/src/Public/RecordedFuture/Hash/Get-RfHashRiskList.ps1 index d6b7b9b2..d3c6cd66 100644 --- a/src/Public/RecordedFuture/Hash/Get-RfHashRiskList.ps1 +++ b/src/Public/RecordedFuture/Hash/Get-RfHashRiskList.ps1 @@ -176,7 +176,7 @@ Function Get-RfHashRiskList { } # Define Search URL - $RequestUrl = $BaseUrl + "hash/risklist" + $QueryString + $RequestUrl = $BaseUrl + "v2/hash/risklist" + $QueryString Write-Verbose "[$Me]: Request URL: $RequestUrl" # Submit API call diff --git a/src/Public/RecordedFuture/Hash/Get-RfHashRiskLists.ps1 b/src/Public/RecordedFuture/Hash/Get-RfHashRiskLists.ps1 index 670613f3..c67b7982 100644 --- a/src/Public/RecordedFuture/Hash/Get-RfHashRiskLists.ps1 +++ b/src/Public/RecordedFuture/Hash/Get-RfHashRiskLists.ps1 @@ -155,7 +155,7 @@ Function Get-RfHashRiskLists { # Define Search URL - $RequestUrl = $BaseUrl + "hash/riskrules" + $RequestUrl = $BaseUrl + "v2/hash/riskrules" Write-Verbose "[$Me]: Request URL: $RequestUrl" Try { diff --git a/src/Public/RecordedFuture/IP/Get-RfIPRiskList.ps1 b/src/Public/RecordedFuture/IP/Get-RfIPRiskList.ps1 index 021a32c5..7a1f4667 100644 --- a/src/Public/RecordedFuture/IP/Get-RfIPRiskList.ps1 +++ b/src/Public/RecordedFuture/IP/Get-RfIPRiskList.ps1 @@ -160,7 +160,7 @@ Function Get-RfIPRiskList { # Define Search URL - $RequestUrl = $BaseUrl + "ip/risklist" + $QueryString + $RequestUrl = $BaseUrl + "v2/ip/risklist" + $QueryString Write-Verbose "[$Me]: Request URL: $RequestUrl" Try { diff --git a/src/Public/RecordedFuture/IP/Get-RfIPRiskLists.ps1 b/src/Public/RecordedFuture/IP/Get-RfIPRiskLists.ps1 index 44288247..bb6cdfb2 100644 --- a/src/Public/RecordedFuture/IP/Get-RfIPRiskLists.ps1 +++ b/src/Public/RecordedFuture/IP/Get-RfIPRiskLists.ps1 @@ -199,7 +199,7 @@ Function Get-RfIPRiskLists { # Define Search URL - $RequestUrl = $BaseUrl + "ip/riskrules" + $RequestUrl = $BaseUrl + "v2/ip/riskrules" Write-Verbose "[$Me]: Request URL: $RequestUrl" Try { diff --git a/src/Public/RecordedFuture/Url/Get-RfUrlRiskList.ps1 b/src/Public/RecordedFuture/Url/Get-RfUrlRiskList.ps1 index c699fc75..71bf668b 100644 --- a/src/Public/RecordedFuture/Url/Get-RfUrlRiskList.ps1 +++ b/src/Public/RecordedFuture/Url/Get-RfUrlRiskList.ps1 @@ -128,7 +128,7 @@ Function Get-RfUrlRiskList { # Define Search URL - $RequestUrl = $BaseUrl + "url/risklist" + $QueryString + $RequestUrl = $BaseUrl + "v2/url/risklist" + $QueryString Write-Verbose "[$Me]: Request URL: $RequestUrl" if ($Compressed -eq $false) { diff --git a/src/Public/RecordedFuture/Url/Get-RfUrlRiskLists.ps1 b/src/Public/RecordedFuture/Url/Get-RfUrlRiskLists.ps1 index ab56f92d..0cec0013 100644 --- a/src/Public/RecordedFuture/Url/Get-RfUrlRiskLists.ps1 +++ b/src/Public/RecordedFuture/Url/Get-RfUrlRiskLists.ps1 @@ -164,7 +164,7 @@ Function Get-RfUrlRiskLists { # Define Search URL - $RequestUrl = $BaseUrl + "url/riskrules" + $RequestUrl = $BaseUrl + "v2/url/riskrules" Write-Verbose "[$Me]: Request URL: $RequestUrl" Try { diff --git a/src/Public/RecordedFuture/Vulnerability/Get-RfVulnerabilityRiskList.ps1 b/src/Public/RecordedFuture/Vulnerability/Get-RfVulnerabilityRiskList.ps1 index d1e70aa5..4cef0716 100644 --- a/src/Public/RecordedFuture/Vulnerability/Get-RfVulnerabilityRiskList.ps1 +++ b/src/Public/RecordedFuture/Vulnerability/Get-RfVulnerabilityRiskList.ps1 @@ -144,7 +144,7 @@ Function Get-RfVulnerabilityRiskList { # Define Search URL - $RequestUrl = $BaseUrl + "vulnerability/risklist" + $QueryString + $RequestUrl = $BaseUrl + "v2/vulnerability/risklist" + $QueryString Write-Verbose "[$Me]: Request URL: $RequestUrl" Try { diff --git a/src/Public/RecordedFuture/Vulnerability/Get-RfVulnerabilityRiskLists.ps1 b/src/Public/RecordedFuture/Vulnerability/Get-RfVulnerabilityRiskLists.ps1 index 31c29636..47a69f9b 100644 --- a/src/Public/RecordedFuture/Vulnerability/Get-RfVulnerabilityRiskLists.ps1 +++ b/src/Public/RecordedFuture/Vulnerability/Get-RfVulnerabilityRiskLists.ps1 @@ -141,16 +141,13 @@ Function Get-RfVulnerabilityRiskLists { Write-Verbose "[$Me]: QueryString is [$QueryString]" } - - # Define Search URL - $RequestUrl = $BaseUrl + "vulnerability/riskrules" + $RequestUrl = $BaseUrl + "v2/vulnerability/riskrules" Write-Verbose "[$Me]: Request URL: $RequestUrl" Try { $Results = Invoke-RestMethod $RequestUrl -Method $Method -Headers $Headers - } - catch { + } catch { If ($_.Exception.Response.StatusCode.value__) { $HTTPCode = ($_.Exception.Response.StatusCode.value__ ).ToString().Trim() Write-Verbose "HTTP Code: $HTTPCode"