Can support EA for this script

JL85
New Contributor III

Dear Team,

I have a script to obtain all browsers extensions (Safari, Edge, Chrome and Firefox) the script is running well and I would like to use it as EA. Can any body give a hand? Thanks

Here is the output 

[Warn] This script may not be able to access all user directories unless run as root.

Safari Extensions:
User: xxx, Name: com.adguard.safari.AdGuard.Extension, Id: TC3Q7MAJXF

Chrome Extensions:
User: xxx, Profile Name: Person 1, Name: Google Docs Offline, Id: ghbmnnjooekpmoecnnnilnnbdlolhkhi, Version: 1.78.1

Firefox Addons:
User: xxx, Profile Name: default-release, Name: AdGuard AdBlocker, Id: adguardadblocker@adguard.com, Description: Unmatched adblock extension against advertising and pop-ups. Blocks ads on Facebook, Youtube and all other websites.

 

Here is the scripts

#!/usr/bin/env bash

_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


# 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

    # 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"  set --stdin "$_arg_multilineField" 2>&1); then
        echo "[Error] $output" >&2
        EXITCODE=1
    fi


# Checks if there is a WYSIWYG custom field set
if [[ -n $_arg_wysiwygField ]]; then
    echo ""
    
    # 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" | set --stdin "$_arg_wysiwygField" 2>&1); then
    EXITCODE=1
    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

 

 

 

1 ACCEPTED SOLUTION

stevewood
Honored Contributor II
Honored Contributor II

It should be as simple as adding <result></result> to the if/then statement that echos output if _arg_multilineField and _arg_wysiwygField are not set. In my IDE I set line 415 to 

echo "<result>"

and line 443 to:

echo "</result>"

So the last 35 or so lines of the script look like this:

 

# 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 "<result>"
    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
    echo "</result>"
    # If no extensions are found, print a message
    if [[ -z "$safari_extensions" ]] && [[ -z "$chrome_extensions" ]] && [[ -z "$firefox_addons" ]] && [[ -z "$edge_extensions" ]]; then
        echo "<result>[Info] No Browser Extensions Found</result>"
    fi
fi

 

That seemed to work in my very limited testing, and produced an EA that looked like this on a machine that only has extensions in Edge:

CleanShot 2024-08-07 at 07.54.32.png

View solution in original post

4 REPLIES 4

stevewood
Honored Contributor II
Honored Contributor II

It should be as simple as adding <result></result> to the if/then statement that echos output if _arg_multilineField and _arg_wysiwygField are not set. In my IDE I set line 415 to 

echo "<result>"

and line 443 to:

echo "</result>"

So the last 35 or so lines of the script look like this:

 

# 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 "<result>"
    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
    echo "</result>"
    # If no extensions are found, print a message
    if [[ -z "$safari_extensions" ]] && [[ -z "$chrome_extensions" ]] && [[ -z "$firefox_addons" ]] && [[ -z "$edge_extensions" ]]; then
        echo "<result>[Info] No Browser Extensions Found</result>"
    fi
fi

 

That seemed to work in my very limited testing, and produced an EA that looked like this on a machine that only has extensions in Edge:

CleanShot 2024-08-07 at 07.54.32.png

husnudagidir
Contributor

Hi Everyone,

 

The script worked for me and reflected its output in the Details section under Logs. However, I did not understand how to export this data. Do you have any comments on this?

stevewood
Honored Contributor II
Honored Contributor II

If you need to export the data you can use an Advanced Search. Select the checkbox on the Extension Attributes tab for the EA that you want to export. Then use the Report tab to export a CSV or schedule the sending of the report.

https://learn.jamf.com/en-US/bundle/jamf-pro-documentation-current/page/Advanced_Computer_Searches.h... 

Thank you @stevewood