009: Closure wave 3 deblob, handoff, and optional wave4/malloc bacons.
Ship script gates wave 4 behind AXP_DEBLOB_WAVE4, documents secrets cert path, and adds deblob appliers plus tests; docs capture Swissquote fix and parity. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
deaaf42caa
commit
3f941e8451
64 files changed
+6270
-86
No files matched your search
@@ -0,0 +1,63 @@
|
||||
#!/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()
|
||||
Reference in new issue
Block a user