File size: 23,215 Bytes
29d46ae
7d2f50f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29d46ae
7d2f50f
29d46ae
 
7d2f50f
 
 
 
29d46ae
 
7d2f50f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d201ac4
 
7d2f50f
 
 
 
 
d201ac4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7d2f50f
d201ac4
 
7d2f50f
 
 
d201ac4
7d2f50f
d201ac4
7d2f50f
 
 
29d46ae
7d2f50f
29d46ae
 
7d2f50f
 
 
 
 
 
 
 
 
 
 
 
 
29d46ae
7d2f50f
29d46ae
 
7d2f50f
 
29d46ae
7d2f50f
29d46ae
 
7d2f50f
 
29d46ae
7d2f50f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29d46ae
7d2f50f
 
29d46ae
7d2f50f
 
 
 
 
 
 
29d46ae
7d2f50f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29d46ae
7d2f50f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29d46ae
7d2f50f
29d46ae
 
7d2f50f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29d46ae
7d2f50f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29d46ae
 
7d2f50f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d201ac4
7d2f50f
 
 
 
 
 
 
 
 
 
 
 
 
 
d201ac4
 
 
7d2f50f
 
 
 
 
d201ac4
7d2f50f
 
d201ac4
7d2f50f
d201ac4
7d2f50f
 
d201ac4
 
7d2f50f
 
 
d201ac4
 
 
 
 
 
7d2f50f
 
 
 
 
 
 
 
29d46ae
7d2f50f
 
 
 
 
29d46ae
 
7d2f50f
 
 
 
 
 
 
 
 
 
 
 
 
 
29d46ae
7d2f50f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29d46ae
7d2f50f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29d46ae
 
7d2f50f
 
 
 
29d46ae
7d2f50f
 
 
 
29d46ae
7d2f50f
 
 
 
29d46ae
7d2f50f
 
29d46ae
7d2f50f
 
 
 
 
 
 
 
 
 
29d46ae
7d2f50f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
#!/bin/bash
#
# After Effects Plugin Diagnostic Tool
# Checks dependencies, code signing, quarantine, and plugin structure
#
# Usage: bash check_plugin.sh /path/to/Plugin.plugin
#

# Don't exit on error - we want to continue checking even if some commands fail
set +e

# Output file on Desktop
REPORT_TIMESTAMP=$(date +%Y%m%d_%H%M%S)
OUTPUT_FILE="$HOME/Desktop/plugin_diagnostic_$REPORT_TIMESTAMP.txt"

# Function to output to both console and file
log() {
    echo -e "$1"
    # Strip color codes for file output
    echo -e "$1" | sed 's/\x1b\[[0-9;]*m//g' >> "$OUTPUT_FILE"
}

# Initialize output file
echo "# After Effects Plugin Diagnostic Report" > "$OUTPUT_FILE"
echo "# Generated: $(date)" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"

# Colors (for console only)
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# Icons
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 path for AIColorMatch
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"

# Check arguments - use default if not provided
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

# Validate plugin exists
if [ ! -d "$PLUGIN_PATH" ]; then
    log "${FAIL} Plugin not found: $PLUGIN_PATH"
    exit 1
fi

# Get plugin name
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)"

# ─────────────────────────────────────────────────────────────
# 0. ALL PLUGINS IN MEDIACORE
# ─────────────────────────────────────────────────────────────
print_section "All Plugins in MediaCore"

if [ -d "$MEDIACORE_PATH" ]; then
    log "${BLUE}Scanning: $MEDIACORE_PATH${NC}"
    log ""

    # Find all .plugin bundles
    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 "?")

        # Check if binary exists
        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 "?")

            # Check code signing
            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
                # Signed but no Authority line (Developer ID)
                team=$(echo "$codesign_check" | grep "TeamIdentifier=" | cut -d= -f2)
                signer="Team: $team"
                sign_status="$PASS"
            else
                signer="unsigned"
                sign_status="$FAIL"
            fi

            # Check notarization
            if echo "$codesign_check" | grep -q "Notarization Ticket"; then
                notarized="yes"
            else
                notarized="no"
            fi

            # Check quarantine
            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

# ─────────────────────────────────────────────────────────────
# 1. PLUGIN STRUCTURE
# ─────────────────────────────────────────────────────────────
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

# ─────────────────────────────────────────────────────────────
# 2. ARCHITECTURE
# ─────────────────────────────────────────────────────────────
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

    # Current machine
    MACHINE_ARCH=$(uname -m)
    log "$INFO Current machine: $MACHINE_ARCH"
fi

# ─────────────────────────────────────────────────────────────
# 3. DEPENDENCIES
# ─────────────────────────────────────────────────────────────
print_section "Dependencies"

if [ -f "$BINARY_PATH" ]; then
    MISSING_DEPS=0

    # Get unique dependencies (otool shows both archs for universal binary)
    while IFS= read -r line; do
        # Skip empty lines and the binary path itself
        [[ -z "$line" ]] && continue
        [[ "$line" == *"$BINARY_PATH"* ]] && continue

        # Extract library path
        LIB_PATH=$(echo "$line" | sed 's/^[[:space:]]*//' | cut -d' ' -f1)

        # Skip if empty
        [[ -z "$LIB_PATH" ]] && continue

        # Categorize
        # Note: Since macOS Big Sur, /usr/lib libraries are in shared cache
        # and don't exist as physical files, but they still work
        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

# ─────────────────────────────────────────────────────────────
# 4. CODE SIGNING & NOTARIZATION
# ─────────────────────────────────────────────────────────────
print_section "Code Signing & Notarization"

CODESIGN_OUTPUT=$(codesign -dv --verbose=4 "$PLUGIN_PATH" 2>&1)
SIGNING_ISSUE=0

# Check signing status
if echo "$CODESIGN_OUTPUT" | grep -q "Authority="; then
    # Extract all authorities (certificate chain)
    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
    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

    # Timestamp
    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 version (hardened runtime)
    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

    # Notarization check
    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

# Verify signature integrity
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

# Gatekeeper assessment (spctl)
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
    # Check the reason
    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

# Check if notarization can be verified online
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

# ─────────────────────────────────────────────────────────────
# 5. QUARANTINE
# ─────────────────────────────────────────────────────────────
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

# ─────────────────────────────────────────────────────────────
# 6. INFO.PLIST CHECK
# ─────────────────────────────────────────────────────────────
print_section "Info.plist Analysis"

if [ -f "$PLIST_PATH" ]; then
    # CFBundleIdentifier
    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

    # CFBundleExecutable
    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

    # LSMinimumSystemVersion
    MIN_VERSION=$(/usr/libexec/PlistBuddy -c "Print :LSMinimumSystemVersion" "$PLIST_PATH" 2>/dev/null || echo "not set")
    log "$INFO Minimum macOS: $MIN_VERSION"

    # Current macOS version
    CURRENT_MACOS=$(sw_vers -productVersion)
    log "$INFO Current macOS: $CURRENT_MACOS"
fi

# ─────────────────────────────────────────────────────────────
# 7. ENTRY POINT CHECK (via nm)
# ─────────────────────────────────────────────────────────────
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

# ─────────────────────────────────────────────────────────────
# 8. FILE PERMISSIONS & OWNERSHIP
# ─────────────────────────────────────────────────────────────
print_section "File Permissions & Ownership"

PERM_ISSUES=0
CURRENT_USER=$(whoami)

log "${BLUE}All files in plugin bundle:${NC}"
log ""

# Check each file and directory
while IFS= read -r item; do
    # Get permissions, owner, group
    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)

    # Get relative path for display
    REL_PATH="${item#$PLUGIN_PATH}"
    [ -z "$REL_PATH" ] && REL_PATH="/"

    # Determine status icon
    HAS_ISSUE=0

    # Check if it's a directory or file
    if [ -d "$item" ]; then
        ITEM_TYPE="d"
        # Directories should be readable and executable (r-x) by all
        if [[ "$PERMS" != d*r?x*r?x*r?x* ]] && [[ "$PERMS" != d*r?x*r?x* ]]; then
            HAS_ISSUE=1
        fi
    else
        # Files should be readable
        if [[ "$PERMS" != -*r* ]]; then
            HAS_ISSUE=1
        fi
        # Binary should be executable
        if [ "$item" = "$BINARY_PATH" ] && [[ "$PERMS" != -*x* ]]; then
            HAS_ISSUE=1
        fi
    fi

    # Always show the file with its permissions
    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)

# Summary of permissions
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

# Show ownership summary
log ""
log "${BLUE}Ownership Summary:${NC}"
# Get unique owners
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

# ─────────────────────────────────────────────────────────────
# 9. EXTENDED ATTRIBUTES
# ─────────────────────────────────────────────────────────────
print_section "Extended Attributes"

# Check for any extended attributes on all files
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

# ─────────────────────────────────────────────────────────────
# SUMMARY
# ─────────────────────────────────────────────────────────────
print_header "Summary"

ISSUES=0

# Check for common issues
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

# ─────────────────────────────────────────────────────────────
# OUTPUT FILE INFO
# ─────────────────────────────────────────────────────────────
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 ""

# Also print to console without colors for clarity
echo ""
echo "════════════════════════════════════════════════════════════"
echo ""
echo "πŸ“„ Report saved to Desktop:"
echo "   $OUTPUT_FILE"
echo ""
echo "πŸ‘‰ Please send this file to BSKL support for analysis."
echo ""