#!/usr/bin/env python3 """Agent for testing deep link / custom URL scheme vulnerabilities in mobile apps.""" import argparse import json import re import subprocess from datetime import datetime, timezone ANDROID_SCHEME_PATTERN = re.compile( r']*>.*?', re.DOTALL ) ANDROID_HOST_PATTERN = re.compile(r'android:host="([^"]+)"') IOS_SCHEME_PATTERN = re.compile(r'(\w+)\s*\s*CFBundleURLName') IOS_UNIVERSAL_PATTERN = re.compile(r'"applinks:\s*([\w.-]+)"') def analyze_android_manifest(manifest_path): """Parse AndroidManifest.xml for deep link configurations.""" findings = [] try: with open(manifest_path, "r", errors="replace") as f: content = f.read() except FileNotFoundError: return findings for match in ANDROID_SCHEME_PATTERN.finditer(content): block = match.group(0) scheme = match.group(1) hosts = ANDROID_HOST_PATTERN.findall(block) exported = "android:exported=\"true\"" in block or "android:exported" not in block findings.append({ "scheme": scheme, "hosts": hosts, "exported": exported, "block": block[:200], "risk": "HIGH" if exported and scheme not in ("https", "http") else "MEDIUM", }) return findings def analyze_ios_plist(plist_path): """Parse iOS Info.plist for URL scheme and universal link configurations.""" findings = [] try: with open(plist_path, "r", errors="replace") as f: content = f.read() except FileNotFoundError: return findings for match in IOS_SCHEME_PATTERN.finditer(content): findings.append({ "type": "custom_url_scheme", "scheme": match.group(1), "risk": "HIGH", "note": "Custom URL schemes lack origin verification", }) for match in IOS_UNIVERSAL_PATTERN.finditer(content): findings.append({ "type": "universal_link", "domain": match.group(1), "risk": "LOW", "note": "Universal links verify domain ownership", }) return findings def test_deep_link_injection(scheme, host, path, payload): """Construct and test a deep link injection payload.""" test_url = f"{scheme}://{host}{path}{payload}" return { "test_url": test_url, "payload": payload, "vectors": [ "Open redirect via deep link", "JavaScript execution in WebView", "Parameter injection", "Intent redirection (Android)", ], } def check_adb_deep_link(package, uri): """Test Android deep link via ADB.""" cmd = ["adb", "shell", "am", "start", "-W", "-a", "android.intent.action.VIEW", "-d", uri, package] try: result = subprocess.check_output(cmd, text=True, errors="replace", timeout=15) return {"uri": uri, "status": "launched", "output": result[:300]} except (subprocess.SubprocessError, FileNotFoundError): return {"uri": uri, "status": "failed"} def main(): parser = argparse.ArgumentParser( description="Test deep link / URL scheme vulnerabilities (authorized testing only)" ) parser.add_argument("--android-manifest", help="Path to AndroidManifest.xml") parser.add_argument("--ios-plist", help="Path to Info.plist") parser.add_argument("--test-scheme", help="URL scheme to test") parser.add_argument("--test-host", help="Host for deep link test") parser.add_argument("--output", "-o", help="Output JSON report") args = parser.parse_args() print("[*] Deep Link Vulnerability Testing Agent") report = {"timestamp": datetime.now(timezone.utc).isoformat(), "findings": []} if args.android_manifest: android = analyze_android_manifest(args.android_manifest) report["findings"].extend(android) print(f"[*] Android deep links found: {len(android)}") if args.ios_plist: ios = analyze_ios_plist(args.ios_plist) report["findings"].extend(ios) print(f"[*] iOS URL schemes found: {len(ios)}") if args.test_scheme and args.test_host: payloads = [ "?redirect=https://evil.com", "#javascript:alert(1)", "?token=stolen&callback=https://evil.com", "/../../sensitive-path", ] for p in payloads: result = test_deep_link_injection(args.test_scheme, args.test_host, "/", p) report["findings"].append(result) report["risk_level"] = "HIGH" if report["findings"] else "LOW" if args.output: with open(args.output, "w") as f: json.dump(report, f, indent=2) print(f"[*] Report saved to {args.output}") else: print(json.dumps(report, indent=2)) if __name__ == "__main__": main()