#!/usr/bin/env python3 """Read only Swissquote's RASP exception classification; never dump reports. Requires an already authorized adb shell su. Does not launch, stop, modify, instrument, or grant root to Swissquote. Raw report data stays in memory. APK/report/session identifiers and other application data are not emitted. """ import argparse import json import subprocess def classifications(value): if isinstance(value, dict): exception = value.get("exception") if isinstance(exception, dict): reason = exception.get("reason", "") if (exception.get("type") == "com.swissquote.android.FreeRaspException" and isinstance(reason, str) and reason.startswith("freeRASP: on")): yield reason for child in value.values(): yield from classifications(child) elif isinstance(value, list): for child in value: yield from classifications(child) def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--device", required=True, choices=["LAB", "GOLDEN"]) args = parser.parse_args() serial = {"LAB": "11231JEC207477", "GOLDEN": "11071JEC203839"}[args.device] base = "/data/user/10/com.swissquote.android/files/.crashlytics.v3/com.swissquote.android" def shell(command): return subprocess.check_output( ["adb", "-s", serial, "shell", "su", "-c", "'" + command + "'"], text=True, timeout=15, ) results = [] for path in shell("find " + base + " -maxdepth 4 -type f").splitlines(): # Never interpolate arbitrary output into a shell command. if not path.startswith(base + "/") or any(c not in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/._:-" for c in path): continue if not any(part in path for part in ("/event", "/reports/", "/priority-reports/")): continue size = shell("stat -c %s " + path).strip() if not size.isdigit() or int(size) > 4 * 1024 * 1024: continue try: value = json.loads(shell("cat " + path)) except json.JSONDecodeError: continue results.extend(classifications(value)) print(json.dumps({"device": args.device, "profile": 10, "rasp_exceptions": results, "note": "Retained events only; absence is not proof of PASS."}, indent=2)) if __name__ == "__main__": main()