import os import plistlib import argparse import base64 class SandboxInspector: def __init__(self, home_dir): self.home_dir = home_dir def container_exists(self, bundle_id): # Construct the expected path of the container container_path = os.path.join(self.home_dir, 'Library', 'Containers', bundle_id) # Check if the path exists return os.path.exists(container_path) @staticmethod def get_bundle_id(app_path): # Construct the path to the Info.plist file info_plist_path = os.path.join(app_path, 'Contents', 'Info.plist') # Read the Info.plist file to get the bundle identifier with open(info_plist_path, 'rb') as f: plist = plistlib.load(f) return plist.get('CFBundleIdentifier') def get_metadata(self, bundle_id): # Construct the path to the metadata plist file metadata_plist_path = os.path.join(self.home_dir, 'Library', 'Containers', bundle_id, '.com.apple.containermanagerd.metadata.plist') if os.path.exists(metadata_plist_path): with open(metadata_plist_path, 'rb') as f: plist = plistlib.load(f) return plistlib.dumps(plist, fmt=plistlib.FMT_XML).decode('utf-8') else: return None def get_redirectable_paths(self, bundle_id): # Construct the path to the metadata plist file metadata_plist_path = os.path.join(self.home_dir, 'Library', 'Containers', bundle_id, '.com.apple.containermanagerd.metadata.plist') if os.path.exists(metadata_plist_path): with open(metadata_plist_path, 'rb') as f: plist = plistlib.load(f) redirectable_paths = plist.get('MCMMetadataInfo', {}).get('SandboxProfileDataValidationInfo', {}).get('RedirectablePaths', []) return redirectable_paths else: return None def get_sandbox_profile_data(self, bundle_id): # Construct the path to the metadata plist file metadata_plist_path = os.path.join(self.home_dir, 'Library', 'Containers', bundle_id, '.com.apple.containermanagerd.metadata.plist') if os.path.exists(metadata_plist_path): with open(metadata_plist_path, 'rb') as f: plist = plistlib.load(f) sandbox_profile_data = plist.get('MCMMetadataInfo', {}).get('SandboxProfileData', None) return sandbox_profile_data else: return None def parse_sandbox_profile_data(self, sandbox_profile_data): # Placeholder for actual parsing logic # This function will take raw bytes and interpret them as Sandbox Profile Language (SBPL) if sandbox_profile_data: return sandbox_profile_data# sandbox_profile_data.decode('utf-8', errors='ignore') return None def main(): parser = argparse.ArgumentParser(description="Inspect sandbox containers for macOS apps.") parser.add_argument('-p', '--path', type=str, required=True, help="Path to the application (e.g., /Applications/Notes.app)") parser.add_argument('-m', '--metadata', action='store_true', help="Print the .com.apple.containermanagerd.metadata.plist contents") parser.add_argument('-r', '--redirectable', action='store_true', help="Print the redirectable paths") parser.add_argument('-s', '--sandbox_profile_data', action='store_true', help="Print the SandboxProfileData as base64-encoded bytes") args = parser.parse_args() app_path = args.path inspector = SandboxInspector(os.path.expanduser("~")) bundle_id = inspector.get_bundle_id(app_path) if bundle_id: exists = inspector.container_exists(bundle_id) # print(f"Container for {bundle_id} exists: {exists}") if args.metadata: metadata = inspector.get_metadata(bundle_id) if metadata: #print(f"Metadata for {bundle_id}:\n{metadata}") print(f"{metadata}") else: print(f"No metadata plist found for {bundle_id}.") if args.redirectable: redirectable_paths = inspector.get_redirectable_paths(bundle_id) if redirectable_paths: print(f"Redirectable paths for {bundle_id}:") for path in redirectable_paths: print(f"{path}") else: print(f"No redirectable paths found for {bundle_id}.") if args.sandbox_profile_data: sandbox_profile_data = inspector.get_sandbox_profile_data(bundle_id) if sandbox_profile_data: parsed_data = inspector.parse_sandbox_profile_data(sandbox_profile_data) encoded_data = base64.b64encode(parsed_data).decode('utf-8') print(f"SandboxProfileData for {bundle_id} (base64-encoded):\n{encoded_data}") else: print(f"No SandboxProfileData found for {bundle_id}.") else: print("Unable to find the CFBundleIdentifier. Please check the app path.") if __name__ == "__main__": main()