| #!/bin/bash |
| |
| |
| |
| |
| |
| |
|
|
| |
| set +e |
|
|
| |
| REPORT_TIMESTAMP=$(date +%Y%m%d_%H%M%S) |
| OUTPUT_FILE="$HOME/Desktop/plugin_diagnostic_$REPORT_TIMESTAMP.txt" |
|
|
| |
| log() { |
| echo -e "$1" |
| |
| echo -e "$1" | sed 's/\x1b\[[0-9;]*m//g' >> "$OUTPUT_FILE" |
| } |
|
|
| |
| echo "# After Effects Plugin Diagnostic Report" > "$OUTPUT_FILE" |
| echo "# Generated: $(date)" >> "$OUTPUT_FILE" |
| echo "" >> "$OUTPUT_FILE" |
|
|
| |
| RED='\033[0;31m' |
| GREEN='\033[0;32m' |
| YELLOW='\033[1;33m' |
| BLUE='\033[0;34m' |
| NC='\033[0m' |
|
|
| |
| PASS="β
" |
| FAIL="β" |
| WARN="β οΈ" |
| INFO="βΉοΈ" |
|
|
| print_header() { |
| log "" |
| log "${BLUE}βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ${NC}" |
| log "${BLUE} $1${NC}" |
| log "${BLUE}βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ${NC}" |
| } |
|
|
| print_section() { |
| log "" |
| log "${YELLOW}ββ $1 ββ${NC}" |
| } |
|
|
| |
| DEFAULT_PLUGIN="/Library/Application Support/Adobe/Common/Plug-ins/7.0/MediaCore/BSKL/AIColorMatch.plugin" |
| MEDIACORE_PATH="/Library/Application Support/Adobe/Common/Plug-ins/7.0/MediaCore" |
|
|
| |
| if [ -z "$1" ]; then |
| if [ -d "$DEFAULT_PLUGIN" ]; then |
| PLUGIN_PATH="$DEFAULT_PLUGIN" |
| echo "Using default plugin path: $PLUGIN_PATH" |
| else |
| echo -e "${RED}Usage: bash check_plugin.sh /path/to/Plugin.plugin${NC}" |
| echo "" |
| echo "Default plugin not found at:" |
| echo " $DEFAULT_PLUGIN" |
| echo "" |
| echo "Please specify plugin path manually." |
| exit 1 |
| fi |
| else |
| PLUGIN_PATH="$1" |
| fi |
|
|
| |
| if [ ! -d "$PLUGIN_PATH" ]; then |
| log "${FAIL} Plugin not found: $PLUGIN_PATH" |
| exit 1 |
| fi |
|
|
| |
| PLUGIN_NAME=$(basename "$PLUGIN_PATH" .plugin) |
|
|
| print_header "After Effects Plugin Diagnostic Report" |
| log "" |
| log "Plugin: ${GREEN}$PLUGIN_NAME${NC}" |
| log "Path: $PLUGIN_PATH" |
| log "Date: $(date)" |
| log "macOS: $(sw_vers -productVersion) ($(sw_vers -buildVersion))" |
| log "Machine: $(uname -m)" |
|
|
| |
| |
| |
| print_section "All Plugins in MediaCore" |
|
|
| if [ -d "$MEDIACORE_PATH" ]; then |
| log "${BLUE}Scanning: $MEDIACORE_PATH${NC}" |
| log "" |
|
|
| |
| PLUGIN_COUNT=0 |
| while IFS= read -r plugin_bundle; do |
| PLUGIN_COUNT=$((PLUGIN_COUNT + 1)) |
| plugin_name=$(basename "$plugin_bundle" .plugin) |
| plugin_modified=$(stat -f "%Sm" -t "%Y-%m-%d %H:%M" "$plugin_bundle" 2>/dev/null || echo "unknown") |
| plugin_size=$(du -sh "$plugin_bundle" 2>/dev/null | cut -f1 || echo "?") |
| plugin_perms=$(stat -f "%Sp" "$plugin_bundle" 2>/dev/null || echo "?") |
| plugin_owner=$(stat -f "%Su:%Sg" "$plugin_bundle" 2>/dev/null || echo "?") |
|
|
| |
| binary_path="$plugin_bundle/Contents/MacOS/$plugin_name" |
| if [ -f "$binary_path" ]; then |
| arch=$(lipo -archs "$binary_path" 2>/dev/null || echo "?") |
| binary_perms=$(stat -f "%Sp" "$binary_path" 2>/dev/null || echo "?") |
|
|
| |
| codesign_check=$(codesign -dv --verbose=2 "$plugin_bundle" 2>&1) |
| if echo "$codesign_check" | grep -q "Authority="; then |
| signer=$(echo "$codesign_check" | grep "Authority=" | head -1 | cut -d= -f2 | cut -c1-40) |
| sign_status="$PASS" |
| elif echo "$codesign_check" | grep -q "Signature=adhoc"; then |
| signer="ad-hoc" |
| sign_status="$WARN" |
| elif echo "$codesign_check" | grep -q "TeamIdentifier="; then |
| |
| team=$(echo "$codesign_check" | grep "TeamIdentifier=" | cut -d= -f2) |
| signer="Team: $team" |
| sign_status="$PASS" |
| else |
| signer="unsigned" |
| sign_status="$FAIL" |
| fi |
|
|
| |
| if echo "$codesign_check" | grep -q "Notarization Ticket"; then |
| notarized="yes" |
| else |
| notarized="no" |
| fi |
|
|
| |
| quarantine_check=$(xattr -p com.apple.quarantine "$plugin_bundle" 2>/dev/null) |
| if [ -n "$quarantine_check" ]; then |
| qflag="$WARN quarantined" |
| else |
| qflag="" |
| fi |
|
|
| log "$sign_status $plugin_name $qflag" |
| log " Modified: $plugin_modified | Size: $plugin_size | Arch: $arch" |
| log " Perms: $plugin_perms | Binary: $binary_perms | Owner: $plugin_owner" |
| log " Signed: $signer | Notarized: $notarized" |
| else |
| log "$WARN $plugin_name (binary missing)" |
| log " Modified: $plugin_modified | Size: $plugin_size" |
| log " Perms: $plugin_perms | Owner: $plugin_owner" |
| fi |
| log "" |
| done < <(find "$MEDIACORE_PATH" -maxdepth 2 -name "*.plugin" -type d 2>/dev/null | sort) |
|
|
| log "Total plugins found: $PLUGIN_COUNT" |
| else |
| log "$WARN MediaCore directory not found: $MEDIACORE_PATH" |
| fi |
|
|
| |
| |
| |
| print_section "Plugin Structure" |
|
|
| BINARY_PATH="$PLUGIN_PATH/Contents/MacOS/$PLUGIN_NAME" |
| PLIST_PATH="$PLUGIN_PATH/Contents/Info.plist" |
| RSRC_PATH="$PLUGIN_PATH/Contents/Resources/$PLUGIN_NAME.rsrc" |
|
|
| if [ -f "$BINARY_PATH" ]; then |
| log "$PASS Binary: $BINARY_PATH" |
| BINARY_SIZE=$(du -h "$BINARY_PATH" | cut -f1) |
| log " Size: $BINARY_SIZE" |
| else |
| log "$FAIL Binary NOT FOUND: $BINARY_PATH" |
| fi |
|
|
| if [ -f "$PLIST_PATH" ]; then |
| log "$PASS Info.plist: exists" |
| else |
| log "$FAIL Info.plist NOT FOUND" |
| fi |
|
|
| if [ -f "$RSRC_PATH" ]; then |
| log "$PASS Resource file: $PLUGIN_NAME.rsrc" |
| else |
| log "$WARN Resource file not found (may be embedded)" |
| fi |
|
|
| |
| |
| |
| print_section "Architecture" |
|
|
| if [ -f "$BINARY_PATH" ]; then |
| ARCHS=$(lipo -archs "$BINARY_PATH" 2>/dev/null || echo "unknown") |
|
|
| if [[ "$ARCHS" == *"arm64"* ]] && [[ "$ARCHS" == *"x86_64"* ]]; then |
| log "$PASS Universal Binary: $ARCHS" |
| elif [[ "$ARCHS" == *"arm64"* ]]; then |
| log "$WARN ARM64 only (no Intel support)" |
| elif [[ "$ARCHS" == *"x86_64"* ]]; then |
| log "$WARN Intel only (no Apple Silicon native)" |
| else |
| log "$FAIL Unknown architecture: $ARCHS" |
| fi |
|
|
| |
| MACHINE_ARCH=$(uname -m) |
| log "$INFO Current machine: $MACHINE_ARCH" |
| fi |
|
|
| |
| |
| |
| print_section "Dependencies" |
|
|
| if [ -f "$BINARY_PATH" ]; then |
| MISSING_DEPS=0 |
|
|
| |
| while IFS= read -r line; do |
| |
| [[ -z "$line" ]] && continue |
| [[ "$line" == *"$BINARY_PATH"* ]] && continue |
|
|
| |
| LIB_PATH=$(echo "$line" | sed 's/^[[:space:]]*//' | cut -d' ' -f1) |
|
|
| |
| [[ -z "$LIB_PATH" ]] && continue |
|
|
| |
| |
| |
| if [[ "$LIB_PATH" == /usr/lib/* ]]; then |
| log "$PASS System: $(basename "$LIB_PATH") (shared cache)" |
| elif [[ "$LIB_PATH" == /System/Library/* ]]; then |
| if [ -d "${LIB_PATH%/*}" ] || [ -f "$LIB_PATH" ]; then |
| log "$PASS Framework: $(echo "$LIB_PATH" | grep -o '[^/]*\.framework')" |
| else |
| log "$FAIL MISSING: $LIB_PATH" |
| MISSING_DEPS=$((MISSING_DEPS + 1)) |
| fi |
| elif [[ "$LIB_PATH" == @rpath/* ]] || [[ "$LIB_PATH" == @loader_path/* ]] || [[ "$LIB_PATH" == @executable_path/* ]]; then |
| log "$WARN Relative: $LIB_PATH" |
| elif [ -f "$LIB_PATH" ]; then |
| log "$PASS External: $LIB_PATH" |
| else |
| log "$FAIL MISSING: $LIB_PATH" |
| MISSING_DEPS=$((MISSING_DEPS + 1)) |
| fi |
| done < <(otool -L "$BINARY_PATH" 2>/dev/null | sort -u) |
|
|
| if [ $MISSING_DEPS -gt 0 ]; then |
| log "" |
| log "${RED}$FAIL Found $MISSING_DEPS missing dependencies!${NC}" |
| else |
| log "" |
| log "${GREEN}$PASS All dependencies found${NC}" |
| fi |
| fi |
|
|
| |
| |
| |
| print_section "Code Signing & Notarization" |
|
|
| CODESIGN_OUTPUT=$(codesign -dv --verbose=4 "$PLUGIN_PATH" 2>&1) |
| SIGNING_ISSUE=0 |
|
|
| |
| if echo "$CODESIGN_OUTPUT" | grep -q "Authority="; then |
| |
| log "${BLUE}Certificate Chain:${NC}" |
| echo "$CODESIGN_OUTPUT" | grep "Authority=" | while read -r line; do |
| CERT=$(echo "$line" | cut -d= -f2) |
| log " $CERT" |
| done |
|
|
| |
| TEAM_ID=$(echo "$CODESIGN_OUTPUT" | grep "TeamIdentifier=" | cut -d= -f2) |
| if [ -n "$TEAM_ID" ] && [ "$TEAM_ID" != "not set" ]; then |
| log "$PASS Team ID: $TEAM_ID" |
| else |
| log "$WARN Team ID not set" |
| fi |
|
|
| |
| SIGN_TIMESTAMP=$(echo "$CODESIGN_OUTPUT" | grep "Timestamp=" | cut -d= -f2) |
| if [ -n "$SIGN_TIMESTAMP" ]; then |
| log "$PASS Signed on: $SIGN_TIMESTAMP" |
| else |
| log "$WARN No timestamp (signature may expire with certificate)" |
| fi |
|
|
| |
| RUNTIME=$(echo "$CODESIGN_OUTPUT" | grep "Runtime Version=" | cut -d= -f2) |
| if [ -n "$RUNTIME" ]; then |
| log "$PASS Hardened Runtime: $RUNTIME" |
| else |
| log "$WARN Hardened runtime not enabled" |
| fi |
|
|
| |
| if echo "$CODESIGN_OUTPUT" | grep -q "Notarization Ticket"; then |
| log "$PASS Notarization ticket stapled" |
| else |
| log "$WARN Notarization ticket NOT stapled" |
| SIGNING_ISSUE=1 |
| fi |
|
|
| elif echo "$CODESIGN_OUTPUT" | grep -q "adhoc"; then |
| log "$WARN Ad-hoc signed (development only)" |
| SIGNING_ISSUE=1 |
| else |
| log "$FAIL NOT SIGNED" |
| SIGNING_ISSUE=1 |
| fi |
|
|
| |
| log "" |
| log "${BLUE}Signature Verification:${NC}" |
| VERIFY_OUTPUT=$(codesign --verify --deep --strict "$PLUGIN_PATH" 2>&1) |
| if [ $? -eq 0 ]; then |
| log "$PASS Signature integrity: valid" |
| else |
| log "$FAIL Signature integrity: INVALID" |
| log " $VERIFY_OUTPUT" |
| SIGNING_ISSUE=1 |
| fi |
|
|
| |
| log "" |
| log "${BLUE}Gatekeeper Assessment:${NC}" |
| SPCTL_OUTPUT=$(spctl --assess --type execute -v "$PLUGIN_PATH" 2>&1) |
| SPCTL_EXIT=$? |
| if [ $SPCTL_EXIT -eq 0 ]; then |
| log "$PASS Gatekeeper: accepted" |
| elif echo "$SPCTL_OUTPUT" | grep -q "rejected"; then |
| |
| if echo "$SPCTL_OUTPUT" | grep -q "notarized"; then |
| log "$WARN Gatekeeper: requires notarization" |
| elif echo "$SPCTL_OUTPUT" | grep -q "not.*app"; then |
| log "$PASS Gatekeeper: valid code (not an app bundle)" |
| else |
| log "$FAIL Gatekeeper: rejected" |
| log " $SPCTL_OUTPUT" |
| fi |
| else |
| log "$INFO Gatekeeper: $SPCTL_OUTPUT" |
| fi |
|
|
| |
| log "" |
| log "${BLUE}Notarization Status (online check):${NC}" |
| STAPLER_OUTPUT=$(xcrun stapler validate "$PLUGIN_PATH" 2>&1) |
| if echo "$STAPLER_OUTPUT" | grep -q "valid"; then |
| log "$PASS Notarization: verified" |
| elif echo "$STAPLER_OUTPUT" | grep -q "not valid"; then |
| log "$FAIL Notarization: NOT valid or not notarized" |
| SIGNING_ISSUE=1 |
| else |
| log "$INFO $STAPLER_OUTPUT" |
| fi |
|
|
| |
| |
| |
| print_section "Quarantine Status" |
|
|
| QUARANTINE=$(xattr -p com.apple.quarantine "$PLUGIN_PATH" 2>/dev/null || echo "") |
|
|
| if [ -n "$QUARANTINE" ]; then |
| log "$WARN Quarantine flag is SET" |
| log " Value: $QUARANTINE" |
| log "" |
| log "${YELLOW}To remove quarantine, run:${NC}" |
| log " xattr -cr \"$PLUGIN_PATH\"" |
| else |
| log "$PASS No quarantine flag" |
| fi |
|
|
| |
| |
| |
| print_section "Info.plist Analysis" |
|
|
| if [ -f "$PLIST_PATH" ]; then |
| |
| BUNDLE_ID=$(/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "$PLIST_PATH" 2>/dev/null || echo "") |
| if [ -n "$BUNDLE_ID" ] && [ "$BUNDLE_ID" != "" ]; then |
| log "$PASS CFBundleIdentifier: $BUNDLE_ID" |
| else |
| log "$FAIL CFBundleIdentifier is EMPTY!" |
| log " ${YELLOW}This may cause loading issues on some systems${NC}" |
| fi |
|
|
| |
| BUNDLE_EXEC=$(/usr/libexec/PlistBuddy -c "Print :CFBundleExecutable" "$PLIST_PATH" 2>/dev/null || echo "") |
| if [ "$BUNDLE_EXEC" = "$PLUGIN_NAME" ]; then |
| log "$PASS CFBundleExecutable: $BUNDLE_EXEC" |
| else |
| log "$WARN CFBundleExecutable: $BUNDLE_EXEC (expected: $PLUGIN_NAME)" |
| fi |
|
|
| |
| MIN_VERSION=$(/usr/libexec/PlistBuddy -c "Print :LSMinimumSystemVersion" "$PLIST_PATH" 2>/dev/null || echo "not set") |
| log "$INFO Minimum macOS: $MIN_VERSION" |
|
|
| |
| CURRENT_MACOS=$(sw_vers -productVersion) |
| log "$INFO Current macOS: $CURRENT_MACOS" |
| fi |
|
|
| |
| |
| |
| print_section "Entry Point" |
|
|
| if [ -f "$BINARY_PATH" ]; then |
| if nm "$BINARY_PATH" 2>/dev/null | grep -q "_EffectMain"; then |
| log "$PASS EffectMain symbol exported" |
| elif nm "$BINARY_PATH" 2>/dev/null | grep -q "_main"; then |
| log "$WARN Found _main but not _EffectMain" |
| else |
| log "$FAIL No EffectMain symbol found!" |
| fi |
| fi |
|
|
| |
| |
| |
| print_section "File Permissions & Ownership" |
|
|
| PERM_ISSUES=0 |
| CURRENT_USER=$(whoami) |
|
|
| log "${BLUE}All files in plugin bundle:${NC}" |
| log "" |
|
|
| |
| while IFS= read -r item; do |
| |
| STAT_OUTPUT=$(stat -f "%Sp %Su %Sg" "$item" 2>/dev/null) |
| PERMS=$(echo "$STAT_OUTPUT" | cut -d' ' -f1) |
| OWNER=$(echo "$STAT_OUTPUT" | cut -d' ' -f2) |
| GROUP=$(echo "$STAT_OUTPUT" | cut -d' ' -f3) |
|
|
| |
| REL_PATH="${item#$PLUGIN_PATH}" |
| [ -z "$REL_PATH" ] && REL_PATH="/" |
|
|
| |
| HAS_ISSUE=0 |
|
|
| |
| if [ -d "$item" ]; then |
| ITEM_TYPE="d" |
| |
| if [[ "$PERMS" != d*r?x*r?x*r?x* ]] && [[ "$PERMS" != d*r?x*r?x* ]]; then |
| HAS_ISSUE=1 |
| fi |
| else |
| |
| if [[ "$PERMS" != -*r* ]]; then |
| HAS_ISSUE=1 |
| fi |
| |
| if [ "$item" = "$BINARY_PATH" ] && [[ "$PERMS" != -*x* ]]; then |
| HAS_ISSUE=1 |
| fi |
| fi |
|
|
| |
| if [ $HAS_ISSUE -eq 1 ]; then |
| log "$FAIL $PERMS $OWNER:$GROUP $REL_PATH" |
| PERM_ISSUES=$((PERM_ISSUES + 1)) |
| else |
| log "$PASS $PERMS $OWNER:$GROUP $REL_PATH" |
| fi |
|
|
| done < <(find "$PLUGIN_PATH" -print 2>/dev/null) |
|
|
| |
| log "" |
| if [ $PERM_ISSUES -eq 0 ]; then |
| log "${GREEN}$PASS All permissions look correct${NC}" |
| else |
| log "${RED}$FAIL Found $PERM_ISSUES permission issue(s)${NC}" |
| log "" |
| log "${YELLOW}To fix permissions, run:${NC}" |
| log " sudo chmod -R a+rX \"$PLUGIN_PATH\"" |
| log " sudo chmod a+x \"$BINARY_PATH\"" |
| fi |
|
|
| |
| log "" |
| log "${BLUE}Ownership Summary:${NC}" |
| |
| OWNERS=$(find "$PLUGIN_PATH" -print0 2>/dev/null | xargs -0 stat -f "%Su" | sort -u) |
| for owner in $OWNERS; do |
| COUNT=$(find "$PLUGIN_PATH" -user "$owner" 2>/dev/null | wc -l | tr -d ' ') |
| log " $owner: $COUNT items" |
| done |
|
|
| |
| |
| |
| print_section "Extended Attributes" |
|
|
| |
| XATTR_FILES=0 |
| log "${BLUE}Checking for extended attributes on all files...${NC}" |
| log "" |
|
|
| while IFS= read -r item; do |
| XATTRS=$(xattr "$item" 2>/dev/null) |
| if [ -n "$XATTRS" ]; then |
| REL_PATH="${item#$PLUGIN_PATH}" |
| [ -z "$REL_PATH" ] && REL_PATH="/" |
|
|
| log "$WARN $REL_PATH" |
| echo "$XATTRS" | while read -r attr; do |
| if [ -n "$attr" ]; then |
| log " $attr" |
| fi |
| done |
| XATTR_FILES=$((XATTR_FILES + 1)) |
| fi |
| done < <(find "$PLUGIN_PATH" -print 2>/dev/null) |
|
|
| if [ $XATTR_FILES -eq 0 ]; then |
| log "${GREEN}$PASS No extended attributes found${NC}" |
| else |
| log "" |
| log "${YELLOW}Found extended attributes on $XATTR_FILES file(s)${NC}" |
| log "${YELLOW}To remove all extended attributes, run:${NC}" |
| log " xattr -cr \"$PLUGIN_PATH\"" |
| fi |
|
|
| |
| |
| |
| print_header "Summary" |
|
|
| ISSUES=0 |
|
|
| |
| if [ -z "$BUNDLE_ID" ] || [ "$BUNDLE_ID" = "" ]; then |
| log "$FAIL Empty CFBundleIdentifier - may cause 'entry point' errors" |
| ISSUES=$((ISSUES + 1)) |
| fi |
|
|
| if [ -n "$QUARANTINE" ]; then |
| log "$WARN Quarantine flag set - may block plugin loading" |
| ISSUES=$((ISSUES + 1)) |
| fi |
|
|
| if [ $MISSING_DEPS -gt 0 ]; then |
| log "$FAIL Missing dependencies detected" |
| ISSUES=$((ISSUES + 1)) |
| fi |
|
|
| if [ $SIGNING_ISSUE -gt 0 ]; then |
| log "$WARN Code signing or notarization issues detected" |
| ISSUES=$((ISSUES + 1)) |
| fi |
|
|
| if [ $PERM_ISSUES -gt 0 ]; then |
| log "$FAIL Permission issues detected" |
| ISSUES=$((ISSUES + 1)) |
| fi |
|
|
| if [ $ISSUES -eq 0 ]; then |
| log "${GREEN}$PASS No issues detected${NC}" |
| else |
| log "" |
| log "${YELLOW}Found $ISSUES potential issue(s)${NC}" |
| log "" |
| log "${BLUE}Quick fixes:${NC}" |
| if [ -n "$QUARANTINE" ]; then |
| log " Remove quarantine: xattr -cr \"$PLUGIN_PATH\"" |
| fi |
| if [ $PERM_ISSUES -gt 0 ]; then |
| log " Fix permissions: sudo chmod -R a+rX \"$PLUGIN_PATH\"" |
| fi |
| fi |
|
|
| |
| |
| |
| log "" |
| log "βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ" |
| log "" |
| log "${GREEN}π Report saved to:${NC}" |
| log " $OUTPUT_FILE" |
| log "" |
| log "${YELLOW}Please send this file to support for analysis.${NC}" |
| log "" |
|
|
| |
| echo "" |
| echo "ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ" |
| echo "" |
| echo "π Report saved to Desktop:" |
| echo " $OUTPUT_FILE" |
| echo "" |
| echo "π Please send this file to BSKL support for analysis." |
| echo "" |
|
|