PSSymantecCloud.psm1
#Region '.\Classes\Exceptions-Policy.ps1' 0 # https://stackoverflow.com/a/74901407/2552996 class Extensions { [Collections.Generic.List[string]] $names = [Collections.Generic.List[string]]::new() [bool] $scheduled [Collections.Generic.List[string]] $features = [Collections.Generic.List[string]]::new() } class UpdateAllowlist { [object] $add [object] $remove UpdateAllowlist() { $AllowListStructureAdd = [AllowListStructure]::new() $AllowListStructureRemove = [AllowListStructure]::new() $this.add = $AllowListStructureAdd $this.remove = $AllowListStructureRemove } } class AllowListStructure { [object] $Applications [object] $Certificates [object] $webdomains [object] $ips_hosts [Extensions] $Extensions [object] $windows [object] $linux [object] $mac # Setting up the PSCustomObject structure from the JSON example : https://pastebin.com/FaKYpgw3 AllowListStructure() { $this.applications = [System.Collections.Generic.List[object]]::new() $this.Certificates = [System.Collections.Generic.List[object]]::new() $this.webdomains = [System.Collections.Generic.List[object]]::new() $this.ips_hosts = [System.Collections.Generic.List[object]]::new() # Extensions obj be hashtable. Converting to JSON will not be incorrect format (list instead of k/v pair) $this.extensions = [Extensions]::new() $this.windows = [PSCustomObject]@{ files = [System.Collections.Generic.List[object]]::new() directories = [System.Collections.Generic.List[object]]::new() } $this.Linux = [PSCustomObject]@{ files = [System.Collections.Generic.List[object]]::new() directories = [System.Collections.Generic.List[object]]::new() } $this.mac = [PSCustomObject]@{ files = [System.Collections.Generic.List[object]]::new() directories = [System.Collections.Generic.List[object]]::new() } } # method to add APPLICATIONS tab to the main obj [void] AddProcessFile( [string] $sha2, [string] $name ) { $this.applications.Add([pscustomobject]@{ processfile = [pscustomobject]@{ sha2 = $sha2 name = $name } }) } # Method to add CERTIFICATES tab to the main obj [void] AddCertificates( [string] $signature_issuer, [string] $signature_company_name, # [string] $signature_fingerprint, [string] $algorithm, [string] $value ) { # $this.certificates.Add() $this.certificates.Add([pscustomobject]@{ signature_issuer = $signature_issuer signature_company_name = $signature_company_name signature_fingerprint = [pscustomobject]@{ algorithm = $algorithm value = $value } }) } # Method to add WEBDOMAINS to the main obj [void] AddWebDomains( [string] $domain ) { $this.webdomains.add([PSCustomObject]@{ domain = $domain }) } # Method to add IPv4 addresses IPS_HOSTS to the main obj [void] AddIpsHostsIpv4Address( [string] $ip ) { $this.ips_hosts.add([PSCustomObject]@{ ip = $ip }) } # Method to add IPv4 subnet IPS_HOSTS to the main obj [void] AddIpsHostsIpv4Subnet( [string] $ip, [string] $mask ) { $this.ips_hosts.add([pscustomobject]@{ ipv4_subnet = [pscustomobject]@{ ip = $ip mask = $mask } }) } # method to add IPv6 subnet IPS_HOSTS to the main obj [void] AddIpsHostsIpv6Subnet( [string] $ipv6_subnet ) { $this.ips_hosts.add([pscustomobject]@{ ipv6_subnet = $ipv6_subnet }) } #method to add ip ranges to the main obj [void] AddIpsRange( [string] $ip_start, [string] $ip_end ) { $this.ips_hosts.add([pscustomobject]@{ ip_range = [pscustomobject]@{ ip_start = $ip_start ip_end = $ip_end } }) } # Method to add EXTENSIONS tab to the main obj [void] AddExtensions([Extensions] $Extension) { $this.Extensions = $Extension } # Method to add Windows FILES excel tab to obj [void] AddWindowsFiles( [string] $pathvariable, [string] $path, [bool] $scheduled, [array] $features ) { $this.windows.files.add([pscustomobject]@{ pathvariable = $pathvariable path = $path scheduled = $scheduled features = $features }) } # Method to add Linux FILES excel tab to obj [void] AddLinuxFiles( [string] $pathvariable, [string] $path, [bool] $scheduled, [array] $features ) { $this.linux.files.add([pscustomobject]@{ pathvariable = $pathvariable path = $path scheduled = $scheduled features = $features }) } # Method to add Mac FILES excel tab to obj [void] AddMacFiles( [string] $pathvariable, [string] $path, [bool] $scheduled, [array] $features ) { $this.mac.files.add([pscustomobject]@{ pathvariable = $pathvariable path = $path scheduled = $scheduled features = $features }) } # Method to add Windows DIRECTORIES excel tab to obj [void] AddWindowsDirectories( [string] $pathvariable, [string] $directory, [bool] $recursive, [bool] $scheduled, [array] $features ) { $this.windows.directories.add([pscustomobject]@{ pathvariable = $pathvariable directory = $directory recursive = $recursive scheduled = $scheduled features = $features }) } # Method to add Linux DIRECTORIES excel tab to obj [void] AddLinuxDirectories( [string] $pathvariable, [string] $directory, [bool] $recursive, [bool] $scheduled, [array] $features ) { $this.linux.directories.add([pscustomobject]@{ pathvariable = $pathvariable directory = $directory recursive = $recursive scheduled = $scheduled features = $features }) } # Method to add Mac DIRECTORIES excel tab to obj [void] AddMacDirectories( [string] $pathvariable, [string] $directory, [bool] $recursive, [bool] $scheduled, [array] $features ) { $this.mac.directories.add([pscustomobject]@{ pathvariable = $pathvariable directory = $directory recursive = $recursive scheduled = $scheduled features = $features }) } } # TODO - Verify the structure is the expected one when converting to JSON class DenyListStructure { [object] $blacklistrules [object] $nonperules denylistStructure() { $this.blacklistrules = [System.Collections.Generic.List[object]]::new() $this.nonperules = [System.Collections.Generic.List[object]]::new() } # Method to add blacklist rules to the main obj (called "Executable files" in the cloud policy) [void] AddBlacklistRules( [string] $sha2, [string] $name ) { $this.blacklistrules.Add([pscustomobject]@{ processfile = [pscustomobject]@{ sha2 = $sha2 name = $name } }) } # Method to add nonpe rules to the main obj (called "Non-executable files" in the cloud policy) [void] AddNonPeRules( [string] $file_name, [string] $file_sha2, [int] $file_size, [string] $file_directory, [string] $actor_directory, [string] $actor_sha2, [string] $actor_md5 ) { $this.nonperules.Add([pscustomobject]@{ file = [pscustomobject]@{ name = $file_name sha2 = $file_sha2 size = $file_size directory = $file_directory } }) # Add actor directory if it is not empty if ($actor_directory -ne "") { $this.nonperules.file.actor = [pscustomobject]@{ directory = $actor_directory } } # Add actor sha2 if it is not empty if ($actor_sha2 -ne "") { $this.nonperules.file.actor = [pscustomobject]@{ sha2 = $actor_sha2 } } # Add actor md5 if it is not empty if ($actor_md5 -ne "") { $this.nonperules.file.actor = [pscustomobject]@{ md5 = $actor_md5 } } } } class UpdateDenylist { [object] $add [object] $remove UpdateDenylist() { $DenyListStructureAdd = [DenyListStructure]::new() $DenyListStructureRemove = [DenyListStructure]::new() $this.add = $DenyListStructureAdd $this.remove = $DenyListStructureRemove } } #EndRegion '.\Classes\Exceptions-Policy.ps1' 308 #Region '.\Private\Build-QueryURI.ps1' 0 function Build-QueryURI { <# .SYNOPSIS Constructs a URI from a base URI and query strings .DESCRIPTION Constructs a URI from a base URI and query strings .PARAMETER BaseURI The base URI to use .PARAMETER QueryStrings A hashtable of query strings to add to the URI .NOTES helper function .EXAMPLE $BaseURI = "https://Server01:8446/sepm/api/v1/computers" $QueryStrings = @{ sort = "COMPUTER_NAME" pageIndex = 1 pageSize = 100 } $URI = Build-QueryURI -BaseURI $BaseURI -QueryStrings $QueryStrings This example will : - Construct a URI from the base URI "https://Server01:8446/sepm/api/v1/computers" - With the query strings "sort=COMPUTER_NAME&pageIndex=1&pageSize=100" resulting in "https://Server01:8446/sepm/api/v1/computers?sort=COMPUTER_NAME&pageIndex=1&pageSize=100" #> param ( [string]$BaseURI, [hashtable]$QueryStrings ) # Construct the URI $builder = New-Object System.UriBuilder($BaseURI) $query = [System.Web.HttpUtility]::ParseQueryString($builder.Query) foreach ($param in $QueryStrings.GetEnumerator()) { $query[$param.Key] = $param.Value } $builder.Query = $query.ToString() $URL = $builder.Uri.AbsoluteUri return $URL } #EndRegion '.\Private\Build-QueryURI.ps1' 44 #Region '.\Private\ConvertTo-FlatObject.ps1' 0 # From https://github.com/EvotecIT/PSSharedGoods/tree/master/Public/Converts Function ConvertTo-FlatObject { <# .SYNOPSIS Flattends a nested object into a single level object. .DESCRIPTION Flattends a nested object into a single level object. .PARAMETER Objects The object (or objects) to be flatten. .PARAMETER Separator The separator used between the recursive property names .PARAMETER Base The first index name of an embedded array: - 1, arrays will be 1 based: <Parent>.1, <Parent>.2, <Parent>.3, … - 0, arrays will be 0 based: <Parent>.0, <Parent>.1, <Parent>.2, … - "", the first item in an array will be unnamed and than followed with 1: <Parent>, <Parent>.1, <Parent>.2, … .PARAMETER Depth The maximal depth of flattening a recursive property. Any negative value will result in an unlimited depth and could cause a infinitive loop. .PARAMETER Uncut The maximal depth of flattening a recursive property. Any negative value will result in an unlimited depth and could cause a infinitive loop. .PARAMETER ExcludeProperty The propertys to be excluded from the output. .EXAMPLE $Object3 = [PSCustomObject] @{ "Name" = "Przemyslaw Klys" "Age" = "30" "Address" = @{ "Street" = "Kwiatowa" "City" = "Warszawa" "Country" = [ordered] @{ "Name" = "Poland" } List = @( [PSCustomObject] @{ "Name" = "Adam Klys" "Age" = "32" } [PSCustomObject] @{ "Name" = "Justyna Klys" "Age" = "33" } [PSCustomObject] @{ "Name" = "Justyna Klys" "Age" = 30 } [PSCustomObject] @{ "Name" = "Justyna Klys" "Age" = $null } ) } ListTest = @( [PSCustomObject] @{ "Name" = "Sława Klys" "Age" = "33" } ) } $Object3 | ConvertTo-FlatObject .NOTES Based on https://powersnippets.com/convertto-flatobject/ #> [CmdletBinding()] Param ( [Parameter(ValueFromPipeLine)][Object[]]$Objects, [String]$Separator = ".", [ValidateSet("", 0, 1)]$Base = 1, [int]$Depth = 5, [string[]] $ExcludeProperty, [Parameter(DontShow)][String[]]$Path, [Parameter(DontShow)][System.Collections.IDictionary] $OutputObject ) Begin { $InputObjects = [System.Collections.Generic.List[Object]]::new() } Process { foreach ($O in $Objects) { if ($null -ne $O) { $InputObjects.Add($O) } } } End { If ($PSBoundParameters.ContainsKey("OutputObject")) { $Object = $InputObjects[0] $Iterate = [ordered] @{} if ($null -eq $Object) { #Write-Verbose -Message "ConvertTo-FlatObject - Object is null" } elseif ($Object.GetType().Name -in 'String', 'DateTime', 'TimeSpan', 'Version', 'Enum') { $Object = $Object.ToString() } elseif ($Depth) { $Depth-- If ($Object -is [System.Collections.IDictionary]) { $Iterate = $Object } elseif ($Object -is [Array] -or $Object -is [System.Collections.IEnumerable]) { $i = $Base foreach ($Item in $Object.GetEnumerator()) { $NewObject = [ordered] @{} If ($Item -is [System.Collections.IDictionary]) { foreach ($Key in $Item.Keys) { if ($Key -notin $ExcludeProperty) { $NewObject[$Key] = $Item[$Key] } } } elseif ($Item -isnot [Array] -and $Item -isnot [System.Collections.IEnumerable]) { foreach ($Prop in $Item.PSObject.Properties) { if ($Prop.IsGettable -and $Prop.Name -notin $ExcludeProperty) { $NewObject["$($Prop.Name)"] = $Item.$($Prop.Name) } } } else { $NewObject = $Item } $Iterate["$i"] = $NewObject $i += 1 } } else { foreach ($Prop in $Object.PSObject.Properties) { if ($Prop.IsGettable -and $Prop.Name -notin $ExcludeProperty) { $Iterate["$($Prop.Name)"] = $Object.$($Prop.Name) } } } } If ($Iterate.Keys.Count) { foreach ($Key in $Iterate.Keys) { if ($Key -notin $ExcludeProperty) { ConvertTo-FlatObject -Objects @(, $Iterate["$Key"]) -Separator $Separator -Base $Base -Depth $Depth -Path ($Path + $Key) -OutputObject $OutputObject -ExcludeProperty $ExcludeProperty } } } else { $Property = $Path -Join $Separator if ($Property) { # We only care if property is not empty if ($Object -is [System.Collections.IDictionary] -and $Object.Keys.Count -eq 0) { $OutputObject[$Property] = $null } else { $OutputObject[$Property] = $Object } } } } elseif ($InputObjects.Count -gt 0) { foreach ($ItemObject in $InputObjects) { $OutputObject = [ordered]@{} ConvertTo-FlatObject -Objects @(, $ItemObject) -Separator $Separator -Base $Base -Depth $Depth -Path $Path -OutputObject $OutputObject -ExcludeProperty $ExcludeProperty [PSCustomObject] $OutputObject } } } } #EndRegion '.\Private\ConvertTo-FlatObject.ps1' 162 #Region '.\Private\Get-ExcelAllowListObject.ps1' 0 function Get-ExcelAllowListObject { <# .SYNOPSIS Imports excel allow list report from its file path as a PSObject .DESCRIPTION Imports excel allow list report as a PSObject. Same structure that Get-SepCloudPolicyDetails uses to compare Excel allow list and SEP Cloud allow list policy .EXAMPLE Get-ExcelAllowListObject -Path "WorkstationsAllowListPolicy.xlsx" Imports the excel file and returns a structured PSObject .INPUTS Excel path of allow list policy previously generated from Export-SepCloudAllowListPolicyToExcel CmdLet .OUTPUTS Custom PSObject #> param ( # excel path [Parameter( ValueFromPipeline )] [string[]] [Alias('Excel', 'Path')] $excel_path ) process { # List all excel tabs $AllSheets = Get-ExcelSheetInfo $excel_path $SheetsInfo = @{} # Import all Excel info in $SheetsInfo hashtable $AllSheets | ForEach-Object { $SheetsInfo[$_.Name] = Import-Excel $_.Path -WorksheetName $_.Name } # Get Object from ExceptionStructure Class $obj_policy_excel = [AllowListStructure]::new() ############################### # Populates $obj_policy_excel # ############################### # Add Applications foreach ($line in $SheetsInfo['Applications']) { $obj_policy_excel.AddProcessFile( $line.sha2, $line.Name ) } # Add Files foreach ($line in $SheetsInfo['Files']) { # Parse "features.X" properties to gather the feature_names in an array [array]$feature_names = @() [array]$nb_features = $line.PSObject.properties.name | Select-String -Pattern feature $i = 0 foreach ($feat in $nb_features) { if ($null -ne $line.($nb_features[$i])) { $feature_names += $line.($nb_features[$i]) } $i++ } # Use AddWindowsFiles $obj_policy_excel.AddWindowsFiles( $line.pathvariable, $line.path, $line.scheduled, $feature_names ) } # Add Directories foreach ($line in $SheetsInfo['Directories']) { # Parse "features.X" properties to gather the feature names in an array [array]$feature_names = @() [array]$nb_features = $line.PSObject.properties.name | Select-String -Pattern feature $i = 0 foreach ($feat in $nb_features) { if ($null -ne $line.($nb_features[$i])) { $feature_names += $line.($nb_features[$i]) } $i++ } # Use AddWindowsDirectories $obj_policy_excel.AddWindowsDirectories( $line.pathvariable, $line.directory, $line.recursive, $line.scheduled, $feature_names ) } # Add Extensions # no loop required, whole array needed $obj_policy_excel.AddExtensions(@{ names = $sheetsInfo['Extensions'].extensions scheduled = $true features = 'AUTO_PROTECT' } ) # Add WebDomains foreach ($line in $SheetsInfo['Webdomains']) { $obj_policy_excel.AddWebDomains( $line.domain ) } # Add IPS Hosts foreach ($line in $SheetsInfo['Ips_Hosts']) { $obj_policy_excel.AddIpsHostsIpv4Address( $line.ip ) } # Add IPS Subnet v4 foreach ($line in $SheetsInfo['Ips_Hosts_subnet_v4']) { $obj_policy_excel.AddIpsHostsIpv4Subnet( $line.ip, $line.mask ) } # Add IPS Subnet v6 foreach ($line in $SheetsInfo['Ips_Hosts_subnet_v6']) { $obj_policy_excel.AddIpsHostsIpv6Subnet( $line.ipv6_subnet ) } # Add IPs ranges (includes both IPv4 & v6) foreach ($line in $SheetsInfo['Ips_range']) { $obj_policy_excel.AddIpsRange( $line.ip_start, $line.ip_end ) } # Add Certificates foreach ($line in $SheetsInfo['Certificates']) { $obj_policy_excel.AddCertificates( $line.signature_issuer, $line.signature_company_name, $line."signature_fingerprint.algorithm", $line."signature_fingerprint.value" ) } # Add Linux Files foreach ($line in $SheetsInfo['Linux Files']) { # Parse "features.X" properties to gather the feature_names in an array [array]$feature_names = @() [array]$nb_features = $line.PSObject.properties.name | Select-String -Pattern feature $i = 0 foreach ($feat in $nb_features) { if ($null -ne $line.($nb_features[$i])) { $feature_names += $line.($nb_features[$i]) } $i++ } # Use AddLinuxFiles $obj_policy_excel.AddLinuxFiles( $line.pathvariable, $line.path, $line.scheduled, $feature_names ) } # Add Mac Files foreach ($line in $SheetsInfo['Mac Files']) { # Parse "features.X" properties to gather the feature_names in an array [array]$feature_names = @() [array]$nb_features = $line.PSObject.properties.name | Select-String -Pattern feature $i = 0 foreach ($feat in $nb_features) { if ($null -ne $line.($nb_features[$i])) { $feature_names += $line.($nb_features[$i]) } $i++ } # Use AddMacFiles $obj_policy_excel.AddMacFiles( $line.pathvariable, $line.path, $line.scheduled, $feature_names ) } # Add Linux Directories foreach ($line in $SheetsInfo['Linux Directories']) { # Parse "features.X" properties to gather the feature names in an array [array]$feature_names = @() [array]$nb_features = $line.PSObject.properties.name | Select-String -Pattern feature $i = 0 foreach ($feat in $nb_features) { if ($null -ne $line.($nb_features[$i])) { $feature_names += $line.($nb_features[$i]) } $i++ } # Use AddLinuxDirectories $obj_policy_excel.AddLinuxDirectories( $line.pathvariable, $line.directory, $line.recursive, $line.scheduled, $feature_names ) } # Add Mac Directories foreach ($line in $SheetsInfo['Mac Directories']) { # Parse "features.X" properties to gather the feature names in an array [array]$feature_names = @() [array]$nb_features = $line.PSObject.properties.name | Select-String -Pattern feature $i = 0 foreach ($feat in $nb_features) { if ($null -ne $line.($nb_features[$i])) { $feature_names += $line.($nb_features[$i]) } $i++ } # Use AddMacDirectories $obj_policy_excel.AddMacDirectories( $line.pathvariable, $line.directory, $line.recursive, $line.scheduled, $feature_names ) } return $obj_policy_excel } } #EndRegion '.\Private\Get-ExcelAllowListObject.ps1' 238 #Region '.\Private\Get-SEPCloudAPIData.ps1' 0 function Get-SEPCloudAPIData { [CmdletBinding()] param ( $endpoint ) process { $api = @{ 'Example' = @{ '1.0' = @{ Description = 'Details about the API endpoint' URI = 'The URI expressed as /api/v#/endpoint' Method = 'Method to use against the endpoint' Body = 'Parameters to use in the body' Query = 'Parameters to use in the URI query' Result = 'If the result content is stored in a higher level key, express it here to be unwrapped in the return' Success = 'The expected HTTP status code for a successful call' Function = 'The PowerShell function to call to process the result' ObjectTName = 'The name of the PSType object to return' } } 'Block-SEPCloudFile' = @{ '1.0' = @{ Description = 'Quarantine files on the device(s)' URI = '/v1/commands/files/contain' Method = 'Post' Body = @{ device_ids = 'device_ids' hash = 'hash' } Query = '' Result = '' Success = '' Function = 'Block-SEPCloudFile' ObjectTName = 'SEPCloud.block-file' } } 'Connect-SEPCloud' = @{ '1.0' = @{ Description = 'Generate new bearer token from the from the oAuth credential' URI = '/v1/oauth2/tokens' Method = 'Post' Body = '' Query = '' Result = '' Filter = '' Success = '200' } } 'Get-SEPCloudComponentType' = @{ '1.0' = @{ Description = 'lets you retrieve policy component host-groups, network-adapters(adapter), network-services(Connection), network IPS details' URI = '/v1/policies/components' Method = 'Get' Body = '' Query = @{ 'offset' = 'offset' 'limit' = 'limit' } Result = 'data' Success = '' Function = 'Get-SEPCloudComponentType' ObjectTName = 'SEPCloud.policyComponentType' # generic PSObject but there could be up to 4 different subtypes # host-group-response # network-services # network-adapter # network_ips_response } } 'Get-SEPCloudDevice' = @{ '1.0' = @{ Description = 'retrieve the list of devices' URI = '/v1/devices' Method = 'Get' Body = '' Query = @{ offset = 'offset' client_version = 'client_version' device_group = 'device_group' # ID of the parent device group device_status = 'device_status' device_status_reason = 'device_status_reason' device_type = 'device_type' edr_enabled = 'edr_enabled' ipv4_address = 'ipv4_address' include_details = 'include_details' # flag to include product and feature details in response. Possible values: true/false is_cloud = 'is_cloud' is_online = 'is_online' is_virtual = 'is_virtual' mac_address = 'mac_address' name = 'name' os = 'os' # Possible values: windows, Linux, iOS, Mac, Android os_version = 'os_version' } Result = 'devices' Success = '' Function = 'Get-SEPCloudDevice' ObjectTName = 'SEPCloud.Device' } } 'Get-SEPCloudDeviceDetails' = @{ '1.0' = @{ Description = 'Details about the SEP client' URI = '/v1/devices' Method = 'Get' Body = '' Query = @{ device_id = 'device_id' } Result = '' Success = '' Function = 'Get-SEPCloudDeviceDetails' ObjectTName = 'SEPCloud.device-details ' } } 'Get-SEPCloudEDRDumpsList' = @{ '1.0' = @{ Description = 'get the list of endpoint search commands' URI = '/v1/commands/endpoint-search' Method = 'Post' Body = @{ query = 'query' next = 'next' limit = 'limit' } Query = '' Result = 'commands' Success = '' Function = 'Get-SEPCloudEDRDumpsList' ObjectTName = 'SEPCloud.commandsResponse' } } 'Get-SEPCloudEvents' = @{ '1.0' = @{ Description = 'retrieve up to ten thousand events' URI = '/v1/event-search' Method = 'Post' Body = @{ feature_name = 'feature_name' product = 'product' query = 'query' start_date = 'start_date' end_date = 'end_date' next = 'next' limit = 'limit' } Query = '' Result = 'events' Success = '' Function = 'Get-SEPCloudEvents' ObjectTName = 'SEPCloud.Event' } } 'Get-SEPCloudFileHashDetails' = @{ '1.0' = @{ Description = 'retrieve all details of a file based off its hash' URI = '/v1/threat-intel/protection/file/{id}' Method = 'Get' Body = '' Query = '' Result = '' Success = '' Function = 'Get-SEPCloudFileHashDetails' ObjectTName = 'SEPCloud.files-hash' } } 'Get-SEPCloudGroup' = @{ '1.0' = @{ Description = 'retrieve a list of device groups' URI = '/v1/device-groups' Method = 'Get' body = '' Query = @{ offset = 'offset' } Result = 'device_groups' Success = '200' Function = 'Get-SEPCloudGroupTest' ObjectTName = 'SEPCloud.Device-Group' # root object is 'SEPCloud.Device-Group-List' but children objects only are exposed as 'SEPCloud.Device-Group' } } 'Get-SEPCloudGroupPolicies' = @{ '1.0' = @{ Description = 'retrieve a list of policies that are targeted on a device group' URI = '/v1/device-groups/{id}/policies' Method = 'Get' body = '' Query = @{ group_id = 'group_id' } Result = 'policies' Success = '200' Function = 'Get-SEPCloudGroupPolicies' ObjectTName = 'SEPCloud.targeted-policy' } } 'Get-SepCloudIncidentDetails' = @{ '1.0' = @{ Description = 'retrieve details for a specific incident' URI = '/v1/incidents' Method = 'Get' body = '' Query = @{ incident_id = 'incident_id' } Result = 'incident' Success = '200' Function = 'Get-SepCloudIncidentDetails' ObjectTName = 'SEPCloud.incident-details' } } 'Get-SEPCloudPolicesSummary' = @{ '1.0' = @{ Description = 'retrieve a list of your policies (without details)' URI = '/v1/policies' Method = 'Get' Body = '' Query = @{ limit = 'limit' offset = 'offset' name = 'name' type = 'type' } Result = 'policies' Success = '' Function = 'Get-SEPCloudPolicesSummary' ObjectTName = 'SEPCloud.policy' } } 'Get-SepCloudTargetRules' = @{ '1.0' = @{ Description = 'retrieve a list of target rules' URI = '/v1/policies/target-rules' Method = 'Get' Body = '' Query = @{ limit = 'limit' offset = 'offset' } Result = 'target_rules' Success = '' Function = 'Get-SepCloudTargetRules' ObjectTName = 'SEPCloud.target-rule' } } 'Get-SEPCloudThreatIntelCveProtection' = @{ '1.0' = @{ Description = 'returns information whether a given CVE has been blocked by any Symantec technologies' URI = '/v1/threat-intel/protection/cve' Method = 'Get' Body = '' Query = '' Result = '' Success = '' Function = 'Get-SEPCloudThreatIntelCveProtection' ObjectTName = 'SEPCloud.cve-protection' } } 'Get-SEPCloudThreatIntelFileInsight' = @{ '1.0' = @{ Description = 'returns file insight enrichments for given file sha256' URI = '/v1/threat-intel/insight/file' Method = 'Get' Body = '' Query = '' Result = '' Success = '' Function = 'Get-SEPCloudThreatIntelFileInsight' ObjectTName = 'SEPCloud.file-insight' } } 'Get-SEPCloudThreatIntelNetworkInsight' = @{ '1.0' = @{ Description = 'returns network enrichments for given URL, domain or IP' URI = '/v1/threat-intel/insight/network' Method = 'Get' Body = '' Query = '' Result = '' Success = '' Function = 'Get-SEPCloudThreatIntelNetworkInsight' ObjectTName = 'SEPCloud.network-insight' } } 'Get-SEPCloudThreatIntelFileProtection' = @{ '1.0' = @{ Description = 'returns information whether a given file has been blocked by any Symantec technologies' URI = '/v1/threat-intel/protection/file' Method = 'Get' Body = '' Query = '' Result = '' Success = '' Function = 'Get-SEPCloudThreatIntelFileProtection' ObjectTName = 'SEPCloud.file-protection' } } 'Get-SEPCloudThreatIntelFileRelated' = @{ '1.0' = @{ Description = 'returns information whether a given file has been blocked by any Symantec technologies' URI = '/v1/threat-intel/related/file/' Method = 'Get' Body = '' Query = '' Result = '' Success = '' Function = 'Get-SEPCloudThreatIntelFileRelated' ObjectTName = 'SEPCloud.file-related' } } 'Get-SEPCloudThreatIntelNetworkProtection' = @{ '1.0' = @{ Description = 'returns information whether a given file has been blocked by any Symantec technologies' URI = '/v1/threat-intel/protection/network' Method = 'Get' Body = '' Query = '' Result = '' Success = '' Function = 'Get-SEPCloudThreatIntelNetworkProtection' ObjectTName = 'SEPCloud.network-protection' } } 'Get-SEPCloudThreatIntelFileProcessChain' = @{ '1.0' = @{ Description = 'returns topK process lineage enrichment for the provided file sha256' URI = '/v1/threat-intel/processchain/file' Method = 'Get' Body = '' Query = '' Result = '' Success = '' Function = 'Get-SEPCloudThreatIntelFileProcessChain' ObjectTName = 'SEPCloud.file-processchain' } } 'Move-SEPCloudDevice' = @{ '1.0' = @{ Description = 'lets you transfer multiple devices from one device group to another device group' URI = '/v1/device-groups/{id}/devices' Method = 'Put' Body = @{ device_uids = 'device_uids' } Query = '' Result = '' Success = '' Function = 'Move-SEPCloudDevice' ObjectTName = 'SEPCloud.Bulk-Device-List' } } 'New-SEPCloudEDRFullDump' = @{ '1.0' = @{ Description = 'Send the full dump command on the device' URI = '/v1/commands/endpoint-search/fulldump' Method = 'Post' Body = @{ device_id = 'device_id' description = 'description' from_date = 'from_date' to_date = 'to_date' } Query = '' Result = '' Success = '' Function = 'New-SEPCloudEDRFullDump' ObjectTName = 'SEPCloud.DumpCommandResponse' } } 'Remove-SEPCloudPolicy' = @{ '1.0' = @{ Description = 'Removes a SEP Cloud policy from a device group' URI = '/v1/policies/{id}/versions/{id}/device-groups' Method = 'Delete' Body = @{ target_rules = 'target_rules' device_group_ids = 'device_group_ids' } Query = '' Result = '' Success = '' Function = 'Remove-SEPCloudPolicy' ObjectTName = 'SEPCloud.remove-policy' } } 'Set-SEPCloudPolicy' = @{ '1.0' = @{ Description = 'apply a policy to device groups' URI = '/v1/policies/{id}/versions/{id}/device-groups' Method = 'Post' Body = @{ target_rules = 'target_rules' device_group_ids = 'device_group_ids' override = 'override' } Query = '' Result = '' Success = '204' Function = 'Set-SEPCloudPolicy' ObjectTName = 'SEPCloud.apply-policy' } } 'Start-SEPCloudDefinitionUpdate' = @{ '1.0' = @{ Description = 'Update security definitions of devices' URI = '/v1/commands/update_content' Method = 'Post' Body = @{ device_ids = 'device_ids' org_unit_ids = 'org_unit_ids' is_recursive = 'is_recursive' } Query = '' Result = '' Success = '' Function = 'Start-SEPCloudDefinitionUpdate' ObjectTName = 'SEPCloud.DeviceCommandResponse' } } 'Start-SEPCloudFullScan' = @{ '1.0' = @{ Description = 'initiate a full scan on devices managed' URI = '/v1/commands/scans/full' Method = 'Post' Body = @{ device_ids = 'device_ids' org_unit_ids = 'org_unit_ids' is_recursive = 'is_recursive' } Query = '' Result = '' Success = '' Function = 'Start-SEPCloudFullScan' ObjectTName = 'SEPCloud.DeviceCommandResponse' } } } # Use the latest version of the API endpoint $version = $api.$endpoint.Keys | Sort-Object | Select-Object -Last 1 if ($null -eq $version) { $ErrorSplat = @{ Message = "No matching endpoint found for $Endpoint that corresponds to the current cluster version." ErrorAction = 'Stop' TargetObject = $api.$endpoint.keys -join ',' Category = 'ObjectNotFound' } Write-Error @ErrorSplat } else { Write-Verbose -Message "Selected $version API Data for $endpoint" return $api.$endpoint.$version } } } #EndRegion '.\Private\Get-SEPCloudAPIData.ps1' 455 #Region '.\Private\Get-SEPCloudGroupFullPath.ps1' 0 function Get-SEPCloudGroupFullPath { <# .SYNOPSIS Recursively builds a chain of group names from a group to the root. .DESCRIPTION Recursively builds a chain of group names from a group to the root. .EXAMPLE Get-SEPCloudGroupFullPath -CurrentGroup $Group -AllGroups $Groups #> param ( [PSCustomObject]$CurrentGroup, [Array]$AllGroups, [String]$Chain = "" ) # If $allGroups is provided, build the chain from the list of groups. if ($AllGroups) { # If the current group is root (no parent_id), prepend its name to the chain. if (-not $CurrentGroup.parent_id) { if ($Chain -eq "") { return $CurrentGroup.name # If chain is empty, it's the root group. } else { return $CurrentGroup.name + "\" + $Chain # Prepend root name to chain. } } else { # Find the parent group. $ParentGroup = $AllGroups | Where-Object { $_.id -eq $CurrentGroup.parent_id } if ($ParentGroup) { # If there's a parent, prepend the parent's name to the chain and recurse. $NewChain = if ($Chain -eq "") { $CurrentGroup.name } else { $CurrentGroup.name + "\" + $Chain } return Get-SEPCloudGroupFullPath -CurrentGroup $ParentGroup -AllGroups $AllGroups -Chain $NewChain } else { # If no parent found (which shouldn't happen), return the current chain. return $Chain } } } # If the allGroups parameter was not provided, recursively build the path by querying the API group by group ID. else { # If the current group is root (no parent_id), prepend its name to the chain. if (-not $CurrentGroup.parent_id) { if ($Chain -eq "") { return $CurrentGroup.name # If chain is empty, it's the root group. } else { return $CurrentGroup.name + "\" + $Chain # Prepend root name to chain. } } else { # Find the parent group. $parentGroupId = $CurrentGroup.parent_id if ($parentGroupId) { # If there's a parent $parentGroup = Get-SEPCloudGroup -GroupID $parentGroupId # prepend the parent's name to the chain and recurse. $NewChain = if ($Chain -eq "") { $CurrentGroup.name } else { $CurrentGroup.name + "\" + $Chain } return Get-SEPCloudGroupFullPath -CurrentGroup $parentGroup -Chain $NewChain } else { # If no parent found (which shouldn't happen), return the current chain. return $Chain } } } } #EndRegion '.\Private\Get-SEPCloudGroupFullPath.ps1' 69 #Region '.\Private\Invoke-SEPCloudWebRequest.ps1' 0 function Invoke-SEPCloudWebRequest { <# .SYNOPSIS Gather WebRequest from a URL or redirect URL. .DESCRIPTION Gather WebRequest from a URL or redirect URL. Preserves the Authorization header upon redirect. This function is a wrapper around the System.Net.WebRequest class. .PARAMETER Uri URL to gather WebRequest from. .PARAMETER Method HTTP method to use. .PARAMETER Headers Headers to include in the request. Must be a hashtable as per example below. .OUTPUTS JSON object .EXAMPLE $params = @{ Method = 'GET' Uri = "https://example.com/v1/endpoint" Headers = @{ Host = "https://example.com/v1/endpoint" Accept = "application/json" Authorization = "Bearer xxxxxxxx" } queryParameters = @{ "ComputerName" = "MyComputer01" } } Invoke-SEPCloudWebRequest @params This example will : - Send a GET request to https://example.com/v1/endpoint - With the Authorization header set to "Bearer xxxxxxxx" - With the query parameter ComputerName set to "MyComputer01" (https://example.com/v1/endpoint?ComputerName=MyComputer01) .EXAMPLE $params = @{ Method = 'POST' Uri = "https://example.com/v1/endpoint" Headers = @{ Host = "https://example.com/v1/endpoint" Accept = "application/json" Authorization = "Bearer xxxxxxxx" } body = @{ "ComputerName" = "MyComputer01" } } Invoke-SEPCloudWebRequest @params This example will : - Send a POST request to https://example.com/v1/endpoint - With the Authorization header set to "Bearer xxxxxxxx" - With the body set to JSON format {"ComputerName":"MyComputer01"} #> [CmdletBinding()] param ( # URL [Parameter(Mandatory = $true)] [string]$uri, # Method [Parameter(Mandatory = $true)] [string]$method, # List of headers [Parameter(Mandatory = $true)] [hashtable]$headers, # Query parameters [hashtable]$queryStrings = @{}, # Body [hashtable]$body = @{} ) process { # Add query parameters if ($queryStrings.Count -gt 0) { # Construct the URI $uri = Build-QueryURI -BaseURI $uri -QueryStrings $queryStrings } # Initial request $initialRequest = [System.Net.WebRequest]::CreateHttp($uri); $initialRequest.Method = $method $initialRequest.AllowAutoRedirect = $false # Add body if ($body.Count -gt 0) { $initialRequest.ContentType = "application/json" $json = $body | ConvertTo-Json -Depth 100 $bytes = [System.Text.Encoding]::UTF8.GetBytes($json) $initialRequest.ContentLength = $bytes.Length $initialRequestStream = $initialRequest.GetRequestStream() $initialRequestStream.Write($bytes, 0, $bytes.Length) $initialRequestStream.Close() } # Add headers foreach ($header in $Headers.GetEnumerator()) { $initialRequest.Headers.Add($header.Key, $header.Value) } # Send the initial request try { $inititalResponse = $initialRequest.GetResponse(); Write-Verbose -Message "URI = $($inititalResponse.GetResponseHeader("Location"))" Write-Verbose -Message "Status code : $($inititalResponse.StatusCode)" } catch { throw $_ } # IF HTTP status code is linked to redirected URL if ($inititalResponse.StatusCode.value__ -in (301, 302, 303, 307, 308)) { Write-Verbose -Message "URI to redirect : $($inititalResponse.GetResponseHeader("Location"))" # Create new request with the redirect URL $redirectUrl = $inititalResponse.GetResponseHeader("Location") $newRequest = [System.Net.WebRequest]::CreateHttp($redirectUrl); $newRequest.Method = $Method $newRequest.AllowAutoRedirect = $false # Add query parameters if ($queryStrings.Count -gt 0) { # Construct the URI $uri = Build-QueryURI -BaseURI $uri -QueryStrings $queryStrings } # Add body if ($body.Count -gt 0) { $newRequest.ContentType = "application/json" $json = $body | ConvertTo-Json -Depth 100 $bytes = [System.Text.Encoding]::UTF8.GetBytes($json) $newRequest.ContentLength = $bytes.Length $newRequestStream = $newRequest.GetRequestStream() $newRequestStream.Write($bytes, 0, $bytes.Length) $newRequestStream.Close() } # Add headers # TODO verify why when adding all the headers and not just the Authorization header, the request fails wih 400 # Reuse all headers from the initial request (including Authorization header) foreach ($header in $Headers.GetEnumerator()) { $newRequest.Headers.Add($header.Key, $header.Value) } # $newRequest.Headers.Add("Authorization", $Headers.Authorization) # $newRequest.Headers.Add("Accept", $Headers.Accept) # $newRequest.Headers.Add("Host", $Headers.Host) # Send the new request try { $newResponse = $newRequest.GetResponse() Write-Verbose -Message "Status Code : $($newResponse.StatusCode.value__)" } catch { Write-Error "Error in Invoke-SEPCloudWebRequest: $($_.Exception.InnerException.Message)" } # Parse the response $stream = $newResponse.GetResponseStream() $reader = New-Object System.IO.StreamReader($stream) $content = $reader.ReadToEnd() } else { # Get the response from the initial request $stream = $inititalResponse.GetResponseStream() $reader = New-Object System.IO.StreamReader($stream) $content = $reader.ReadToEnd() } return $content | ConvertFrom-Json -Depth 100 } } #EndRegion '.\Private\Invoke-SEPCloudWebRequest.ps1' 174 #Region '.\Private\Merge-SEPCloudAllowList.ps1' 0 function Merge-SepCloudAllowList { <# .SYNOPSIS Merges 2 SEP Cloud allow list policy to a single PSObject .DESCRIPTION Returns a custom PSObject ready to be converted in json as HTTP Body for Update-SepCloudAllowlistPolicy CmdLet Excel file takes precedence in case of conflicts. It is the main "source of truth". Logic goes as below - If SEP exception present in both excel & policy : no changes - If SEP exception present only in Excel : add exception - If SEP exception present only in policy (so not in Excel) : remove exception .NOTES Excel file takes precedence in case of conflicts .INPUTS - SEP cloud allow list policy PSObject - Excel report file path (previously generated from Export-SepCloudAllowListPolicyToExcel CmdLet) .OUTPUTS - Custom PSObject .EXAMPLE Merge-SepCloudAllowList -Policy_Name "My Allow List Policy For Servers" -Excel ".\Data\Centralized_exceptions_for_servers.xlsx" | Update-SepCloudAllowlistPolicy #> [CmdletBinding()] param ( # Policy version [Parameter( )] [string] [Alias("Version")] $Policy_Version, # Exact policy name [Parameter( Mandatory )] [string] [Alias("PolicyName")] $Policy_Name, # excel path [Parameter( Mandatory )] [string] [Alias('Excel', 'Path')] $excel_path ) # Get policy details to compare with Excel file # Use specific version or by default latest version switch ($PSBoundParameters.Keys) { 'Policy_Version' { $obj_policy = Get-SepCloudPolicyDetails -Policy_Name $Policy_Name -Policy_Version $Policy_Version } Default {} } if ($null -eq $PSBoundParameters['Policy_Version']) { $obj_policy = Get-SepCloudPolicyDetails -Policy_Name $Policy_Name } # Import excel report as a structured object with $obj_policy_excel = Get-ExcelAllowListObject -Path $excel_path # Initialize structured obj that will be later converted # to HTTP JSON Body with "add" and "remove" hive $obj_body = [UpdateAllowlist]::new() ########################### # Comparison starts here # ########################### # "Applications" tab # Parsing excel object first $policy_sha2 = $obj_policy.features.configuration.applications.processfile $excel_sha2 = $obj_policy_excel.Applications.processfile # Parsing first excel object foreach ($line in $excel_sha2) { # if sha2 appears in both lists if ($policy_sha2.sha2.contains($line.sha2)) { # No changes needed continue } else { # if sha2 only in excel list, set the sha to the "add" hive $obj_body.add.AddProcessFile( $line.sha2, $line.name ) } } # Parsing then policy object foreach ($line in $policy_sha2) { # if sha2 appears only in policy (so not in Excel) if (-not $excel_sha2.sha2.contains($line.sha2)) { # set the sha to the "remove" hive $obj_body.remove.AddProcessFile( $line.sha2, $line.name ) } } # "Files" tab # Parsing excel object first $policy_files = $obj_policy.features.configuration.windows.files $excel_files = $obj_policy_excel.windows.files foreach ($line in $excel_files) { # If file appears in both lists if ($policy_files.path.contains($line.Path)) { # No changes needed continue } else { # if file only in excel list, set the file to the "add" hive $obj_body.add.AddWindowsFiles( $line.pathvariable, $line.path, $line.scheduled, $line.features ) } } # Parsing then policy object foreach ($line in $policy_files) { # if file appears only in policy (so not in Excel) if (-not $excel_files.path.contains($line.path)) { # set the file to the "remove" hive $obj_body.remove.AddWindowsFiles( $line.pathvariable, $line.path, $line.scheduled, $line.features ) } } # "Directories" tab # Parsing excel object first $policy_directories = $obj_policy.features.configuration.windows.directories $excel_directories = $obj_policy_excel.windows.directories foreach ($line in $excel_directories) { # If directory appears in both lists if ($policy_directories.directory.contains($line.directory)) { # No changes needed continue } else { # if directory only in excel list, set the directory to the "add" hive $obj_body.add.AddWindowsDirectories( $line.pathvariable, $line.directory, $line.recursive, $line.scheduled, $line.features ) } } # parsing then policy object foreach ($line in $policy_directories) { # if directory appears only in policy (so not in Excel) if (-not $excel_directories.directory.contains($line.directory)) { # set the directory to the "remove" hive $obj_body.remove.AddWindowsDirectories( $line.pathvariable, $line.directory, $line.recursive, $line.scheduled, $line.features ) } } # "Certificates" tab # Parsing excel object first # TODO confirm this is the right way to compare certificates $policy_certs = $obj_policy.features.configuration.certificates $excel_certs = $obj_policy_excel.certificates foreach ($line in $excel_certs) { # If certs appears in both lists if ($policy_certs.signature_fingerprint.value.contains($line.signature_fingerprint.value)) { # No changes needed continue } else { # if cert only in excel list, set the cert to the "add" hive $obj_body.add.AddCertificates( $line.signature_issuer, $line.signature_company_name, $line.signature_fingerprint.algorithm, $line.signature_fingerprint.value ) } } # Parsing then policy object foreach ($line in $policy_certs) { # if cert appears only in policy (so not in Excel) if (-not $excel_certs.signature_fingerprint.value.contains($line.signature_fingerprint.value)) { # set the cert to the "remove" hive $obj_body.remove.AddCertificates( $line.signature_issuer, $line.signature_company_name, $line.signature_fingerprint.algorithm, $line.signature_fingerprint.value ) } } # "Webdomains" tab # Parsing excel object first $policy_webdomains = $obj_policy.features.configuration.webdomains $excel_webdomains = $obj_policy_excel.webdomains foreach ($line in $excel_webdomains) { # If webdomain appears in both lists if ($policy_webdomains.domain.contains($line.domain)) { # No changes needed continue } else { # if webdomain only in excel list, set the webdomain to the "add" hive $obj_body.add.AddWebDomains( $line.domain ) } } # Parsing then policy object foreach ($line in $policy_webdomains) { # if webdomain appears only in policy (so not in Excel) if (-not $excel_webdomains.domain.contains($line.domain)) { # set the webdomain to the "remove" hive $obj_body.remove.AddWebDomains( $line.domain ) } } # "Ips_hosts" tab # Parsing excel object first $policy_ips_hosts = $obj_policy.features.configuration.ips_hosts $excel_ips_hosts = $obj_policy_excel.ips_hosts foreach ($line in $excel_ips_hosts) { # If Ips_hosts appears in both lists if ($policy_ips_hosts.ip.contains($line.ip)) { # No changes needed continue } else { # if Ips_hosts only in excel list, set the Ips_hosts to the "add" hive $obj_body.add.AddIpsHostsIpv4Address( $line.ip ) } } # Parsing then policy object foreach ($line in $policy_ips_hosts) { # if Ips_hosts appears only in policy (so not in Excel) if (-not $excel_ips_hosts.ip.contains($line.ip)) { # set the Ips_hosts to the "remove" hive $obj_body.remove.AddIpsHostsIpv4Address( $line.ip ) } } # "Ips_Hosts_subnet_v6" tab # Parsing excel object first $policy_ips_hosts_subnet_v6 = $obj_policy.features.configuration.ips_hosts.ipv6_subnet | Where-Object { $_ } $excel_ips_hosts_subnet_v6 = $obj_policy_excel.ips_hosts.ipv6_subnet | Where-Object { $_ } foreach ($line in $excel_ips_hosts_subnet_v6) { # if subnet appears in both lists if ($policy_ips_hosts_subnet_v6.contains($line)) { # no changes continue } else { # if subnet only in excel list, set the subnet to the "add" hive $obj_body.add.AddIpsHostsIpv6Subnet( $line ) } # } } # parsing then policy object foreach ($line in $policy_ips_hosts_subnet_v6) { # if subnet appears only in policy (so not in Excel) if (-not $excel_ips_hosts_subnet_v6.contains($line)) { # set the subnet to the "remove" hive $obj_body.remove.AddIpsHostsIpv6Subnet( $line ) } } # "ip ranges" tab # Parsing excel object first $policy_ip_range = $obj_policy.features.configuration.ips_hosts.ip_range | Where-Object { $_ } $excel_ip_range = $obj_policy_excel.ips_hosts.ip_range | Where-Object { $_ } foreach ($line in $excel_ip_range) { # If ip_start appears in both lists if ($policy_ip_range.ip_start.contains($line.ip_start)) { # find the index of the ip_start in the policy list $policy_index = $policy_ip_range.ip_start.IndexOf($line.ip_start) # use index to find the corresponding ip_end $policy_ip_end = $policy_ip_range.ip_end[$policy_index] # if policy_ip_end is the same as in excel list, no changes needed if ($policy_ip_end -eq $line.ip_end) { continue } else { # if policy_ip_end is different, remove the ip_start & ip_end from policy and ... $obj_body.remove.AddIpsRange( $policy_ip_range.ip_start[$policy_index], $policy_ip_range.ip_end[$policy_index] ) # ... set the ip range from excel to the "add" hive $obj_body.add.AddIpsRange( $line.ip_start, $line.ip_end ) } } # if ip_start appears only in excel list else { # set the ip range to the "add" hive $obj_body.add.AddIpsRange( $line.ip_start, $line.ip_end ) } } # then parsing policy object foreach ($line in $policy_ip_range) { # if ip_start appears only in policy (so not in Excel) if (-not $excel_ip_range.ip_start.contains($line.ip_start)) { # set the ip range to the "remove" hive $obj_body.remove.AddIpsRange( $line.ip_start, $line.ip_end ) } } # "Ips_Hosts_subnet_v4" tab # Parsing excel object first $policy_ips_hosts_subnet_v4 = $obj_policy.features.configuration.ips_hosts.ipv4_subnet | Where-Object { $_ } $excel_ips_hosts_subnet_v4 = $obj_policy_excel.ips_hosts.ipv4_subnet | Where-Object { $_ } foreach ($line in $excel_ips_hosts_subnet_v4) { # If ip appears in both lists if ($policy_ips_hosts_subnet_v4.ip.contains($line.ip)) { # find the index of the ip in the policy list $policy_index = $policy_ips_hosts_subnet_v4.ip.IndexOf($line.ip) # use index to find the corresponding mask $policy_mask = $policy_ips_hosts_subnet_v4.mask[$policy_index] # if policy_mask is the same as in excel list, no changes needed if ($policy_mask -eq $line.mask) { continue } else { # if policy_mask is different, remove the ip and mask from policy and ... $obj_body.remove.AddIpsHostsIpv4Subnet( $policy_ips_hosts_subnet_v4.ip[$policy_index], $policy_ips_hosts_subnet_v4.mask[$policy_index] ) # ... set the ip from excel to the "add" hive $obj_body.add.AddIpsHostsIpv4Subnet( $line.ip, $line.mask ) } } } # then parsing policy object foreach ($line in $policy_ips_hosts_subnet_v4) { # if ip appears only in policy (so not in Excel) if (-not $excel_ips_hosts_subnet_v4.ip.contains($line.ip)) { # set the ip to the "remove" hive $obj_body.remove.AddIpsHostsIpv4Subnet( $line.ip, $line.mask ) } } # "Extensions" tab # Parsing excel object first $policy_extensions = $obj_policy.features.configuration.extensions $excel_extensions = $obj_policy_excel.extensions $extensions_list_to_add = @() foreach ($line in $excel_extensions.names) { # If extension appears in both lists if ($policy_extensions.names.contains($line)) { # No changes needed continue } else { # if extension only in excel list, set the extension to the "add" hive # Adding it to $extensions_list_to_add $extensions_list_to_add += $line } } # If extensions to add not empty if ($null -ne $extensions_list_to_add) { [PSCustomObject]$ext = @{ Names = $extensions_list_to_add scheduled = $true features = 'AUTO_PROTECT' } $obj_body.add.AddExtensions( $ext ) } # Parsing then policy object $extensions_list_to_remove = @() foreach ($line in $policy_extensions.names) { # if extension appears only in policy (so not in Excel) # Adding it to the $extensions_list_to_remove if (-not $excel_extensions.names.contains($line)) { $extensions_list_to_remove += $line } } # If extensions to remove not empty if ($null -ne $extensions_list_to_remove) { # set the extension to the "remove" hive [PSCustomObject]$ext = @{ Names = $extensions_list_to_remove scheduled = $true features = 'AUTO_PROTECT' } $obj_body.remove.AddExtensions( $ext ) } # "Linux Files" tab # Parsing excel object first $policy_linux_files = $obj_policy.features.configuration.linux.files $excel_linux_files = $obj_policy_excel.linux.files foreach ($line in $excel_linux_files) { # If file appears in both lists if ($policy_linux_files.contains($line.Path)) { # No changes needed continue } else { # if file only in excel list, set the file to the "add" hive $obj_body.add.AddLinuxFiles( $line.pathvariable, $line.path, $line.scheduled, $line.features ) } } # Parsing then policy object foreach ($line in $policy_linux_files) { # if file appears only in policy (so not in Excel) if (-not $excel_linux_files.path.contains($line.path)) { # set the file to the "remove" hive $obj_body.remove.AddLinuxFiles( $line.pathvariable, $line.path, $line.scheduled, $line.features ) } } # "Linux Directories" tab # Parsing excel object first $policy_linux_directories = $obj_policy.features.configuration.linux.directories $excel_linux_directories = $obj_policy_excel.linux.directories foreach ($line in $excel_linux_directories) { # If directory appears in both lists if ($policy_linux_directories.contains($line.directory)) { # No changes needed continue } else { # if directory only in excel list, set the directory to the "add" hive $obj_body.add.AddLinuxDirectories( $line.pathvariable, $line.directory, $line.recursive, $line.scheduled, $line.features ) } } # Parsing then policy object foreach ($line in $policy_linux_directories) { # if directory appears only in policy (so not in Excel) if (-not $excel_linux_directories.directory.contains($line.directory)) { # set the directory to the "remove" hive $obj_body.remove.AddLinuxDirectories( $line.pathvariable, $line.directory, $line.recursive, $line.scheduled, $line.features ) } } # "Mac Files" tab # Parsing excel object first $policy_mac_files = $obj_policy.features.configuration.mac.files $excel_mac_files = $obj_policy_excel.mac.files foreach ($line in $excel_mac_files) { # If file appears in both lists if ($policy_mac_files.contains($line.Path)) { # No changes needed continue } else { # if file only in excel list, set the file to the "add" hive $obj_body.add.AddMacFiles( $line.pathvariable, $line.path, $line.scheduled, $line.features ) } } # Parsing then policy object foreach ($line in $policy_mac_files) { # if file appears only in policy (so not in Excel) if (-not $excel_mac_files.path.contains($line.path)) { # set the file to the "remove" hive $obj_body.remove.AddMacFiles( $line.pathvariable, $line.path, $line.scheduled, $line.features ) } } # "Mac Directories" tab # Parsing excel object first $policy_mac_directories = $obj_policy.features.configuration.mac.directories $excel_mac_directories = $obj_policy_excel.mac.directories foreach ($line in $excel_mac_directories) { # If directory appears in both lists if ($policy_mac_directories.contains($line.directory)) { # No changes needed continue } else { # if directory only in excel list, set the directory to the "add" hive $obj_body.add.AddMacDirectories( $line.pathvariable, $line.directory, $line.recursive, $line.scheduled, $line.features ) } } # Parsing then policy object foreach ($line in $policy_mac_directories) { # if directory appears only in policy (so not in Excel) if (-not $excel_mac_directories.directory.contains($line.directory)) { # set the directory to the "remove" hive $obj_body.remove.AddMacDirectories( $line.pathvariable, $line.directory, $line.recursive, $line.scheduled, $line.features ) } } return $obj_body } #EndRegion '.\Private\Merge-SEPCloudAllowList.ps1' 573 #Region '.\Private\New-BodyString.ps1' 0 function New-BodyString($bodykeys, $parameters) { <# .SYNOPSIS Function to create the body payload for an API request .DESCRIPTION This function compares the defined body parameters within Get-SEPCloudAPIData with any parameters set within the invocation process. If matches are found, a properly formatted and valid body payload is created and returned. .PARAMETER bodykeys All of the body options available to the endpoint .PARAMETER parameters All of the parameter options available within the parent function #> # If sending a GET request, no body is needed if ($resources.Method -eq 'Get') { return $null } # Look at the list of parameters that were set by the invocation process # This is how we know which params were actually set by the call, versus defaulting to some zero, null, or false value # Now that custom params are added, let's inventory all invoked params Write-Verbose -Message 'Build the body parameters' $bodystring = @{ } # Walk through all of the available body options presented by the endpoint # Note: Keys are used to search in case the value changes in the future across different API versions foreach ($body in $bodykeys) { Write-Verbose "Adding $body..." # Array Object if ($resources.Body.$body.GetType().BaseType.Name -eq 'Array') { $bodyarray = $resources.Body.$body.Keys $arraystring = @{ } foreach ($arrayitem in $bodyarray) { # Walk through all of the parameters defined in the function # Both the parameter name and parameter alias are used to match against a body option # It is suggested to make the parameter name "human friendly" and set an alias corresponding to the body option name foreach ($param in $parameters) { # If the parameter name or alias matches the body option name, build a body string if ($param.Name -eq $arrayitem -or $param.Aliases -eq $arrayitem) { # Switch variable types if ((Get-Variable -Name $param.Name).Value.GetType().Name -eq 'SwitchParameter') { $arraystring.Add($arrayitem, (Get-Variable -Name $param.Name).Value.IsPresent) } # All other variable types elseif ($null -ne (Get-Variable -Name $param.Name).Value) { $arraystring.Add($arrayitem, (Get-Variable -Name $param.Name).Value) } } } } $bodystring.Add($body, @($arraystring)) } # Non-Array Object else { # Walk through all of the parameters defined in the function # Both the parameter name and parameter alias are used to match against a body option # It is suggested to make the parameter name "human friendly" and set an alias corresponding to the body option name foreach ($param in $parameters) { # If the parameter name or alias matches the body option name, build a body string if (($param.Name -eq $body -or $param.Aliases -eq $body)) { if ((Get-Variable -Name $param.Name).Value) { # Switch variable types if ((Get-Variable -Name $param.Name).Value.GetType().Name -eq 'SwitchParameter') { $bodystring.Add($body, (Get-Variable -Name $param.Name).Value.IsPresent) } # All other variable types elseif ($null -ne (Get-Variable -Name $param.Name).Value -and (Get-Variable -Name $param.Name).Value.Length -gt 0) { $bodystring.Add($body, (Get-Variable -Name $param.Name).Value) } } } } } } # Store the results into a JSON string if (0 -ne $bodystring.count) { # $bodystring = ConvertTo-Json -InputObject $bodystring Write-Verbose -Message "Body = $(ConvertTo-Json -InputObject $bodystring)" } else { Write-Verbose -Message 'No body for this request' } return $bodystring } #EndRegion '.\Private\New-BodyString.ps1' 92 #Region '.\Private\New-QueryString.ps1' 0 function New-QueryString($query, $uri) { <# .SYNOPSIS Adds query parameters to a URI .DESCRIPTION This function compares the defined query parameters within SEPCloudAPIData with any parameters set within the invocation process. If matches are found, a properly formatted and valid query string is created and appended to a returned URI .PARAMETER query An array of query values that are added based on which $objects have been passed by the user .PARAMETER uri The entire URI without any query values added #> # TODO: It seems like there's a more elegant way to do this logic, but this code is stable and functional. foreach ($_ in $query) { # The query begins with a "?" character, which is appended to the $uri after determining that at least one $params was collected if ($_ -eq $query[0]) { $uri += '?' + $_ } # Subsequent queries are separated by a "&" character else { $uri += '&' + $_ } } return $uri } #EndRegion '.\Private\New-QueryString.ps1' 31 #Region '.\Private\New-URIQuery.ps1' 0 function New-URIQuery($queryKeys, $parameters, $uri) { <# .SYNOPSIS Builds a URI with query parameters for an uri .DESCRIPTION Builds a URI with query parameters for an uri. This function takes a list of keys and values, and constructs an URI with the query parameters. .PARAMETER queryKeys The query keys as defined in Get-SEPCloudAPIData .PARAMETER parameters The set of parameters passed as query values .PARAMETER uri The base URI to build from #> # Construct the uri $builder = New-Object System.UriBuilder($uri) $query = [System.Web.HttpUtility]::ParseQueryString($builder.Query) Write-Verbose -Message "Build the query parameters for $(if ($queryKeys){$queryKeys -join ','}else{'<null>'})" # Walk through all of the available query options foreach ($queryKey in $queryKeys) { # Walk through all of the parameters defined in the function # Both the parameter name and parameter alias are used to match against a query option # This will allow for easier readability of the code foreach ($param in $parameters) { # If the parameter name matches the query option name, build a query string if ($param.ContainsKey($queryKey)) { if ($null -ne $param.Values) { $query.Add($queryKey, $param[$queryKey]) } } } } $builder.Query = $query.ToString() $uri = $builder.Uri.AbsoluteUri Write-Verbose -Message "URI = $uri" return $uri } #EndRegion '.\Private\New-URIQuery.ps1' 45 #Region '.\Private\New-URIString.ps1' 0 function New-URIString { <# .SYNOPSIS Builds a valid URI .DESCRIPTION Builds a valid URI based off of the constructs defined in the Get-SEPCLoudAPIData resources for the cmdlet. Inserts any object IDs into the URI if {id} is specified within the constructs. .PARAMETER baseURL The base URL for the API .PARAMETER id The ID of an object to be inserted into the URI Accepts an array of IDs from 0 to 2 strings .PARAMETER endpoint The endpoint to be inserted into the URI Optionally at the end of the base URL if no {id} is specified .EXAMPLE New-URIString -baseURL "192.168.3.11" -id 56789 -endpoint "/v1/device-groups" Returns "https://192.168.3.11/v1/device-groups/56789" .EXAMPLE New-URIString -baseURL "192.168.3.11" -id 56789 -endpoint "/v1/device-groups/{id}/devices" Returns "https://192.168.3.11/v1/device-groups/56789/devices" #> [CmdletBinding()] param ( [Parameter()] [string] $baseURL = $script:SEPCloudConnection.BaseURL, [Parameter()] [ValidateCount(0, 2)] [array] $id, [Parameter( Mandatory = $true )] [string] $endpoint ) Write-Verbose -Message 'Build the URI' $uri = ('https://' + $baseUrl + $endpoint) # If we find {id} in the path, replace it with the $id value if ($endpoint -match '{id}') { # regex to replace the {id} with the next value in the array $idx = @(0) $uri = [regex]::Replace($uri, '{id}|$', { # if there is a "next value" in the list if ($next = $id[$idx[0]++]) { # if matching EOL if (-not $args[0].Value) { return '/' + $next } else { $next } } }) } # Otherwise, only add the $id value at the end if it exists (for single object retrieval) else { # If $id has 2 elements can't append both ids to URI if ($id.Count -gt 1) { $message = "2 ids provided : '$id'" $message += "endpoint $endpoint allows only one id :" Write-Error -Message $message -ErrorAction Stop } if ($id.Count -eq 1) { $uri += "/$id" } } Write-Verbose -Message "URI = $uri" return $uri } # live test # $BaseURL = "api.my.test.com" # $endpoint = "/v1/{id}/device-groups" # $id = @("123456", "789012") # New-URIString -endpoint $endpoint -id $id -baseURL $BaseURL #EndRegion '.\Private\New-URIString.ps1' 86 #Region '.\Private\New-UserAgentString.ps1' 0 function New-UserAgentString { <# .SYNOPSIS Helper function, creates a user agent string .DESCRIPTION Function that generates a user agent string containing the module name, version and OS / platform information .NOTES Written by Jaap Brasser for community usage Twitter: @jaap_brasser GitHub: jaapbrasser .EXAMPLE New-UserAgentString PSSymantecCloud-0.0--7.4.2--platform--Win32NT--platform_version--Microsoft.Windows.10.0.22631 Will generate a new user agent string containing the module name, version and OS / platform information New-UserAgentString -UserAgentHash @{platform_integration='Poshbot.Rubrik'} Will generate a new user agent string containing the module name, version and OS / platform information with the additional information specified in UserAgentHash #> param( [hashtable] $UserAgentHash ) process { $OS, $OSVersion = if ($psversiontable.PSVersion.Major -lt 6) { 'Win32NT' try { Get-WmiObject -Class Win32_OperatingSystem -ErrorAction Stop | ForEach-Object { ($_.Name -Split '\|')[0], $_.BuildNumber -join '' } } catch {} } else { $psversiontable.platform if ($psversiontable.os.Length -gt 64) { $psversiontable.os.Substring(0, 64) -replace ':', '.' $psversiontable.os.Substring(0, 64) -replace ' ', '.' } else { $psversiontable.os.Trim() -replace ' ', '.' } } $PlatformDetails = "platform--$OS--platform_version--$OSVersion" $ModuleVersion = try { if (-not [string]::IsNullOrWhiteSpace($MyInvocation.MyCommand.ScriptBlock.Module.PrivateData.PSData.Prerelease)) { $MyInvocation.MyCommand.ScriptBlock.Module.Version.ToString(), $MyInvocation.MyCommand.ScriptBlock.Module.PrivateData.PSData.Prerelease.ToString() -join '.' } else { $MyInvocation.MyCommand.ScriptBlock.Module.Version.ToString() } } catch { } $UserAgent = "$script:ModuleName-{0}--{1}--{2}" -f $ModuleVersion, $psversiontable.psversion.tostring(), $PlatformDetails if ($UserAgentHash) { $UserAgentHash.keys | ForEach-Object -Begin { [string]$StringBuilder = '' } -Process { $StringBuilder += "--$_--$($UserAgentHash[$_])" } -End { $UserAgent += $StringBuilder } } return $UserAgent } } #EndRegion '.\Private\New-UserAgentString.ps1' 79 #Region '.\Private\Optimize-SEPCloudAllowListPolicyObject.ps1' 0 function Optimize-SepCloudAllowListPolicyObject { <# .SYNOPSIS Removes empty properties from the SEP Cloud Allow List Policy Object .DESCRIPTION Removes empty properties from the SEP Cloud Allow List Policy Object. This is required to avoid errors when creating a new policy. .EXAMPLE $AllowListPolicyOptimized = $AllowListPolicy | Optimize-SepCloudAllowListPolicyObject #> [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [PSObject] $obj ) process { # Listing all properties of the object $AllProperties = $obj | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name foreach ($property in $AllProperties) { switch ($property) { "add" { # recursively call the function to dig deeper $obj.add = Optimize-SepCloudAllowListPolicyObject $obj.$property # Verify if the add object has no properties if (($obj.add | Get-Member -MemberType NoteProperty).count -eq 0) { # Remove the "add" property from the object $obj = $obj | Select-Object -ExcludeProperty $property } } "remove" { # recursively call the function to dig deeper $obj.remove = Optimize-SepCloudAllowListPolicyObject $obj.$property # Verify if the remove object has no properties if (($obj.remove | Get-Member -MemberType NoteProperty).count -eq 0) { # Remove the "remove" property from the object $obj = $obj | Select-Object -ExcludeProperty $property } } "applications" { if ($obj.$property.processfile.count -eq 0) { # Remove Applications property $obj = $obj | Select-Object -ExcludeProperty $property } } "webdomains" { if ($obj.$property.count -eq 0) { # Remove webdomains property $obj = $obj | Select-Object -ExcludeProperty $property } } "certificates" { if ($obj.$property.count -eq 0) { # Remove certificates property $obj = $obj | Select-Object -ExcludeProperty $property } } "ips_hosts" { if ($obj.$property.count -eq 0) { # Remove ips_hosts property $obj = $obj | Select-Object -ExcludeProperty $property } } "extensions" { if ($obj.$property.names.count -eq 0) { # Remove extensions property $obj = $obj | Select-Object -ExcludeProperty $property } } "linux" { # Checking linux files if ($obj.$property.files.count -eq 0) { # Remove linux files property only $obj.$property = $obj.$property | Select-Object -ExcludeProperty "files" } # Checking linux folders if ($obj.$property.directories.count -eq 0) { # Remove linux folders property only $obj.$property = $obj.$property | Select-Object -ExcludeProperty "directories" } # If both files and folders are empty, remove the property if ($obj.$property.files.count -eq 0 -and $obj.$property.directories.count -eq 0) { $obj = $obj | Select-Object -ExcludeProperty $property } } "windows" { # Checking windows files if ($obj.$property.files.count -eq 0) { # Remove windows files property only $obj.$property = $obj.$property | Select-Object -ExcludeProperty "files" } # Checking windows folders if ($obj.$property.directories.count -eq 0) { # Remove windows folders property only $obj.$property = $obj.$property | Select-Object -ExcludeProperty "directories" } # If both files and folders are empty, remove the property if ($obj.$property.files.count -eq 0 -and $obj.$property.directories.count -eq 0) { $obj = $obj | Select-Object -ExcludeProperty $property } } "mac" { # Checking mac files if ($obj.$property.files.count -eq 0) { # Remove mac files property only $obj.$property = $obj.$property | Select-Object -ExcludeProperty "files" } # Checking mac folders if ($obj.$property.directories.count -eq 0) { # Remove mac folders property only $obj.$property = $obj.$property | Select-Object -ExcludeProperty "directories" } # If both files and folders are empty, remove the property if ($obj.$property.files.count -eq 0 -and $obj.$property.directories.count -eq 0) { $obj = $obj | Select-Object -ExcludeProperty $property } } Default {} } } return $obj } } #EndRegion '.\Private\Optimize-SEPCloudAllowListPolicyObject.ps1' 125 #Region '.\Private\Remove-SEPCloudToken.ps1' 0 function Remove-SEPCloudToken { $script:SEPCloudConnection | Add-Member -MemberType NoteProperty -Name AccessToken -Value $null -Force -ErrorAction SilentlyContinue $script:configuration | Add-Member -MemberType NoteProperty -Name AccessToken -Value $null -Force -ErrorAction SilentlyContinue if ($script:configuration.CachedTokenPath) { try { Remove-Item $script:configuration.CachedTokenPath -Force } catch {} } } #EndRegion '.\Private\Remove-SEPCloudToken.ps1' 9 #Region '.\Private\Set-ObjectTypeName.ps1' 0 function Set-ObjectTypeName($typename, $result) { <# .SYNOPSIS Assigns an Object TypeName to cmdlet results .DESCRIPTION In order to better display results for cmdlets returning many objects with many properties TypeName formats may be used. This function will assign a TypeName if it exists to a set of returned objects. .PARAMETER typename The name of the TypeName to insert .PARAMETER result The response content which recieves the typename. #> if ($null -ne $result -and $null -ne $typename) { # Using ForEach-Object instead of .ForEach as .ForEach doesn't support single results. Write-Verbose -Message "Applying $typename TypeName to results" $result | ForEach-Object { $_.PSObject.TypeNames.Insert(0, $typename) } } return $result } #EndRegion '.\Private\Set-ObjectTypeName.ps1' 28 #Region '.\Private\Submit-Request.ps1' 0 function Submit-Request { [cmdletbinding()] param( # The endpoint's URI $uri, # The header containing authentication details $header = $script:SEPCloudConnection.header, # The action (method) to perform on the endpoint $method = $($resources.Method), # Any optional body data being submitted to the endpoint $body ) Write-Verbose -Message 'Submitting the request' Write-Verbose -Message "method : $method" $WebResult = Invoke-SEPCloudWebRequest -Uri $uri -Headers $header -Method $method -Body $body return $WebResult } #EndRegion '.\Private\Submit-Request.ps1' 24 #Region '.\Private\Test-QueryObject.ps1' 0 function Test-QueryObject($object, $location, $query) { <# .SYNOPSIS Builds a query string for an endpoint .DESCRIPTION The Test-QueryObject function is used to build a custom query string for supported endpoints .PARAMETER object The parent function's variable holding the user generated query data .PARAMETER location The key/value pair that contains the correct query name value .PARAMETER params An array of query values that are added based on which $objects have been passed by the user #> if ((-not [string]::IsNullOrWhiteSpace($object)) -and ($location)) { # This builds the individual query item for the endpoint # Example: /vmware/vm?search_value=SE-CWAHL-WIN&limit=9999 contains 2 queries - search_value and limit return "$location=$object" } } #EndRegion '.\Private\Test-QueryObject.ps1' 25 #Region '.\Private\Test-QueryParam.ps1' 0 function Test-QueryParam($querykeys, $parameters, $uri) { <# .SYNOPSIS Builds a URI with query parameters for an endpoint .DESCRIPTION The Test-QueryParam function is used to build and test a custom query string for supported endpoints. .PARAMETER querykeys The endpoints query keys as defined in Get-SEPCloudAPIData .PARAMETER parameters The set of parameters passed within the cmdlets invocation .PARAMETER uri The endpoints URI #> Write-Verbose -Message "Build the query parameters for $(if ($querykeys){$querykeys -join ','}else{'<null>'})" $querystring = @() # Walk through all of the available query options presented by the endpoint # Note: Keys are used to search in case the value changes in the future across different API versions foreach ($query in $querykeys) { # Walk through all of the parameters defined in the function # Both the parameter name and parameter alias are used to match against a query option # It is suggested to make the parameter name "human friendly" and set an alias corresponding to the query option name foreach ($param in $parameters) { # If the parameter name matches the query option name, build a query string if (($param.Name -eq $query.Keys) -or ($param.Name -eq $query)) { if ((Get-Variable -Name $param.Name).Value) { Write-Verbose ('Building Query with "{0}: {1}"' -f $resources.Query[$param.Name], (Get-Variable -Name $param.Name).Value) } $querystring += Test-QueryObject -object (Get-Variable -Name $param.Name).Value -location $resources.Query[$param.Name] -params $querystring } # If the parameter alias matches the query option name, build a query string elseif (($param.Aliases -eq $query.Keys) -or ($param.Aliases -eq $query)) { if ((Get-Variable -Name $param.Name).Value) { Write-Verbose ('Building Query with "{0}: {1}"' -f (-join $resources.Query[$param.Aliases]), (Get-Variable -Name $param.Name).Value) } $querystring += Test-QueryObject -object (Get-Variable -Name $param.Name).Value -location $resources.Query[$param.Aliases] -params $querystring } } } # After all query options are exhausted, build a new URI with all defined query options $uri = New-QueryString -query $querystring -uri $uri Write-Verbose -Message "URI = $uri" return $uri } #EndRegion '.\Private\Test-QueryParam.ps1' 51 #Region '.\Private\Test-ReturnFormat.ps1' 0 function Test-ReturnFormat($result, $location) { <# .SYNOPSIS Removes parent encapsulation from returned responses .DESCRIPTION The Test-ReturnFormat function is used to remove any parent variables surrounding return data, such as encapsulating results in a "data" key .PARAMETER result The unformatted API response content .PARAMETER location The key/value pair that contains the name of the key holding the response content's data #> Write-Verbose -Message 'Formatting return value' if ($location -and ($null -ne ($result).$location)) { # The $location check assumes that not all endpoints will require findng (and removing) a parent key # If one does exist, this extracts the value so that the $result data is consistent across API versions return ($result).$location } else { # When no $location is found, return the original $result return $result } } #EndRegion '.\Private\Test-ReturnFormat.ps1' 26 #Region '.\Private\Test-SEPCloudConnection.ps1' 0 function Test-SEPCloudConnection { Write-Verbose -Message "Test-SEPCloudConnection: $script:SEPCloudConnection.AccessToken.Token" Write-Verbose -Message "Validate the SEP Cloud token" if (Test-SEPCloudToken) { Write-Verbose -Message "token valid - returning" return $True } else { Write-Verbose -Message "token expired or invalid - requesting a new one" Get-SEPCloudToken Write-Verbose -Message ("New token will expire at" + $((Get-Date) -lt $script:SEPCloudConnection.AccessToken.Expiration)) } } #EndRegion '.\Private\Test-SEPCloudConnection.ps1' 15 #Region '.\Private\Test-SEPCloudToken.ps1' 0 function Test-SEPCloudToken { [CmdletBinding()] param ( [Parameter()] [PSCustomObject] $token ) # Token passed as parameter if ($token) { if ((Get-Date) -lt $token.Expiration) { return $True } } # In memory token if ($script:SEPCloudConnection.AccessToken) { if ((Get-Date) -lt $script:SEPCloudConnection.AccessToken.Expiration) { return $True } else { Remove-SEPCloudToken return $false } } return $False } #EndRegion '.\Private\Test-SEPCloudToken.ps1' 28 #Region '.\Public\Block-SEPCloudFile.ps1' 0 function Block-SEPCloudFile { <# .SYNOPSIS Quarantines one or many files on one or many SEP Cloud managed endpoint .DESCRIPTION Quarantines one or many files on one or many SEP Cloud managed endpoint .PARAMETER device_ids The ID of the SEP Cloud managed endpoint to quarantine file(s) from .PARAMETER hash hash of the file to quarantine .LINK https://github.com/Douda/PSSymantecCloud .EXAMPLE Block-SEPCloudFile -Verbose -device_ids "dGKQS2SyQlCbPjC2VxqO0w" -hash "C4C3115E3A1AF01D6747401AA22AF90A047292B64C4EEFF4D8021CC0CB60B22D" BLocks a specific file on a specific computer by its device_id and hash #> [CmdletBinding()] Param( [Alias('deviceId')] [String[]] $device_ids, [Alias('sha256')] [String[]] $hash ) begin { # Check to ensure that a session to the SaaS exists and load the needed header data for authentication Test-SEPCloudConnection | Out-Null # API data references the name of the function # For convenience, that name is saved here to $function $function = $MyInvocation.MyCommand.Name # Retrieve all of the URI, method, body, query, result, and success details for the API endpoint Write-Verbose -Message "Gather API Data for $function" $resources = Get-SEPCLoudAPIData -endpoint $function Write-Verbose -Message "Load API data for $($resources.Function)" Write-Verbose -Message "Description: $($resources.Description)" } process { # changing "Host" header specifically for this query, otherwise 500 $script:SEPCloudConnection.header += @{ 'Host' = $script:SEPCloudConnection.BaseURL } $uri = New-URIString -endpoint ($resources.URI) -id $id $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) $result = Submit-Request -uri $uri -header $script:SEPCloudConnection.header -method $($resources.Method) -body $body $result = Test-ReturnFormat -result $result -location $resources.Result $result = Set-ObjectTypeName -TypeName $resources.ObjectTName -result $result # removing the "Host" header specifically for this query, otherwise 500 $script:SEPCloudConnection.header.remove('Host') return $result } } #EndRegion '.\Public\Block-SEPCloudFile.ps1' 63 #Region '.\Public\Clear-SepCloudAuthentication.ps1' 0 function Clear-SEPCloudAuthentication { <# .SYNOPSIS Clears out any API token from memory, as well as from local file storage. .DESCRIPTION Clears out any API token from memory, as well as from local file storage. .EXAMPLE Clear-SepCloudAuthentication Clears out any API token from memory, as well as from local file storage. #> [CmdletBinding(SupportsShouldProcess)] param () # Remove the cached authentication data from memory # Using if statements to avoid errors when the variable does not exist or not initialized if ($script:configuration.CachedToken) { $script:configuration.CachedToken = $null } if ($script:Credential) { $script:Credential = $null } if ($script:SEPCloudConnection.AccessToken) { $script:SEPCloudConnection.AccessToken = $null } if ($script:SEPCloudConnection.Credential) { $script:SEPCloudConnection.Credential = $null } # remove the cached authentication data from disk Remove-Item -Path $($script:configuration.CachedTokenPath) -Force -ErrorAction SilentlyContinue -ErrorVariable ev Remove-Item -Path $($script:configuration.SEPCloudCredsPath) -Force -ErrorAction SilentlyContinue -ErrorVariable ev if (($null -ne $ev) -and ($ev.Count -gt 0) -and ($ev[0].FullyQualifiedErrorId -notlike 'PathNotFound*')) { $message = "Experienced a problem trying to remove the file that persists the Access Token " + $($script:configuration.SEPCloudCredsPath) $message += "Experienced a problem trying to remove the file that persists the Access Credentials " + $($script:configuration.CachedTokenPath) Write-Warning -Message $message } } #EndRegion '.\Public\Clear-SepCloudAuthentication.ps1' 43 #Region '.\Public\Connect-SEPCloud.ps1' 0 function Connect-SEPCloud { [CmdletBinding()] param ( # Additional information to be added, takes hashtable as input [hashtable] $UserAgent, [switch] $cacheOnly, $clientId, $secret ) process { # Create User Agent string $UserAgentString = New-UserAgentString -UserAgentHash $UserAgent $PSBoundParameters.Remove($UserAgent) | Out-Null Remove-Variable -Force -Name UserAgent -ErrorAction SilentlyContinue Write-Verbose -Message "Using User Agent $($UserAgentString)" # If called from Initialize-SEPCloudConfiguration # get token from cache only to avoid prompting for creds while loading the module if ($cacheOnly) { Write-Verbose -Message "Token request using cachedOnly" $token = Get-SEPCloudToken -cacheOnly } elseif ($clientId -and $secret) { Write-Verbose -Message "Token request using client and secret" $token = Get-SEPCloudToken -client $clientId -secret $secret } else { $token = Get-SEPCloudToken } # if we have a token, add it to the header if ($null -ne $token) { $head = @{ 'Authorization' = "$($Token.Token_Bearer)"; 'User-Agent' = $UserAgentString#; # 'Host' = $($script:SEPCloudConnection.BaseURL) } $script:SEPCloudConnection | Add-Member -Type NoteProperty -Name 'header' -Value $head -Force } else { # If no token, just add User-Agent $head = @{ 'User-Agent' = $UserAgentString#; # 'Host' = $($script:SEPCloudConnection.BaseURL) } $script:SEPCloudConnection | Add-Member -Type NoteProperty -Name 'header' -Value $head -Force } } } #EndRegion '.\Public\Connect-SEPCloud.ps1' 49 #Region '.\Public\Get-SEPCloudComponentType.ps1' 0 function Get-SEPCloudComponentType { <# .SYNOPSIS This API lets you retrieve policy component host-groups, network-adapters(adapter), network-services(Connection), network IPS details .DESCRIPTION This API lets you retrieve policy component host-groups, network-adapters(adapter), network-services(Connection), network IPS details .NOTES Information or caveats about the function e.g. 'This function is not supported in Linux' .LINK https://github.com/Douda/PSSymantecCloud .EXAMPLE Get-SEPCloudComponentType -componentType 'network-adapters' Provides the full list of network adapters available on the cloud. #> [CmdletBinding()] Param( # Component Type is one of the list [Parameter( Mandatory = $true )] [ValidateSet( 'network-ips', 'host-groups', 'network-adapters', 'network-services' )] [string] $ComponentType, $offset, $limit# = 1000 ) begin { # Check to ensure that a session to the SaaS exists and load the needed header data for authentication Test-SEPCloudConnection | Out-Null # API data references the name of the function # For convenience, that name is saved here to $function $function = $MyInvocation.MyCommand.Name # Retrieve all of the URI, method, body, query, result, and success details for the API endpoint Write-Verbose -Message "Gather API Data for $function" $resources = Get-SEPCLoudAPIData -endpoint $function Write-Verbose -Message "Load API data for $($resources.Function)" Write-Verbose -Message "Description: $($resources.Description)" } process { # changing "Content-Type" header specifically for this query, otherwise 415 : unsupported media type $script:SEPCloudConnection.header += @{ 'Content-Type' = 'application/json' } $uri = New-URIString -endpoint ($resources.URI) -id $ComponentType $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) Write-Verbose -Message "Body is $(ConvertTo-Json -InputObject $body)" $result = Submit-Request -uri $uri -header $script:SEPCloudConnection.header -method $($resources.Method) -body $body # Test if pagination required if (($result.total -gt $result.data.count) -or ($result.total_count -gt $result.data.count)) { Write-Verbose -Message "Result limits hit. Retrieving remaining data based on pagination" do { # Update offset query param for pagination $offset = $result.data.count $uri = New-URIString -endpoint ($resources.URI) -id $id $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri $nextResult = Submit-Request -uri $uri -header $script:SEPCloudConnection.header -method $($resources.Method) -body $body $result.data += $nextResult.data } until (($result.data.count -ge $result.total) -or ($result.data.count -ge $result.total_count)) } $result = Test-ReturnFormat -result $result -location $resources.Result # apply correct PSType based on the 4 possible results options if ($null -ne $result.identification) { $resources.ObjectTName = "SEPCloud.adapter" } if ($null -ne $result.classifications) { $resources.ObjectTName = "SEPCloud.ips_metadata" } if ($null -ne $result.services) { $resources.ObjectTName = "SEPCloud.network-services" } if ($null -ne $result.hosts) { $resources.ObjectTName = "SEPCloud.host-group" } # Setting PSType to the correct type $result = Set-ObjectTypeName -TypeName $resources.ObjectTName -result $result # Removing "Content-Type: application/json" header $script:SEPCloudConnection.header.remove('Content-Type') return $result } } #EndRegion '.\Public\Get-SEPCloudComponentType.ps1' 100 #Region '.\Public\Get-SEPCloudDevice.ps1' 0 function Get-SEPCloudDevice { <# .SYNOPSIS Gathers list of devices from the SEP Cloud console .DESCRIPTION Gathers list of devices from the SEP Cloud console .PARAMETER client_version Version of agent installed on device. [NOTE] Provide comma seperated values in case of multiple version search. .PARAMETER device_group ID of the parent device group. [NOTE] Provide comma seperated values in case of multiple name search. .PARAMETER device_status Device status Possible values: SECURE,AT_RISK, COMPROMISED,NOT_COMPUTED [NOTE] Provide comma seperated values in case of multiple status search. .PARAMETER device_type os type of the device [NOTE] Provide comma seperated values in case of multiple os type search. Possible values: WORKSTATION, SERVER, MOBILE .PARAMETER name name of the device. [NOTE] Provide comma seperated values in case of multiple name search. .PARAMETER ipv4_address ipv4 address of a device. .EXAMPLE Get-SEPCloudDevice Get all devices (very slow) .EXAMPLE Get-SEPCloudDevice -Computername MyComputer Get detailed information about a computer .EXAMPLE Get-SEPCloudDevice -client_version "14.2.1031.0100,14.2.770.0000" Get all devices with client version 14.2.1031.0100 and 14.2.770.0000 .EXAMPLE Get-SEPCloudDevice -device_group "Fmp5838YRsyElHM27PdZww,123456789" Get all devices from the 2 groups with the group IDs "Fmp5838YRsyElHM27PdZww" and "123456789 .EXAMPLE Get-SEPCloudDevice -device_status AT_RISK Get all online devices with AT_RISK status .EXAMPLE Get-SEPCloudDevice -Client_version "14.3.9681.7000" -device_type WORKSTATION Get all workstations with client version 14.3.9681.7000 .EXAMPLE Get-SEPCloudDevice -IPv4 "192.168.1.1" Get all devices with IPv4 address #> [CmdletBinding()] param ( [Alias("ClientVersion")] $client_version, [Alias("Group")] $device_group, [Alias("DeviceStatus")] [ValidateSet("SECURE", "AT_RISK", "COMPROMISED", "NOT_COMPUTED")] $device_status, [Alias("DeviceType")] [ValidateSet("WORKSTATION", "SERVER", "MOBILE")] $device_type, [Alias("IPv4")] $ipv4_address, [Alias("computername")] $name, [switch] $is_virtual, $offset ) begin { # Check to ensure that a session to the SaaS exists and load the needed header data for authentication Test-SEPCloudConnection | Out-Null # API data references the name of the function # For convenience, that name is saved here to $function $function = $MyInvocation.MyCommand.Name # Retrieve all of the URI, method, body, query, result, and success details for the API endpoint Write-Verbose -Message "Gather API Data for $function" $resources = Get-SEPCLoudAPIData -endpoint $function Write-Verbose -Message "Load API data for $($resources.Function)" Write-Verbose -Message "Description: $($resources.Description)" } process { $uri = New-URIString -endpoint ($resources.URI) -id $id $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) Write-Verbose -Message "Body is $(ConvertTo-Json -InputObject $body)" $result = Submit-Request -uri $uri -header $script:SEPCloudConnection.header -method $($resources.Method) -body $body # Test if pagination required if ($result.total -gt $result.devices.count) { Write-Verbose -Message "Result limits hit. Retrieving remaining data based on pagination" do { # Update offset query param for pagination $offset = $result.devices.count $uri = New-URIString -endpoint ($resources.URI) -id $id $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri $nextResult = Submit-Request -uri $uri -header $script:SEPCloudConnection.header -method $($resources.Method) -body $body $result.devices += $nextResult.devices } until ($result.devices.count -ge $result.total) } $result = Test-ReturnFormat -result $result -location $resources.Result $result = Set-ObjectTypeName -TypeName $resources.ObjectTName -result $result return $result } } #EndRegion '.\Public\Get-SEPCloudDevice.ps1' 121 #Region '.\Public\Get-SEPCloudDeviceDetails.ps1' 0 function Get-SEPCloudDeviceDetails { <# .SYNOPSIS Gathers device details from the SEP Cloud console .DESCRIPTION Gathers device details from the SEP Cloud console .LINK https://github.com/Douda/PSSymantecCloud .PARAMETER device_id id used to lookup a unique computer .OUTPUTS PSObject .EXAMPLE Get-SepCloudDeviceDetails -id wduiKXDDSr2CVrRaqrFKNx #> [CmdletBinding()] Param( [Parameter( ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)] [Alias("id")] [string] $device_id ) begin { # Check to ensure that a session to the SaaS exists and load the needed header data for authentication Test-SEPCloudConnection | Out-Null # API data references the name of the function # For convenience, that name is saved here to $function $function = $MyInvocation.MyCommand.Name # Retrieve all of the URI, method, body, query, result, and success details for the API endpoint Write-Verbose -Message "Gather API Data for $function" $resources = Get-SEPCLoudAPIData -endpoint $function Write-Verbose -Message "Load API data for $($resources.Function)" Write-Verbose -Message "Description: $($resources.Description)" } process { $uri = New-URIString -endpoint ($resources.URI) -id $device_id $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) $result = Submit-Request -uri $uri -header $script:SEPCloudConnection.header -method $($resources.Method) -body $body $result = Test-ReturnFormat -result $result -location $resources.Result $result = Set-ObjectTypeName -TypeName $resources.ObjectTName -result $result return $result } } #EndRegion '.\Public\Get-SEPCloudDeviceDetails.ps1' 54 #Region '.\Public\Get-SEPCloudEDRDumpsList.ps1' 0 function Get-SEPCloudEDRDumpsList { <# .SYNOPSIS Gets a list of the SEP Cloud Commands .DESCRIPTION Gets a list of the SEP Cloud Commands. All commands are returned by default. .LINK https://github.com/Douda/PSSymantecCloud .PARAMETER query Query to be used in the search Uses Lucene syntax. Is optional. If not used returns all commands by default .PARAMETER next The next page of results. Used for pagination .PARAMETER limit The maximum number of results returned. Used for pagination default is 25 .EXAMPLE Get-SEPCloudCommand Gets a list of the SEP Cloud Commands #> [CmdletBinding()] Param( $next, $limit = 25, $query ) begin { # Check to ensure that a session to the SaaS exists and load the needed header data for authentication Test-SEPCloudConnection | Out-Null # API data references the name of the function # For convenience, that name is saved here to $function $function = $MyInvocation.MyCommand.Name # Retrieve all of the URI, method, body, query, result, and success details for the API endpoint Write-Verbose -Message "Gather API Data for $function" $resources = Get-SEPCLoudAPIData -endpoint $function Write-Verbose -Message "Load API data for $($resources.Function)" Write-Verbose -Message "Description: $($resources.Description)" } process { $uri = New-URIString -endpoint ($resources.URI) -id $id $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) $result = Submit-Request -uri $uri -header $script:SEPCloudConnection.header -method $($resources.Method) -body $body # Test if pagination required if ($result.total -gt $result.commands.count) { Write-Verbose -Message "Result limits hit. Retrieving remaining data based on pagination" do { # Update offset/next query param for pagination $next = $result.next $uri = New-URIString -endpoint ($resources.URI) -id $id $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri $nextResult = Submit-Request -uri $uri -header $script:SEPCloudConnection.header -method $($resources.Method) -body $body $result.commands += $nextResult.commands } until ($result.commands.count -ge $result.total) } $result = Test-ReturnFormat -result $result -location $resources.Result $result = Set-ObjectTypeName -TypeName $resources.ObjectTName -result $result return $result } } #EndRegion '.\Public\Get-SEPCloudEDRDumpsList.ps1' 72 #Region '.\Public\Get-SEPCloudEvents.ps1' 0 function Get-SEPCloudEvents { <# .SYNOPSIS Get list of SEP Cloud Events. By default it will gather data for past 30 days .DESCRIPTION Get list of SEP Cloud Events. You can use the following parameters to filter the results: FileDetection, FullScan, or a custom Lucene query .LINK https://github.com/Douda/PSSymantecCloud .PARAMETER feature_name Filters events based on a product feature. [NOTE] ==== You can add a comma separated list of feature_name values (i.e. Agent Framework, Deception, Firewall) to define a unique set of events to search. ==== .PARAMETER product The value is SAEP. This represents Symantec Endpoint Security events. [NOTE] ==== SAEP is the only available product value. ==== .PARAMETER query A custom Lucene query to filter the results e.g. type_id:8001 .PARAMETER start_date This value identifies the beginning date to filter events. .PARAMETER end_date This value identifies the ending date to filter events. .PARAMETER next represents the starting index of the record in a given set.This is used for pagination. .PARAMETER limit This value identifies batch size.This is also used for pagination. .EXAMPLE Get-SepCloudEvents Gather all possible events. ** very slow approach & limited to 10k events ** .EXAMPLE Get-SepCloudEvents -Query "type_id:8031 OR type_id:8032 OR type_id:8033" Runs a custom Lucene query #> [CmdletBinding(DefaultParameterSetName = 'Query')] param ( $feature_name = "ALL", $product = "SAEP", $query, $start_date = ((Get-Date).AddDays(-29) | Get-Date -Format "yyyy-MM-ddTHH:mm:ss.fffK"), # Default is 29 days ago $end_date = (Get-Date -Format "yyyy-MM-ddTHH:mm:ss.fffK"), # Default is today $next, # for pagination $limit = 1000 # Maximum number of results per page (API default = 100) ) begin { # Check to ensure that a session to the SaaS exists and load the needed header data for authentication Test-SEPCloudConnection | Out-Null # API data references the name of the function # For convenience, that name is saved here to $function $function = $MyInvocation.MyCommand.Name # Retrieve all of the URI, method, body, query, result, and success details for the API endpoint Write-Verbose -Message "Gather API Data for $function" $resources = Get-SEPCLoudAPIData -endpoint $function Write-Verbose -Message "Load API data for $($resources.Function)" Write-Verbose -Message "Description: $($resources.Description)" } process { $uri = New-URIString -endpoint ($resources.URI) $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) Write-Verbose -Message "Body is $(ConvertTo-Json -InputObject $body)" $result = Submit-Request -uri $uri -header $script:SEPCloudConnection.header -method $($resources.Method) -body $body # Test if pagination required if ($result.total -gt $result.events.count) { Write-Verbose -Message "Result limits hit. Retrieving remaining data based on pagination" do { # Update offset query param for pagination (called next) $next = $next + $result.next $uri = New-URIString -endpoint ($resources.URI) $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) $nextResult = Submit-Request -uri $uri -header $script:SEPCloudConnection.header -method $($resources.Method) -body $body $result.events += $nextResult.events } until ($result.events.count -ge $result.total) } $result = Test-ReturnFormat -result $result -location $resources.Result $result = Set-ObjectTypeName -TypeName $resources.ObjectTName -result $result return $result } } #EndRegion '.\Public\Get-SEPCloudEvents.ps1' 89 #Region '.\Public\Get-SEPCloudFileHashDetails.ps1' 0 function Get-SEPCloudFileHashDetails { <# .SYNOPSIS Returns information whether a given file has been blocked by any Symantec technologies .DESCRIPTION Returns information whether a given file has been blocked by any Symantec technologies .LINK https://github.com/Douda/PSSymantecCloud .PARAMETER file_hash required hash to lookup .EXAMPLE Get-SEPCloudFileHashDetails -file_hash "eec3f761f7eabe9ed569f39e896be24c9bbb8861b15dbde1b3d539505cd9dd8d" #> [CmdletBinding()] Param( [Alias('hash')] $file_hash ) begin { # Check to ensure that a session to the SaaS exists and load the needed header data for authentication Test-SEPCloudConnection | Out-Null # API data references the name of the function # For convenience, that name is saved here to $function $function = $MyInvocation.MyCommand.Name # Retrieve all of the URI, method, body, query, result, and success details for the API endpoint Write-Verbose -Message "Gather API Data for $function" $resources = Get-SEPCLoudAPIData -endpoint $function Write-Verbose -Message "Load API data for $($resources.Function)" Write-Verbose -Message "Description: $($resources.Description)" } process { $uri = New-URIString -endpoint ($resources.URI) -id $file_hash $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) $result = Submit-Request -uri $uri -header $script:SEPCloudConnection.header -method $($resources.Method) -body $body $result = Test-ReturnFormat -result $result -location $resources.Result $result = Set-ObjectTypeName -TypeName $resources.ObjectTName -result $result return $result } } #EndRegion '.\Public\Get-SEPCloudFileHashDetails.ps1' 47 #Region '.\Public\Get-SEPCloudGroup.ps1' 0 function Get-SEPCloudGroup { <# .SYNOPSIS Gathers list of device groups from SEP Cloud .DESCRIPTION Gathers list of device groups from SEP Cloud. Does not contains device information .PARAMETER offset Page number to query. Defaults to 0. If pagination is required, this parameter is used to specify the page number .EXAMPLE Get-SEPCloudGroup Gets the full list of groups #> [CmdletBinding()] param ( # Query [Alias('api_page')] $offset ) begin { # Check to ensure that a session to the SaaS exists and load the needed header data for authentication Test-SEPCloudConnection | Out-Null # API data references the name of the function # For convenience, that name is saved here to $function $function = $MyInvocation.MyCommand.Name # Retrieve all of the URI, method, body, query, result, and success details for the API endpoint Write-Verbose -Message "Gather API Data for $function" $resources = Get-SEPCLoudAPIData -endpoint $function Write-Verbose -Message "Load API data for $($resources.Function)" Write-Verbose -Message "Description: $($resources.Description)" } process { $uri = New-URIString -endpoint ($resources.URI) -id $id $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) Write-Verbose -Message "Body is $(ConvertTo-Json -InputObject $body)" $result = Submit-Request -uri $uri -header $script:SEPCloudConnection.header -method $($resources.Method) -body $body # Test if pagination required if ($result.total -gt $result.device_groups.count) { Write-Verbose -Message "Result limits hit. Retrieving remaining data based on pagination" do { # Update offset query param for pagination $offset = $result.device_groups.count $uri = New-URIString -endpoint ($resources.URI) -id $id $uri = Test-QueryParam -querykeys $resources.query -parameters ((Get-Command $function).Parameters.Values) -uri $uri $nextResult = Submit-Request -uri $uri -header $script:SEPCloudConnection.header -method $($resources.Method) -body $body $result.device_groups += $nextResult.device_groups } until ($result.device_groups.count -ge $result.total) } $result = Test-ReturnFormat -result $result -location $resources.Result $result = Set-ObjectTypeName -TypeName $resources.ObjectTName -result $result # Add custom property fullPathName Write-Verbose -Message "Adding new property fullPathName for each group" $result | ForEach-Object { $fullPathName = Get-SEPCloudGroupFullPath -CurrentGroup $_ -AllGroups $result -Chain "" $_ | Add-Member -NotePropertyName "fullPathName" -NotePropertyValue $fullPathName.TrimEnd(" > ") } return $result } } #EndRegion '.\Public\Get-SEPCloudGroup.ps1' 72 #Region '.\Public\Get-SEPCloudGroupPolicies.ps1' 0 function Get-SEPCloudGroupPolicies { <# .SYNOPSIS Gathers list of policies applied for a device group .DESCRIPTION Gathers list of policies applied for a device group .LINK https://github.com/Douda/PSSymantecCloud .PARAMETER group_id id of device group .EXAMPLE Get-SEPCloudGroupPolicies -GroupID "Fmp5838YRsyElHM27PdZxx" Gets the list of every policies applied to a device group #> [CmdletBinding()] param ( # Group ID [Parameter( ValueFromPipelineByPropertyName = $true )] [Alias('groupID')] [String] $group_id ) begin { # Check to ensure that a session to the SaaS exists and load the needed header data for authentication Test-SEPCloudConnection | Out-Null # API data references the name of the function # For convenience, that name is saved here to $function $function = $MyInvocation.MyCommand.Name # Retrieve all of the URI, method, body, query, result, and success details for the API endpoint Write-Verbose -Message "Gather API Data for $function" $resources = Get-SEPCLoudAPIData -endpoint $function Write-Verbose -Message "Load API data for $($resources.Function)" Write-Verbose -Message "Description: $($resources.Description)" } process { $uri = New-URIString -endpoint ($resources.URI) -id $group_id $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) Write-Verbose -Message "Body is $(ConvertTo-Json -InputObject $body)" $result = Submit-Request -uri $uri -header $script:SEPCloudConnection.header -method $($resources.Method) -body $body $result = Test-ReturnFormat -result $result -location $resources.Result $result = Set-ObjectTypeName -TypeName $resources.ObjectTName -result $result return $result } } #EndRegion '.\Public\Get-SEPCloudGroupPolicies.ps1' 56 #Region '.\Public\Get-SepCloudIncidentDetails.ps1' 0 function Get-SepCloudIncidentDetails { <# .SYNOPSIS Gathers details about an open incident .DESCRIPTION Gathers details about an open incident .LINK https://github.com/Douda/PSSymantecCloud .PARAMETER incidentId ID of incident .EXAMPLE Get-SepCloudIncidentDetails -incident_ID "21b23af2-ea44-479c-a235-9540082da98f" #> [CmdletBinding()] Param( # Query [Alias('incident_id')] [String]$incidentId ) begin { # Check to ensure that a session to the SaaS exists and load the needed header data for authentication Test-SEPCloudConnection | Out-Null # API data references the name of the function # For convenience, that name is saved here to $function $function = $MyInvocation.MyCommand.Name # Retrieve all of the URI, method, body, query, result, and success details for the API endpoint Write-Verbose -Message "Gather API Data for $function" $resources = Get-SEPCLoudAPIData -endpoint $function Write-Verbose -Message "Load API data for $($resources.Function)" Write-Verbose -Message "Description: $($resources.Description)" } process { $uri = New-URIString -endpoint ($resources.URI) -id $incidentId $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) Write-Verbose -Message "Body is $(ConvertTo-Json -InputObject $body)" $result = Submit-Request -uri $uri -header $script:SEPCloudConnection.header -method $($resources.Method) -body $body $result = Test-ReturnFormat -result $result -location $resources.Result $result = Set-ObjectTypeName -TypeName $resources.ObjectTName -result $result return $result } } #EndRegion '.\Public\Get-SepCloudIncidentDetails.ps1' 53 #Region '.\Public\Get-SEPCloudPolicesSummary.ps1' 0 function Get-SEPCloudPolicesSummary { <# .SYNOPSIS Provides a list of all SEP Cloud policies .DESCRIPTION Provides a list of all SEP Cloud policies .LINK https://github.com/Douda/PSSymantecCloud .PARAMETER limit The number of records fetched in a given request . [NOTE] The maximum number of records supported per request is 1000 .PARAMETER offset When this field is not present, it returns the first page .PARAMETER name The name of the policy you want to search for .PARAMETER type The type of policy you want to search for .EXAMPLE Get-SEPCloudPolicesSummary Gathers all possible policies in your SEP Cloud account .EXAMPLE Get-SEPCloudPolicesSummary -name "My Exploit Protection Policy" name : My Exploit Protection Policy author : Imported from SEPM policy_uid : abcdef123-abcd-5678-1234-123456789012 policy_version : 1 policy_type : Exploit Protection is_imported : True locked : True created : 28/06/2024 11:33:45 modified : 28/06/2024 11:33:45 Gathers a summary of your specific policy .EXAMPLE Get-SEPCloudPolicesSummary -type "Exploit Protection" Gathers all Exploit Protection policies from your tenant #> [CmdletBinding()] param ( $limit, # Defaults maximum limit is 1000 $offset, $name, $type ) begin { # Check to ensure that a session to the SaaS exists and load the needed header data for authentication Test-SEPCloudConnection | Out-Null # API data references the name of the function # For convenience, that name is saved here to $function $function = $MyInvocation.MyCommand.Name # Retrieve all of the URI, method, body, query, result, and success details for the API endpoint Write-Verbose -Message "Gather API Data for $function" $resources = Get-SEPCLoudAPIData -endpoint $function Write-Verbose -Message "Load API data for $($resources.Function)" Write-Verbose -Message "Description: $($resources.Description)" } process { $uri = New-URIString -endpoint ($resources.URI) -id $id $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) Write-Verbose -Message "Body is $(ConvertTo-Json -InputObject $body)" $result = Submit-Request -uri $uri -header $script:SEPCloudConnection.header -method $($resources.Method) -body $body # Test if pagination required if ($result.total -gt $result.policies.count) { Write-Verbose -Message "Result limits hit. Retrieving remaining data based on pagination" do { # Update offset query param for pagination $offset = $result.policies.count $uri = New-URIString -endpoint ($resources.URI) -id $id $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri $nextResult = Submit-Request -uri $uri -header $script:SEPCloudConnection.header -method $($resources.Method) -body $body $result.policies += $nextResult.policies } until ($result.policies.count -ge $result.total) } $result = Test-ReturnFormat -result $result -location $resources.Result $result = Set-ObjectTypeName -TypeName $resources.ObjectTName -result $result return $result } } #EndRegion '.\Public\Get-SEPCloudPolicesSummary.ps1' 92 #Region '.\Public\Get-SEPCloudTargetRules.ps1' 0 function Get-SepCloudTargetRules { <# .SYNOPSIS Provides a list of all target rules in your SEP Cloud account .DESCRIPTION Provides a list of all target rules in your SEP Cloud account. Formely known as SEP Location awareness .PARAMETER None .OUTPUTS PSObject .EXAMPLE Get-SepCloudTargetRules Gathers all possible target rules #> [CmdletBinding()] param ( # Query [Alias('api_page')] $offset ) begin { # Check to ensure that a session to the SaaS exists and load the needed header data for authentication Test-SEPCloudConnection | Out-Null # API data references the name of the function # For convenience, that name is saved here to $function $function = $MyInvocation.MyCommand.Name # Retrieve all of the URI, method, body, query, result, and success details for the API endpoint Write-Verbose -Message "Gather API Data for $function" $resources = Get-SEPCLoudAPIData -endpoint $function Write-Verbose -Message "Load API data for $($resources.Function)" Write-Verbose -Message "Description: $($resources.Description)" } process { $uri = New-URIString -endpoint ($resources.URI) -id $id $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) Write-Verbose -Message "Body is $(ConvertTo-Json -InputObject $body)" $result = Submit-Request -uri $uri -header $script:SEPCloudConnection.header -method $($resources.Method) -body $body # Test if pagination required if ($result.total -gt $result.target_rules.count) { Write-Verbose -Message "Result limits hit. Retrieving remaining data based on pagination" do { # Update offset query param for pagination $offset = $result.target_rules.count $uri = Test-QueryParam -querykeys $resources.query -parameters ((Get-Command $function).Parameters.Values) -uri $uri $nextResult = Submit-Request -uri $uri -header $script:SEPCloudConnection.header -method $($resources.Method) -body $body $result.target_rules += $nextResult.target_rules } until ($result.target_rules.count -ge $result.total) } $result = Test-ReturnFormat -result $result -location $resources.Result $result = Set-ObjectTypeName -TypeName $resources.ObjectTName -result $result return $result } } #EndRegion '.\Public\Get-SEPCloudTargetRules.ps1' 65 #Region '.\Public\Get-SEPCloudThreatIntelCveProtection.ps1' 0 function Get-SEPCloudThreatIntelCveProtection { <# .SYNOPSIS Provide information whether a given CVE has been blocked by any of Symantec technologies .DESCRIPTION Provide information whether a given URL/domain has been blocked by any of Symantec technologies. These technologies include Antivirus (AV), Intrusion Prevention System (IPS) and Behavioral Analysis & System Heuristics (BASH) .PARAMETER cve Specify one or many CVE to check .LINK https://github.com/Douda/PSSymantecCloud .EXAMPLE Get-SepThreatIntelCveProtection -cve CVE-2023-35311 Gathers information whether CVE-2023-35311 has been blocked by any of Symantec technologies .EXAMPLE "CVE-2023-35311","CVE-2023-35312" | Get-SepThreatIntelCveProtection Gathers cve from pipeline by value whether CVE-2023-35311 & CVE-2023-35312 have been blocked by any of Symantec technologies #> [CmdletBinding()] param ( # Mandatory cve [Parameter( Mandatory, ValueFromPipeline = $true)] [Alias('vuln', 'vulnerability')] [string[]] $cve ) begin { # Check to ensure that a session to the SaaS exists and load the needed header data for authentication Test-SEPCloudConnection | Out-Null # API data references the name of the function # For convenience, that name is saved here to $function $function = $MyInvocation.MyCommand.Name # Retrieve all of the URI, method, body, query, result, and success details for the API endpoint Write-Verbose -Message "Gather API Data for $function" $resources = Get-SEPCLoudAPIData -endpoint $function Write-Verbose -Message "Load API data for $($resources.Function)" Write-Verbose -Message "Description: $($resources.Description)" } process { $uri = New-URIString -endpoint ($resources.URI) -id $cve $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) Write-Verbose -Message "Body is $(ConvertTo-Json -InputObject $body)" $result = Submit-Request -uri $uri -header $script:SEPCloudConnection.header -method $($resources.Method) -body $body $result = Test-ReturnFormat -result $result -location $resources.Result $result = Set-ObjectTypeName -TypeName $resources.ObjectTName -result $result return $result } } #EndRegion '.\Public\Get-SEPCloudThreatIntelCveProtection.ps1' 61 #Region '.\Public\Get-SEPCloudThreatIntelFileInsight.ps1' 0 function Get-SEPCloudThreatIntelFileInsight { <# .SYNOPSIS Provide file insight enrichments for a given file .DESCRIPTION Provide file insight enrichments for a given file .INPUTS sha256 .OUTPUTS PSObject .LINK https://github.com/Douda/PSSymantecCloud .PARAMETER file_sha256 Specify one or many sha256 hash .EXAMPLE PS C:\PSSymantecCloud> Get-SepThreatIntelFileInsight -file_sha256 eec3f761f7eabe9ed569f39e896be24c9bbb8861b15dbde1b3d539505cd9dd8d file : eec3f761f7eabe9ed569f39e896be24c9bbb8861b15dbde1b3d539505cd9dd8d reputation : BAD prevalence : Hundreds firstSeen : 2018-04-13 lastSeen : 2023-09-03 targetOrgs : .EXAMPLE "eec3f761f7eabe9ed569f39e896be24c9bbb8861b15dbde1b3d539505cd9dd8d" | Get-SepThreatIntelFileInsight #> [CmdletBinding()] param ( # Mandatory file sha256 [Parameter( Mandatory, ValueFromPipeline = $true)] [Alias('sha256')] $file_sha256 ) begin { # Check to ensure that a session to the SaaS exists and load the needed header data for authentication Test-SEPCloudConnection | Out-Null # API data references the name of the function # For convenience, that name is saved here to $function $function = $MyInvocation.MyCommand.Name # Retrieve all of the URI, method, body, query, result, and success details for the API endpoint Write-Verbose -Message "Gather API Data for $function" $resources = Get-SEPCLoudAPIData -endpoint $function Write-Verbose -Message "Load API data for $($resources.Function)" Write-Verbose -Message "Description: $($resources.Description)" } process { $uri = New-URIString -endpoint ($resources.URI) -id $file_sha256 $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) Write-Verbose -Message "Body is $(ConvertTo-Json -InputObject $body)" $result = Submit-Request -uri $uri -header $script:SEPCloudConnection.header -method $($resources.Method) -body $body $result = Test-ReturnFormat -result $result -location $resources.Result $result = Set-ObjectTypeName -TypeName $resources.ObjectTName -result $result return $result } } #EndRegion '.\Public\Get-SEPCloudThreatIntelFileInsight.ps1' 68 #Region '.\Public\Get-SEPCloudThreatIntelFileProtection.ps1' 0 function Get-SEPCloudThreatIntelFileProtection { <# .SYNOPSIS Provide information whether a given file has been blocked by any of Symantec technologies .DESCRIPTION Provide information whether a given file has been blocked by any of Symantec technologies. These technologies include Antivirus (AV), Intrusion Prevention System (IPS) and Behavioral Analysis & System Heuristics (BASH) .INPUTS sha256 .OUTPUTS PSObject .LINK https://github.com/Douda/PSSymantecCloud .PARAMETER file_sha256 Specify one or many sha256 hash .EXAMPLE Get-SepThreatIntelFileProtection -file_sha256 eec3f761f7eabe9ed569f39e896be24c9bbb8861b15dbde1b3d539505cd9dd8d Gathers information whether the file with sha256 has been blocked by any of Symantec technologies .EXAMPLE "eec3f761f7eabe9ed569f39e896be24c9bbb8861b15dbde1b3d539505cd9dd8d","eec3f761f7eabe9ed569f39e896be24c9bbb8861b15dbde1b3d539505cd9dd8e" | Get-SepThreatIntelFileProtection Gathers sha from pipeline by value whether the files with sha256 have been blocked by any of Symantec technologies #> [CmdletBinding()] Param( # Mandatory file sha256 [Parameter( Mandatory, ValueFromPipeline = $true)] [Alias('sha256')] $file_sha256 ) begin { # Check to ensure that a session to the SaaS exists and load the needed header data for authentication Test-SEPCloudConnection | Out-Null # API data references the name of the function # For convenience, that name is saved here to $function $function = $MyInvocation.MyCommand.Name # Retrieve all of the URI, method, body, query, result, and success details for the API endpoint Write-Verbose -Message "Gather API Data for $function" $resources = Get-SEPCLoudAPIData -endpoint $function Write-Verbose -Message "Load API data for $($resources.Function)" Write-Verbose -Message "Description: $($resources.Description)" } process { $uri = New-URIString -endpoint ($resources.URI) -id $file_sha256 $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) Write-Verbose -Message "Body is $(ConvertTo-Json -InputObject $body)" $result = Submit-Request -uri $uri -header $script:SEPCloudConnection.header -method $($resources.Method) -body $body $result = Test-ReturnFormat -result $result -location $resources.Result $result = Set-ObjectTypeName -TypeName $resources.ObjectTName -result $result return $result } } #EndRegion '.\Public\Get-SEPCloudThreatIntelFileProtection.ps1' 64 #Region '.\Public\Get-SEPCloudThreatIntelFileRelated.ps1' 0 function Get-SEPCloudThreatIntelFileRelated { <# .SYNOPSIS Provide related file for a given file .DESCRIPTION Provide related file for a given file .INPUTS sha256 .OUTPUTS PSObject .LINK https://github.com/Douda/PSSymantecCloud .PARAMETER file_sha256 Specify one or many sha256 hash .EXAMPLE PS C:\PSSymantecCloud> "eec3f761f7eabe9ed569f39e896be24c9bbb8861b15dbde1b3d539505cd9dd8d" | Get-SepThreatIntelFileRelated file related ---- ------- eec3f761f7eabe9ed569f39e896be24c9bbb8861b15dbde1b3d539505cd9dd8d {@{iocType=File; iocValues=System.Object[]; relation=byProcessChain}, @{iocType=File; iocValues=System.Object[]; relation=bySignature}} #> [CmdletBinding()] Param( # Mandatory file sha256 [Parameter( Mandatory, ValueFromPipeline = $true)] [Alias('sha256')] $file_sha256 ) begin { # Check to ensure that a session to the SaaS exists and load the needed header data for authentication Test-SEPCloudConnection | Out-Null # API data references the name of the function # For convenience, that name is saved here to $function $function = $MyInvocation.MyCommand.Name # Retrieve all of the URI, method, body, query, result, and success details for the API endpoint Write-Verbose -Message "Gather API Data for $function" $resources = Get-SEPCLoudAPIData -endpoint $function Write-Verbose -Message "Load API data for $($resources.Function)" Write-Verbose -Message "Description: $($resources.Description)" } process { $uri = New-URIString -endpoint ($resources.URI) -id $file_sha256 $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) Write-Verbose -Message "Body is $(ConvertTo-Json -InputObject $body)" $result = Submit-Request -uri $uri -header $script:SEPCloudConnection.header -method $($resources.Method) -body $body $result = Test-ReturnFormat -result $result -location $resources.Result $result = Set-ObjectTypeName -TypeName $resources.ObjectTName -result $result return $result } } #EndRegion '.\Public\Get-SEPCloudThreatIntelFileRelated.ps1' 63 #Region '.\Public\Get-SEPCloudThreatIntelNetworkInsight.ps1' 0 function Get-SEPCloudThreatIntelNetworkInsight { <# .SYNOPSIS Provide domain insight enrichments for a given domain .DESCRIPTION Provide domain insight enrichments for a given domain .INPUTS domain .OUTPUTS PSObject .LINK https://github.com/Douda/PSSymantecCloud .PARAMETER domain Specify one or many domain .EXAMPLE PS C:\PSSymantecCloud> Get-SepThreatIntelNetworkInsight -domain "elblogdeloscachanillas.com.mx/s3sy8rq10/ophn.png" network : elblogdeloscachanillas.com.mx/s3sy8rq10/ophn.png threatRiskLevel : @{level=10} categorization : @{categories=System.Object[]} reputation : BAD targetOrgs : @{topCountries=System.Object[]; topIndustries=System.Object[]} .EXAMPLE "elblogdeloscachanillas.com.mx/s3sy8rq10/ophn.png" | Get-SepThreatIntelNetworkInsight #> [CmdletBinding()] Param( # Mandatory domain [Parameter( Mandatory, ValueFromPipeline = $true)] [Alias('URL')] $domain ) begin { # Check to ensure that a session to the SaaS exists and load the needed header data for authentication Test-SEPCloudConnection | Out-Null # API data references the name of the function # For convenience, that name is saved here to $function $function = $MyInvocation.MyCommand.Name # Retrieve all of the URI, method, body, query, result, and success details for the API endpoint Write-Verbose -Message "Gather API Data for $function" $resources = Get-SEPCLoudAPIData -endpoint $function Write-Verbose -Message "Load API data for $($resources.Function)" Write-Verbose -Message "Description: $($resources.Description)" } process { $uri = New-URIString -endpoint ($resources.URI) -id $domain $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) Write-Verbose -Message "Body is $(ConvertTo-Json -InputObject $body)" $result = Submit-Request -uri $uri -header $script:SEPCloudConnection.header -method $($resources.Method) -body $body $result = Test-ReturnFormat -result $result -location $resources.Result $result = Set-ObjectTypeName -TypeName $resources.ObjectTName -result $result return $result } } #EndRegion '.\Public\Get-SEPCloudThreatIntelNetworkInsight.ps1' 67 #Region '.\Public\Get-SEPCloudThreatIntelNetworkProtection.ps1' 0 function Get-SEPCloudThreatIntelNetworkProtection { <# .SYNOPSIS Provide information whether a given URL/domain has been blocked by any of Symantec technologies .DESCRIPTION Provide information whether a given URL/domain has been blocked by any of Symantec technologies. These technologies include Antivirus (AV), Intrusion Prevention System (IPS) and Behavioral Analysis & System Heuristics (BASH) .PARAMETER domain Specify one or many URL/domain to check .LINK https://github.com/Douda/PSSymantecCloud .OUTPUTS PSObject .EXAMPLE Get-SepThreatIntelNetworkProtection -domain nicolascoolman.eu Gathers information whether the URL/domain has been blocked by any of Symantec technologies .EXAMPLE "nicolascoolman.eu" | Get-SepThreatIntelNetworkProtection Gathers somains from pipeline by value whether the URLs/domains have been blocked by any of Symantec technologies #> [CmdletBinding()] param ( # Mandatory domain name [Parameter( Mandatory, ValueFromPipeline = $true)] [Alias('domain', 'url')] $network ) begin { # Check to ensure that a session to the SaaS exists and load the needed header data for authentication Test-SEPCloudConnection | Out-Null # API data references the name of the function # For convenience, that name is saved here to $function $function = $MyInvocation.MyCommand.Name # Retrieve all of the URI, method, body, query, result, and success details for the API endpoint Write-Verbose -Message "Gather API Data for $function" $resources = Get-SEPCLoudAPIData -endpoint $function Write-Verbose -Message "Load API data for $($resources.Function)" Write-Verbose -Message "Description: $($resources.Description)" } process { $uri = New-URIString -endpoint ($resources.URI) -id $network $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) Write-Verbose -Message "Body is $(ConvertTo-Json -InputObject $body)" $result = Submit-Request -uri $uri -header $script:SEPCloudConnection.header -method $($resources.Method) -body $body $result = Test-ReturnFormat -result $result -location $resources.Result $result = Set-ObjectTypeName -TypeName $resources.ObjectTName -result $result return $result } } #EndRegion '.\Public\Get-SEPCloudThreatIntelNetworkProtection.ps1' 62 #Region '.\Public\Get-SEPCloudToken.ps1' 0 function Get-SEPCloudToken { <# .SYNOPSIS Generates an authenticated Token from the SEP Cloud API .DESCRIPTION Gathers Bearer Token from the SEP Cloud console to interact with the authenticated API Securely stores credentials or valid token locally (By default on TEMP location) Connection information available here : https://sep.securitycloud.symantec.com/v2/integration/client-applications .PARAMETER clientId clientId parameter required to generate a token .PARAMETER secret secret parameter required in combinaison to clientId to generate a token .PARAMETER cacheOnly if set to $true, will only lookup for in-memory or local cache of token/credentials. Will not prompt for credentials or generate a new token. Usefful for automation. .INPUTS [string] clientId [string] secret .OUTPUTS [PSCustomObject] Token .EXAMPLE Get-SEPCloudToken .EXAMPLE Get-SEPCloudToken(clientId,secret) .EXAMPLE Get-SEPCloudToken -clientId "myclientid" -secret "mysecret" .NOTES Function logic - Test if token is already loaded in memory (and verify its validity) - Test locally stored encrypted token (and verify its validity) - Test if credentials is already loaded in memory to generate a token - Test locally stored encrypted Client/secret to generate a token - Requests Client/secret to generate token #> [CmdletBinding(DefaultParameterSetName = 'ClientIdSecret')] param ( # clientId from SEP Cloud Connection App [Parameter( ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ClientIdSecret' )] [string] $clientId, # secret from SEP Cloud Connection App [Parameter( ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ClientIdSecret' )] [string] $secret, # Unattended [switch] [Alias("unattended")] $cacheOnly ) # If -cacheOnly do not attempt to prompt for credentials for unattended mode if ($cacheOnly) { return $null } # Test if clientId and secret are provided to generate a token without testing for locally stored encrypted token/credentials if ($clientId -and $secret) { Write-Verbose -Message "clientId & secret provided - testing to generate a token" $encodedCreds = [convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(($clientId + ':' + $secret))) try { $params = @{ Uri = 'https://' + $script:SEPCloudConnection.baseURL + '/v1/oauth2/tokens' Method = 'POST' Headers = @{ Host = $script:SEPCloudConnection.baseURL Accept = "application/json" Authorization = "Basic " + $encodedCreds } useBasicParsing = $true } $Response = Invoke-RestMethod @params if ($null -ne $response) { # Cache the credentials Write-Verbose -Message "credentials valid - caching credentials : $($script:configuration.SEPCloudCredsPath)" $credentialsDirectory = Split-Path -Path $script:configuration.SEPCloudCredsPath -Parent if (-not (Test-Path -Path $credentialsDirectory)) { New-Item -ItemType Directory -Path $credentialsDirectory | Out-Null } $encodedCreds | Export-Clixml -Path $script:configuration.SEPCloudCredsPath $script:SEPCloudConnection.Credential = $encodedCreds # Cache the token Write-Verbose "credentials valid - returning valid Bearer token" $cachedToken = [PSCustomObject]@{ Token = $response.access_token Token_Type = $response.token_type Token_Bearer = $response.token_type.ToString() + " " + $response.access_token Expiration = (Get-Date).AddSeconds($response.expires_in) # token expiration is 3600s } $script:SEPCloudConnection.AccessToken = $cachedToken $tokenDirectory = Split-Path -Path $script:configuration.cachedTokenPath -Parent if (-not (Test-Path -Path $tokenDirectory)) { New-Item -ItemType Directory -Path $tokenDirectory | Out-Null } $cachedToken | Export-Clixml -Path $script:configuration.cachedTokenPath Write-Verbose -Message "stored valid token : $($script:configuration.cachedTokenPath)" return $cachedToken } } catch { $message = "Authentication error - Failed to gather token from locally stored credentials" $message = $message + "`n" + "Expected HTTP 200, got $($_.Exception.Response.StatusCode)" + "`n" $message = $message + "delete cached credentials" $message = $message + "`n" + "Error : $($_.Exception.Response.StatusCode) : $($_.Exception.Response.StatusDescription)" Write-Verbose -Message $message } } # Test if token already in memory if ($null -ne $script:SEPCloudConnection.AccessToken.access_token) { # Check if still valid if (Test-SEPCloudToken) { Write-Verbose -Message "Token in-memory is still valid" return $script:SEPCloudConnection.AccessToken } else { try { Remove-Item -Path $script:configuration.cachedTokenPath -ErrorAction SilentlyContinue } catch {} $script:SEPCloudConnection.AccessToken = $null } } # Test if token present on the disk if (Test-Path -Path $script:configuration.cachedTokenPath) { $cachedToken = Import-Clixml -Path $script:configuration.cachedTokenPath # Check if still valid if (Test-SEPCloudToken -token $cachedToken) { Write-Verbose "Token from disk is still valid" return $cachedToken } else { Write-Verbose -Message "Token from disk expired - deleting" try { Remove-Item -Path $script:configuration.cachedTokenPath -ErrorAction SilentlyContinue } catch {} $script:SEPCloudConnection.AccessToken = $null } } # Test if OAuth cred present in memory if ($script:SEPCloudConnection.Credential) { Write-Verbose -Message "credentials in-memory available - testing" try { $params = @{ Uri = 'https://' + $script:SEPCloudConnection.baseURL + '/v1/oauth2/tokens' Method = 'POST' Headers = @{ Host = $script:SEPCloudConnection.baseURL Accept = "application/json" Authorization = "Basic " + $script:SEPCloudConnection.Credential } } $response = Invoke-RestMethod @params if ($null -ne $response) { # Get the auth token from the response. Store it locally & in memory Write-Verbose -Message "credentials in-memory valid - returning valid token" $cachedToken = [PSCustomObject]@{ Token = $response.access_token Token_Type = $response.token_type Token_Bearer = $response.token_type + " " + $response.access_token Expiration = (Get-Date).AddSeconds($response.expires_in) # token expiration is 3600s } $cachedToken | Export-Clixml -Path $script:configuration.cachedTokenPath $script:SEPCloudConnection.AccessToken = $cachedToken Write-Verbose -Message "stored valid token : $($script:configuration.cachedTokenPath)" return $cachedToken } } catch { $message = "Authentication error - Failed to gather token from locally stored credentials" $message = $message + "`n" + "Expected HTTP 200, got $($_.Exception.Response.StatusCode)" + "`n" $message = $message + "delete cached credentials" $message = $message + "`n" + "Error : $($_.Exception.Response.StatusCode) : $($_.Exception.Response.StatusDescription)" Write-Verbose -Message $message $script:SEPCloudConnection.Credential = $null } } # Test if OAuth cred present on the disk if ((Test-Path -Path $script:configuration.SEPCloudCredsPath)) { Write-Verbose "credentials on disk available - testing" try { $params = @{ Uri = 'https://' + $script:SEPCloudConnection.baseURL + '/v1/oauth2/tokens' Method = 'POST' Headers = @{ Host = $script:SEPCloudConnection.baseURL Accept = "application/json" Authorization = "Basic " + (Import-Clixml -Path $script:configuration.SEPCloudCredsPath) } } $response = Invoke-RestMethod @params if ($null -ne $response) { # Get the auth token from the response. Store it locally & in memory Write-Verbose "credentials valid - returning valid token" $cachedToken = [PSCustomObject]@{ Token = $response.access_token Token_Type = $response.token_type Token_Bearer = $response.token_type + " " + $response.access_token Expiration = (Get-Date).AddSeconds($response.expires_in) # token expiration is 3600s } $cachedToken | Export-Clixml -Path $script:configuration.cachedTokenPath Write-Verbose -Message "stored valid token : $($script:configuration.cachedTokenPath)" $script:SEPCloudConnection.AccessToken = $cachedToken return $cachedToken } } catch { $message = "Authentication error - Failed to gather token from locally stored credentials" $message = $message + "`n" + "Expected HTTP 200, got $($_.Exception.Response.StatusCode)" + "`n" $message = $message + "delete cached credentials" $message = $message + "`n" + "Error : $($_.Exception.Response.StatusCode) : $($_.Exception.Response.StatusDescription)" Write-Verbose -Message $message } } # If no token nor OAuth creds available locally # Encode clientId and secret to create Basic Auth string # Authentication requires the following "Basic + encoded CliendID:Clientsecret" Write-Verbose -Message "testing authentication with client/secret provided" if ($clientID -eq "" -or $secret -eq "") { Write-Host "No local credentials found. Please provide clientId and secret to generate a token" $clientId = Read-Host -Prompt "Enter clientId" $secret = Read-Host -Prompt "Enter secret" # -MaskInput TODO removed maskedInput. Not working with PS 5.1 } $encodedCreds = [convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(($clientId + ':' + $secret))) try { $params = @{ Uri = 'https://' + $script:SEPCloudConnection.baseURL + '/v1/oauth2/tokens' Method = 'POST' Headers = @{ Host = $script:SEPCloudConnection.baseURL Accept = "application/json" Authorization = "Basic " + $encodedCreds } useBasicParsing = $true } $Response = Invoke-RestMethod @params if ($null -ne $response) { # Cache the credentials Write-Verbose -Message "credentials valid. caching credentials : $($script:configuration.SEPCloudCredsPath)" $credentialsDirectory = Split-Path -Path $script:configuration.SEPCloudCredsPath -Parent if (-not (Test-Path -Path $credentialsDirectory)) { New-Item -ItemType Directory -Path $credentialsDirectory | Out-Null } $encodedCreds | Export-Clixml -Path $script:configuration.SEPCloudCredsPath $script:SEPCloudConnection.Credential = $encodedCreds # Cache the token Write-Verbose "credentials valid - returning valid Bearer token" $cachedToken = [PSCustomObject]@{ Token = $response.access_token Token_Type = $response.token_type Token_Bearer = $response.token_type.ToString() + " " + $response.access_token Expiration = (Get-Date).AddSeconds($response.expires_in) # token expiration is 3600s } $script:SEPCloudConnection.AccessToken = $cachedToken $tokenDirectory = Split-Path -Path $script:configuration.cachedTokenPath -Parent if (-not (Test-Path -Path $tokenDirectory)) { New-Item -ItemType Directory -Path $tokenDirectory | Out-Null } $cachedToken | Export-Clixml -Path $script:configuration.cachedTokenPath Write-Verbose -Message "stored valid token : $($script:configuration.cachedTokenPath)" return $cachedToken } } catch { $message = "Authentication error - Failed to gather token from locally stored credentials" $message = $message + "`n" + "Expected HTTP 200, got $($_.Exception.Response.StatusCode)" + "`n" $message = $message + "delete cached credentials" $message = $message + "`n" + "Error : $($_.Exception.Response.StatusCode) : $($_.Exception.Response.StatusDescription)" Write-Verbose -Message $message } } #EndRegion '.\Public\Get-SEPCloudToken.ps1' 294 #Region '.\Public\Get-SEPThreatIntelFileProcessChain.ps1' 0 function Get-SEPCloudThreatIntelFileProcessChain { <# .SYNOPSIS Provide topK process lineage enrichment for the provided file sha256. .DESCRIPTION Provide topK process lineage enrichment for the provided file sha256. .INPUTS sha256 .LINK https://github.com/Douda/PSSymantecCloud .OUTPUTS PSObject .PARAMETER file_sha256 Specify one or many sha256 hash .EXAMPLE PS C:\PSSymantecCloud> $ProcessChain = Get-SEPCloudThreatIntelFileProcessChain -file_sha256 eec3f761f7eabe9ed569f39e896be24c9bbb8861b15dbde1b3d539505cd9dd8d file chain ---- ----- eec3f761f7eabe9ed569f39e896be24c9bbb8861b15dbde1b3d539505cd9dd8d {@{parent=}} PS C:\PSSymantecCloud> $ProcessChain.chain parent ------ @{parent=; file=18bba9ff311154415404e2fb16f3784e4c82b57ad110092ea5f9b76ed549e7cb; processName=fe392ea0a9f14s4dfeda8d9u0233a6ioq6e47a5n3.exe} .EXAMPLE "eec3f761f7eabe9ed569f39e896be24c9bbb8861b15dbde1b3d539505cd9dd8d" | Get-SEPCloudThreatIntelFileProcessChain #> [CmdletBinding()] Param( # Mandatory file sha256 [Parameter( Mandatory, ValueFromPipeline = $true)] [Alias('sha256')] $file_sha256 ) begin { # Check to ensure that a session to the SaaS exists and load the needed header data for authentication Test-SEPCloudConnection | Out-Null # API data references the name of the function # For convenience, that name is saved here to $function $function = $MyInvocation.MyCommand.Name # Retrieve all of the URI, method, body, query, result, and success details for the API endpoint Write-Verbose -Message "Gather API Data for $function" $resources = Get-SEPCLoudAPIData -endpoint $function Write-Verbose -Message "Load API data for $($resources.Function)" Write-Verbose -Message "Description: $($resources.Description)" } process { $uri = New-URIString -endpoint ($resources.URI) -id $file_sha256 $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) Write-Verbose -Message "Body is $(ConvertTo-Json -InputObject $body)" $result = Submit-Request -uri $uri -header $script:SEPCloudConnection.header -method $($resources.Method) -body $body $result = Test-ReturnFormat -result $result -location $resources.Result $result = Set-ObjectTypeName -TypeName $resources.ObjectTName -result $result return $result } } #EndRegion '.\Public\Get-SEPThreatIntelFileProcessChain.ps1' 72 #Region '.\Public\Move-SEPCloudDevice.ps1' 0 function Move-SEPCloudDevice { <# .SYNOPSIS Moves one or many devices to a different group .DESCRIPTION Moves one or many devices to a different group. Requires group ID and device ID. does not support device name or group name. You can use : Get-SEPCloudDevice to get the device ID Get-SEPCloudGroup to get the group ID .LINK https://github.com/Douda/PSSymantecCloud .PARAMETER GroupID The group ID to move the device to .PARAMETER deviceId The device ID to move can be an array of device ID's [NOTE] maximum of 200 devices per call None .EXAMPLE Move-SEPCloudDevice -GroupID "tqrSman3RyqFFd1EqLlZZA" -DeviceID "f3teVmApQlya8XJvEf-wpw" Move-SEPCloudDevice -GroupID "tqrSman3RyqFFd1EqLlZZA" -DeviceID "f3teVmApQlya8XJvEf-wpw" device_uid message status ---------- ------- ------ f3teVmApQlya8XJvEf-wpw Moved successfully MOVED Moves a device to a different group, returns the status of the move. .EXAMPLE $list = @('123', '456', '789') Move-SEPCloudDevice -groupId "I5tExK6hQfC-cnUXk1Siug" -deviceId $list Moves all devices from their identifier (here 123,456,789) to a group from a single API call #> [CmdletBinding()] Param( # Group ID [Parameter( ValueFromPipelineByPropertyName = $true )] $groupId, # Device ID [Parameter( ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true )] [Alias('device_uids')] [ValidateCount(1, 200)] [ValidateNotNullOrEmpty()] [String[]] $deviceId ) begin { # Check to ensure that a session to the SaaS exists and load the needed header data for authentication Test-SEPCloudConnection | Out-Null # API data references the name of the function # For convenience, that name is saved here to $function $function = $MyInvocation.MyCommand.Name # Retrieve all of the URI, method, body, query, result, and success details for the API endpoint Write-Verbose -Message "Gather API Data for $function" $resources = Get-SEPCLoudAPIData -endpoint $function Write-Verbose -Message "Load API data for $($resources.Function)" Write-Verbose -Message "Description: $($resources.Description)" } process { $uri = New-URIString -endpoint ($resources.URI) -id $groupId $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) $result = Submit-Request -uri $uri -header $script:SEPCloudConnection.header -method $($resources.Method) -body $body $result = Test-ReturnFormat -result $result -location $resources.Result $result = Set-ObjectTypeName -TypeName $resources.ObjectTName -result $result return $result } } #EndRegion '.\Public\Move-SEPCloudDevice.ps1' 83 #Region '.\Public\Remove-SEPCloudPolicy.ps1' 0 function Remove-SEPCloudPolicy { <# .SYNOPSIS Removes a SEP Cloud policy from a device group .DESCRIPTION Removes a SEP Cloud policy from a device group. Must include a specific location (also called policy target rule) .LINK https://github.com/Douda/PSSymantecCloud .PARAMETER policyName Name of the policy to apply .PARAMETER policyVersion Version of the policy to apply. If not provided, the latest version will be used .PARAMETER targetRules Alias: location Location (policy target rule) to apply the policy to If not provided, the default location will be used .PARAMETER deviceGroupId Device group ID to apply the policy to .OUTPUTS None .EXAMPLE Remove-SEPCloudPolicy -policyName "My Policy" -location "Default" -deviceGroupId "123456" Removes the latest version of the SEP Cloud policy named "My Policy" to the device group with ID "123456" at the location "Default" #> [CmdletBinding()] param ( $policyName, [Alias("version")] $policyVersion, [Alias("policy_uid")] $policyId, [Parameter(Mandatory = $true)] [Alias("target_rules")] [Alias("location")] [string[]] $targetRule = "Default", [Parameter(Mandatory = $true)] [Alias("device_group_ids")] [string[]] $deviceGroupId ) begin { # Check to ensure that a session to the SaaS exists and load the needed header data for authentication Test-SEPCloudConnection | Out-Null # API data references the name of the function # For convenience, that name is saved here to $function $function = $MyInvocation.MyCommand.Name # Retrieve all of the URI, method, body, query, result, and success details for the API endpoint Write-Verbose -Message "Gather API Data for $function" $resources = Get-SEPCLoudAPIData -endpoint $function Write-Verbose -Message "Load API data for $($resources.Function)" Write-Verbose -Message "Description: $($resources.Description)" } process { # changing "Content-Type" header specifically for this query, otherwise 415 : unsupported media type # $script:SEPCloudConnection.header += @{ 'Accept' = 'application/json' } if ($policyName -and ($null -eq $policyId)) { Write-Verbose -Message "Searching ID for $policyName" $policyId = (Get-SEPCloudPolicesSummary | Where-Object { $_.name -eq "$policyName" }).policy_uid } if ($null -eq $policyVersion ) { Write-Verbose -Message "No policy version provided, retrieving the latest version of $policyName" # By default the API returns the latest version of a policy $policyVersion = (Get-SEPCloudPolicesSummary | Where-Object { $_.name -eq "$policyName" }).policy_version } $id = @($policyId, $policyVersion) $uri = New-URIString -endpoint ($resources.URI) -id $id $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) $result = Submit-Request -uri $uri -header $script:SEPCloudConnection.header -method $($resources.Method) -body $body $result = Test-ReturnFormat -result $result -location $resources.Result $result = Set-ObjectTypeName -TypeName $resources.ObjectTName -result $result # Removing "Content-Type: application/json" header # $script:SEPCloudConnection.header.remove('Content-Type') return $result } } #EndRegion '.\Public\Remove-SEPCloudPolicy.ps1' 93 #Region '.\Public\Set-SEPCloudPolicy.ps1' 0 function Set-SEPCloudPolicy { <# .SYNOPSIS Apply a SEP Cloud policy to a device group .DESCRIPTION Apply a SEP Cloud policy to a device group. Must include a specific location (also called policy target rule) .LINK https://github.com/Douda/PSSymantecCloud .PARAMETER policyName Name of the policy to apply .PARAMETER policyId Id of the policy to apply If not provided, Id will be pulled from the policy name .PARAMETER policyVersion Version of the policy to apply. If not provided, the latest version will be used by default .PARAMETER target_rules Alias: location Location (policy target rule) to apply the policy to. If not provided, the default location will be used .PARAMETER device_group_ids Device group ID to apply the policy to .EXAMPLE Set-SEPCloudPolicy -policyName "My Policy" -location "Default" -deviceGroupID "123456" Apply the latest version of the SEP Cloud policy named "My Policy" to the device group with ID "123456" at the location "Default" #> [CmdletBinding(DefaultParameterSetName = "byName")] Param( [Parameter(ParameterSetName = "byName")] $policyName, [Alias("policy_uid")] $policyId, [Parameter(ParameterSetName = "byId")] [Alias("version")] $policyVersion, [Alias("location")] [Alias("targetRule")] [string[]] $target_rules = "Default", [Alias("deviceGroupId")] [string[]] $device_group_ids, $override = $true ) begin { # Check to ensure that a session to the SaaS exists and load the needed header data for authentication Test-SEPCloudConnection | Out-Null # API data references the name of the function # For convenience, that name is saved here to $function $function = $MyInvocation.MyCommand.Name # Retrieve all of the URI, method, body, query, result, and success details for the API endpoint Write-Verbose -Message "Gather API Data for $function" $resources = Get-SEPCLoudAPIData -endpoint $function Write-Verbose -Message "Load API data for $($resources.Function)" Write-Verbose -Message "Description: $($resources.Description)" } process { if ($policyName -and ($null -eq $policyId)) { Write-Verbose -Message "Searching ID for $policyName" $policyId = (Get-SEPCloudPolicesSummary | Where-Object { $_.name -eq "$policyName" }).policy_uid } if ($null -eq $policyVersion ) { Write-Verbose -Message "No policy version provided, retrieving the latest version of $policyName" # By default the API returns the latest version of a policy $policyVersion = (Get-SEPCloudPolicesSummary | Where-Object { $_.name -eq "$policyName" }).policy_version } # Mandatory field for the API call # $override = $true $id = @($policyId, $policyVersion) $uri = New-URIString -endpoint ($resources.URI) -id $id $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) $result = Submit-Request -uri $uri -header $script:SEPCloudConnection.header -method $($resources.Method) -body $body $result = Test-ReturnFormat -result $result -location $resources.Result $result = Set-ObjectTypeName -TypeName $resources.ObjectTName -result $result return $result } } #EndRegion '.\Public\Set-SEPCloudPolicy.ps1' 93 #Region '.\Public\Start-SEPCloudDefinitionUpdate.ps1' 0 function Start-SEPCloudDefinitionUpdate { <# .SYNOPSIS Initiate a definition update request command on SEP Cloud managed devices .DESCRIPTION Initiate a definition update request command on SEP Cloud managed devices .PARAMETER device_ids Array of device ids for which to initiate a definition update request .EXAMPLE Start-SepCloudDefinitionUpdate -deviceId "u7IcxqPvQKmH47MPinPsFw" .LINK https://github.com/Douda/PSSymantecCloud #> [CmdletBinding()] Param( [Alias('deviceId')] [string[]] $device_ids, [string[]] [Alias('orgId')] $org_unit_ids, [Alias('recursive')] [switch] $is_recursive ) begin { # Check to ensure that a session to the SaaS exists and load the needed header data for authentication Test-SEPCloudConnection | Out-Null # API data references the name of the function # For convenience, that name is saved here to $function $function = $MyInvocation.MyCommand.Name # Retrieve all of the URI, method, body, query, result, and success details for the API endpoint Write-Verbose -Message "Gather API Data for $function" $resources = Get-SEPCLoudAPIData -endpoint $function Write-Verbose -Message "Load API data for $($resources.Function)" Write-Verbose -Message "Description: $($resources.Description)" } process { # TODO function is not working (500 error response) # changing "Content-Type" header specifically for this query, otherwise 415 : unsupported media type # $script:SEPCloudConnection.header += @{ 'Content-Type' = 'application/json' } $script:SEPCloudConnection.header += @{ 'Accept' = 'application/json' } $uri = New-URIString -endpoint ($resources.URI) -id $id $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) $result = Submit-Request -uri $uri -header $script:SEPCloudConnection.header -method $($resources.Method) -body $body $result = Test-ReturnFormat -result $result -location $resources.Result $result = Set-ObjectTypeName -TypeName $resources.ObjectTName -result $result # Removing "Content-Type: application/json" header $script:SEPCloudConnection.header.remove('Accept') return $result } } #EndRegion '.\Public\Start-SEPCloudDefinitionUpdate.ps1' 65 #Region '.\Public\Start-SEPCloudFullScan.ps1' 0 function Start-SEPCloudFullScan { <# .SYNOPSIS Initiate a full scan command on SEP Cloud managed devices .DESCRIPTION Initiate a full scan command on SEP Cloud managed devices Currently only takes a device_id as parameter device ID can be gathered from Get-SEPCloudDevice .EXAMPLE Start-SEPCloudFullScan -device_ids "u7IcxqPvQKmH47MPinPsFw" #> [CmdletBinding()] Param( [Alias('deviceId')] [string[]] $device_ids, [string[]] [Alias('orgId')] $org_unit_ids, [Alias('recursive')] [switch] $is_recursive ) begin { # Check to ensure that a session to the SaaS exists and load the needed header data for authentication Test-SEPCloudConnection | Out-Null # API data references the name of the function # For convenience, that name is saved here to $function $function = $MyInvocation.MyCommand.Name # Retrieve all of the URI, method, body, query, result, and success details for the API endpoint Write-Verbose -Message "Gather API Data for $function" $resources = Get-SEPCLoudAPIData -endpoint $function Write-Verbose -Message "Load API data for $($resources.Function)" Write-Verbose -Message "Description: $($resources.Description)" } process { $uri = New-URIString -endpoint ($resources.URI) -id $id $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) $result = Submit-Request -uri $uri -header $script:SEPCloudConnection.header -method $($resources.Method) -body $body $result = Test-ReturnFormat -result $result -location $resources.Result $result = Set-ObjectTypeName -TypeName $resources.ObjectTName -result $result return $result } } #EndRegion '.\Public\Start-SEPCloudFullScan.ps1' 54 #Region '.\Public\zz_Initialize-SEPCloudConfiguration.ps1' 0 #################################### # Init script for the whole module # #################################### ## This is the initialization script for the module. It is invoked at the end of the module's ## prefix file as "zz_" to load this module at last. This is done to ensure that all other functions are first loaded ## This function should be private but will stay Public for the moment as it needs to be the last function to be loaded in the module ## TODO make this function private # Update the data types when loading the module Update-TypeData -PrependPath (Join-Path -Path $PSScriptRoot -ChildPath 'PSSymantecCloud.Types.ps1xml') # The credentials used to authenticate to the SES Cloud API [string] $script:Credential = $null # string type used as credentials is OAuth2 token # The session-cached copy of the module's configuration properties # Configuration contains user-defined properties # SEPCloudConnection contains the connection information to the SES Cloud API [PSCustomObject] $script:configuration = $null [PSCustomObject] $script:SEPCloudConnection = [PSCustomObject]@{ BaseURL = "api.sep.eu.securitycloud.symantec.com" Credential = $null AccessToken = $null time = (Get-Date) header = $null } # Module name [string] $script:ModuleName = "PSSymantecCloud" # Load the configuration file $script:configuration = [PSCustomObject]@{ BaseURL = "api.sep.securitycloud.symantec.com" SEPCloudCredsPath = [System.IO.Path]::Combine( [System.Environment]::GetFolderPath('LocalApplicationData'), 'PSSymantecCloud', 'creds.xml') CachedTokenPath = [System.IO.Path]::Combine( [System.Environment]::GetFolderPath('LocalApplicationData'), 'PSSymantecCloud', 'accessToken.xml') } function Initialize-SEPCloudConfiguration { <# .SYNOPSIS Populates the configuration of the module for this session, loading in any values that may have been saved to disk. .DESCRIPTION Populates the configuration of the module for this session, loading in any values that may have been saved to disk. .NOTES Internal helper method. This is actually invoked at the END of this file. #> [CmdletBinding()] param() # Load credential from disk if it exists if (Test-Path -Path $($script:configuration.SEPCloudCredsPath)) { try { Write-Verbose -Message "Loading credential from $($script:configuration.SEPCloudCredsPath)" $script:Credential = Import-Clixml -Path $($script:configuration.SEPCloudCredsPath) -ErrorAction SilentlyContinue $script:SEPCloudConnection.Credential = Import-Clixml -Path $($script:configuration.SEPCloudCredsPath) -ErrorAction SilentlyContinue } catch { Write-Verbose "No credentials found from $($script:configuration.SEPCloudCredsPath)" } } # Load access token from disk if (Test-Path -Path $($script:configuration.CachedTokenPath)) { try { Write-Verbose -Message "Loading access token from $($script:configuration.CachedTokenPath)" Add-Member -Type NoteProperty -Name AccessToken -Value (Import-Clixml -Path $($script:configuration.CachedTokenPath) -ErrorAction SilentlyContinue) -InputObject $SEPCloudConnection -Force } catch { Write-Verbose -Message "Failed to import access token from $($script:configuration.CachedTokenPath): $_" -Verbose } } # Test for existing access token and refresh token if (Test-SEPCloudToken) { # Load headers if access token exists $UserAgentString = New-UserAgentString $script:SEPCloudConnection.Header = @{ 'Authorization' = $script:SEPCloudConnection.AccessToken.Token_Bearer 'User-Agent' = $UserAgentString } } # Attempt to connect to the SaaS with cached token or credentials # Will only attempt to connect via cached method (token or credentials) and not prompt for credentials Connect-SEPCloud -cacheOnly } # Invoke the initialization method to populate the configuration Initialize-SEPCloudConfiguration #-Verbose #TODO remove verbose when done testing #EndRegion '.\Public\zz_Initialize-SEPCloudConfiguration.ps1' 99 |