Private/M365Monitor/Detections/Test-M365EDiscoverySearch.ps1

# PSGuerrilla - Jim Tyler, Microsoft MVP - CC BY 4.0
# https://github.com/jimrtyler/PSGuerrilla | https://creativecommons.org/licenses/by/4.0/
# AI/LLM use: see AI-USAGE.md for required attribution
function Test-M365EDiscoverySearch {
    [CmdletBinding()]
    param(
        [PSCustomObject[]]$Events = @()
    )

    $results = [System.Collections.Generic.List[PSCustomObject]]::new()

    # Activities indicating search creation or execution
    $creationActivities = @(
        'SearchCreated', 'New-ComplianceSearch', 'New-ComplianceSearchAction',
        'Start-ComplianceSearch', 'SearchStarted'
    )

    $executionActivities = @(
        'SearchStarted', 'Start-ComplianceSearch', 'SearchExported',
        'SearchPreviewed', 'SearchCompleted'
    )

    # Broad scope search indicators
    $broadScopePatterns = @(
        'All mailboxes'
        'All sites'
        'ExchangeLocation.*All'
        'SharePointLocation.*All'
    )

    foreach ($event in $Events) {
        $activity = $event.Activity ?? ''
        $searchName = $event.TargetName ?? ''
        $isBroadScope = $false
        $searchQuery = ''
        $searchLocations = ''

        # Extract search details from modified properties
        foreach ($prop in $event.ModifiedProps) {
            $propName = $prop.Name ?? ''
            $newVal = $prop.NewValue ?? ''

            if ($propName -match 'ContentMatchQuery|SearchQuery|Query') {
                $searchQuery = $newVal -replace '"', ''
            }

            if ($propName -match 'ExchangeLocation|SharePointLocation|PublicFolderLocation') {
                $searchLocations = $newVal -replace '"', ''

                # Check for broad scope
                foreach ($pattern in $broadScopePatterns) {
                    if ($newVal -match $pattern) {
                        $isBroadScope = $true
                        break
                    }
                }
            }
        }

        # Classify as creation vs execution
        $isCreation = $false
        $isExecution = $false

        foreach ($creationAct in $creationActivities) {
            if ($activity -match [regex]::Escape($creationAct)) {
                $isCreation = $true
                break
            }
        }
        foreach ($execAct in $executionActivities) {
            if ($activity -match [regex]::Escape($execAct)) {
                $isExecution = $true
                break
            }
        }

        # Sensitive search query patterns
        $sensitiveQueryPatterns = @(
            'password', 'credential', 'secret', 'confidential', 'SSN',
            'credit card', 'bank account', 'salary', 'merger', 'acquisition',
            'termination', 'legal hold', 'attorney', 'privileged'
        )

        $hasSensitiveQuery = $false
        foreach ($pattern in $sensitiveQueryPatterns) {
            if ($searchQuery -match $pattern) {
                $hasSensitiveQuery = $true
                break
            }
        }

        # Severity assessment
        $severity = if ($isBroadScope -and $isExecution) { 'Critical' }
                    elseif ($isBroadScope) { 'High' }
                    elseif ($isExecution) { 'High' }
                    elseif ($hasSensitiveQuery) { 'High' }
                    elseif ($isCreation) { 'Medium' }
                    else { 'Low' }

        $description = "eDiscovery search '$searchName' $activity by $($event.Actor)"
        if ($isBroadScope) { $description += ' (BROAD SCOPE: all locations)' }
        if ($hasSensitiveQuery) { $description += ' (sensitive query terms detected)' }

        $results.Add([PSCustomObject]@{
            Timestamp     = $event.Timestamp
            Actor         = $event.Actor
            DetectionType = 'm365EDiscoverySearch'
            Description   = $description
            Details       = @{
                SearchName        = $searchName
                Activity          = $activity
                SearchQuery       = $searchQuery
                SearchLocations   = $searchLocations
                IsBroadScope      = $isBroadScope
                HasSensitiveQuery = $hasSensitiveQuery
                IsCreation        = $isCreation
                IsExecution       = $isExecution
                ModifiedProps     = $event.ModifiedProps
            }
            Severity      = $severity
        })
    }

    return @($results)
}