| name | ios-app-security-audit |
| description | Comprehensive iOS app security audit with static analysis, dynamic instrumentation, source-to-sink tracing, IPC/component abuse analysis, and CVSS 4.0 reporting. Covers decompilation, Info.plist analysis, URL schemes and universal links, secrets detection, crypto analysis, Frida/Objection integration, and IPA modification. Use when user says "audit IPA", "analyze iOS app", "iOS pentest", "app security", "decompile IPA", "iOS vulnerability assessment", "reverse engineer iOS", "modify IPA", "URL scheme abuse", "bypass SSL pinning", "bypass jailbreak detection", or provides an IPA for security review, decompiled iOS sources, or decoded resources.
|
| license | Apache-2.0 |
| allowed-tools | Bash(frida:*) Bash(objection:*) Bash(dsdump:*) Bash(classdump:*) Bash(codesign:*) Bash(security:*) Bash(lipo:*) Bash(nm:*) Bash(strings:*) Bash(otool:*) Bash(jtool2:*) Bash(plutil:*) Bash(python3:*) Bash(ipa:*) Read Write Edit Glob Grep |
| metadata | {"author":"DragonJAR SAS","version":"1.0.0","category":"mobile-security","tags":["ios","ipa-audit","static-analysis","dynamic-analysis","frida","objection","security-pentest","cvss-scoring","reverse-engineering"]} |
iOS App Security Audit
Overview
Deterministic 6-phase static analysis + optional dynamic confirmation workflow. Remove noise early, keep the bundle scope tight. Only report vulnerabilities where source, propagation, and sink are understood or clearly marked as needing dynamic confirmation.
When to Use
- User provides an IPA file for security review
- User asks to analyze decompiled iOS source code
- User needs to modify or repackage an IPA for testing
- User wants iOS mobile security testing or vulnerability assessment
- User needs help testing URL schemes, universal links, or IPC mechanisms
- User mentions iOS reverse engineering or app analysis
Critical Rules
- NEVER report bare grep hits without traced context
- ALWAYS constrain searches to the app namespace (avoid framework noise)
- STOP and report immediately if decoding fails
- ALWAYS use imperative language in findings
- NEVER skip validation — if unsure, mark as "Needs Dynamic Confirmation"
- ALWAYS provide concrete PoC (frida script, objection command, or URL scheme payload)
- NEVER duplicate findings for the same root cause
Quality Note: Take your time to analyze thoroughly. Quality is more important than speed. Do not skip validation steps — a false positive is worse than a missed finding.
Execution Model
Tools to use: bash, write, edit, read, glob, grep for core workflow.
Toolchain Requirements
Before starting, verify these tools are installed:
- Frida 17.9.1 (
frida --version) — ⚠️ Frida-server on device must match frida-tools version exactly
- Objection 1.12.4 (
objection --version) — Note: Objection is in maintenance mode
- dsdump 0.13+ (
dsdump --version) — Swift class dump for modern iOS binaries
- classdump (optional) — Objective-C class dump for legacy apps
- plutil (system) — plist manipulation (preinstalled on macOS)
- codesign (system) — code signing (preinstalled on macOS)
- security (system) — keychain access (preinstalled on macOS)
- lipo (system) — fat binary manipulation (preinstalled on macOS)
- nm (system) — symbol extraction (preinstalled on macOS)
- strings (system) — string extraction (preinstalled on macOS)
- otool (system) — Mach-O analysis (preinstalled on macOS)
- jtool2 (optional) — Advanced Mach-O analysis
- Python 3.10+ (
python3 --version)
Run scripts/preflight-check.sh (bash), scripts/preflight_check.py (universal, JSON output), or scripts/preflight-check.ps1 (PowerShell) to verify all tools. See references/environment-setup-ios.md for installation instructions.
Phase 0 — Extract and Detect Framework
Extract IPA
unzip app.ipa -d extracted/
cd extracted/Payload/YourApp.app/
Mach-O Analysis
otool -l YourApp | grep -A 10 LC_ENCRYPTION_INFO
lipo -info YourApp
file YourApp
dsdump YourApp > classdump-swift.txt
classdump YourApp > classdump-objc.txt
Framework Detection
Identify the app's architecture early to tailor analysis.
| Framework | Detection Method | Key Artifacts |
|---|
| React Native | grep -r "RCTBridgeModule" · Check for libReactNative.a | index.ios.bundle, libReactNative.a |
| Flutter | grep -r "io.flutter" in Info.plist · Check for Flutter.framework | App.framework, Flutter.framework |
| Cordova/Ionic | grep -r "cordova" in Info.plist · Check www/ directory | cordova.js, config.xml |
| Xamarin | grep -r "xamarin" · Check for Mono runtime | libmonosgen-2.0.dylib |
| Unity | grep -r "UnityEngine" · Check for UnityFramework.framework | Data/Managed/, UnityFramework.framework |
| Swift Native | Check for Swift symbols · dsdump output shows Swift classes | Swift-only codebase |
| Objective-C | Check for ObjC runtime · classdump shows ObjC classes | ObjC-only codebase |
Reference: references/framework-detection-ios.md for complete detection scripts, Frida detection hooks, and framework-specific security considerations.
Obfuscation Detection
- Swift Obfuscation: Generic class names (
a.b.c, x_1, internal_), mangled symbols
- LLVM Obfuscator: Mixed naming, string encryption
- Custom Obfuscation: Unusual patterns, mixed naming schemes
- Third-Party Packers: iMAS, Arxan, GuardSquare (DexGuard iOS)
Reference: references/static-analysis-patterns-ios.md for detailed detection patterns.
OPTIONAL: MobSF Integration
Reference: references/mobsf-integration-guide.md for complete MobSF setup and API usage.
The iOS-Pentesting-Skill offers optional Mobile Security Framework (MobSF) integration for automated static and dynamic analysis.
Key Points:
- ✅ OPTIONAL: User choice, no hard dependency
- ✅ NON-BLOCKING: Works perfectly without MobSF
- ✅ ENHANCEMENT: MobSF results complement manual analysis
- ✅ CORRELATION: Findings merged with manual analysis
Interactive Setup (automatically prompts):
python3 scripts/setup_mobsf.py
This script will:
- Check if MobSF is already installed
- Offer installation options if not found
- Configure API access
- Test connectivity
- Save configuration for future sessions
Quick Start (Docker):
./scripts/install-mobsf-docker.sh
python3 scripts/mobsf_config.py set-url http://127.0.0.1:8000
python3 scripts/mobsf_config.py set-key generate
python3 scripts/mobsf_config.py status
Using MobSF in Analysis:
python3 scripts/mobsf_upload.py --ipa app.ipa
python3 scripts/mobsf_export.py --scan-id <SCAN_ID>
python3 scripts/mobsf_export.py --scan-id <SCAN_ID> --format json
NOTE: MobSF integration is OPTIONAL. The skill works perfectly without it.
Phase 1 — Attack Surface Mapping
Analyze Info.plist
plutil -p Info.plist
plutil -extract CFBundleURLSchemes xml1 -o - Info.plist
plutil -extract NSAppTransportSecurity xml1 -o - Info.plist
URL Schemes (Deep Links)
Extract and document all custom URL schemes:
| Key | What It Controls | Security Impact |
|---|
| CFBundleURLSchemes | Custom schemes (e.g., myapp://) | Intent injection via malicious URLs |
| CFBundleURLTypes | Scheme declarations | Attack surface enumeration |
| NSAppTransportSecurity | TLS/ATS settings | Cleartext traffic, insecure connections |
Info.plist Security Keys
| Key | What It Controls | Security Checks |
|---|
| UIBackgroundModes | Background capabilities | Unintended background data collection |
| NSPhotoLibraryUsageDescription | Photo access | Permission request clarity |
| NSCameraUsageDescription | Camera access | Permission request clarity |
| NSMicrophoneUsageDescription | Microphone access | Permission request clarity |
| NSLocationWhenInUseUsageDescription | Location access | Permission request clarity |
| UIFileSharingEnabled | iTunes file sharing | Data exposure via iTunes |
| UISupportedExternalAccessoryProtocols | External accessories | Attack surface expansion |
Entitlements Analysis
codesign -d --entitlements - YourApp.app/YourApp
| Entitlement | Purpose | Security Impact |
|---|
| keychain-access-groups | Shared keychain access | Cross-app data sharing |
| associated-domains | Universal Links / Handoff | Potential abuse for data exfiltration |
| get-task-allow | Debugger attachment | Debug mode in production |
| application-groups | App group containers | Shared data between apps |
| com.apple.security.application-groups | App group containers (iOS 14+) | Shared data, potential IPC |
| com.apple.security.network.client | Network access (iOS 14+) | Outbound network control |
| com.apple.security.network.server | Server capabilities (iOS 14+) | Inbound network exposure |
App Extensions Detection
find . -name "*.appex" -type d
Common extensions:
- Today Widget:
TodayExtension.appex
- Share Extension:
ShareExtension.appex
- Action Extension:
ActionExtension.appex
- Notification Content:
NotificationContentExtension.appex
Reference: references/info-plist-checklist.md for complete 50+ Info.plist checks.
IPC / URL scheme abuse: see references/url-scheme-hijacking.md and references/universal-links-guide.md.
Phase 2 — Targeted Triage
Scoped Grep Patterns
ALWAYS grep within the app namespace only. Use patterns from references/static-analysis-patterns-ios.md:
| Category | Example Patterns (ObjC/Swift) | What to Look For |
|---|
| WebView sinks | loadRequest, loadHTMLString, evaluateJavaScript | Loading untrusted URLs |
| IPC sources | openURL(_:), application(_:open:), continue(_:) | Unsanitized data entry from URL schemes |
| Keychain | SecItemAdd, SecItemCopyMatching, kSecAttrAccessible | Insecure storage, weak accessibility |
| UserDefaults | UserDefaults, standardUserDefaults, set | Cleartext storage of sensitive data |
| Pasteboard | UIPasteboard.general, string, setItems | Data exposure via clipboard |
| Hardcoded secrets | password.*=, api[_-]?key, "sk_live_, secret | Credentials in code |
| Crypto | kCCAlgorithmAES, kCCOptionECBMode, MD5, SHA1 | Weak algorithms, insecure modes |
| TLS/ATS | NSAllowsArbitraryLoads, NSExceptionDomains | Disabled certificate validation |
| Logging | NSLog, print, os_log | Sensitive data in logs |
| File I/O | writeToFile, FileManager, createFile | Path traversal, insecure file ops |
| Background | beginBackgroundTask, BGTaskScheduler | Unintended background data collection |
| Biometric | LocalAuthentication, evaluatePolicy | Biometric bypass, fallback to PIN |
| SQLite | sqlite3_exec, sqlite3_prepare_v2 | SQL injection |
| CoreData | NSPersistentContainer, execute | Data query injection |
Binary Analysis Commands
strings YourApp | grep -iE "(password|key|secret|token|api)"
nm YourApp | grep " T "
otool -L YourApp
classdump YourApp
dsdump YourApp
nm YourApp | grep -iE "(AES|DES|MD5|SHA)"
Resource File Analysis
Check Info.plist and embedded plists for secrets:
grep -iE "(key|token|secret|password|api)" Info.plist
find . -name "*.plist" -exec grep -iE "(key|token|secret|password|api)" {} +
Reference: references/static-analysis-patterns-ios.md for 100+ grep patterns organized by vulnerability type.
Phase 3 — Data Flow Tracing
Source-to-Sink Methodology
Map data flow from untrusted sources to dangerous sinks:
Common Sources (iOS)
| Source | Method | Example |
|---|
| URL Schemes | application(_:open:), continue(_:) | Malicious scheme URLs |
| Universal Links | application(_:continue:) | Universal link abuse |
| Pasteboard | UIPasteboard.general.string | Data from clipboard |
| App Groups | Shared container NSFileManager | Inter-app data |
| XPC | NSXPCConnection | Inter-process communication |
| Keychain | SecItemCopyMatching | Stored credentials |
| Notifications | UNUserNotificationCenter | Notification payloads |
| WebView JS Bridge | WKScriptMessageHandler | Untrusted JS calls |
Common Sinks (iOS)
| Sink | Method | Impact |
|---|
| Command Execution | Process(), NSTask, popen() | RCE |
| WebView Load | loadRequest(_:), loadHTMLString | XSS, Phishing |
| File Operations | writeToFile, FileManager.createFile | Path traversal, LFI |
| XPC Messages | NSXPCConnection.sendMessage | IPC injection |
| openURL | openURL(_:), canOpenURL(_:) | Intent injection |
| Keychain Write | SecItemAdd, SecItemUpdate | Credential poisoning |
| Pasteboard Write | UIPasteboard.general.setItems | Data exposure |
| SQLite | sqlite3_exec | SQL injection |
| UserDefaults Write | UserDefaults.set | Config tampering |
| Logging | NSLog, print, os_log | Data leakage |
Decision Rules
| Rule | Condition | Action |
|---|
| 1 | Direct flow source → sink | Report as Likely |
| 2 | Indirect flow via static analysis | Report as Likely if path clear |
| 3 | Dynamic/reflective call | Mark as Needs Dynamic Confirmation |
| 4 | Native boundary | Mark as Needs Dynamic Confirmation |
| 5 | Library code | Verify if app wraps securely |
| 6 | No sanitization | Escalate severity |
Manual Checks Grep Misses
| Check | Why grep misses | How to verify |
|---|
| Runtime permissions | requestAuthorization() calls | Trace completion handlers |
| Custom entitlement protections | Entitlement checks in code | Look for permission checks |
| URL scheme handling | openURL(_:) hides parameter extraction | Follow URL parsing and validation |
| Shared containers | App groups + shared paths | Map containerURL(forSecurityApplicationGroupIdentifier:) |
| XPC communication | Interface definitions + dynamic calls | Trace XPC connection setup |
| WebView JS Bridge | WKScriptMessageHandler + JS code | Map message handlers to JS injection points |
Reference: references/deep-link-exploitation-ios.md for modern attack vectors: URL scheme injection, universal link abuse, WebView XSS, pasteboard attacks, XPC injection, app group data leakage, notification abuse.
Phase 4 — Dynamic Analysis (Optional)
Use when static analysis hits a wall: obfuscation, reflection, native code, runtime protections.
Frida iOS Integration
frida -U -f com.example.app -l script.js
frida -U com.example.app -l script.js
Frida for iOS
if (ObjC.available) {
console.log("Objective-C runtime available");
console.log(ObjC.classes);
} else {
console.log("Objective-C runtime not available");
}
Reference: bundled Frida scripts in assets/frida-scripts/. See references/frida-scripts-index.md for the canonical catalog.
Focused runtime triage: use pasteboard-monitor.js for clipboard visibility, xpc-tracer.js for XPC/native boundary discovery, and url-scheme-monitor.js for URL scheme logging.
Script Maturity Levels:
- STABLE: Production-ready (
ssl-pinning-bypass.js, jailbreak-detection-bypass.js, biometric-bypass.js, network-interceptor.js, etc.)
- BETA: Functional but incomplete — use with caution
Objection iOS Commands
objection -g com.example.app explore
ios hooking list classes
ios jailbreak disable
ios sslpinning disable
ios keychain dump
ios biometrics bypass
ios pasteboard monitor
ios userdefaults get
iOS Runtime Testing
xcrun simctl openurl booted "scheme://host/path?param=value"
idevicesyslog | grep com.example.app
log stream --predicate 'process == "YourApp"' | grep com.example.app
Test URL Scheme Payload
myapp://deeplink?param=<script>alert(1)</script>
SSL Pinning bypass: references/ssl-pinning-bypass-ios.md + assets/frida-scripts/ssl-pinning-bypass-ios.js
Anti-tamper bypass: references/runtime-integrity-bypass.md + assets/frida-scripts/jailbreak-detection-bypass.js for anti-debug, anti-frida, and jailbreak detection bypass.
Phase 5 — Classification and Reporting
Confidence Levels
| Level | Definition | Example Evidence |
|---|
| Confirmed | Full source-to-sink trace validated | Direct call chain from URL scheme source to NSTask.launch() with no sanitization |
| Likely | Strong evidence, minor gaps | Static trace clear but reflection obscures final sink |
| Needs Dynamic Confirmation | Static analysis inconclusive | Obfuscated code or native boundary requiring runtime verification |
Severity
Use CVSS 4.0. See references/cvss-scoring-guide.md for complete methodology and severity mapping.
Finding Template
## [ID] - [Title]
**Confidence**: [Confirmed/Likely/Needs Dynamic Confirmation]
**Severity**: [Critical/High/Medium/Low] (CVSS: [X.X])
**CWE**: [CWE-ID]
**OWASP**: [OWASP Category]
### Description
[1-2 sentences explaining what the vulnerability is]
### Affected Components
- **File**: `path/to/file.m` or `path/to/ViewController.swift`
- **Method**: `methodName` or `@objc func methodName()`
- **Component**: `[ViewController/Service/Extension]` (if applicable)
### Attack Scenario
1. Attacker [action, e.g., crafts malicious URL scheme with payload]
2. App [processing step, e.g., extracts parameter without validation]
3. Data propagates through [call chain]
4. Reaches sink [dangerous operation]
5. Results in [impact, e.g., arbitrary command execution]
### Proof of Concept
```bash
# Example: URL scheme injection
xcrun simctl openurl booted "myapp://deeplink?path=../../../../etc/passwd"
Or provide Frida hook script for dynamic verification:
const targetClass = ObjC.classes.MyViewController;
if (targetClass) {
const method = targetClass['- processURL:'];
if (method) {
Interceptor.attach(method.implementation, {
onEnter: function(args) {
console.log("URL processed: " + ObjC.Object(args[2]));
}
});
}
}
Impact
- Confidentiality: [High/Medium/Low/None] — [explanation]
- Integrity: [High/Medium/Low/None] — [explanation]
- Availability: [High/Medium/Low/None] — [explanation]
Remediation
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
let queryItems = components.queryItems,
let input = queryItems.first(where: { $0.name == "path" })?.value,
isValidPath(input) else {
os_log("Invalid path detected")
return false
}
loadContent(at: input)
return true
}
private func isValidPath(_ path: String) -> Bool {
return !path.contains("..") && path.range(of: "^/safe/[a-zA-Z0-9_]+\\.html$", options: .regularExpression) != nil
}
Or Objective-C:
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options {
NSString *input = [url queryParameterValue:@"path"];
if ([self isValidPath:input]) {
[self loadContentAtPath:input];
} else {
NSLog(@"Invalid path detected");
return NO;
}
return YES;
}
CVSS 4.0 Calculation
[Show vector string and score breakdown]
### Coverage Statement
End your report with:
Coverage Analysis:
- Static Analysis: Complete (all decompiled sources analyzed)
- Dynamic Analysis: [Complete/Partial/Not Performed] (reason if partial)
- Scope: [com.example.app.* bundle only]
- Framework: [React Native/Flutter/Native/Standard]
- Obfuscation: [Swift Obfuscator/Custom/None]
Limitations:
- [List any limitations, e.g., "Native code analysis requires additional tools"]
- [Any components that could not be analyzed]
- [Any findings requiring additional verification]
Total Findings: X (Critical: Y, High: Z, Medium: A, Low: B)
> **Reference**: `references/reporting-templates-ios.md` for executive summary format, remediation priority matrix, and presentation templates.
### Automated Report Generation
Use to `generate-report.py` script to generate professional HTML or Markdown reports from findings JSON:
```bash
# Generate HTML report
python3 scripts/generate-report.py \
--input findings.json \
--output report.html \
--app-name "My App" \
--bundle-id "com.example.app"
# Generate Markdown report
python3 scripts/generate-report.py \
--input findings.json \
--output report.md \
--app-name "My App" \
--bundle-id "com.example.app"
The script supports both JSON array format and JSONL (one finding per line) and automatically:
- Sorts findings by severity (Critical first)
- Calculates CVSS 4.0 severity scores
- Generates executive summary with risk rating
- Maps OWASP MASTG categories
- Provides formatted proof of concept and remediation sections
See scripts/test-findings.json for the expected JSON structure.
IPA Modification Workflow
1. Extract
unzip app.ipa -d app-extracted/
cd app-extracted/Payload/YourApp.app/
2. Modify
Binary Patching
Use tools like otool, jtool2, or hex editor to patch binary:
jtool2 -disarch arm64 YourApp > disassembly.txt
Resource Modification
Edit Info.plist or embedded resources:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
3. Re-sign
codesign --remove-signature YourApp.app
codesign -f -s "iPhone Distribution: Your Team" \
--entitlements entitlements.plist \
--provisioning-profile YourProfile.mobileprovision \
YourApp.app
codesign -dvv YourApp.app
4. Re-package
cd ../../
zip -r app-modified.ipa Payload/
5. Install and Verify
ideviceinstaller -i app-modified.ipa
xcrun simctl install booted app-modified.ipa
Reference: references/dynamic-analysis-setup-ios.md for advanced repackaging, code signing, and installation. See references/repackaging-ios-guide.md for binary patching edge cases and troubleshooting.
Troubleshooting
Encrypted Binary
otool -l YourApp | grep -A 10 LC_ENCRYPTION_INFO
Frida Cannot Attach
otool -l YourApp | grep LC_CODE_SIGNATURE
codesign -d --entitlements - YourApp.app/YourApp
frida -U -f com.example.app -l assets/frida-scripts/jailbreak-detection-bypass.js
Obfuscated Swift Code Unreadable
- Identify obfuscation:
dsdump YourApp | head -50
- Use SwiftDemangle:
swift-demangle <mangled_symbol>
- See
references/static-analysis-patterns-ios.md → "Obfuscation Patterns"
- Switch to Phase 4 for runtime behavior
When to Escalate to Dynamic Analysis
Static analysis reaches limits when: obfuscation unclear, reflection, native boundaries, anti-debug/jailbreak detection, SSL pinning. → Proceed to Phase 4 using references/dynamic-analysis-setup-ios.md.
Examples
Example 1: Quick Assessment
com.example.app.ipa → Extract → Framework detect → Info.plist audit → Secrets grep → CVSS report
Example 2: SSL Pinning Bypass
frida -U -f com.target.app -l ssl-pinning-bypass-ios.js → See references/runtime-integrity-bypass.md if fails
Example 3: IPA Repackaging
Extract → Modify → Re-sign → Re-package → Install (see references/repackaging-ios-guide.md)
References Index
| Phase | Files |
|---|
| 0 | environment-setup-ios, tool-installation-ios, static-analysis-patterns-ios, macho-binary-analysis, framework-detection-ios, ipa-extraction-guide, mobsf-integration-guide |
| 1 | info-plist-checklist, entitlements-security-guide, ios-version-security-changes, app-extensions-security, url-scheme-security, universal-links-guide, ats-configuration-guide, privacy-manifest-ios |
| 2 | commoncrypto-analysis, keychain-security-guide, userdefaults-security, coredata-security, pasteboard-security, data-protection-api-guide |
| 3 | url-scheme-hijacking, deep-link-exploitation-ios, app-groups-security, xpc-services-security, ipc-xpc-guide, webview-security-ios, notification-security |
| 4 | jailbreak-setup-guide, frida-server-ios-install, frida-ios-guide, objection-ios-guide, ssl-pinning-bypass-ios, dynamic-analysis-setup-ios, debugger-detection-bypass, runtime-integrity-bypass, secure-enclave-testing, icloud-private-relay-testing |
| 5 | cvss-scoring-guide, reporting-templates-ios, owasp-mastg-ios-checklist, ios-privacy-testing, cvss-examples-ios, frida-scripts-index |
| Mod | repackaging-ios-guide, obfuscation-detection-ios, bitcode-analysis |
| FW | react-native-ios-security, flutter-ios-security, cordova-ios-security, xamarin-ios-security, unity-ios-security, swiftui-security-deep-dive, core-ml-security, core-ml-security-expanded |
| Tools | advanced-tools-ios, environment-setup-ios, tool-installation-ios |
| CI | automation-scripts-ios, ci-cd-integration-ios, github-actions-ios-security, fastlane-security |
| Cross | ios-version-security-changes, objc-security-patterns, swift-security-patterns |
Scripts: preflight-check.sh, preflight_check.py, preflight-check.ps1, auto-audit-static-ios.sh, generate-report.py, test-findings.json
Assets: Frida scripts in assets/frida-scripts/ (see references/frida-scripts-index.md for canonical catalog)
Loading Strategy: Load reference files only when encountering a specific technical challenge they cover. Do NOT load all references at once.
Platform-Specific Notes
See references/environment-setup-ios.md for detailed macOS setup. Most iOS analysis tools require macOS. rg (ripgrep) is recommended over grep -P — BSD grep does not support PCRE.