@@ -536,11 +536,79 @@ function Invoke-Falcon {
536
536
[string ]$HostUrl
537
537
)
538
538
begin {
539
+ function Invoke-Loop ([hashtable ]$Splat , [object ]$Object , [int ]$Int ) {
540
+ do {
541
+ # Determine next offset value
542
+ [string []]$Next = if ($Object.after ) {
543
+ @ (' after' , $Object.after )
544
+ } elseif ($Object.next_token ) {
545
+ @ (' next_token' , $Object.next_token )
546
+ } elseif ($null -ne $Object.offset ) {
547
+ $Value = if ($Object.offset -match ' ^\d{1,}$' ) { $Int } else { $Object.offset }
548
+ @ (' offset' , $Value )
549
+ }
550
+ if ($Next ) {
551
+ # Clone parameters and make request
552
+ $Clone = $Splat.Clone ()
553
+ $Clone.Endpoint = $Splat.Endpoint.Clone ()
554
+ $Clone.Endpoint.Path = if ($Clone.Endpoint.Path -match " $ ( $Next [0 ]) =\d{1,}" ) {
555
+ # If offset was input, continue from that value
556
+ $Current = [regex ]::Match($Clone.Endpoint.Path , ' offset=(\d+)(^&)?' ).Captures.Value
557
+ $Next [1 ] += [int ]$Current.Split (' =' )[-1 ]
558
+ $Clone.Endpoint.Path -replace $Current , ($Next -join ' =' )
559
+ } elseif ($Clone.Endpoint.Path -match " $ ( $Splat.Endpoint ) ^" -and
560
+ $Clone.Endpoint.Path -notmatch ' \?' ) {
561
+ # Add pagination
562
+ $Clone.Endpoint.Path , ($Next -join ' =' ) -join ' ?'
563
+ } else {
564
+ # Append pagination
565
+ $Clone.Endpoint.Path , ($Next -join ' =' ) -join ' &'
566
+ }
567
+ if ($Script :Falcon.Expiration -le (Get-Date ).AddSeconds(60 )) { Request-FalconToken }
568
+ $Script :Falcon.Api.Invoke ($Clone.Endpoint ) | ForEach-Object {
569
+ if ($_.Result.Content ) {
570
+ # Output result, update pagination and received count
571
+ $Object = (ConvertFrom-Json (
572
+ $_.Result.Content ).ReadAsStringAsync().Result).meta.pagination
573
+ Write-Request $Clone $_ - OutVariable Output
574
+ [int ]$Int += ($Output | Measure-Object ).Count
575
+ if ($Object.total ) {
576
+ Write-Verbose " [Invoke-Falcon] Retrieved $Int of $ ( $Object.total ) "
577
+ }
578
+ } elseif ($Object.total ) {
579
+ [string ]$Message = " [Invoke-Falcon] Total results limited by API '$ (
580
+ ($Clone.Endpoint.Path ).Split(' ?' )[0 ] -replace $Script :Falcon.Hostname ,
581
+ $null ) ' ($Int of $ ( $Object.total ) )."
582
+ Write-Error $Message
583
+ }
584
+ }
585
+ }
586
+ } while ( $Object.total -and $Int -lt $Object.total )
587
+ }
588
+ function Write-Request {
589
+ [CmdletBinding ()]
590
+ param (
591
+ [hashtable ]$Splat ,
592
+ [object ]$Object
593
+ )
594
+ [boolean ]$NoDetail = if ($Splat.Endpoint.Path -match ' (/combined/|/rule-groups-full/)' ) {
595
+ # Determine if endpoint requires a secondary 'Detailed' request
596
+ $true
597
+ } else {
598
+ $false
599
+ }
600
+ if ($Splat.Detailed -eq $true -and $NoDetail -eq $false ) {
601
+ $Output = Write-Result $Object
602
+ if ($Output ) { & $Command - Id $Output }
603
+ } else {
604
+ Write-Result $Object
605
+ }
606
+ }
539
607
if (! $Script :Falcon.Api.Client.DefaultRequestHeaders.Authorization -or ! $Script :Falcon.Hostname ) {
540
- # Request initial authorization token
608
+ # Force initial authorization token request
541
609
Request-FalconToken
542
610
}
543
- # Gather parameters for 'Get-ParamSet'
611
+ # Gather request parameters and split into groups
544
612
$GetParam = @ {}
545
613
$PSBoundParameters.GetEnumerator ().Where ({ $_.Key -notmatch ' ^(Command|RawOutput)$' }).foreach {
546
614
$GetParam.Add ($_.Key , $_.Value )
@@ -581,120 +649,47 @@ function Invoke-Falcon {
581
649
$_.Name -eq $Endpoint }).Parameters.Where({ $_.Name -eq ' Limit' }).Attributes.MaxRange
582
650
if ($Limit ) { $Inputs.Add (' Limit' , $Limit ) }
583
651
}
584
- # Regex for URL paths that don't need a secondary 'Detailed' request
585
- [regex ]$NoDetail = ' (/combined/|/rule-groups-full/)'
586
652
}
587
653
process {
588
- foreach ($Set in (Get-ParamSet @GetParam )) {
589
- [string ]$Operation = $Set.Endpoint.Method.ToUpper ()
590
- [string ]$Target = New-ShouldMessage $Set.Endpoint
591
- try {
592
- # Refresh authorization token during loop
654
+ Get-ParamSet @GetParam | ForEach-Object {
655
+ [string ]$Operation = $_.Endpoint.Method.ToUpper ()
656
+ [string ]$Target = New-ShouldMessage $_.Endpoint
657
+ if ($_.Endpoint.Headers.ContentType -eq ' application/json' -and $_.Endpoint.Body ) {
658
+ # Convert body to Json
659
+ $_.Endpoint.Body = ConvertTo-Json $_.Endpoint.Body - Depth 32 - Compress
660
+ }
661
+ if ($PSCmdlet.ShouldProcess ($Target , $Operation )) {
593
662
if ($Script :Falcon.Expiration -le (Get-Date ).AddSeconds(60 )) { Request-FalconToken }
594
- if ($Set.Endpoint.Headers.ContentType -eq ' application/json' -and $Set.Endpoint.Body ) {
595
- # Convert body to Json
596
- $Set.Endpoint.Body = ConvertTo-Json $Set.Endpoint.Body - Depth 32 - Compress
597
- }
598
- $Request = if ($PSCmdlet.ShouldProcess ($Target , $Operation )) {
599
- $Script :Falcon.Api.Invoke ($Set.Endpoint )
600
- }
601
- if ($RawOutput ) {
602
- # Return result if 'RawOutput' is defined
603
- $Request
604
- } elseif ($Set.Endpoint.Outfile -and (Test-Path $Set.Endpoint.Outfile )) {
605
- # Display 'Outfile'
606
- Get-ChildItem $Set.Endpoint.Outfile | Select-Object FullName, Length, LastWriteTime
607
- } elseif ($Request.Result.Content ) {
608
- # Capture pagination for 'Total' and 'All'
609
- $Pagination = (ConvertFrom-Json (
610
- $Request.Result.Content ).ReadAsStringAsync().Result).meta.pagination
611
- if ($Set.Total -eq $true -and $Pagination ) {
612
- # Output 'Total'
613
- $Pagination.total
614
- } else {
615
- $Result = Write-Result $Request
616
- if ($null -ne $Result ) {
617
- if ($Set.Detailed -eq $true -and $Set.Endpoint.Path -notmatch $NoDetail ) {
618
- # Output 'Detailed'
619
- & $Command - Id $Result
620
- } else {
621
- # Output result
622
- $Result
623
- }
624
- if ($Set.All -eq $true -and ($Result | Measure-Object ).Count -lt
625
- $Pagination.total ) {
663
+ try {
664
+ $Request = $Script :Falcon.Api.Invoke ($_.Endpoint )
665
+ if ($_.Endpoint.Outfile -and (Test-Path $_.Endpoint.Outfile )) {
666
+ # Display 'Outfile'
667
+ Get-ChildItem $_.Endpoint.Outfile | Select-Object FullName, Length, LastWriteTime
668
+ } elseif ($Request -and $RawOutput ) {
669
+ # Return result if 'RawOutput' is defined
670
+ $Request
671
+ } elseif ($Request.Result.Content ) {
672
+ # Capture pagination for 'Total' and 'All'
673
+ $Pagination = (ConvertFrom-Json (
674
+ $Request.Result.Content ).ReadAsStringAsync().Result).meta.pagination
675
+ if ($Pagination.total -and $_.Total -eq $true ) {
676
+ # Output 'Total'
677
+ $Pagination.total
678
+ } else {
679
+ Write-Request $_ $Request - OutVariable Result
680
+ if ($Result -and $_.All -eq $true ) {
626
681
# Repeat request(s)
627
- Invoke-Loop $Set $Pagination $Result
682
+ [int ]$Count = ($Result | Measure-Object ).Count
683
+ if ($Pagination.total -and $Count -lt $Pagination.total ) {
684
+ Write-Verbose " [Invoke-Falcon] Retrieved $Count of $ ( $Pagination.total ) "
685
+ Invoke-Loop $_ $Pagination $Count
686
+ }
628
687
}
629
688
}
630
689
}
690
+ } catch {
691
+ Write-Error $_
631
692
}
632
- } catch {
633
- Write-Error $_
634
- }
635
- }
636
- }
637
- }
638
- function Invoke-Loop {
639
- [CmdletBinding ()]
640
- param (
641
- [Parameter (Mandatory )]
642
- [System.Collections.Hashtable ]$ParamSet ,
643
- [Parameter (Mandatory )]
644
- [System.Object ]$Pagination ,
645
- [Parameter (Mandatory )]
646
- [System.Object ]$Result
647
- )
648
- begin {
649
- # Regex for URL paths that don't need a secondary 'Detailed' request
650
- [regex ]$NoDetail = ' (/combined/|/rule-groups-full/)'
651
- }
652
- process {
653
- for ($i = ($Result | Measure-Object ).Count; $Pagination.next_page -or $i -lt $Pagination.total ;
654
- $i += ($Result | Measure-Object ).Count) {
655
- Write-Verbose " [Invoke-Loop] $i of $ ( $Pagination.total ) "
656
- # Clone endpoint parameters and update pagination
657
- $Clone = $ParamSet.Clone ()
658
- $Clone.Endpoint = $ParamSet.Endpoint.Clone ()
659
- $Page = if ($Pagination.after ) {
660
- @ (' after' , $Pagination.after )
661
- } elseif ($Pagination.next_token ) {
662
- @ (' next_token' , $Pagination.next_token )
663
- } elseif ($Pagination.next_page ) {
664
- @ (' offset' , $Pagination.offset )
665
- } elseif ($Pagination.offset -match ' ^\d{1,}$' ) {
666
- @ (' offset' , $i )
667
- } else {
668
- @ (' offset' , $Pagination.offset )
669
- }
670
- $Clone.Endpoint.Path = if ($Clone.Endpoint.Path -match " $ ( $Page [0 ]) =\d{1,}" ) {
671
- # If offset was input, continue from that value
672
- $Current = [regex ]::Match($Clone.Endpoint.Path , ' offset=(\d+)(^&)?' ).Captures.Value
673
- $Page [1 ] += [int ]$Current.Split (' =' )[-1 ]
674
- $Clone.Endpoint.Path -replace $Current , ($Page -join ' =' )
675
- } elseif ($Clone.Endpoint.Path -match " $Endpoint ^" -and $Clone.Endpoint.Path -notmatch ' \?' ) {
676
- # Add pagination
677
- $Clone.Endpoint.Path , ($Page -join ' =' ) -join ' ?'
678
- } else {
679
- # Update pagination
680
- $Clone.Endpoint.Path , ($Page -join ' =' ) -join ' &'
681
- }
682
- $Request = $Script :Falcon.Api.Invoke ($Clone.Endpoint )
683
- if ($Request.Result.Content ) {
684
- $Result = Write-Result $Request
685
- if ($null -ne $Result ) {
686
- if ($Clone.Detailed -eq $true -and $Clone.Endpoint.Path -notmatch $NoDetail ) {
687
- & $Command - Id $Result
688
- } else {
689
- $Result
690
- }
691
- } else {
692
- [string ]$Message = " [Invoke-Loop] Results limited by API '$ ( ($Clone.Endpoint.Path ).Split(
693
- ' ?' )[0 ] -replace $Script :Falcon.Hostname , $null ) ' ($i of $ ( $Pagination.total ) )."
694
- Write-Error $Message
695
- }
696
- $Pagination = (ConvertFrom-Json (
697
- $Request.Result.Content ).ReadAsStringAsync().Result).meta.pagination
698
693
}
699
694
}
700
695
}
@@ -714,31 +709,25 @@ function New-ShouldMessage {
714
709
$Path = $Path -replace $Script :Falcon.Hostname , $null
715
710
}
716
711
if ($Path -match ' \?' ) {
717
- # Add 'Path' without query values
712
+ # Add 'Path' without query values, and 'Query' as an array
718
713
[string []]$Array = $Path -split ' \?'
719
714
[string []]$Query = $Array [-1 ] -split ' &'
720
715
Set-Property $Output Path $Array [0 ]
716
+ if ($Query ) { Set-Property $Output Query $Query }
721
717
} else {
722
718
Set-Property $Output Path $Path
723
719
}
724
720
}
725
721
if ($Object.Headers ) {
726
722
# Add 'Headers' value
727
- Set-Property $Output Headers ($Object.Headers.GetEnumerator ().foreach {
723
+ [ string ] $Header = ($Object.Headers.GetEnumerator ().foreach {
728
724
$_.Key , $_.Value -join ' =' } -join ' , ' )
725
+ if ($Header ) { Set-Property $Output Headers $Header }
729
726
}
730
- if ($Query ) {
731
- # Add 'Query' value as an array
732
- Set-Property $Output Query $Query
733
- }
734
- foreach ($Pair in $Object.GetEnumerator ().Where ({ $_.Key -ne ' ^(Headers|Method|Path)$' })) {
735
- [string ]$Value = switch ($Pair.Key ) {
736
- ' Body' {
737
- # Convert 'Body' to Json
738
- $Pair.Value | ConvertTo-Json - Depth 8
739
- }
740
- }
741
- if ($Value ) { Set-Property $Output $Pair.Key $Value }
727
+ if ($Object.Body -and $Object.Headers.ContentType -eq ' application/json' ) {
728
+ # Add 'Body' value
729
+ [string ]$Body = try { $Object.Body | ConvertTo-Json - Depth 8 } catch {}
730
+ if ($Body ) { Set-Property $Output Body $Body }
742
731
}
743
732
" `r`n " , ($Output | Format-List | Out-String ).Trim(), " `r`n " -join " `r`n "
744
733
} catch {}
@@ -810,7 +799,8 @@ function Test-RegexValue {
810
799
$RegEx = @ {
811
800
md5 = [regex ]' ^[A-Fa-f0-9]{32}$'
812
801
sha256 = [regex ]' ^[A-Fa-f0-9]{64}$'
813
- ipv4 = [regex ]' ^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.'
802
+ ipv4 = [regex ](' ((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1' +
803
+ ' }[0-9])' )
814
804
ipv6 = [regex ]' ^[0-9a-fA-F]{1,4}:'
815
805
domain = [regex ]' ^(https?://)?((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}$'
816
806
email = [regex ]" ^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$"
@@ -832,7 +822,7 @@ function Test-RegexValue {
832
822
}
833
823
end {
834
824
if ($Output ) {
835
- Write-Verbose " [Test-RegexValue] $ ( @ ($Output , $String ) -join ' : ' ) "
825
+ Write-Verbose " [Test-RegexValue] $ ( @ ((( $Output | Out-String ).Trim()) , $String ) -join ' : ' ) "
836
826
$Output
837
827
}
838
828
}
0 commit comments