Umfassender Guide zum Auflisten von Browsererweiterungen unter macOS

Browsererweiterungen sind leistungsstarke Tools, die die Funktionalität von Webbrowsern verbessern, aber sie können auch Sicherheitsrisiken bergen, wenn sie nicht richtig verwaltet werden. Für IT-Experten und Managed Service Provider (MSPs), die für die Aufrechterhaltung einer sicheren und effizienten Computerumgebung verantwortlich sind, ist eine Methode zur Inventarisierung von Browsererweiterungen in verschiedenen Browsern auf macOS-Systemen entscheidend.

In diesem Blogbeitrag wird ein umfassendes Skript zum Auflisten von Browsererweiterungen unter macOS vorgestellt, das sich für Safari, Chrome, Firefox und Edge verwenden lässt. Das Verständnis und die Verwaltung dieser Erweiterungen können die Sicherheitslage eines Unternehmens erheblich verbessern.

Kontext

In der modernen IT-Welt ist es eine grundlegende Aufgabe, dafür zu sorgen, dass die gesamte Software, einschließlich der Browsererweiterungen, auf dem neuesten Stand und sicher ist. Erweiterungen können große Vorteile bieten, aber sie stellen auch potenzielle Schwachstellen dar, die böswillige Akteure ausnutzen können.

Dieses Skript wurde entwickelt, um IT-Experten dabei zu helfen, schnell und effizient Informationen über installierte Browsererweiterungen in mehreren Browsern auf macOS-Systemen zu sammeln. Diese Funktion ist unerlässlich, um die Einhaltung der Sicherheitskonformität zu gewährleisten und sicherzustellen, dass nur autorisierte Erweiterungen verwendet werden.

Das Skript

#!/usr/bin/env bash

#
# Description: Get the browser extensions for Safari, Chrome, Firefox, and Edge that are installed on a macOS system.
#
# Usage: [multilineCustomFieldName] [wysiwygCustomFieldName]
#
# Release Notes: Fixed 10% width bug.
# By using this script, you indicate your acceptance of the following legal terms as well as our Terms of Use at https://www.ninjaone.com/terms-of-use.
# Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms. 
# Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party. 
# Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library or website belonging to or under the control of any other software provider. 
# Warranty Disclaimer: The script is provided “as is” and “as available”, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations. 
# Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks. 
# Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script. 
# EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA).
#
# Example Output:
#
# Safari Extensions:
# User: john, Name: com.betafish.adblock-mac.SafariMenu, Id: 3KKZV48AQD
# User: ken, Name: com.rockysandstudio.MKPlayer.MKPlayer-Extension, Id: 2N35YUC83U
#
# Chrome Extensions:
# User: john, Name: Chrome Web Store Payments, Id: nmmhkkegccagdldgiimedpiccmgmieda, Version: 1.0.0.6, Profile Name: test1
# User: john, Name: Turn Off the Lights, Id: bfbmjmiodbnnpllbbbfblcplfjjepjdn, Version: 4.2.6.0, Profile Name: test2
# User: ken, Name: Google Docs Offline, Id: ghbmnnjooekpmoecnnnilnnbdlolhkhi, Version: 1.76.1, Profile Name: Person 1
#
# Firefox Addons:
# User: john, Name: Return YouTube Dislike, Id: {762f9885-5a13-4abd-9c77-433dcd38b8fd}, Description: Returns ability to see dislike statistics on youtube, Profile Name: default-release
# User: john, Name: Stylebot, Id: {52bda3fd-dc48-4b3d-a7b9-58af57879f1e}, Description: Change the appearance of the web instantly, Profile Name: default-release
# User: ken, Name: Search by Image, Id: {2e5ff8c8-32fe-46d0-9fc8-6b8986621f3c}, Description: A powerful reverse image search tool, with support for various search engines, such as Google, Bing, Yandex, Baidu and TinEye., Profile Name: default-release
#
# Edge Extensions:
# User: john, Name: Google Docs Offline, Id: ghbmnnjooekpmoecnnnilnnbdlolhkhi, Version: 1.76.2, Profile Name: test 3
# User: john, Name: Edge relevant text changes, Id: jmjflgjpcpepeafmmgdpfkogkghcpiha, Version: 1.2.1, Profile Name: test 3
# User: ken, Name: Google Docs Offline, Id: ghbmnnjooekpmoecnnnilnnbdlolhkhi, Version: 1.76.2, Profile Name: Profile 1

_arg_multilineField=$1
_arg_wysiwygField=$2

# Determines whether or not help text is necessary and routes the output to stderr
die() {
    local _ret="${2:-1}"
    echo "$1" >&2
    test "${_PRINT_HELP:-no}" = yes && print_help >&2
    exit "${_ret}"
}

# Prints the help text
print_help() {
    echo "Get the browser extensions for Safari, Chrome, Firefox, and Edge that are installed on a macOS system."
    echo "Usage: [multilineCustomFieldName] [wysiwygCustomFieldName]"
}

# Check if --help or -h is passed as an argument
for _arg in "$@"; do
    case "$_arg" in
    --help | -h)
        print_help
        exit 0
        ;;
    esac
done

# Set the custom field value using ninjarmm-cli
GetCustomField() {
    customfieldName=$1
    dataPath=$(printenv | grep -i NINJA_DATA_PATH | awk -F = '{print $2}')
    value=""
    if [ -e "${dataPath}/ninjarmm-cli" ]; then
        value=$("${dataPath}"/ninjarmm-cli get "$customfieldName")
    else
        value=$(/Applications/NinjaRMMAgent/programdata/ninjarmm-cli get "$customfieldName")
    fi
    if [[ "${value}" == *"Unable to find the specified field"* ]]; then
        echo ""
        return 1
    else
        echo "$value"
    fi
}

# Check if the multilineCustomFieldName is set as environment variables
multiline=$(printenv | grep -i multilineCustomFieldName | awk -F = '{print $2}')
if [[ -n "$multiline" ]] && [[ "${multiline}" != "null" ]]; then
    _arg_multilineField=$(printenv | grep -i multilineCustomFieldName | awk -F = '{print $2}')
fi

# Check if the wysiwygCustomFieldName is set as environment variables
wysiwygField=$(printenv | grep -i wysiwygCustomFieldName | awk -F = '{print $2}')
if [[ -n "$wysiwygField" ]] && [[ "${wysiwygField}" != "null" ]]; then
    _arg_wysiwygField=$(printenv | grep -i wysiwygCustomFieldName | awk -F = '{print $2}')
fi

# Check if both _arg_multilineField and _arg_wysiwygField are set and not empty
if [[ -n "$_arg_multilineField" && -n "$_arg_wysiwygField" ]]; then
    # Convert both field names to uppercase to check for equality
    multiline=$(echo "$_arg_multilineField" | tr '[:lower:]' '[:upper:]')
    wysiwyg=$(echo "$_arg_wysiwygField" | tr '[:lower:]' '[:upper:]')

    # If the converted names are the same, it means both fields cannot be identical
    # If they are, terminate the script with an error
    if [[ "$multiline" == "$wysiwyg" ]]; then
        _PRINT_HELP=yes die '[Error] Multline Field and WYSIWYG Field cannot be the same name. https://ninjarmm.zendesk.com/hc/en-us/articles/360060920631-Custom-Fields-Configuration-Device-Role-Fields'
    fi
fi

# Warn that if not running as root, the script may not be able to access all user directories
if [[ $EUID -ne 0 ]]; then
    echo "[Warn] This script may not be able to access all user directories unless run as root."
fi

# Converts a string input into an HTML table format
convertToHTMLTable() {
    local _arg_delimiter=" "
    local _arg_inputObject

    # Process command-line arguments for the function
    while test $# -gt 0; do
        _key="$1"
        case "$_key" in
        --delimiter | -d)
            test $# -lt 2 && echo "[Error] Missing value for the optional argument" >&2 && return 1
            _arg_delimiter=$2
            shift
            ;;
        --*)
            echo "[Error] Got an unexpected argument" >&2
            return 1
            ;;
        *)
            _arg_inputObject=$1
            ;;
        esac
        shift
    done

    # Handles missing input by checking stdin or returning an error
    if [[ -z $_arg_inputObject ]]; then
        if [ -p /dev/stdin ]; then
            _arg_inputObject=$(cat)
        else
            echo "[Error] Missing input object to convert to table" >&2
            return 1
        fi
    fi

    local htmlTable="<table>\n"
    htmlTable+=$(printf '%b' "$_arg_inputObject" | head -n1 | awk -F "$_arg_delimiter" '{
    printf "<tr>"
    for (i=1; i<=NF; i+=1)
      { printf "<th>"$i"</th>" }
    printf "</tr>"
    }')
    htmlTable+="\n"
    htmlTable+=$(printf '%b' "$_arg_inputObject" | tail -n +2 | awk -F "$_arg_delimiter" '{
    printf "<tr>"
    for (i=1; i<=NF; i+=1)
      { printf "<td>"$i"</td>" }
    print "</tr>"
    }')
    htmlTable+="\n</table>"

    printf '%b' "$htmlTable" '\n'
}

createExtList() {
    userHome=$1
    browser=$2 # chrome or edge
    user_name=$(echo "$userHome" | cut -d "/" -f 3)
    if [[ "${browser}" == "chrome" ]]; then
        manifests=$(find "${userHome}/Library/Application Support/Google/Chrome" -type f -name 'manifest.json' 2>/dev/null | grep "Extensions")
        profileLocalState="${userHome}/Library/Application Support/Google/Chrome/Local State"
    elif [[ "${browser}" == "edge" ]]; then
        manifests=$(find "${userHome}/Library/Application Support/Microsoft Edge" -type f -name 'manifest.json' 2>/dev/null | grep "Extensions")
        profileLocalState="${userHome}/Library/Application Support/Microsoft Edge/Local State"
    fi

    en_messages="_locales/en/messages.json"
    enUS_messages="_locales/en_US/messages.json"

    while IFS= read -r manifest; do
        # Check if we can read the manifest file
        if [ ! -f "$manifest" ]; then
            continue
        fi

        profileName=$(
            # Get profile name from profileLocalState that contains json data
            # "profile.info_cache.Default" is the default profile
            osascript -l JavaScript -e "
                    var profileLocalState = JSON.parse(ObjC.unwrap($.NSString.alloc.initWithDataEncoding($.NSData.dataWithContentsOfFile('$profileLocalState'), $.NSUTF8StringEncoding)));
                    // Get each profile name based on the profile path
                    if ('$browser' === 'chrome') {
                        var profilePathName = '$manifest'.split('/')[7];
                    } else if ('$browser' === 'edge') {
                        var profilePathName = '$manifest'.split('/')[6];
                    }
                    for (var key in profileLocalState.profile.info_cache) {
                        if (key.toLowerCase() === profilePathName.toLowerCase()) {
                            var profileName = profileLocalState.profile.info_cache[key].name;
                            break;
                        }
                    }
                    profileName;
                "
        )

        # Get the id of the extension from the manifest path by splitting the path on '/' and getting the 9th element
        if [[ "${browser}" == "chrome" ]]; then
            extID=$(awk -F '/' '{print $10}' <<<"$manifest")
        elif [[ "${browser}" == "edge" ]]; then
            extID=$(awk -F '/' '{print $9}' <<<"$manifest")
        fi
        # Get name from manifest
        name=$(
            osascript -l JavaScript -e "
                var data = JSON.parse(ObjC.unwrap($.NSString.alloc.initWithDataEncoding($.NSData.dataWithContentsOfFile('$manifest'), $.NSUTF8StringEncoding)));
                var name = data.name;
                name;
            "
        )
        # Get version from manifest
        version=$(
            osascript -l JavaScript -e "
                var data = JSON.parse(ObjC.unwrap($.NSString.alloc.initWithDataEncoding($.NSData.dataWithContentsOfFile('$manifest'), $.NSUTF8StringEncoding)));
                var version = data.version;
                version;
            "
        )

        # Get the name of the extension from the messages.json file if the name contains "__MSG_"
        if [[ "${name}" =~ "__MSG_" ]]; then
            name=$(echo "$name" | sed 's/__MSG_//g' | sed 's/__$//g')
            if [ -f "$(dirname "$manifest")/$en_messages" ]; then
                # Check if message.json exists in en folder
                name=$(
                    osascript -l JavaScript -e "
                        var data = JSON.parse(ObjC.unwrap($.NSString.alloc.initWithDataEncoding($.NSData.dataWithContentsOfFile('$(dirname "$manifest")/$en_messages'), $.NSUTF8StringEncoding)));
                        var searchKey = '$name';
                        var asLowercase = searchKey.toLowerCase();
                        var name = data[Object.keys(data).find(key => key.toLowerCase() === asLowercase)].message;
                        name;
                    " 2>/dev/null
                )
            elif [ -f "$(dirname "$manifest")/$enUS_messages" ]; then
                # Check if message.json exists in enUS folder
                name=$(
                    osascript -l JavaScript -e "
                        var data = JSON.parse(ObjC.unwrap($.NSString.alloc.initWithDataEncoding($.NSData.dataWithContentsOfFile('$(dirname "$manifest")/$enUS_messages'), $.NSUTF8StringEncoding)));
                        var searchKey = '$name';
                        var asLowercase = searchKey.toLowerCase();
                        var name = data[Object.keys(data).find(key => key.toLowerCase() === asLowercase)].message;
                        name;
                    " 2>/dev/null
                )
            fi
        fi
        if [[ -n "$name" ]]; then
            printf "%s|%s|%s|%s|%s\n" "$user_name" "$profileName" "$name" "$extID" "$version"
        else
            printf "%s|%s|%s|%s|%s\n" "$user_name" "$profileName" "Name Not Found" "$extID" "$version"
        fi
    done <<<"$manifests"
}

# Get a list of all users in the /Users directory except Shared
users=()
for user in /Users/*; do
    if [[ -d "$user" && ! "$user" =~ /Users/Shared ]]; then
        users+=("${user##*/}")
    fi
done

# Error when no users are found
if [[ ${#users[@]} -eq 0 ]]; then
    _PRINT_HELP=no die "[Error] No user directories found in /Users"
fi

safari_title="Safari Extensions"
safari_extensions=""
chrome_title="Chrome Extensions"
chrome_extensions=""
firefox_title="Firefox Addons"
firefox_addons=""
edge_title="Edge Extensions"
edge_extensions=""

# Safari
for user in "${users[@]}"; do
    ext_plist="/Users/$user/Library/Containers/com.apple.Safari/Data/Library/Safari/AppExtensions/Extensions.plist"
    pattern='(com\..*)\s\((.*)\)'
    if [[ -f "$ext_plist" ]]; then
        safari_extensions+=$'\n'"$(
            grep -Eo "$pattern" "$ext_plist" | while read -r line; do
                ext_name=$(echo "$line" | awk -F ' ' '{print $1}')
                ext_id=$(echo "$line" | awk -F ' ' '{print $2}' | tr -d '()')
                printf "%s|%s|%s\n" "$user" "$ext_name" "$ext_id"
            done
        )"
    fi
done

# Chrome
for user in "${users[@]}"; do
    # Add a newline to the beginning of the string to separate the results from the previous user
    chrome_extensions+=$'\n'"$(createExtList "/Users/$user" "chrome")"
done

# Firefox Get installed Addons (not extensions)
firefox_addons=$(
    # Loop through each user profile
    for user in "${users[@]}"; do
        firefox_path="/Users/$user/Library/Application Support/Firefox"
        profile_dir="$firefox_path/Profiles"
        # Check if Profiles directory exists
        if [[ -d "$profile_dir" ]]; then
            profiles_ini_path="$firefox_path/profiles.ini"
            # Check if profiles.ini file exists
            if [[ -f "$profiles_ini_path" ]]; then
                profiles_ini=$(cat "$profiles_ini_path")
                # Parse the profiles.ini file and get the profile names and paths
                # Get each section
                awk -F '=' '/\[/{print $1}' <<<"$profiles_ini" | while read -r section; do
                    # Get the profile name
                    profile_name=$(
                        awk -F'=' -v section="$section" -v k="Name" '
                        $0==section{ f=1; next }
                        /\[/{ f=0; next }
                        f && $1==k{ print $0 }
                        ' <<<"$profiles_ini" | sed 's/^Name=//'
                    )
                    # Get the profile path
                    profile_path=$(
                        awk -F'=' -v section="$section" -v k="Path" '
                        $0==section{ f=1; next }
                        /\[/{ f=0; next }
                        f && $1==k{ print $0 }
                        ' <<<"$profiles_ini" | sed 's/^Path=//'
                    )
                    # Get the addons json file path
                    ext_dir="$firefox_path/$profile_path/addons.json"
                    if [[ -f "$ext_dir" ]]; then
                        # Get name, id, and description from the addons.json file
                        ff_results=$(
                            osascript -l JavaScript -e "
                                var data = JSON.parse(ObjC.unwrap($.NSString.alloc.initWithDataEncoding($.NSData.dataWithContentsOfFile('$ext_dir'), $.NSUTF8StringEncoding)));
                                var addons = data.addons;
                                var result = '';
                                for (var i = 0; i < addons.length; i++) {
                                    var addon = addons[i];
                                    result += addon.name + ',' + addon.id + ',' + addon.description + '\n';
                                }
                                result;
                            "
                        )
                        Old_IFS=$IFS
                        echo "$ff_results" | while IFS=, read -r name id description; do
                            printf "%s|%s|%s|%s|%s\n" "$user" "$profile_name" "$name" "$id" "$description"
                        done
                        IFS=$Old_IFS
                    fi
                done
            fi
        fi
    done
)

# Edge
for user in "${users[@]}"; do
    # Add a newline to the beginning of the string to separate the results from the previous user
    edge_extensions+=$'\n'"$(createExtList "/Users/$user" "edge")"
done

if [[ -n $_arg_multilineField ]] || [[ -n $_arg_wysiwygField ]]; then
    # Add headers if a custom field is set
    if [[ -n "${safari_extensions}" ]]; then
        safari_extensions=$(echo -e "User|Name|Id\n$safari_extensions")
    fi
    if [[ -n "${chrome_extensions}" ]]; then
        chrome_extensions=$(echo -e "User|Profile Name|Name|Id|Version\n$chrome_extensions")
    fi
    if [[ -n "${firefox_addons}" ]]; then
        firefox_addons=$(echo -e "User|Profile Name|Name|Id|Description\n$firefox_addons")
    fi
    if [[ -n "${edge_extensions}" ]]; then
        edge_extensions=$(echo -e "User|Profile Name|Name|Id|Version\n$edge_extensions")
    fi
fi

# Checks if there is a multiline custom field set
if [[ -n $_arg_multilineField ]]; then
    echo ""
    echo "Attempting to set Custom Field '$_arg_multilineField'..."

    # Formats the extension data for the multiline custom field
    multilineValue=""
    # Check if any browser extensions were found
    if [[ -n "$safari_title" ]] && [[ -n "${safari_extensions}" ]]; then
        multilineValue+=$'\n'
        multilineValue+="$safari_title:"
        multilineValue+=$'\n'
        multilineValue+=$(
            # Convert the safari_extensions string to a table format. Skip the first line as it is the header
            echo "$safari_extensions" | tail -n +2 | while IFS='|' read -r user name id; do
                printf "User: %s, Name: %s, Id: %s\n" "$user" "$name" "$id"
            done
        )
    fi
    if [[ -n "$chrome_title" ]] && [[ -n "${chrome_extensions}" ]]; then
        multilineValue+=$'\n'
        multilineValue+="$chrome_title:"
        multilineValue+=$'\n'
        multilineValue+=$(
            # Convert the chrome_extensions string to a table format. Skip the first line as it is the header
            echo "$chrome_extensions" | tail -n +2 | while IFS='|' read -r user_name profileName name id version; do
                if [[ -n "$name" ]]; then
                    printf "User: %s, Profile Name: %s, Name: %s, Id: %s, Version: %s\n" "$user_name" "$profileName" "$name" "$id" "$version"
                fi
            done
        )
    fi
    if [[ -n "$firefox_title" ]] && [[ -n "${firefox_addons}" ]]; then
        multilineValue+=$'\n'
        multilineValue+="$firefox_title:"
        multilineValue+=$'\n'
        multilineValue+=$(
            # Convert the firefox_addons string to a table format. Skip the first line as it is the header
            echo "$firefox_addons" | tail -n +2 | while IFS='|' read -r user profileName name id description; do
                if [[ -n "$name" ]]; then
                    printf "User: %s, Profile Name: %s, Name: %s, Id: %s, Description: %s\n" "$user" "$profileName" "$name" "$id" "$description"
                fi
            done
        )
    fi
    if [[ -n "$edge_title" ]] && [[ -n "${edge_extensions}" ]]; then
        multilineValue+=$'\n'
        multilineValue+="$edge_title:"
        multilineValue+=$'\n'
        multilineValue+=$(
            # Convert the edge_extensions string to a table format. Skip the first line as it is the header
            echo "$edge_extensions" | tail -n +2 | while IFS='|' read -r user_name profileName name id version; do
                if [[ -n "$name" ]]; then
                    printf "User: %s, Profile Name: %s, Name: %s, Id: %s, Version: %s\n" "$user_name" "$profileName" "$name" "$id" "$version"
                fi
            done
        )
    fi
    # if all multilineValue is empty, set it to "No browser extensions found"
    if [[ -z "$multilineValue" ]]; then
        multilineValue="No browser extensions found"
    fi

    # Tries to set the multiline custom field using ninjarmm-cli and captures the output
    if ! output=$(printf '%b' "$multilineValue" | /Applications/NinjaRMMAgent/programdata/ninjarmm-cli set --stdin "$_arg_multilineField" 2>&1); then
        echo "[Error] $output" >&2
        EXITCODE=1
    else
        echo "[Info] Successfully set Custom Field '$_arg_multilineField'!"
    fi
fi

# Checks if there is a WYSIWYG custom field set
if [[ -n $_arg_wysiwygField ]]; then
    echo ""
    echo "Attempting to set Custom Field '$_arg_wysiwygField'..."

    # Initializes an HTML formatted string with headers and extension details
    # Check if any browser extensions were found
    if [[ -n "$safari_title" ]] && [[ -n "${safari_extensions}" ]]; then
        htmlObject+="<h2>$safari_title</h2>"
        htmlObject+=$(echo "$safari_extensions" | convertToHTMLTable --delimiter '|')
    fi
    if [[ -n "$chrome_title" ]] && [[ -n "${chrome_extensions}" ]]; then
        htmlObject+="<h2>$chrome_title</h2>"
        htmlObject+=$(echo "$chrome_extensions" | convertToHTMLTable --delimiter '|')
    fi
    if [[ -n "$firefox_title" ]] && [[ -n "${firefox_addons}" ]]; then
        htmlObject+="<h2>$firefox_title</h2>"
        htmlObject+=$(echo "$firefox_addons" | convertToHTMLTable --delimiter '|')
    fi
    if [[ -n "$edge_title" ]] && [[ -n "${edge_extensions}" ]]; then
        htmlObject+="<h2>$edge_title</h2>"
        htmlObject+=$(echo "$edge_extensions" | convertToHTMLTable --delimiter '|')
    fi
    # if all htmlObject is empty, set it to "No browser extensions found"
    if [[ -z "$htmlObject" ]]; then
        htmlObject="<p>No browser extensions found</p>"
    fi
    # Replace <table> with <table style='white-space:nowrap;'>
    htmlObject=${htmlObject//<table>/<table style=\'white-space:nowrap;\'>}
    # Tries to set the WYSIWYG custom field using ninjarmm-cli and captures the output
    if ! output=$(printf '%b' "$htmlObject" | /Applications/NinjaRMMAgent/programdata/ninjarmm-cli set --stdin "$_arg_wysiwygField" 2>&1); then
        echo "[Error] $output" >&2
        EXITCODE=1
    else
        echo "[Info] Successfully set Custom Field '$_arg_wysiwygField'!"
    fi
fi

# If both _arg_multilineField and _arg_wysiwygField are not set, or if there is an error saving to the custom fields, print the results
if [[ -z $_arg_multilineField && -z $_arg_wysiwygField ]] || [[ $EXITCODE -ne 0 ]]; then
    echo ""
    echo "$safari_title:"
    echo "$safari_extensions" | while IFS='|' read -r user name id; do
        if [[ -n "$name" ]]; then
            printf "User: %s, Name: %s, Id: %s\n" "$user" "$name" "$id"
        fi
    done
    echo ""
    echo "$chrome_title:"
    echo "$chrome_extensions" | while IFS='|' read -r user profileName name id version; do
        if [[ -n "$name" ]]; then
            printf "User: %s, Profile Name: %s, Name: %s, Id: %s, Version: %s\n" "$user" "$profileName" "$name" "$id" "$version"
        fi
    done
    echo ""
    echo "$firefox_title:"
    echo "$firefox_addons" | while IFS='|' read -r user profileName name id description; do
        if [[ -n "$name" ]]; then
            printf "User: %s, Profile Name: %s, Name: %s, Id: %s, Description: %s\n" "$user" "$profileName" "$name" "$id" "$description"
        fi
    done
    echo ""
    echo "$edge_title:"
    echo "$edge_extensions" | while IFS='|' read -r user profileName name id version; do
        if [[ -n "$name" ]]; then
            printf "User: %s, Profile Name: %s, Name: %s, Id: %s, Version: %s\n" "$user" "$profileName" "$name" "$id" "$version"
        fi
    done
    # If no extensions are found, print a message
    if [[ -z "$safari_extensions" ]] && [[ -z "$chrome_extensions" ]] && [[ -z "$firefox_addons" ]] && [[ -z "$edge_extensions" ]]; then
        echo "[Info] No Browser Extensions Found"
    fi
fi

# Checks if an error code is set and exits the script with that code
if [[ -n $EXITCODE ]]; then
    echo "[Error] Failed to save browser extensions to custom field '$_arg_multilineField' or '$_arg_wysiwygField'."
    exit "$EXITCODE"
fi

 

Greifen Sie auf über 300 Skripte im NinjaOne Dojo zu.

Zugang erhalten

Detailansicht

Das Skript beginnt mit einer Definition seines Zwecks und seiner Verwendung, einer klaren Beschreibung und einer Beispielausgabe. Anschließend wird verifiziert, ob Hilfsargumente vorhanden sind, und es werden Werte für benutzerdefinierte Felder mit Hilfe von ninjarmm-cli gesetzt, einem Tool zur Verwaltung benutzerdefinierter Felder in NinjaOne RMM.

1. Initialisierung und Argumentbehandlung

  • Bei der Initialisierung des Skripts wird geprüft, ob Hilfe-Argumente (–help oder -h) angegeben wurden. Ist dies der Fall, erscheinen die Verwendungshinweise und das Skript beendet sich.
  • Es definiert eine Funktion ‚GetCustomField‘ zum Abrufen von Werten der benutzerdefinierten Felder mit ninjarmm-cli.

2. Überprüfungen von Umgebungsvariablen

  • Das Skript überprüft, ob ‚multilineCustomFieldName‘ und ‚wysiwygCustomFieldName‘ als Umgebungsvariablen gesetzt sind und aktualisiert die Argumente des Skripts dementsprechend.

3. Identifikation des Benutzerverzeichnisses

  • Es identifiziert die Benutzerverzeichnisse in /Users, mit Ausnahme des Verzeichnisses ‚Shared‘, und sammelt alle Benutzerkonten auf dem System.

4. Safari-Erweiterungen

  • Für jeden Benutzer sucht das Skript nach plist-Dateien der Safari-Erweiterungen und extrahiert mithilfe regulärer Ausdrücke Details.

5. Erweiterungen für Chrome und Edge

  • Das Skript verwendet die Funktion ‚createExtList‘, um Chrome- und Edge-Erweiterungen zu finden und aufzulisten. Es analysiert manifest.json-Dateien in den Erweiterungsverzeichnissen des Browsers und extrahiert Erweiterungsnamen, IDs und Versionen.

6. Firefox-Erweiterungen

  • Für Firefox analysiert das Skript die Dateien profiles.ini und addons.json, um installierte Addons aufzulisten, einschließlich Namen, IDs und Beschreibungen.

7. Einstellung der benutzerdefinierten Felder und Ausgabe

  • Wenn benutzerdefinierte Felder gesetzt sind, formatiert das Skript die gesammelten Daten in HTML-Tabellen oder Plain-Text und versucht dann, diese Felder mit ninjarmm-cli einzurichten. Wenn die Einrichtung der Felder fehlschlägt, werden die gesammelten Daten auf der Konsole ausgegeben.

Dieser strukturierte Ansatz gewährleistet eine gründliche Abdeckung aller wichtigen Browser und bietet ein klares und detailliertes Inventar durch das Auflisten von Browsererweiterungen unter macOS.

Potenzielle Anwendungsfälle

Stellen Sie sich einen MSP vor, der dafür sorgen soll, dass alle Kunden-Computer sicher sind. Mit diesem Skript kann der MSP schnell eine Liste aller Browsererweiterungen erstellen, die auf den macOS-Systemen der Kunden installiert sind. Wird beispielsweise eine Sicherheitsschwachstelle in einer bestimmten Erweiterung entdeckt, kann der MSP mithilfe des Skripts feststellen, auf welchen Clients die Erweiterung installiert ist, und entsprechende Maßnahmen zur Risikominimierung ergreifen.

Vergleiche

Andere Methoden zum Auflisten von Browsererweiterungen unter macOS bestehen in der Regel in manuellen Überprüfungen oder der Verwendung browserspezifischer Tools, was zeitaufwändig und fehleranfällig sein kann. Dieses Skript bietet eine einheitliche, automatisierte Lösung, die in mehreren Browsern funktioniert, Zeit spart und die Wahrscheinlichkeit eines Flüchtigkeitsfehlers verringert. Darüber hinaus ermöglicht die Integration mit NinjaOne RMM eine zentralisierte Verwaltung und Berichterstattung, die mit manuellen Methoden nicht möglich ist.

FAQs

  • Wie geht das Skript mit verschiedenen Benutzerprofilen um?
    Das Skript durchläuft jedes Benutzerverzeichnis in /Users und verarbeitet jedes in den jeweiligen Browserverzeichnissen gefundene Profil.
  • Was passiert, wenn das Skript nicht auf ein Benutzerverzeichnis zugreifen kann?
    Das Skript warnt, dass es möglicherweise Root-Zugriff benötigt, um alle Benutzerverzeichnisse zu lesen. Wenn Sie das Skript mit Root-Rechten ausführen, haben Sie umfassenden Zugriff.
  • Kann das Skript auch auf Nicht-macOS-Systemen verwendet werden?
    Nein, dieses Skript ist aufgrund der verwendeten Pfade und Tools speziell für macOS konzipiert.

Folgen

Die Ergebnisse dieses Skripts bieten wichtige Einblicke in die in mehreren Browsern installierten Erweiterungen und weisen auf potenzielle Sicherheitsrisiken hin. Durch eine klare Inventarisierung können IT-Experten die Einhaltung von Sicherheitsrichtlinien sicherstellen, nicht autorisierte Erweiterungen erkennen und Schwachstellen umgehend beheben.

Empfehlungen

  • Führen Sie das Skript als mit Root-Rechten aus, um den Zugriff auf alle Benutzerverzeichnisse zu gewährleisten.
  • Überprüfen Sie regelmäßig die Erweiterungslisten, um unnötige oder potenziell schädliche Erweiterungen zu identifizieren und zu entfernen.
  • Integrieren Sie das Skript in ein Überwachungs-Tool wie NinjaOne RMM für kontinuierliche Verwaltung und Berichterstellung.

Abschließende Überlegungen

Die Verwaltung von Browser-Erweiterungen ist ein wichtiger Aspekt bei der Aufrechterhaltung einer sicheren IT-Umgebung. Dieses Skript bietet eine leistungsstarke, automatisierte Lösung zum Auflisten von Browsererweiterungen unter macOS und ist damit ein unschätzbares Tool für IT-Experten und MSPs. Durch die Integration von NinjaOne können Sie Ihre Möglichkeiten zur Überwachung und Verwaltung von Browsererweiterungen verbessern und so für eine sichere und Compliance-konforme Computerumgebung sorgen.

Nächste Schritte

Der Aufbau eines effizienten und effektiven IT-Teams erfordert eine zentralisierte Lösung, die als einheitliches Tool zur Bereitstellung von IT-Dienstleistungen fungiert. NinjaOne ermöglicht es IT-Teams, alle Geräte zu überwachen, zu verwalten, zu sichern und zu unterstützen, unabhängig vom Standort, ohne dass eine komplexe Infrastruktur vor Ort erforderlich ist.

Erfahren Sie mehr über NinjaOne Endpoint Management schauen Sie sich eine Live-Tour an oder starten Sie Ihre kostenlose Testversion der NinjaOne Plattform.

Kategorien:

Das könnte Sie auch interessieren

Umfassender Guide für die Proxmox-Cluster-Statusüberwachung unter Linux

Umfassender Guide zum Auflisten von Browsererweiterungen unter macOS

Schritt-für-Schritt-Guide zur Automatisierung der ConnectWise ScreenConnect-Installation unter Linux

Wie man mit einem Bash-Skript Absturzdateien unter MacOS entdeckt

Linux-Skript-Guide: Abruf vom Status des Netzwerk-Knoten-Speichers in Proxmox

Wie man TeamViewer-Dienste mit PowerShell neu startet und verwaltet

×

Sehen Sie NinjaOne in Aktion!

Mit dem Absenden dieses Formulars akzeptiere ich die Datenschutzerklärung von NinjaOne.

NinjaOne Allgemeine Geschäftsbedingungen für Skripte

Indem Sie unten auf die Schaltfläche „Ich akzeptiere“ klicken, erklären Sie Ihr Einverständnis mit den folgenden rechtlichen Bedingungen sowie mit unseren Nutzungsbedingungen:

  • Eigentumsrechte: NinjaOne besitzt und wird weiterhin alle Rechte, Titel und Interessen an dem Skript (einschließlich des Urheberrechts) behalten. NinjaOne gewährt Ihnen eine eingeschränkte Lizenz zur Nutzung des Skripts in Übereinstimmung mit diesen rechtlichen Bedingungen.
  • Einschränkung der Nutzung: Sie dürfen das Skript nur für Ihre legitimen persönlichen oder internen Geschäftszwecke verwenden und es nicht an Dritte weitergeben.
  • Verbot der Wiederveröffentlichung: Sie sind unter keinen Umständen berechtigt, das Skript in einer Skriptbibliothek, die einem anderen Softwareanbieter gehört oder von diesem kontrolliert wird, erneut zu veröffentlichen.
  • Gewährleistungsausschluss: Das Skript wird „wie gesehen“ und „wie verfügbar“ bereitgestellt, ohne jegliche Garantie. NinjaOne gibt keine Versprechen oder Garantien, dass das Skript frei von Fehlern ist oder dass es Ihre speziellen Bedürfnisse oder Erwartungen erfüllt.
  • Risikoübernahme: Die Verwendung des Skripts erfolgt auf eigene Gefahr. Sie erkennen an, dass die Nutzung des Skripts mit bestimmten Risiken verbunden ist, und Sie verstehen und übernehmen jedes dieser Risiken.
  • Verzicht und Freigabe: Sie machen NinjaOne nicht für nachteilige oder unbeabsichtigte Folgen verantwortlich, die sich aus Ihrer Nutzung des Skripts ergeben, und Sie verzichten auf alle gesetzlichen oder billigkeitsrechtlichen Rechte oder Rechtsmittel, die Sie gegen NinjaOne im Zusammenhang mit Ihrer Nutzung des Skripts haben könnten.
  • EULA: Wenn Sie ein NinjaOne-Kunde sind, unterliegt Ihre Nutzung des Skripts dem für Sie geltenden Endbenutzer-Lizenzvertrag (EULA).