#!/usr/bin/env bash
########################################################################################################################################
# A script to report the state of CrowdStrike Falcon (thanks, ZT and mrw!)                                                                     #
# - If CrowdStrike Falcon is not installed, "Not Installed" will be returned.                                                          #
########################################################################################################################################

export PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin/
scriptVersion="0.0.12"
RESULT="Failed: Not Installed"

# The number of days before reporting device has not connected to the CrowdStrike Cloud.
lastConnectedVariance="7"

# The date format expected from date fields generated by `falconctl`.
falconctl_date_format="%b %d %Y %H:%M:%S"



###
# Pre-flight: Check the Locale; this will affect the output of falconctl stats
###

lib_locale=$( /usr/bin/defaults read "/Library/Preferences/.GlobalPreferences.plist" AppleLocale )
root_locale=$( /usr/bin/defaults read "/var/root/Library/Preferences/.GlobalPreferences.plist" AppleLocale )

if [[ "${lib_locale}" != "en_US" ]]; then
    /usr/bin/defaults write "/Library/Preferences/.GlobalPreferences.plist" AppleLocale "en_US"
fi

if [[ "${root_locale}" != "en_US" ]]; then
    /usr/bin/defaults write "/var/root/Library/Preferences/.GlobalPreferences.plist" AppleLocale "en_US"
fi



###
# Functions
###

fixup_date_string(){
    # Convert unicode spaces in a string into a basic space
    # Arguments
    # $1 = (str) a string

    /usr/bin/sed 's/,//g; s/ at//g; s/[[:space:]]/ /g; s/[AP]M//g;' <<< "${1}"
}

get_time_stamp() {
    # Helper function to provide a standard date-time stamp
    # Arguments
    # --format | -f = (str) Date output format to use; otherwise the default will be used
    # --adjust | -a = (str) Modify the date (e.g. future/past)
    # --epoch | -e = (str) Output date in epoch format
    local date_format
    local args
    declare -a args

    if
        printf '%s\n' "$@" | grep --extended-regexp --line-regexp --quiet "\-f|\-\-format" && \
        printf '%s\n' "$@" | grep --extended-regexp --line-regexp --quiet "\-e|\-\-epoch" \
    ; then
        echo "[Error] Unable to pass both --format and --epoch parameters"
        exit 200
    fi

    # Loop through the passed arguments
    while (( "$#" )); do
        case "${1}" in
            --format | -f )
                shift
                local date_format="${1}"
            ;;
            --adjust | -a )
                shift
                args+=("'${1}'")
            ;;
            --epoch | -e )
                local epoch="true"
                args+=("+%s")
            ;;
            * )
                echo "[Warning] Unsupported parameter passed to \`get_time_stamp()\`"
                exit 200
            ;;
        esac
        shift
    done

    if [[ -z "${date_format}" && -z "${epoch}" ]]; then
        date_format="${log_date_format}"
    fi

    if [[ -n "${date_format}" ]]; then
        args=( "'${date_format}'" "${args[@]}")
    fi

    # shellcheck disable=SC2294
    eval /bin/date -j "${args[@]}"
}

convert_date(){
    # Convert a formatted date string into another format
    # Arguments
    # $1 = (str) a date in string format
    # $2 = (str) the input format of the date string
    # $3 = (str) the output format of the date string
    local date_string="${1}"
    local input_format="${2}"
    local output_format="${3}"

    if [[ -n "${date_string}" ]]; then
        /bin/date -j -f \
            "${input_format}" \
            "$( fixup_date_string "${date_string}" )" \
            +"${output_format}" 2>/dev/null
    fi
}

check_last_connection() {
    # Check if the last connected date is older than seven days.
    # Arguments
    # $1 = (str) date formatted string, captured from 
        # the `falconctl stats Communications` "last connected date" attribute
    # $2 = (int) number (of days)
    local fixed_date
    fixed_date="$( fixup_date_string "${1}" )"

    if [[ 
        $( convert_date "${fixed_date}" "${falconctl_date_format}" "%s" ) -lt \
        $( get_time_stamp --adjust -v-"${2}"d --epoch )
    ]]; then
        returnResult+="Last Connected: ${1};"
    else
        returnResult+="Last Connected: ${1};"
    fi
}




###
# Program
###

falconBinary="/Applications/Falcon.app/Contents/Resources/falconctl"
falconAgentStats=$( "$falconBinary" stats -p agent_info Communications CloudInfo 2>&1 )
plistBuddyBinary="/usr/libexec/PlistBuddy"

# Get the sensor operational status
sensorOperational=$("$plistBuddyBinary" -c 'Print ":agent_info:sensor_operational"' /dev/stdin <<< "$falconAgentStats")
# echo "sensorOperational: ${sensorOperational}"

# Get the connection established dates
connectionState=$("$plistBuddyBinary" -c 'Print ":CloudInfo:State"' /dev/stdin <<< "$falconAgentStats")
# echo "connectionState: ${connectionState}"

established=$("$plistBuddyBinary" -c 'Print ":Communications:Communication:Cloud Activity:Established At"' /dev/stdin <<< "$falconAgentStats")
# echo "established: ${established}"

lastEstablished=$("$plistBuddyBinary" -c 'Print ":Communications:Communication:Cloud Activity:Last Established At"' /dev/stdin <<< "$falconAgentStats")
# echo "lastEstablished: ${lastEstablished}"

if [[ "${connectionState}" == "connected" ]]; then

    # Compare if both were available.
    if [[ -n "${established}" ]] && [[ -n "${lastEstablished}" ]]; then

        # Check which is more recent.
        if [[ 
            $( convert_date "${established}" "${falconctl_date_format}" "%s" ) -ge \
            $( convert_date "${lastEstablished}" "${falconctl_date_format}" "%s" )
        ]]; then
            testConnectionDate="${established}"
        else
            testConnectionDate="${lastEstablished}"
        fi

        # Check if the more recent date is older than seven days
        check_last_connection "${testConnectionDate}" $lastConnectedVariance

    elif [[ -n "${established}" ]]; then
        # If only the Established date was available, check if it is older than seven days.
        check_last_connection "${established}" $lastConnectedVariance
    elif [[ -n "${lastEstablished}" ]]; then
        # If only the Last Established date was available, check if it is older than seven days.
        check_last_connection "${lastEstablished}" $lastConnectedVariance
    else
        # If no connection date was available, return disconnected
        returnResult+=" Unknown Connection State;"
    fi

elif [[ -n "${connectionState}" ]]; then
    # If no connection date was available, return state
    returnResult+=" Connection State: ${connectionState};"
fi

if [[ "${sensorOperational}" == "true" ]]; then
    RESULT="Running; ${returnResult%;}"
else
    case ${falconAgentStats} in

        *"status.bin"* )
            RESULT="Failed: 'status.bin' NOT found; ${returnResult}"
            ;;
        *"No such file"* )
            RESULT="Not Installed; ${returnResult}"
            ;;
        *"Error"* )
            RESULT="Error: ${falconAgentStats}; ${returnResult}"
            ;;
        * )
            RESULT="Unknown Error: ${returnResult}"
            ;;

    esac
fi

echo "${RESULT}"

exit 0