Public/Validation/Test-DirectoryIsProtected.ps1

<#
.SYNOPSIS
    Determines if a directory is protected by the operating system or user environment.
 
.DESCRIPTION
    This function checks if the provided directories are protected system directories or user environment directories. It returns a boolean value indicating whether each directory is protected.
 
.PARAMETER Directory
    The list of directories to check. Only directories will be accepted.
 
.EXAMPLE
    Test-DirectoryIsProtected -Directory "C:\Windows", "C:\Users\Public"
 
    This example checks if "C:\Windows" and "C:\Users\Public" are protected directories.
 
.NOTES
    Author: Futuremotion
    Date: 2023-10-10
    Website: https://github.com/futuremotiondev
#>

function Test-DirectoryIsProtected {

    [OutputType([bool])]
    [CmdletBinding()]

    param(
        [Parameter(Mandatory,Position=0,ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [String[]] $Path
    )

    begin {

        $VAR_OS_DRIVE = ((Get-CimInstance -ClassName CIM_OperatingSystem).SystemDrive)
        if ([String]::IsNullOrEmpty($VAR_OS_DRIVE)) {
            $VAR_OS_DRIVE = $env:SystemDrive
            if([String]::IsNullOrEmpty($VAR_OS_DRIVE)){
                throw [System.IO.DriveNotFoundException] "Could not determine the system drive."
            }
        }
        $VAR_USER_HOME = [Environment]::GetFolderPath([Environment+SpecialFolder]::UserProfile)

        $UNSAFE_STATIC = @{
            STATIC_USER_HOME    = $VAR_USER_HOME
            STATIC_USER_DESKTOP = [Environment]::GetFolderPath([Environment+SpecialFolder]::Desktop)
            STATIC_USERS_DIR    = Join-Path $VAR_OS_DRIVE "Users"
        }

        $UNSAFE_RECURSIVE = @{
            RECURSE_SYS_WINDOWS_DIR        = [Environment]::GetFolderPath([Environment+SpecialFolder]::Windows)
            RECURSE_SYS_COMMON_FILES64     = [Environment]::GetFolderPath([Environment+SpecialFolder]::CommonProgramFiles)
            RECURSE_SYS_COMMON_FILES86     = [Environment]::GetFolderPath([Environment+SpecialFolder]::CommonProgramFilesX86)
            RECURSE_SYS_APPDATA            = Join-Path $VAR_USER_HOME 'AppData'
            RECURSE_SYS_PROGRAM_FILES86    = [Environment]::GetFolderPath([Environment+SpecialFolder]::ProgramFilesX86)
            RECURSE_SYS_PROGRAM_FILES64    = [Environment]::GetFolderPath([Environment+SpecialFolder]::ProgramFiles)
            RECURSE_SYS_PROGRAM_DATA       = [Environment]::GetFolderPath([Environment+SpecialFolder]::CommonApplicationData)
            RECURSE_USER_PUBLIC_ROOT       = Join-Path $VAR_OS_DRIVE 'Users\Public'
            RECURSE_USER_DEFAULT_ROOT      = Join-Path $VAR_OS_DRIVE 'Users\Default'
            RECURSE_USER_HOME_NUGET        = Join-Path $VAR_USER_HOME '.nuget'
            RECURSE_USER_HOME_VSCODE       = Join-Path $VAR_USER_HOME '.vscode'
            RECURSE_USER_HOME_ECLIPSE      = Join-Path $VAR_USER_HOME '.eclipse'
            RECURSE_USER_HOME_DOTNET       = Join-Path $VAR_USER_HOME '.dotnet'
            RECURSE_USER_HOME_CONFIG       = Join-Path $VAR_USER_HOME '.config'
            RECURSE_OS_RECYCLEBIN          = Join-Path $VAR_OS_DRIVE '$Recycle.Bin'
            RECURSE_OS_WINDOWSBT           = Join-Path $VAR_OS_DRIVE '$WINDOWS.~BT'
            RECURSE_OS_WINDOWSWS           = Join-Path $VAR_OS_DRIVE '$Windows.~WS'
            RECURSE_OS_WINREAGENT          = Join-Path $VAR_OS_DRIVE '$WinREAgent'
            RECURSE_OS_RECOVERY            = Join-Path $VAR_OS_DRIVE 'Recovery'
            RECURSE_OS_SYS_VOLUME          = Join-Path $VAR_OS_DRIVE 'System Volume Information'
            RECURSE_OS_MINICONDA           = Join-Path $VAR_OS_DRIVE 'Python\miniconda3'
        }

        $VAR_OS_DRIVE_ESCAPED = [regex]::Escape($VAR_OS_DRIVE)
        $VAR_USER_HOME_ESCAPED = [regex]::Escape($VAR_USER_HOME)

        $UNSAFE_RECURSIVE_REGEX = @{
            RECURSE_REGEX_PYTHON1       = "$VAR_OS_DRIVE_ESCAPED\\Python\\Python[\d]{2,4}\\?$"
            RECURSE_REGEX_PYTHON2       = "$VAR_USER_HOME_ESCAPED\\Python[\d]{2,4}\\?$"
            RECURSE_REGEX_MINICONDA1    = "$VAR_OS_DRIVE_ESCAPED\\Python\\Miniconda[\d]{1,}\\?$"
            RECURSE_REGEX_MINICONDA2    = "$VAR_USER_HOME_ESCAPED\\Miniconda[\d]{1,}\\?$"
        }

        $UNSAFE_STATIC_PERSONAL = @{
            STATIC_BIN            = $env:FM_BIN
            STATIC_WRAPPERS       = $env:FM_PS_WRAPPERS
            STATIC_PYTHON_VENVS   = $env:FM_PY_VENV
            STATIC_TOOLS          = $env:FM_TOOLS
        }
    }

    process {
        :outer
        foreach ($Directory in $Path) {

            if(-not(Test-Path $Directory -PathType Container)){
                Write-Warning "Passed value is not a folder or doesn't exist."
                continue
            }

            $Directory = $Directory.TrimEnd('\')

            foreach ($UNSAFE_DIR in $UNSAFE_STATIC.GetEnumerator()) {
                if ($Directory -eq $UNSAFE_DIR.Value) { $true; continue outer}
            }
            foreach ($UNSAFE_DIR in $UNSAFE_STATIC_PERSONAL.GetEnumerator()) {
                if ($Directory -eq $UNSAFE_DIR.Value) { $true; continue outer}
            }
            foreach ($UNSAFE_DIR in $UNSAFE_RECURSIVE.GetEnumerator()) {
                if ($Directory -like "$($UNSAFE_DIR.Value)*") { $true; continue outer}
            }
            foreach ($UNSAFE_DIR in $UNSAFE_RECURSIVE_REGEX.GetEnumerator()) {
                if ($Directory -match $UNSAFE_DIR.Value) { $true; continue outer}
            }

            $Directory -match '^[a-zA-Z]:\\?(?:System Volume Information|\$RECYCLE\.BIN)?$'
        }
    }

    end {}
}