Private/BrowserHistory.Safari.ps1

# BrowserHistory.Safari.ps1 - Safari browser history scanning (macOS only)

function Get-SafariHistory {
    <#
    .SYNOPSIS
    Parse Safari browser history for AI usage indicators.

    .DESCRIPTION
    Scans Safari browser history SQLite database for URLs matching known AI tools.
    Safari is only available on macOS, so this function only works on macOS.

    .PARAMETER DaysBack
    Number of days of history to scan. Default is 90.

    .PARAMETER AllUsers
    Scan all users on the system instead of just the current user.
    Requires elevated/admin permissions.

    .EXAMPLE
    Get-SafariHistory -DaysBack 30

    .EXAMPLE
    Get-SafariHistory -AllUsers
    #>

    [CmdletBinding()]
    param(
        [int] $DaysBack = 90,

        [switch] $AllUsers
    )

    Write-Verbose "Starting Safari history scan..."
    $findings = @()

    # Safari is only available on macOS
    $isMacOS = $PSVersionTable.PSVersion.Major -ge 6 -and $IsMacOS
    if (-not $isMacOS) {
        Write-Verbose "Safari is only available on macOS. Skipping."
        return $findings
    }

    # Get AI tool patterns
    $aiPatterns = Get-AIToolPatterns

    # Get Safari profile paths
    $profiles = Get-BrowserProfilePaths -Browser 'Safari' -AllUsers:$AllUsers

    if ($profiles.Count -eq 0) {
        Write-Verbose "No Safari history found."
        return $findings
    }

    Write-Verbose "Found $($profiles.Count) Safari profile(s)"

    foreach ($profile in $profiles) {
        Write-Verbose "Scanning Safari profile: $($profile.ProfileName)"

        # Query the history database
        # Safari uses history_items for URLs and history_visits for timestamps
        # visit_time is seconds since 2001-01-01 (Mac absolute time / Cocoa epoch)
        $query = @"
SELECT
    hi.url,
    hi.domain_expansion as title,
    hi.visit_count,
    hv.visit_time
FROM history_items hi
LEFT JOIN history_visits hv ON hi.id = hv.history_item
WHERE hv.visit_time IS NOT NULL
ORDER BY hv.visit_time DESC
"@


        $historyEntries = Invoke-SQLiteQuery -DatabasePath $profile.HistoryPath -Query $query

        if ($historyEntries.Count -eq 0) {
            Write-Verbose "No history entries found in Safari"
            continue
        }

        Write-Verbose "Found $($historyEntries.Count) history entries in Safari"

        foreach ($entry in $historyEntries) {
            $url = $entry.url
            if (-not $url) { continue }

            # Check against each AI pattern
            foreach ($pattern in $aiPatterns) {
                if ($url -match $pattern.Pattern) {
                    # Convert timestamp (Safari uses Mac absolute time - seconds since 2001-01-01)
                    $timestamp = $null
                    if ($entry.visit_time) {
                        try {
                            $timestamp = Convert-SafariTimestamp -Timestamp ([double]$entry.visit_time)
                            
                            # Filter by DaysBack
                            $cutoffDate = (Get-Date).AddDays(-$DaysBack)
                            if ($timestamp -lt $cutoffDate) {
                                continue
                            }
                        }
                        catch {
                            Write-Verbose "Failed to parse timestamp for URL: $url"
                        }
                    }

                    $findings += [PSCustomObject]@{
                        Browser    = 'Safari'
                        Username   = $profile.Username
                        Profile    = $profile.ProfileName
                        Tool       = $pattern.Name
                        Category   = $pattern.Category
                        Url        = $url
                        Title      = $entry.title
                        VisitCount = [int]$entry.visit_count
                        Timestamp  = $timestamp
                    }

                    # Only match first pattern per URL
                    break
                }
            }
        }
    }

    Write-Verbose "Safari scan complete. Found $($findings.Count) AI-related entries."
    return $findings
}