From 1fec7b17caa4fa5b43acf10e22aa6e98a8bcf12b Mon Sep 17 00:00:00 2001 From: Jonathan Butler Date: Fri, 29 Nov 2024 16:24:39 -0500 Subject: [PATCH 1/7] Uncomment validate.yml to run tests to see what tests are failing. --- .github/workflows/validate.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index dc1aa49..181e5ee 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -3,6 +3,7 @@ on: branches: - master - Development + - bugfix/157-bug-multiple-failed-tests-after-fixing-workflow push: branches: - master @@ -16,6 +17,6 @@ jobs: - name: Install Prerequisites run: .\build\vsts-prerequisites.ps1 shell: powershell - # - name: Validate - # run: .\build\vsts-validate.ps1 - # shell: powershell + - name: Validate + run: .\build\vsts-validate.ps1 + shell: powershell From 648aaff071124f8e4fe07158674386d9bbbb3551 Mon Sep 17 00:00:00 2001 From: Jonathan Butler Date: Fri, 29 Nov 2024 16:25:56 -0500 Subject: [PATCH 2/7] Uncomment validate.yml to run tests to see what tests are failing. --- .github/workflows/validate.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 181e5ee..2a4f171 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -3,11 +3,12 @@ on: branches: - master - Development - - bugfix/157-bug-multiple-failed-tests-after-fixing-workflow push: branches: - master - Development + - bugfix/157-bug-multiple-failed-tests-after-fixing-workflow + jobs: validate: runs-on: windows-2019 From e12627cd6234d23f959034f8bcbe173dfee89c7c Mon Sep 17 00:00:00 2001 From: Jonathan Butler Date: Fri, 29 Nov 2024 17:41:49 -0500 Subject: [PATCH 3/7] fix: Add ShouldProcess support to Start-HawkTenantInvestigation - Added CmdletBinding attribute with SupportsShouldProcess - Wrapped operations in ShouldProcess checks with descriptive messages - Added WhatIf example to help documentation - Fixed typos and inconsistent function names in comments - Fixed parameter placement in EDiscoveryLogs call - Addresses PSScriptAnalyzer warning about state-changing verbs Required for PowerShell standard practices when using Start verb. --- Hawk/Hawk.psd1 | 2 +- Hawk/changelog.md | 13 +- .../Tenant/Get-HawkTenantEDiscoveryLog.ps1 | 76 +++++++++++ .../Tenant/Get-HawkTenantEDiscoveryLogs.ps1 | 42 ------ .../Tenant/Search-HawkTenantEXOAuditLog.ps1 | 4 +- .../Tenant/Start-HawkTenantInvestigation.ps1 | 121 +++++++++++------- .../User/Get-HawkUserConfiguration.ps1 | 40 +++--- .../functions/Out-MultipleFileType.ps1 | 2 + .../functions/Test-GraphConnection.ps1 | 4 +- .../Invoke-PowerShellScriptAnalyzer.ps1 | 2 +- 10 files changed, 191 insertions(+), 115 deletions(-) create mode 100644 Hawk/functions/Tenant/Get-HawkTenantEDiscoveryLog.ps1 delete mode 100644 Hawk/functions/Tenant/Get-HawkTenantEDiscoveryLogs.ps1 diff --git a/Hawk/Hawk.psd1 b/Hawk/Hawk.psd1 index 154cbe1..39b0f42 100644 --- a/Hawk/Hawk.psd1 +++ b/Hawk/Hawk.psd1 @@ -80,7 +80,7 @@ 'Get-HawkTenantAppAndSPNCredentialDetails', 'Get-HawkTenantAzureADUsers', 'Get-HawkTenantDomainActivity', - 'Get-HawkTenantEDiscoveryLogs' + 'Get-HawkTenantEDiscoveryLog' # Cmdlets to export from this module # CmdletsToExport = '' diff --git a/Hawk/changelog.md b/Hawk/changelog.md index 6d1a63e..38d18dc 100644 --- a/Hawk/changelog.md +++ b/Hawk/changelog.md @@ -1,26 +1,32 @@ # Changelog ## 2.0.0 (2021-01-05) + - Initial Transmigrated Release with new owner + ## 2.0.1 (2021-02-07) + - Incorporated workflow and pester tests - Readme file updated with https://cloudforensicator.com link - Updated Azure AD SKU options that identity "Premium Licensing" - Issue #25 - Unified Audit Log AuditData JSON parsing added to "Exchange_UAL_Audit.csv" ## 2.0.2 (2021-05-05) + - Fixed Hidden Mailbox Rule EWS Credential - Updated Robust Cloud Command version to 2.0.1 - Updated Get-HawkTenantInboxRules.ps1 to new switch in update Robust Cloud Command - Deprecate "Get-HawkTenantAzureAuthenticationLogs" from Hawk. Azure AD Graph was deprecated and no longer supported. Currently -seeking alternate solution to retrieve Azure AD Sign-in logs. + seeking alternate solution to retrieve Azure AD Sign-in logs. - Removed dependency of Cloud Connect - Added dependency of Exchange Online Management V2 PowerShell module and updated functions to reflect ## 2.0.3.1 (2021-05-05) + - Fixed MSOnline Requirement to manifest ## 3.0.0 (2022-04-09) + - Updated community pull requests - Encoding to UTF8 - Enhancement - TakayukiTomatsuri - Updated $RangeEnd to datetime - Bug - cfc-zcarter @@ -29,10 +35,11 @@ seeking alternate solution to retrieve Azure AD Sign-in logs. - Updated Get-HawkTenantEXOAdmins to accurately list admins that is a group ## 3.1.0 (2023-03-30) + - Updated community pull requests fixing typo - Updated Get-HawkTenantAuditLog.ps1 to Get-HawkTenantAppAuditLog.ps1 - Added "Get-HawkTenantDomainActivity" function - This function will pull domain config changes from the UAL -- Added "Get-HawkTenantEDiscoveryLogs" function - This function will pull EDiscovery logs from the UAL +- Added "Get-HawkTenantEDiscoveryLog" function - This function will pull EDiscovery logs from the UAL - Added Export of JSON to "Out-Multifileype" function. This will export returned results to JSON file for further ingestion into a SIEM or other data analysis platform - Remove MSOnline requirements - Added MS Graph requirements to replace MSOnline @@ -46,4 +53,4 @@ seeking alternate solution to retrieve Azure AD Sign-in logs. - Removed Test-MSOnlineConnection.ps1 - MSOnline requirements have been removed from Hawk - Added logging filepath checking the Start-HawkUserInvestigation.ps1 - Updated Get-HawkTenantAZAdmins.ps1. Removed AzureAD module. Added MS Graph cmdlets. -- Updated contact email \ No newline at end of file +- Updated contact email diff --git a/Hawk/functions/Tenant/Get-HawkTenantEDiscoveryLog.ps1 b/Hawk/functions/Tenant/Get-HawkTenantEDiscoveryLog.ps1 new file mode 100644 index 0000000..d37004b --- /dev/null +++ b/Hawk/functions/Tenant/Get-HawkTenantEDiscoveryLog.ps1 @@ -0,0 +1,76 @@ +Function Get-HawkTenantEDiscoveryLog { + <# + .SYNOPSIS + Gets Unified Audit Logs (UAL) data for eDiscovery + .DESCRIPTION + Searches the Unified Audit Log (UAL) for eDiscovery events and activities. + This includes searches, exports, and management activities related to + eDiscovery cases. The function checks for any eDiscovery activities within + the timeframe specified in the Hawk global configuration object. + + The results can help identify: + * When eDiscovery searches were performed + * Who performed eDiscovery activities + * Which cases were accessed or modified + * What operations were performed + + .EXAMPLE + Get-HawkTenantEDiscoveryLog + + This will search for all eDiscovery-related activities in the Unified Audit Log + for the configured time period and export the results to CSV format. + + .EXAMPLE + $logs = Get-HawkTenantEDiscoveryLog + $logs | Where-Object {$_.Operation -eq "SearchCreated"} + + This example shows how to retrieve eDiscovery logs and filter for specific + operations like new search creation. + + .OUTPUTS + File: eDiscoveryLogs.csv + Path: \Tenant + Description: Contains all eDiscovery activities found in the UAL with fields for: + - CreationTime: When the activity occurred + - Id: Unique identifier for the activity + - Operation: Type of eDiscovery action performed + - Workload: The workload where the activity occurred + - UserID: User who performed the action + - Case: eDiscovery case name + - CaseId: Unique identifier for the eDiscovery case + - Cmdlet: Command that was executed (if applicable) + #> + # Search UAL audit logs for any Domain configuration changes + Test-EXOConnection + Send-AIEvent -Event "CmdRun" + + Out-LogFile "Gathering any eDiscovery logs" -action + + # Search UAL audit logs for any Domain configuration changes + $eDiscoveryLogs = Get-AllUnifiedAuditLogEntry -UnifiedSearch ("Search-UnifiedAuditLog -RecordType 'Discovery'") + # If null we found no changes to nothing to do here + if ($null -eq $eDiscoveryLogs) { + Out-LogFile "No eDiscovery Logs found" + } + + # If not null then we must have found some events so flag them + else { + Out-LogFile "eDiscovery Log have been found." -Notice + Out-LogFile "Please review these eDiscoveryLogs.csv to validate the activity is legitimate." -Notice + # Go thru each even and prepare it to output to CSV + Foreach ($log in $eDiscoveryLogs) { + $log1 = $log.auditdata | ConvertFrom-Json + $report = $log1 | Select-Object -Property CreationTime, + Id, + Operation, + Workload, + UserID, + Case, + @{Name = 'CaseID'; Expression = { ($_.ExtendedProperties | Where-Object { $_.Name -eq 'CaseId' }).value } }, + @{Name = 'Cmdlet'; Expression = { ($_.Parameters | Where-Object { $_.Name -eq 'Cmdlet' }).value } } + + $report | Out-MultipleFileType -fileprefix "eDiscoveryLogs" -csv -append + } + + } +} diff --git a/Hawk/functions/Tenant/Get-HawkTenantEDiscoveryLogs.ps1 b/Hawk/functions/Tenant/Get-HawkTenantEDiscoveryLogs.ps1 deleted file mode 100644 index b3126d2..0000000 --- a/Hawk/functions/Tenant/Get-HawkTenantEDiscoveryLogs.ps1 +++ /dev/null @@ -1,42 +0,0 @@ -Function Get-HawkTenantEDiscoveryLogs{ - <# - .SYNOPSIS - Gets Unified Audit Logs (UAL) data for eDiscovery - .DESCRIPTION - Searches the UAL for eDiscovery events - - #> - # Search UAL audit logs for any Domain configuration changes - Test-EXOConnection - Send-AIEvent -Event "CmdRun" - - Out-LogFile "Gathering any eDiscovery logs" -action - - # Search UAL audit logs for any Domain configuration changes - $eDiscoveryLogs = Get-AllUnifiedAuditLogEntry -UnifiedSearch ("Search-UnifiedAuditLog -RecordType 'Discovery'") - # If null we found no changes to nothing to do here - if ($null -eq $eDiscoveryLogs){ - Out-LogFile "No eDiscovery Logs found" - } - - # If not null then we must have found some events so flag them - else { - Out-LogFile "eDiscovery Log have been found." -Notice - Out-LogFile "Please review these eDiscoveryLogs.csv to validate the activity is legitimate." -Notice - # Go thru each even and prepare it to output to CSV - Foreach ($log in $eDiscoveryLogs){ - $log1 = $log.auditdata | ConvertFrom-Json - $report = $log1 | Select-Object -Property CreationTime, - Id, - Operation, - Workload, - UserID, - Case, - @{Name='CaseID';Expression={($_.ExtendedProperties | Where-Object {$_.Name -eq 'CaseId'}).value}}, - @{Name='Cmdlet';Expression={($_.Parameters | Where-Object {$_.Name -eq 'Cmdlet'}).value}} - - $report | Out-MultipleFileType -fileprefix "eDiscoveryLogs" -csv -append - } - - } - } diff --git a/Hawk/functions/Tenant/Search-HawkTenantEXOAuditLog.ps1 b/Hawk/functions/Tenant/Search-HawkTenantEXOAuditLog.ps1 index 6469aff..1470cb4 100644 --- a/Hawk/functions/Tenant/Search-HawkTenantEXOAuditLog.ps1 +++ b/Hawk/functions/Tenant/Search-HawkTenantEXOAuditLog.ps1 @@ -1,5 +1,5 @@ -Function Search-HawkTenantEXOAuditLog { -<# +Function Search-HawkTenantEXOAuditLog { + <# .SYNOPSIS Searches the admin audit logs for possible bad actor activities .DESCRIPTION diff --git a/Hawk/functions/Tenant/Start-HawkTenantInvestigation.ps1 b/Hawk/functions/Tenant/Start-HawkTenantInvestigation.ps1 index cb01a1c..c6abe33 100644 --- a/Hawk/functions/Tenant/Start-HawkTenantInvestigation.ps1 +++ b/Hawk/functions/Tenant/Start-HawkTenantInvestigation.ps1 @@ -1,24 +1,32 @@ Function Start-HawkTenantInvestigation { -<# -.SYNOPSIS - Gathers common data about a tenant. -.DESCRIPTION - Runs all Hawk Basic tenant related cmdlets and gathers the data. - - Cmdlet Information Gathered - ------------------------- ------------------------- - Get-HawkTenantConfigurationn Basic Tenant information - Get-HawkTenantEDiscoveryConfiguration Looks for changes to ediscovery configuration - Search-HawkTenantEXOAuditLog Searches the EXO audit log for activity - Get-HawkTenantRBACChanges Looks for changes to Roles Based Access Control -.OUTPUTS - See help from individual cmdlets for output list. - All outputs are placed in the $Hawk.FilePath directory -.EXAMPLE - Start-HawkTenantInvestigation - -R uns all of the tenant investigation cmdlets. -#> + [CmdletBinding(SupportsShouldProcess)] + param() + + <# + .SYNOPSIS + Gathers common data about a tenant. + .DESCRIPTION + Runs all Hawk Basic tenant related cmdlets and gathers the data. + + Cmdlet Information Gathered + ------------------------- ------------------------- + Get-HawkTenantConfigurationn Basic Tenant information + Get-HawkTenantEDiscoveryConfiguration Looks for changes to ediscovery configuration + Search-HawkTenantEXOAuditLog Searches the EXO audit log for activity + Get-HawkTenantRBACChanges Looks for changes to Roles Based Access Control + .OUTPUTS + See help from individual cmdlets for output list. + All outputs are placed in the $Hawk.FilePath directory + .EXAMPLE + Start-HawkTenantInvestigation + + Runs all of the tenant investigation cmdlets. + .EXAMPLE + Start-HawkTenantInvestigation -WhatIf + + Shows what actions would be performed without actually executing them. + #> + if ([string]::IsNullOrEmpty($Hawk.FilePath)) { Initialize-HawkGlobalObject } @@ -26,39 +34,64 @@ R uns all of the tenant investigation cmdlets. Out-LogFile "Starting Tenant Sweep" -action Send-AIEvent -Event "CmdRun" - Out-LogFile "Running Get-HawkTenantConfiguration" -action - Get-HawkTenantConfiguration + # Wrap operations in ShouldProcess checks + if ($PSCmdlet.ShouldProcess("Tenant Configuration", "Get configuration data")) { + Out-LogFile "Running Get-HawkTenantConfiguration" -action + Get-HawkTenantConfiguration + } - Out-LogFile "Running Get-HawkTenantEDiscoveryConfiguration" -action - Get-HawkTenantEDiscoveryConfiguration + if ($PSCmdlet.ShouldProcess("EDiscovery Configuration", "Get eDiscovery configuration")) { + Out-LogFile "Running Get-HawkTenantEDiscoveryConfiguration" -action + Get-HawkTenantEDiscoveryConfiguration + } - Out-LogFile "Running Search-HawkTenantEXOAuditLog" -action - Search-HawkTenantEXOAuditLog + if ($PSCmdlet.ShouldProcess("Exchange Audit Log", "Search audit logs")) { + Out-LogFile "Running Search-HawkTenantEXOAuditLog" -action + Search-HawkTenantEXOAuditLog + } - Out-LogFile "Running Get-HawkTenantEDiscoveryLogs" - Get-HawkTenantEDiscoveryLogs -action + if ($PSCmdlet.ShouldProcess("EDiscovery Logs", "Get eDiscovery logs")) { + Out-LogFile "Running Get-HawkTenantEDiscoveryLogs" -action + Get-HawkTenantEDiscoveryLogs + } - Out-LogFile "Running Get-HawkTenantDomainActivity" -action - Get-HawkTenantDomainActivity + if ($PSCmdlet.ShouldProcess("Domain Activity", "Get domain activity")) { + Out-LogFile "Running Get-HawkTenantDomainActivity" -action + Get-HawkTenantDomainActivity + } - Out-LogFile "Running Get-HawkTenantRBACChanges" -action - Get-HawkTenantRBACChanges + if ($PSCmdlet.ShouldProcess("RBAC Changes", "Get RBAC changes")) { + Out-LogFile "Running Get-HawkTenantRBACChanges" -action + Get-HawkTenantRBACChanges + } - Out-LogFile "Running Get-HawkTenantAzureAppAuditLog" -action - Get-HawkTenantAzureAppAuditLog + if ($PSCmdlet.ShouldProcess("Azure App Audit Log", "Get app audit logs")) { + Out-LogFile "Running Get-HawkTenantAzureAppAuditLog" -action + Get-HawkTenantAzureAppAuditLog + } - Out-LogFile "Running Get-HawkTenantEXOAdmins" -action - Get-HawkTenantEXOAdmins + if ($PSCmdlet.ShouldProcess("Exchange Admins", "Get Exchange admin list")) { + Out-LogFile "Running Get-HawkTenantEXOAdmins" -action + Get-HawkTenantEXOAdmins + } - Out-LogFile "Running Get-HawkTenantConsentGrants" -action - Get-HawkTenantConsentGrants + if ($PSCmdlet.ShouldProcess("Consent Grants", "Get consent grants")) { + Out-LogFile "Running Get-HawkTenantConsentGrants" -action + Get-HawkTenantConsentGrants + } - Out-LogFile "Running Get-HawkTenantAZAdmins" -action - Get-HawkTenantAZAdmins + if ($PSCmdlet.ShouldProcess("Azure Admins", "Get Azure admin list")) { + Out-LogFile "Running Get-HawkTenantAZAdmins" -action + Get-HawkTenantAZAdmins + } - Out-LogFile "Running Get-HawkTenantAppAndSPNCredentialDetails" -action - Get-HawkTenantAppAndSPNCredentialDetails + if ($PSCmdlet.ShouldProcess("App and SPN Credentials", "Get credential details")) { + Out-LogFile "Running Get-HawkTenantAppAndSPNCredentialDetails" -action + Get-HawkTenantAppAndSPNCredentialDetails + } - Out-Logfile "Running Get-HawkTenantAzureADUsers" -action - Get-HawkTenantAzureADUsers + if ($PSCmdlet.ShouldProcess("Azure AD Users", "Get Azure AD user list")) { + Out-LogFile "Running Get-HawkTenantAzureADUsers" -action + Get-HawkTenantAzureADUsers + } } \ No newline at end of file diff --git a/Hawk/functions/User/Get-HawkUserConfiguration.ps1 b/Hawk/functions/User/Get-HawkUserConfiguration.ps1 index c44e5ba..3a03173 100644 --- a/Hawk/functions/User/Get-HawkUserConfiguration.ps1 +++ b/Hawk/functions/User/Get-HawkUserConfiguration.ps1 @@ -1,5 +1,5 @@ -Function Get-HawkUserConfiguration { -<# +Function Get-HawkUserConfiguration { + <# .SYNOPSIS Gathers baseline information about the provided user. .DESCRIPTION @@ -38,29 +38,29 @@ Function Get-HawkUserConfiguration { Gathers the user configuration for all users who have "C-Level" set in CustomAttribute1 #> - param - ( - [Parameter(Mandatory = $true)] - [array]$UserPrincipalName - ) + param + ( + [Parameter(Mandatory = $true)] + [array]$UserPrincipalName + ) - Test-EXOConnection - Send-AIEvent -Event "CmdRun" + Test-EXOConnection + Send-AIEvent -Event "CmdRun" - # Verify our UPN input - [array]$UserArray = Test-UserObject -ToTest $UserPrincipalName + # Verify our UPN input + [array]$UserArray = Test-UserObject -ToTest $UserPrincipalName - foreach ($Object in $UserArray) { - [string]$User = $Object.UserPrincipalName + foreach ($Object in $UserArray) { + [string]$User = $Object.UserPrincipalName - Out-LogFile ("Gathering information about " + $User) -action + Out-LogFile ("Gathering information about " + $User) -action - #Gather mailbox information - Out-LogFile "Gathering Mailbox Information" - $mbx = Get-EXOMailbox -Identity $user + #Gather mailbox information + Out-LogFile "Gathering Mailbox Information" + $mbx = Get-EXOMailbox -Identity $user # Test to see if we have an archive and include that info as well - if (!($null -eq $mbx.archivedatabase)){ + if (!($null -eq $mbx.archivedatabase)) { Get-EXOMailboxStatistics -identity $user -Archive | Out-MultipleFileType -FilePrefix "Mailbox_Archive_Statistics" -user $user -txt } @@ -68,8 +68,8 @@ Function Get-HawkUserConfiguration { Get-EXOMailboxStatistics -Identity $user | Out-MultipleFileType -FilePrefix "Mailbox_Statistics" -User $User -txt Get-EXOMailboxFolderStatistics -identity $user | Out-MultipleFileType -FilePrefix "Mailbox_Folder_Statistics" -User $User -txt - # Gather cas mailbox sessions - Out-LogFile "Gathering CAS Mailbox Information" + # Gather cas mailbox sessions + Out-LogFile "Gathering CAS Mailbox Information" Get-EXOCasMailbox -identity $user | Out-MultipleFileType -FilePrefix "CAS_Mailbox_Info" -User $User -txt } } diff --git a/Hawk/internal/functions/Out-MultipleFileType.ps1 b/Hawk/internal/functions/Out-MultipleFileType.ps1 index 5fae6b2..b471a39 100644 --- a/Hawk/internal/functions/Out-MultipleFileType.ps1 +++ b/Hawk/internal/functions/Out-MultipleFileType.ps1 @@ -17,6 +17,8 @@ csv file format .PARAMETER txt txt file format +.PARAMETER json + Export data in JSON format. The data will be converted using ConvertTo-Json with a depth of 100 to preserve object structure. .PARAMETER Notice Notification that data retrieved meets the investigation criteria .EXAMPLE diff --git a/Hawk/internal/functions/Test-GraphConnection.ps1 b/Hawk/internal/functions/Test-GraphConnection.ps1 index e54309d..7e44415 100644 --- a/Hawk/internal/functions/Test-GraphConnection.ps1 +++ b/Hawk/internal/functions/Test-GraphConnection.ps1 @@ -1,4 +1,4 @@ -<# +<# .SYNOPSIS Test if we are connected to Graph and connect if not .DESCRIPTION @@ -27,7 +27,7 @@ Function Test-GraphConnection { Out-LogFile "Connecting to MGGraph using MGGraph Module" } # Connect to the MG Graph. The following scopes allow to retrieve Domain, Organization, and Sku data from the Graph. - Connect-MGGraph -Scopes "User.Read.All","Directory.Read.All" + Connect-MGGraph -Scopes "User.Read.All", "Directory.Read.All" Select-MgProfile -Name "v1.0" } }#End Function Test-GraphConnection \ No newline at end of file diff --git a/Hawk/internal/scripts/pre_commit_hook_scripts/Invoke-PowerShellScriptAnalyzer.ps1 b/Hawk/internal/scripts/pre_commit_hook_scripts/Invoke-PowerShellScriptAnalyzer.ps1 index e337edd..fd14d4a 100644 --- a/Hawk/internal/scripts/pre_commit_hook_scripts/Invoke-PowerShellScriptAnalyzer.ps1 +++ b/Hawk/internal/scripts/pre_commit_hook_scripts/Invoke-PowerShellScriptAnalyzer.ps1 @@ -1,4 +1,4 @@ -$ErrorActionPreference = 'Stop' +$ErrorActionPreference = 'Stop' # Import the module Import-Module PSScriptAnalyzer From c1ad4b61da87e6c7b6a633f984e54aeba966539b Mon Sep 17 00:00:00 2001 From: Jonathan Butler Date: Fri, 29 Nov 2024 17:56:52 -0500 Subject: [PATCH 4/7] Change plural noun of function name to sinular noun Get-HawkTenantEDiscoveryLogs --- .../Tenant/Get-HawkTenantEDiscoveryLog.ps1 | 6 +- .../Tenant/Start-HawkTenantInvestigation.ps1 | 56 ++++++++++--------- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/Hawk/functions/Tenant/Get-HawkTenantEDiscoveryLog.ps1 b/Hawk/functions/Tenant/Get-HawkTenantEDiscoveryLog.ps1 index d37004b..978fa31 100644 --- a/Hawk/functions/Tenant/Get-HawkTenantEDiscoveryLog.ps1 +++ b/Hawk/functions/Tenant/Get-HawkTenantEDiscoveryLog.ps1 @@ -3,9 +3,9 @@ .SYNOPSIS Gets Unified Audit Logs (UAL) data for eDiscovery .DESCRIPTION - Searches the Unified Audit Log (UAL) for eDiscovery events and activities. - This includes searches, exports, and management activities related to - eDiscovery cases. The function checks for any eDiscovery activities within + Searches the Unified Audit Log (UAL) for eDiscovery events and activities. + This includes searches, exports, and management activities related to + eDiscovery cases. The function checks for any eDiscovery activities within the timeframe specified in the Hawk global configuration object. The results can help identify: diff --git a/Hawk/functions/Tenant/Start-HawkTenantInvestigation.ps1 b/Hawk/functions/Tenant/Start-HawkTenantInvestigation.ps1 index c6abe33..5c2cb11 100644 --- a/Hawk/functions/Tenant/Start-HawkTenantInvestigation.ps1 +++ b/Hawk/functions/Tenant/Start-HawkTenantInvestigation.ps1 @@ -1,31 +1,37 @@ Function Start-HawkTenantInvestigation { + <# +.SYNOPSIS + Gathers common data about a tenant. +.DESCRIPTION + Runs all Hawk Basic tenant related cmdlets and gathers data about the tenant's configuration, + security settings, and audit logs. This comprehensive investigation helps identify potential + security issues and configuration changes. + +.PARAMETER Confirm + Prompts for confirmation before running operations that could modify system state. + +.PARAMETER WhatIf + Shows what would happen if the command runs. The command is not run. + +.EXAMPLE + PS C:\> Start-HawkTenantInvestigation + Runs a complete tenant investigation, gathering all available data. + +.EXAMPLE + PS C:\> Start-HawkTenantInvestigation -WhatIf + Shows what data gathering operations would be performed without executing them. + +.EXAMPLE + PS C:\> Start-HawkTenantInvestigation -Confirm + Prompts for confirmation before running each data gathering operation. + +.OUTPUTS + Various CSV, JSON, and XML files containing investigation results. + See help from individual cmdlets for specific output details. + All outputs are placed in the $Hawk.FilePath directory. +#> [CmdletBinding(SupportsShouldProcess)] param() - - <# - .SYNOPSIS - Gathers common data about a tenant. - .DESCRIPTION - Runs all Hawk Basic tenant related cmdlets and gathers the data. - - Cmdlet Information Gathered - ------------------------- ------------------------- - Get-HawkTenantConfigurationn Basic Tenant information - Get-HawkTenantEDiscoveryConfiguration Looks for changes to ediscovery configuration - Search-HawkTenantEXOAuditLog Searches the EXO audit log for activity - Get-HawkTenantRBACChanges Looks for changes to Roles Based Access Control - .OUTPUTS - See help from individual cmdlets for output list. - All outputs are placed in the $Hawk.FilePath directory - .EXAMPLE - Start-HawkTenantInvestigation - - Runs all of the tenant investigation cmdlets. - .EXAMPLE - Start-HawkTenantInvestigation -WhatIf - - Shows what actions would be performed without actually executing them. - #> if ([string]::IsNullOrEmpty($Hawk.FilePath)) { Initialize-HawkGlobalObject From 0461e30ef89ed621cec16c51ed051f2f1e8c2bcd Mon Sep 17 00:00:00 2001 From: Jonathan Butler Date: Fri, 29 Nov 2024 18:27:39 -0500 Subject: [PATCH 5/7] Add exclude trailing whitespace back to PSSA config file. --- .../PSScriptAnalyzerSettings.psd1 | 2 +- Hawk/tests/general/FileIntegrity.Tests.ps1 | 102 ++++++++---------- 2 files changed, 46 insertions(+), 58 deletions(-) diff --git a/Hawk/internal/configurations/PSScriptAnalyzerSettings.psd1 b/Hawk/internal/configurations/PSScriptAnalyzerSettings.psd1 index feeeb82..d9f535d 100644 --- a/Hawk/internal/configurations/PSScriptAnalyzerSettings.psd1 +++ b/Hawk/internal/configurations/PSScriptAnalyzerSettings.psd1 @@ -1,7 +1,7 @@ @{ # Rules to be excluded from analysis ExcludeRules = @( - # These are both excluded because they was a hardcoded in the Hawk PSScriptAnalyzer.Tests originally. + # These are both excluded because they were hardcoded in the Hawk PSScriptAnalyzer.Tests originally. # It is assumed this was done with good reason. 'PSAvoidTrailingWhitespace' 'PSShouldProcess' diff --git a/Hawk/tests/general/FileIntegrity.Tests.ps1 b/Hawk/tests/general/FileIntegrity.Tests.ps1 index 89e6c9c..a95dc2e 100644 --- a/Hawk/tests/general/FileIntegrity.Tests.ps1 +++ b/Hawk/tests/general/FileIntegrity.Tests.ps1 @@ -1,73 +1,66 @@ -$moduleRoot = (Resolve-Path "$global:testroot\..").Path +$script:moduleRoot = (Resolve-Path "$PSScriptRoot\..").Path -. "$global:testroot\general\FileIntegrity.Exceptions.ps1" +. "$PSScriptRoot\general\FileIntegrity.Exceptions.ps1" Describe "Verifying integrity of module files" { BeforeAll { - function Get-FileEncoding - { - <# - .SYNOPSIS - Tests a file for encoding. - - .DESCRIPTION - Tests a file for encoding. - - .PARAMETER Path - The file to test - #> + function Get-FileEncoding { + <# + .SYNOPSIS + Tests a file for encoding. + .DESCRIPTION + Tests a file for encoding. + .PARAMETER Path + The file to test + .OUTPUTS + System.String + #> [CmdletBinding()] + [OutputType([string])] Param ( [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True)] [Alias('FullName')] [string] $Path ) - - if ($PSVersionTable.PSVersion.Major -lt 6) - { - [byte[]]$byte = get-content -Encoding byte -ReadCount 4 -TotalCount 4 -Path $Path - } - else - { - [byte[]]$byte = Get-Content -AsByteStream -ReadCount 4 -TotalCount 4 -Path $Path + + process { + if ($PSVersionTable.PSVersion.Major -lt 6) { + [byte[]]$byte = get-content -Encoding byte -ReadCount 4 -TotalCount 4 -Path $Path + } + else { + [byte[]]$byte = Get-Content -AsByteStream -ReadCount 4 -TotalCount 4 -Path $Path + } + + if ($byte[0] -eq 0xef -and $byte[1] -eq 0xbb -and $byte[2] -eq 0xbf) { 'UTF8 BOM' } + elseif ($byte[0] -eq 0xfe -and $byte[1] -eq 0xff) { 'Unicode' } + elseif ($byte[0] -eq 0 -and $byte[1] -eq 0 -and $byte[2] -eq 0xfe -and $byte[3] -eq 0xff) { 'UTF32' } + elseif ($byte[0] -eq 0x2b -and $byte[1] -eq 0x2f -and $byte[2] -eq 0x76) { 'UTF7' } + else { 'Unknown' } } - - if ($byte[0] -eq 0xef -and $byte[1] -eq 0xbb -and $byte[2] -eq 0xbf) { 'UTF8 BOM' } - elseif ($byte[0] -eq 0xfe -and $byte[1] -eq 0xff) { 'Unicode' } - elseif ($byte[0] -eq 0 -and $byte[1] -eq 0 -and $byte[2] -eq 0xfe -and $byte[3] -eq 0xff) { 'UTF32' } - elseif ($byte[0] -eq 0x2b -and $byte[1] -eq 0x2f -and $byte[2] -eq 0x76) { 'UTF7' } - else { 'Unknown' } } } Context "Validating PS1 Script files" { - $allFiles = Get-ChildItem -Path $moduleRoot -Recurse | Where-Object Name -like "*.ps1" | Where-Object FullName -NotLike "$moduleRoot\tests\*" - - foreach ($file in $allFiles) - { - $name = $file.FullName.Replace("$moduleRoot\", '') - + $allFiles = Get-ChildItem -Path $script:moduleRoot -Recurse | Where-Object Name -like "*.ps1" | Where-Object FullName -NotLike "$script:moduleRoot\tests\*" + + foreach ($file in $allFiles) { + $name = $file.FullName.Replace("$script:moduleRoot\", '') + It "[$name] Should have UTF8 encoding with Byte Order Mark" -TestCases @{ file = $file } { Get-FileEncoding -Path $file.FullName | Should -Be 'UTF8 BOM' } - - It "[$name] Should have no trailing space" -TestCases @{ file = $file } { - ($file | Select-String "\s$" | Where-Object { $_.Line.Trim().Length -gt 0}).LineNumber | Should -BeNullOrEmpty - } - + $tokens = $null $parseErrors = $null - $ast = [System.Management.Automation.Language.Parser]::ParseFile($file.FullName, [ref]$tokens, [ref]$parseErrors) - + [System.Management.Automation.Language.Parser]::ParseFile($file.FullName, [ref]$tokens, [ref]$parseErrors) + It "[$name] Should have no syntax errors" -TestCases @{ parseErrors = $parseErrors } { $parseErrors | Should -BeNullOrEmpty } - - foreach ($command in $global:BannedCommands) - { - if ($global:MayContainCommand["$command"] -notcontains $file.Name) - { + + foreach ($command in $script:BannedCommands) { + if ($script:MayContainCommand["$command"] -notcontains $file.Name) { It "[$name] Should not use $command" -TestCases @{ tokens = $tokens; command = $command } { $tokens | Where-Object Text -EQ $command | Should -BeNullOrEmpty } @@ -75,21 +68,16 @@ Describe "Verifying integrity of module files" { } } } - + Context "Validating help.txt help files" { - $allFiles = Get-ChildItem -Path $moduleRoot -Recurse | Where-Object Name -like "*.help.txt" | Where-Object FullName -NotLike "$moduleRoot\tests\*" - - foreach ($file in $allFiles) - { - $name = $file.FullName.Replace("$moduleRoot\", '') - + $allFiles = Get-ChildItem -Path $script:moduleRoot -Recurse | Where-Object Name -like "*.help.txt" | Where-Object FullName -NotLike "$script:moduleRoot\tests\*" + + foreach ($file in $allFiles) { + $name = $file.FullName.Replace("$script:moduleRoot\", '') + It "[$name] Should have UTF8 encoding" -TestCases @{ file = $file } { Get-FileEncoding -Path $file.FullName | Should -Be 'UTF8 BOM' } - - It "[$name] Should have no trailing space" -TestCases @{ file = $file } { - ($file | Select-String "\s$" | Where-Object { $_.Line.Trim().Length -gt 0 } | Measure-Object).Count | Should -Be 0 - } } } } \ No newline at end of file From 98b7fd1808dac81740a47f7c2effad78d6b822a5 Mon Sep 17 00:00:00 2001 From: Jonathan Butler Date: Sat, 30 Nov 2024 10:04:14 -0500 Subject: [PATCH 6/7] Add comment to run workflow to see failing tests. --- Hawk/tests/general/Test-PreCommitHook.ps1 | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Hawk/tests/general/Test-PreCommitHook.ps1 b/Hawk/tests/general/Test-PreCommitHook.ps1 index c3626ea..e7d3845 100644 --- a/Hawk/tests/general/Test-PreCommitHook.ps1 +++ b/Hawk/tests/general/Test-PreCommitHook.ps1 @@ -5,7 +5,7 @@ # 3. Observe the PSScriptAnalyzer warnings/errors #region Good Code Examples - These will pass PSScriptAnalyzer -# tests +# testss function Test-GoodFunction { [CmdletBinding()] param ( @@ -13,7 +13,7 @@ function Test-GoodFunction { HelpMessage = "Enter a string parameter")] [string]$Parameter ) - + Write-Output $Parameter } @@ -25,7 +25,7 @@ function Test-ValidatedFunction { [ValidateNotNullOrEmpty()] [string]$Path ) - + $items = Get-ChildItem -Path $Path Write-Output $items } @@ -37,13 +37,13 @@ function Test-AdvancedFunction { [ValidateRange(0, [int]::MaxValue)] [int]$SizeInBytes ) - + $result = [PSCustomObject]@{ SizeInBytes = $SizeInBytes SizeInKB = [math]::Round($SizeInBytes / 1KB, 2) SizeInMB = [math]::Round($SizeInBytes / 1MB, 2) } - + Write-Output $result } @@ -56,12 +56,12 @@ function Test-AdvancedFunction { # Bad function - Multiple issues function test-badfunction { # Wrong capitalization param([string]$param1) # Missing CmdletBinding - + $Global:badVariable = $param1 # Using global variable - + write-output $badVariable # Incorrect capitalization - -} # Trailing whitespace after this line + +} # Trailing whitespace after this line # More issues $unusedVariable = "test" # Variable declared but never used @@ -72,7 +72,7 @@ function Test-BadValidation { [string] $Parameter # Missing mandatory and help message ) - + Write-host $Parameter # Write-Host instead of Write-Output } From 3ffe946732f70abaeef995f887d9ad8b7634d0bf Mon Sep 17 00:00:00 2001 From: Jonathan Butler Date: Sun, 1 Dec 2024 10:10:52 -0500 Subject: [PATCH 7/7] fix: Update module function exports and help documentation --- Hawk/Hawk.psd1 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Hawk/Hawk.psd1 b/Hawk/Hawk.psd1 index 39b0f42..87edf14 100644 --- a/Hawk/Hawk.psd1 +++ b/Hawk/Hawk.psd1 @@ -52,13 +52,12 @@ 'Get-HawkTenantInboxRules', 'Get-HawkTenantConsentGrants', 'Get-HawkTenantRBACChanges', - 'Get-HawkTenantAzureAuditLog', + 'Get-HawkTenantAzureAppAuditLog', 'Get-HawkUserAuthHistory', 'Get-HawkUserConfiguration', 'Get-HawkUserEmailForwarding', 'Get-HawkUserInboxRule', 'Get-HawkUserMailboxAuditing', - 'Initialize-HawkGlobalObject', 'Search-HawkTenantActivityByIP', 'Search-HawkTenantEXOAuditLog', 'Show-HawkHelp',