Code Signature Analysis and Security on macOS
Analyze and manage application code signatures on your MacFleet devices to enhance security, verify application authenticity, and implement enterprise signing policies. This tutorial covers code requirement extraction, signature verification, and comprehensive security analysis.
Understanding macOS Code Signatures
Code signatures on macOS provide cryptographic verification that applications haven't been tampered with and come from trusted sources. Key components include:
- Code Requirements - Constraints that code must satisfy to be considered valid
- Bundle Identifiers - Unique identifiers for applications
- Developer Certificates - Digital certificates used to sign applications
- Entitlements - Permissions and capabilities granted to applications
Enterprise Security Importance
Code signature analysis is crucial for enterprise security:
- Application Authenticity - Verify applications come from trusted developers
- Tampering Detection - Identify modified or compromised applications
- Privacy Policy Enforcement - Control access to protected user data
- Compliance Requirements - Meet security standards for regulated industries
- Malware Prevention - Block unauthorized or suspicious applications
Basic Code Signature Analysis
Extract Code Requirement
#!/bin/bash
# Basic code requirement extraction
APP_PATH="/System/Applications/Time Machine.app"
echo "Extracting code requirement for: $APP_PATH"
codesign -dr - "$APP_PATH"
Extract Code Requirement with Error Handling
#!/bin/bash
# Enhanced code requirement extraction with validation
extract_code_requirement() {
local app_path="$1"
# Validate input
if [[ -z "$app_path" ]]; then
echo "Error: No application path provided"
echo "Usage: extract_code_requirement <app_path>"
return 1
fi
# Check if application exists
if [[ ! -e "$app_path" ]]; then
echo "Error: Application not found at path: $app_path"
return 1
fi
echo "=== Code Requirement Analysis ==="
echo "Application: $app_path"
echo ""
# Extract code requirement
local code_req
code_req=$(codesign -dr - "$app_path" 2>&1)
if [[ $? -eq 0 ]]; then
echo "Code Requirement:"
echo "$code_req"
# Extract just the designated requirement
local designated_req
designated_req=$(echo "$code_req" | grep "designated =>" | sed 's/designated => //')
if [[ -n "$designated_req" ]]; then
echo ""
echo "Designated Requirement:"
echo "$designated_req"
fi
else
echo "Error extracting code requirement:"
echo "$code_req"
return 1
fi
}
# Usage examples
extract_code_requirement "/System/Applications/Safari.app"
extract_code_requirement "/Applications/Microsoft Word.app"
Comprehensive Application Analysis
#!/bin/bash
# Complete application signature analysis
analyze_application_signature() {
local app_path="$1"
local detailed="${2:-false}"
if [[ ! -e "$app_path" ]]; then
echo "Error: Application not found: $app_path"
return 1
fi
echo "=== Complete Application Signature Analysis ==="
echo "Application: $app_path"
echo "Analysis Date: $(date)"
echo ""
# Basic information
echo "--- Basic Information ---"
if [[ -d "$app_path" ]]; then
local bundle_id
bundle_id=$(defaults read "$app_path/Contents/Info.plist" CFBundleIdentifier 2>/dev/null || echo "Unknown")
echo "Bundle ID: $bundle_id"
local app_version
app_version=$(defaults read "$app_path/Contents/Info.plist" CFBundleShortVersionString 2>/dev/null || echo "Unknown")
echo "Version: $app_version"
fi
# Code signature verification
echo ""
echo "--- Code Signature Verification ---"
if codesign -v "$app_path" 2>/dev/null; then
echo "✓ Code signature is valid"
else
echo "⚠️ Code signature verification failed"
fi
# Detailed signature information
echo ""
echo "--- Signature Details ---"
codesign -dv "$app_path" 2>&1
# Code requirements
echo ""
echo "--- Code Requirements ---"
codesign -dr - "$app_path" 2>&1
# Entitlements (if detailed analysis requested)
if [[ "$detailed" == "true" ]]; then
echo ""
echo "--- Entitlements ---"
codesign -d --entitlements - "$app_path" 2>/dev/null || echo "No entitlements found"
fi
echo ""
echo "=== Analysis Complete ==="
}
# Usage
analyze_application_signature "/Applications/Safari.app" "true"
Enterprise Code Signature Management
Batch Application Analysis
#!/bin/bash
# Analyze multiple applications for enterprise security review
batch_analyze_applications() {
local scan_path="${1:-/Applications}"
local output_format="${2:-table}"
local include_system="${3:-false}"
echo "=== Batch Application Signature Analysis ==="
echo "Scan Path: $scan_path"
echo "Include System Apps: $include_system"
echo "Output Format: $output_format"
echo ""
local app_count=0
local valid_count=0
local invalid_count=0
local results=()
# Find all applications
local find_paths=("$scan_path")
if [[ "$include_system" == "true" ]]; then
find_paths+=("/System/Applications" "/System/Library/CoreServices")
fi
for search_path in "${find_paths[@]}"; do
if [[ -d "$search_path" ]]; then
while IFS= read -r -d '' app_path; do
((app_count++))
local app_name=$(basename "$app_path" .app)
local bundle_id="Unknown"
local signature_status="Invalid"
local developer="Unknown"
local code_req="Unknown"
# Extract bundle ID
if [[ -f "$app_path/Contents/Info.plist" ]]; then
bundle_id=$(defaults read "$app_path/Contents/Info.plist" CFBundleIdentifier 2>/dev/null || echo "Unknown")
fi
# Check signature validity
if codesign -v "$app_path" 2>/dev/null; then
signature_status="Valid"
((valid_count++))
# Extract developer information
local sig_info
sig_info=$(codesign -dv "$app_path" 2>&1)
developer=$(echo "$sig_info" | grep "Authority=" | head -1 | sed 's/Authority=//' | tr -d '\n')
# Extract code requirement
code_req=$(codesign -dr - "$app_path" 2>/dev/null | grep "designated =>" | sed 's/designated => //')
else
((invalid_count++))
fi
# Store results
results+=("$app_name|$bundle_id|$signature_status|$developer|$code_req")
# Progress indicator
if (( app_count % 10 == 0 )); then
echo "Analyzed $app_count applications..."
fi
done < <(find "$search_path" -name "*.app" -type d -print0 2>/dev/null)
fi
done
echo ""
echo "=== Analysis Summary ==="
echo "Total Applications: $app_count"
echo "Valid Signatures: $valid_count"
echo "Invalid Signatures: $invalid_count"
echo ""
# Output results in requested format
case "$output_format" in
"table")
printf "%-30s %-40s %-10s %-50s\n" "Application" "Bundle ID" "Signature" "Developer"
printf "%-30s %-40s %-10s %-50s\n" "----------" "---------" "---------" "---------"
for result in "${results[@]}"; do
IFS='|' read -r app_name bundle_id sig_status developer code_req <<< "$result"
printf "%-30s %-40s %-10s %-50s\n" \
"${app_name:0:29}" \
"${bundle_id:0:39}" \
"$sig_status" \
"${developer:0:49}"
done
;;
"csv")
echo "Application,Bundle ID,Signature Status,Developer,Code Requirement"
for result in "${results[@]}"; do
echo "$result" | tr '|' ','
done
;;
"json")
echo "{"
echo " \"analysis_summary\": {"
echo " \"total_applications\": $app_count,"
echo " \"valid_signatures\": $valid_count,"
echo " \"invalid_signatures\": $invalid_count,"
echo " \"scan_date\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\""
echo " },"
echo " \"applications\": ["
local first=true
for result in "${results[@]}"; do
IFS='|' read -r app_name bundle_id sig_status developer code_req <<< "$result"
if [[ "$first" == "true" ]]; then
first=false
else
echo ","
fi
echo -n " {"
echo -n "\"name\": \"$app_name\", "
echo -n "\"bundle_id\": \"$bundle_id\", "
echo -n "\"signature_valid\": $([ "$sig_status" == "Valid" ] && echo "true" || echo "false"), "
echo -n "\"developer\": \"$developer\", "
echo -n "\"code_requirement\": \"$code_req\""
echo -n "}"
done
echo ""
echo " ]"
echo "}"
;;
esac
}
# Usage examples
batch_analyze_applications "/Applications" "table" "false"
batch_analyze_applications "/Applications" "json" "true" > /tmp/app_analysis.json
Security Policy Enforcement
#!/bin/bash
# Enforce enterprise code signing policies
enforce_code_signing_policy() {
local policy_name="$1"
local action="${2:-report}"
local target_path="${3:-/Applications}"
echo "=== Enforcing Code Signing Policy: $policy_name ==="
echo "Action: $action"
echo "Target Path: $target_path"
echo ""
local violations=()
local compliant_apps=()
case "$policy_name" in
"enterprise_strict")
echo "Policy: Enterprise Strict - Only signed applications from trusted developers"
# Define trusted developers
local trusted_developers=(
"Apple Inc."
"Microsoft Corporation"
"Adobe Inc."
"Google LLC"
"Zoom Video Communications, Inc."
)
check_enterprise_strict_policy "$target_path" trusted_developers violations compliant_apps
;;
"apple_signed_only")
echo "Policy: Apple Signed Only - Only Apple-signed applications allowed"
check_apple_signed_policy "$target_path" violations compliant_apps
;;
"no_unsigned")
echo "Policy: No Unsigned - All applications must be signed"
check_no_unsigned_policy "$target_path" violations compliant_apps
;;
"developer_id_required")
echo "Policy: Developer ID Required - All apps must have valid Developer ID"
check_developer_id_policy "$target_path" violations compliant_apps
;;
*)
echo "Error: Unknown policy '$policy_name'"
echo "Available policies: enterprise_strict, apple_signed_only, no_unsigned, developer_id_required"
return 1
;;
esac
# Report results
echo ""
echo "=== Policy Enforcement Results ==="
echo "Compliant Applications: ${#compliant_apps[@]}"
echo "Policy Violations: ${#violations[@]}"
if [[ ${#violations[@]} -gt 0 ]]; then
echo ""
echo "Policy Violations Found:"
for violation in "${violations[@]}"; do
echo " ⚠️ $violation"
done
fi
# Take action based on policy
case "$action" in
"report")
echo ""
echo "Action: Report Only - No changes made"
generate_policy_report "$policy_name" violations compliant_apps
;;
"quarantine")
echo ""
echo "Action: Quarantine - Moving violating applications"
quarantine_violations violations
;;
"remove")
echo ""
echo "Action: Remove - Deleting violating applications"
remove_violations violations
;;
*)
echo "Error: Unknown action '$action'"
;;
esac
}
# Check enterprise strict policy
check_enterprise_strict_policy() {
local target_path="$1"
local -n trusted_devs=$2
local -n violations_ref=$3
local -n compliant_ref=$4
while IFS= read -r -d '' app_path; do
local app_name=$(basename "$app_path")
if codesign -v "$app_path" 2>/dev/null; then
local developer
developer=$(codesign -dv "$app_path" 2>&1 | grep "Authority=" | head -1 | sed 's/Authority=//')
local is_trusted=false
for trusted_dev in "${trusted_devs[@]}"; do
if [[ "$developer" == *"$trusted_dev"* ]]; then
is_trusted=true
break
fi
done
if [[ "$is_trusted" == "true" ]]; then
compliant_ref+=("$app_name")
else
violations_ref+=("$app_name (Developer: $developer)")
fi
else
violations_ref+=("$app_name (Unsigned)")
fi
done < <(find "$target_path" -name "*.app" -type d -print0 2>/dev/null)
}
# Check Apple-signed only policy
check_apple_signed_policy() {
local target_path="$1"
local -n violations_ref=$2
local -n compliant_ref=$3
while IFS= read -r -d '' app_path; do
local app_name=$(basename "$app_path")
if codesign -v "$app_path" 2>/dev/null; then
local developer
developer=$(codesign -dv "$app_path" 2>&1 | grep "Authority=" | head -1 | sed 's/Authority=//')
if [[ "$developer" == *"Apple"* ]]; then
compliant_ref+=("$app_name")
else
violations_ref+=("$app_name (Non-Apple Developer: $developer)")
fi
else
violations_ref+=("$app_name (Unsigned)")
fi
done < <(find "$target_path" -name "*.app" -type d -print0 2>/dev/null)
}
# Generate policy compliance report
generate_policy_report() {
local policy_name="$1"
local -n violations_ref=$2
local -n compliant_ref=$3
local report_file="/tmp/code_signing_policy_report_$(date +%Y%m%d_%H%M%S).json"
cat > "$report_file" << EOF
{
"code_signing_policy_report": {
"policy_name": "$policy_name",
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"hostname": "$(hostname)",
"summary": {
"total_applications": $((${#violations_ref[@]} + ${#compliant_ref[@]})),
"compliant_applications": ${#compliant_ref[@]},
"policy_violations": ${#violations_ref[@]},
"compliance_percentage": $(( ${#compliant_ref[@]} * 100 / (${#violations_ref[@]} + ${#compliant_ref[@]}) ))
},
"violations": [
EOF
local first=true
for violation in "${violations_ref[@]}"; do
if [[ "$first" == "true" ]]; then
first=false
else
echo "," >> "$report_file"
fi
echo -n " \"$violation\"" >> "$report_file"
done
cat >> "$report_file" << EOF
],
"compliant_applications": [
EOF
first=true
for compliant in "${compliant_ref[@]}"; do
if [[ "$first" == "true" ]]; then
first=false
else
echo "," >> "$report_file"
fi
echo -n " \"$compliant\"" >> "$report_file"
done
cat >> "$report_file" << EOF
]
}
}
EOF
echo "Policy report generated: $report_file"
}
# Usage
enforce_code_signing_policy "enterprise_strict" "report" "/Applications"
Enterprise Code Signature Management System
#!/bin/bash
# MacFleet Code Signature Management Tool
# Comprehensive code signature analysis and security policy enforcement
# Configuration
SCRIPT_VERSION="1.0.0"
LOG_FILE="/var/log/macfleet_codesigning.log"
REPORT_DIR="/etc/macfleet/reports/codesigning"
CONFIG_DIR="/etc/macfleet/codesigning"
POLICY_DIR="/etc/macfleet/policies/codesigning"
QUARANTINE_DIR="/var/quarantine/applications"
# Create directories if they don't exist
mkdir -p "$REPORT_DIR" "$CONFIG_DIR" "$POLICY_DIR" "$QUARANTINE_DIR"
# Code signing policy templates
declare -A SIGNING_POLICIES=(
["enterprise_standard"]="signed_required,developer_id_preferred,apple_signed_trusted,third_party_review"
["enterprise_strict"]="apple_signed_only,developer_id_required,no_unsigned,enterprise_review"
["development"]="signed_preferred,developer_id_optional,self_signed_allowed,minimal_restrictions"
["kiosk_lockdown"]="apple_signed_only,no_third_party,system_apps_only,maximum_security"
["healthcare"]="apple_signed_required,enterprise_approved_only,no_unsigned,hipaa_compliant"
["financial"]="apple_signed_required,enterprise_certified_only,strict_validation,sox_compliant"
["education"]="signed_required,educational_approved,apple_preferred,moderate_security"
["government"]="apple_signed_only,government_approved,no_unsigned,maximum_security"
["retail"]="signed_required,pos_approved,apple_preferred,moderate_security"
["manufacturing"]="signed_required,industrial_approved,apple_preferred,operational_security"
)
# Trusted developer registry
declare -A TRUSTED_DEVELOPERS=(
["apple"]="Apple Inc.,Software Signing,Apple Mac OS Application Signing"
["microsoft"]="Microsoft Corporation,Microsoft Corporation"
["adobe"]="Adobe Inc.,Adobe Systems Incorporated"
["google"]="Google LLC,Google Inc."
["zoom"]="Zoom Video Communications, Inc."
["slack"]="Slack Technologies, Inc."
["dropbox"]="Dropbox, Inc."
["1password"]="AgileBits Inc."
["firefox"]="Mozilla Corporation"
["chrome"]="Google LLC"
)
# Security classification levels
declare -A SECURITY_LEVELS=(
["maximum"]="apple_signed_only,no_exceptions,immediate_quarantine"
["high"]="signed_required,trusted_developers_only,review_required"
["medium"]="signed_preferred,warnings_enabled,monitoring_active"
["low"]="unsigned_allowed,notification_only,basic_monitoring"
)
# Logging function
log_action() {
local message="$1"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$timestamp] $message" | tee -a "$LOG_FILE"
}
# Advanced code signature analysis
analyze_code_signature() {
local app_path="$1"
local output_format="${2:-detailed}"
local security_check="${3:-true}"
log_action "Analyzing code signature for: $app_path"
if [[ ! -e "$app_path" ]]; then
log_action "ERROR: Application not found: $app_path"
return 1
fi
local analysis_data=()
local app_name=$(basename "$app_path" .app)
echo "=== Advanced Code Signature Analysis ==="
echo "Application: $app_name"
echo "Path: $app_path"
echo "Analysis Date: $(date)"
echo ""
# Basic application information
extract_app_metadata "$app_path" analysis_data
# Code signature verification
verify_code_signature "$app_path" analysis_data
# Extract code requirements
extract_code_requirements "$app_path" analysis_data
# Security analysis
if [[ "$security_check" == "true" ]]; then
perform_security_analysis "$app_path" analysis_data
fi
# Generate output in requested format
case "$output_format" in
"detailed")
display_detailed_analysis analysis_data
;;
"summary")
display_summary_analysis analysis_data
;;
"json")
generate_json_analysis "$app_path" analysis_data
;;
"csv")
generate_csv_analysis "$app_path" analysis_data
;;
esac
log_action "Code signature analysis completed for: $app_path"
}
# Extract application metadata
extract_app_metadata() {
local app_path="$1"
local -n data_ref=$2
echo "--- Application Metadata ---"
if [[ -f "$app_path/Contents/Info.plist" ]]; then
local bundle_id=$(defaults read "$app_path/Contents/Info.plist" CFBundleIdentifier 2>/dev/null || echo "Unknown")
local version=$(defaults read "$app_path/Contents/Info.plist" CFBundleShortVersionString 2>/dev/null || echo "Unknown")
local build=$(defaults read "$app_path/Contents/Info.plist" CFBundleVersion 2>/dev/null || echo "Unknown")
local min_os=$(defaults read "$app_path/Contents/Info.plist" LSMinimumSystemVersion 2>/dev/null || echo "Unknown")
echo "Bundle ID: $bundle_id"
echo "Version: $version"
echo "Build: $build"
echo "Minimum OS: $min_os"
data_ref+=("bundle_id:$bundle_id")
data_ref+=("version:$version")
data_ref+=("build:$build")
data_ref+=("min_os:$min_os")
else
echo "No Info.plist found"
data_ref+=("bundle_id:Unknown")
fi
# File size and modification date
local file_size=$(du -sh "$app_path" | cut -f1)
local mod_date=$(stat -f "%Sm" -t "%Y-%m-%d %H:%M:%S" "$app_path")
echo "Application Size: $file_size"
echo "Last Modified: $mod_date"
data_ref+=("size:$file_size")
data_ref+=("modified:$mod_date")
}
# Verify code signature
verify_code_signature() {
local app_path="$1"
local -n data_ref=$2
echo ""
echo "--- Code Signature Verification ---"
# Basic verification
if codesign -v "$app_path" 2>/dev/null; then
echo "✓ Code signature is valid"
data_ref+=("signature_valid:true")
# Detailed signature information
local sig_info=$(codesign -dv "$app_path" 2>&1)
# Extract key information
local identifier=$(echo "$sig_info" | grep "Identifier=" | sed 's/Identifier=//')
local format=$(echo "$sig_info" | grep "Format=" | sed 's/Format=//')
local cdhash=$(echo "$sig_info" | grep "CDHash=" | sed 's/CDHash=//')
local team_id=$(echo "$sig_info" | grep "TeamIdentifier=" | sed 's/TeamIdentifier=//')
echo "Identifier: $identifier"
echo "Format: $format"
echo "CDHash: $cdhash"
echo "Team ID: $team_id"
data_ref+=("identifier:$identifier")
data_ref+=("format:$format")
data_ref+=("cdhash:$cdhash")
data_ref+=("team_id:$team_id")
# Extract certificate chain
local authorities=$(echo "$sig_info" | grep "Authority=" | sed 's/Authority=//')
echo "Certificate Chain:"
while IFS= read -r authority; do
echo " - $authority"
done <<< "$authorities"
data_ref+=("authorities:$authorities")
else
echo "⚠️ Code signature verification FAILED"
data_ref+=("signature_valid:false")
local error=$(codesign -v "$app_path" 2>&1)
echo "Error: $error"
data_ref+=("signature_error:$error")
fi
}
# Extract code requirements
extract_code_requirements() {
local app_path="$1"
local -n data_ref=$2
echo ""
echo "--- Code Requirements ---"
local req_output=$(codesign -dr - "$app_path" 2>&1)
if [[ $? -eq 0 ]]; then
echo "$req_output"
# Extract designated requirement
local designated_req=$(echo "$req_output" | grep "designated =>" | sed 's/designated => //')
if [[ -n "$designated_req" ]]; then
echo ""
echo "Designated Requirement:"
echo "$designated_req"
data_ref+=("designated_requirement:$designated_req")
fi
data_ref+=("requirements_extracted:true")
else
echo "Failed to extract code requirements"
echo "$req_output"
data_ref+=("requirements_extracted:false")
fi
}
# Perform security analysis
perform_security_analysis() {
local app_path="$1"
local -n data_ref=$2
echo ""
echo "--- Security Analysis ---"
local security_score=100
local security_issues=()
# Check if application is signed
if ! codesign -v "$app_path" 2>/dev/null; then
security_score=$((security_score - 50))
security_issues+=("Application is not signed")
fi
# Check for hardened runtime
local flags=$(codesign -dv "$app_path" 2>&1 | grep "CodeDirectory" | grep "runtime")
if [[ -n "$flags" ]]; then
echo "✓ Hardened runtime enabled"
data_ref+=("hardened_runtime:true")
else
echo "⚠️ Hardened runtime not enabled"
security_score=$((security_score - 20))
security_issues+=("Hardened runtime not enabled")
data_ref+=("hardened_runtime:false")
fi
# Check for notarization
if spctl -a -v "$app_path" 2>&1 | grep -q "notarized"; then
echo "✓ Application is notarized"
data_ref+=("notarized:true")
else
echo "⚠️ Application is not notarized"
security_score=$((security_score - 15))
security_issues+=("Application not notarized")
data_ref+=("notarized:false")
fi
# Check for known vulnerabilities (basic check)
check_known_vulnerabilities "$app_path" security_score security_issues
echo ""
echo "Security Score: $security_score/100"
data_ref+=("security_score:$security_score")
if [[ ${#security_issues[@]} -gt 0 ]]; then
echo "Security Issues Found:"
for issue in "${security_issues[@]}"; do
echo " ⚠️ $issue"
done
data_ref+=("security_issues:${security_issues[*]}")
else
echo "✓ No security issues found"
data_ref+=("security_issues:none")
fi
}
# Check for known vulnerabilities
check_known_vulnerabilities() {
local app_path="$1"
local -n score_ref=$2
local -n issues_ref=$3
local bundle_id
bundle_id=$(defaults read "$app_path/Contents/Info.plist" CFBundleIdentifier 2>/dev/null || echo "Unknown")
# Check against known vulnerable applications (simplified example)
case "$bundle_id" in
"com.adobe.flashplayer"*)
score_ref=$((score_ref - 30))
issues_ref+=("Flash Player has known security vulnerabilities")
;;
"com.java."*)
# Check Java version for known vulnerabilities
local java_version
java_version=$(defaults read "$app_path/Contents/Info.plist" CFBundleShortVersionString 2>/dev/null)
if [[ -n "$java_version" ]]; then
echo "Java version detected: $java_version"
# Add specific version checks here
fi
;;
esac
# Check for applications with expired certificates
local cert_info=$(codesign -dv "$app_path" 2>&1 | grep "Timestamp=")
if [[ -n "$cert_info" ]]; then
# Extract timestamp and check if certificate is old
local timestamp=$(echo "$cert_info" | sed 's/Timestamp=//')
echo "Certificate timestamp: $timestamp"
fi
}
# Generate comprehensive fleet analysis
analyze_fleet_applications() {
local scan_paths=("$@")
local report_file="$REPORT_DIR/fleet_analysis_$(date +%Y%m%d_%H%M%S).json"
echo "=== Fleet-Wide Application Analysis ==="
echo "Scanning paths: ${scan_paths[*]}"
echo ""
local total_apps=0
local signed_apps=0
local unsigned_apps=0
local notarized_apps=0
local security_issues=0
local applications=()
for scan_path in "${scan_paths[@]}"; do
if [[ -d "$scan_path" ]]; then
echo "Scanning: $scan_path"
while IFS= read -r -d '' app_path; do
((total_apps++))
local app_name=$(basename "$app_path" .app)
local analysis_data=()
# Quick analysis for fleet overview
analyze_code_signature "$app_path" "summary" "true" > /dev/null
# Extract key metrics
if codesign -v "$app_path" 2>/dev/null; then
((signed_apps++))
else
((unsigned_apps++))
fi
if spctl -a -v "$app_path" 2>&1 | grep -q "notarized"; then
((notarized_apps++))
fi
# Add to applications array
applications+=("$app_path")
# Progress indicator
if (( total_apps % 25 == 0 )); then
echo "Analyzed $total_apps applications..."
fi
done < <(find "$scan_path" -name "*.app" -type d -maxdepth 2 -print0 2>/dev/null)
fi
done
# Generate fleet analysis report
cat > "$report_file" << EOF
{
"fleet_analysis": {
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"hostname": "$(hostname)",
"scan_paths": [$(printf '"%s",' "${scan_paths[@]}" | sed 's/,$//')],
"summary": {
"total_applications": $total_apps,
"signed_applications": $signed_apps,
"unsigned_applications": $unsigned_apps,
"notarized_applications": $notarized_apps,
"signing_compliance": $(( signed_apps * 100 / total_apps )),
"notarization_compliance": $(( notarized_apps * 100 / total_apps ))
},
"analysis_date": "$(date)",
"script_version": "$SCRIPT_VERSION"
}
}
EOF
echo ""
echo "=== Fleet Analysis Summary ==="
echo "Total Applications: $total_apps"
echo "Signed Applications: $signed_apps ($(( signed_apps * 100 / total_apps ))%)"
echo "Unsigned Applications: $unsigned_apps ($(( unsigned_apps * 100 / total_apps ))%)"
echo "Notarized Applications: $notarized_apps ($(( notarized_apps * 100 / total_apps ))%)"
echo ""
echo "Fleet analysis report: $report_file"
log_action "Fleet analysis completed: $total_apps applications analyzed"
}
# Main execution function
main() {
local action="${1:-help}"
local param1="${2:-}"
local param2="${3:-}"
local param3="${4:-}"
local param4="${5:-}"
log_action "=== MacFleet Code Signature Management Started ==="
log_action "Action: $action"
case "$action" in
"analyze")
if [[ -z "$param1" ]]; then
echo "Usage: $0 analyze <app_path> [output_format] [security_check]"
echo "Output formats: detailed, summary, json, csv"
exit 1
fi
analyze_code_signature "$param1" "${param2:-detailed}" "${param3:-true}"
;;
"extract")
if [[ -z "$param1" ]]; then
echo "Usage: $0 extract <app_path>"
exit 1
fi
extract_code_requirement "$param1"
;;
"batch")
if [[ -z "$param1" ]]; then
param1="/Applications"
fi
batch_analyze_applications "$param1" "${param2:-table}" "${param3:-false}"
;;
"policy")
if [[ -z "$param1" ]]; then
echo "Available policies: ${!SIGNING_POLICIES[*]}"
exit 1
fi
enforce_code_signing_policy "$param1" "${param2:-report}" "${param3:-/Applications}"
;;
"fleet")
local paths=("${@:2}")
if [[ ${#paths[@]} -eq 0 ]]; then
paths=("/Applications" "/System/Applications")
fi
analyze_fleet_applications "${paths[@]}"
;;
"verify")
if [[ -z "$param1" ]]; then
echo "Usage: $0 verify <app_path>"
exit 1
fi
if codesign -v "$param1" 2>/dev/null; then
echo "✓ Code signature is valid for: $param1"
else
echo "⚠️ Code signature verification failed for: $param1"
codesign -v "$param1"
fi
;;
"help")
echo "Usage: $0 [action] [options...]"
echo "Actions:"
echo " analyze <app_path> [format] [security] - Analyze application signature"
echo " extract <app_path> - Extract code requirement only"
echo " batch <scan_path> [format] [include_system] - Batch analyze applications"
echo " policy <policy_name> [action] [path] - Enforce signing policy"
echo " fleet [paths...] - Analyze fleet-wide applications"
echo " verify <app_path> - Quick signature verification"
echo " help - Show this help"
echo ""
echo "Output Formats: detailed, summary, json, csv, table"
echo "Policies: ${!SIGNING_POLICIES[*]}"
echo "Security Levels: ${!SECURITY_LEVELS[*]}"
;;
*)
log_action "ERROR: Unknown action: $action"
echo "Use '$0 help' for usage information"
exit 1
;;
esac
log_action "=== Code signature management completed ==="
}
# Execute main function
main "$@"
Security Best Practices
Enterprise Certificate Management
#!/bin/bash
# Enterprise certificate validation
validate_enterprise_certificates() {
local cert_policy="${1:-strict}"
echo "=== Enterprise Certificate Validation ==="
echo "Policy: $cert_policy"
echo ""
local expired_certs=()
local revoked_certs=()
local untrusted_certs=()
# Check system keychain for enterprise certificates
security find-certificate -a -p /Library/Keychains/System.keychain | \
while IFS= read -r cert_line; do
if [[ "$cert_line" == "-----BEGIN CERTIFICATE-----" ]]; then
# Process certificate
echo "Checking enterprise certificate..."
fi
done
echo "✓ Enterprise certificate validation completed"
}
validate_enterprise_certificates "strict"
Compliance Reporting
#!/bin/bash
# Generate compliance report for regulatory requirements
generate_compliance_report() {
local compliance_type="${1:-general}"
local output_file="$REPORT_DIR/compliance_${compliance_type}_$(date +%Y%m%d_%H%M%S).json"
echo "=== Generating Compliance Report: $compliance_type ==="
local total_apps=0
local compliant_apps=0
local non_compliant_apps=0
# Scan applications for compliance
while IFS= read -r -d '' app_path; do
((total_apps++))
case "$compliance_type" in
"hipaa")
check_hipaa_compliance "$app_path" compliant_apps non_compliant_apps
;;
"sox")
check_sox_compliance "$app_path" compliant_apps non_compliant_apps
;;
"pci_dss")
check_pci_compliance "$app_path" compliant_apps non_compliant_apps
;;
*)
check_general_compliance "$app_path" compliant_apps non_compliant_apps
;;
esac
done < <(find /Applications -name "*.app" -type d -print0 2>/dev/null)
# Generate report
local compliance_percentage=$(( compliant_apps * 100 / total_apps ))
cat > "$output_file" << EOF
{
"compliance_report": {
"type": "$compliance_type",
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"hostname": "$(hostname)",
"summary": {
"total_applications": $total_apps,
"compliant_applications": $compliant_apps,
"non_compliant_applications": $non_compliant_apps,
"compliance_percentage": $compliance_percentage
}
}
}
EOF
echo "Compliance report generated: $output_file"
echo "Compliance rate: $compliance_percentage%"
}
generate_compliance_report "general"
Important Notes
- Code signatures verify application authenticity and integrity
- Code requirements specify constraints for application validation
- Enterprise policies should balance security with operational needs
- Regular monitoring helps detect unauthorized applications
- Compliance requirements vary by industry and regulatory framework
- Notarization provides additional security validation from Apple
- Certificate management is crucial for enterprise security
- Fleet-wide analysis enables proactive security management