#!/usr/bin/env python3 """Build the sanitized publication tree of Project Aurora. usage: publish-sanitize.py SRC_LOCAL SRC_480S_OVERLAY OUT_DIR IDS_FILE REPORT_DIR - SRC_LOCAL: project copy (mirror); SRC_480S_OVERLAY: files taken from the 480s copy instead (longer UART logs, 480s-only text); OUT_DIR must not exist. - IDS_FILE: private "KINDVALUE" lines (IMEI, EMMC_CID, EMMC_SERIAL, SAHARA_SERIAL, PUBLIC_IP, ...) - never published. - Only whitelisted text files are copied; binaries/dumps/NV/firmware are never copied. - Originals are never modified. """ import os, re, sys, shutil, json SRC, OVL, OUT, IDS, REP = sys.argv[1:6] INCLUDE = [r'^(README\.md|NOTES\.md|MANIFEST\.md|manifest\.tsv|firmware-fat-compare\.tsv)$', r'^tools/[^/]+\.(py|sh)$', r'^android/(?!logcat\.txt$)[^/]+\.(txt|prop)$', r'^b5a/([^/]+\.(sh|h|diff|log)|SHA256SUMS)$', r'^b5b/([^/]+\.sh|SHA256SUMS|extlinux\.conf|components/cmdline\.txt)$', r'^b5b/emu/([^/]+\.(out|txt|py|sh|c)|srclist|shim/.*\.h)$', r'^b6/(aurora-modem[^/]*|b6-run\.sh|ro-check\.sh|SHA256SUMS)$', r'^b6j/([^/]+\.(sh|conf|md)|aurora-modem[^/]*|SHA256SUMS)$', r'^b6p/([^/]+\.(md|sh|py|diff|conf|sha256)|SHA256SUMS)$', r'^b6p/rootfs/(init|usr/sbin/aurora-modem|etc/init\.d/aurora-modem|etc/aurora/modem\.conf)$', r'^b6p/out/(SHA256SUMS|manifest-diff\.txt|overlay\.list)$', r'^b6p/test/([^/]+\.(sh|txt)|p-datatest|SHA256SUMS)$', r'^b6p/emu/[^/]+\.(out|txt|sh)$', r'^bootchain-migration/([^/]+\.(md|py)|SHA256SUMS)$', r'^bootchain/(elfinfo\.txt|(boot|recovery)/(info\.txt|ramdisk\.list|ramdisk\.type))$', r'^(firehose|gpt)/SHA256SUMS$', r'^partitions/(SHA256SUMS|rawprogram0\.xml)$', r'^lk2nd-build/(BUILD\.md|WRITE-PLAN\.md|build\.log|build-lk1st\.log)$', r'^linux/([^/]+\.(config|diff|sh|log)|[^/]+-apk-closure[^/]*\.txt)$', r'^linux/patches/[^/]+\.patch$', r'^linux/dts/msm8916-jz08-aurora[^/.]*\.dts$', r'^linux/b4[^/]*/[^/]+\.sh$', r'^linux/b4b2/tcpdump/APK-SHA256SUMS$', r'^linux/beeline/curl/SHA256SUMS$', r'^linux/initramfs[^/]*/(init|initramfs\.list|dl/APK-SHA256SUMS|src/[^/]+\.c|root/etc/(passwd|group))$', r'^linux/initramfs[^/]*/root/bin/(?!udevadm$)[^/]+$', r'^linux/(artifacts/[^/]+|ramboot[^/]*)/(SHA256SUMS|kernel\.config|cmdline\.txt|[^/.]+\.sh|b4b2-mm[^/]*)$', r'^logs/[^/]+\.(log|txt|json)$', r'^logs/(?!uart/|b6/)[^/]+/.*\.(txt|log|sh|out|tsv|json|md)$', r'^logs/(?!uart/|b6/)[^/]+/.*/[^/.]+$', r'^logs/uart/[^/]+\.(log|txt)$', r'^logs/b6/all/(?!.*\.tar$)(?!b6/mm-|b6-try\d/(amd/)?mm-).*$', ] NEVER = r'^logs/b6[jp]/.*(^|/)mm-[^/]*\.log$|(^|/)(dumps|partitions/.*\.bin|firmware-fat/)|\.(bin|img|mbn|pad\d*[KM]?|tar|tgz|gz|pcap|raw|apk|so|dtb|elf|mdt|b\d\d|dis)$|modemst|(^|/)(fsg|fsc|persist|userdata)\b' inc = [re.compile(p) for p in INCLUDE]; nev = re.compile(NEVER) ids = [] for line in open(IDS): k, v = line.rstrip('\n').split('\t', 1) ids.append((k, v)) def is_text(b): return b'\0' not in b[:65536] HEXB = r'(?:[0-9A-Fa-f]{2}:){5,}[0-9A-Fa-f]{2}' RULES = [ # QMI RAW/TLV hex payloads carry IMEI/IMSI/ICCID bytes (ModemManager only hides the translated text) (re.compile(r'((?:data|value)\s*=\s*)' + HEXB + r'(?:\.\.\.)?'), r'\1'), (re.compile(r'(?'), (re.compile(r'4G-MIFI-[0-9A-Za-z_]+'), '4G-MIFI-'), (re.compile(r'\+7\d{7,}|\+\*{4,}\d{3,}'), ''), (re.compile(r'\b\d{4,8}\*{4,}\d{3,6}\b|\*{6,}\d{3,6}\b'), ''), (re.compile(r'(?i)\b(Global Cell ID|Cell ID|Tracking Area Code|Location Area Code|ECI|GCI|TAC|LAC)(\b[\'": =]{0,4})(0x[0-9a-f]+|\d{3,})'), r'\1\2'), # HTTP test responses: tracking cookies, captcha/redirect tokens, request ids (re.compile(r'(?im)^(set-cookie:\s*).*$'), r'\1'), (re.compile(r'(?im)^(location:\s*[^?\s]*\?).*$'), r'\1'), (re.compile(r'(?im)^(x-yandex-req-id:\s*).*$'), r'\1'), ] def sanitize(t, counts): for k, v in ids: for vv in {v, v.upper(), v.lower()}: if vv in t: counts[k] = counts.get(k, 0) + t.count(vv); t = t.replace(vv, f'<{k}>') for rx, rep in RULES: t, n = rx.subn(rep, t) if n: counts[rx.pattern[:40]] = counts.get(rx.pattern[:40], 0) + n return t def walk(root): for d, _, fs in os.walk(root): for f in fs: p = os.path.relpath(os.path.join(d, f), root) if p.startswith(('linux/src/', 'linux/out/', 'lk2nd-build/src/')): continue yield p src = {p: os.path.join(SRC, p) for p in walk(SRC)} for p in walk(OVL): src[p] = os.path.join(OVL, p) # 480s overlay wins (longer/only-480s) os.makedirs(OUT); os.makedirs(REP, exist_ok=True) taken, skipped, total = [], [], {} for p in sorted(src): if not any(r.search(p) for r in inc) or nev.search(p): skipped.append(p); continue b = open(src[p], 'rb').read() uart = p.startswith('logs/uart/') if not uart and not is_text(b): skipped.append(p + ' [binary]'); continue t = b.decode('utf-8', 'replace'); c = {} if uart: # serial line noise: NULs, stray control bytes, ANSI escapes t = re.sub(r'\x1b\[[0-9;?]*[A-Za-z]', '', t) t = re.sub(r'[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]', '', t) t = sanitize(t, c) o = os.path.join(OUT, p); os.makedirs(os.path.dirname(o), exist_ok=True) open(o, 'w').write(t); shutil.copymode(src[p], o) taken.append((p, len(t.encode()), c)) for k, n in c.items(): total[k] = total.get(k, 0) + n with open(os.path.join(REP, 'taken.tsv'), 'w') as f: for p, n, c in taken: f.write(f'{p}\t{n}\t{json.dumps(c)}\n') open(os.path.join(REP, 'skipped.txt'), 'w').write('\n'.join(skipped) + '\n') print(f'taken {len(taken)} files, skipped {len(skipped)}'); print(json.dumps(total, indent=1))