checkrequests.py
Every host a browser really contacted
117 lines. This is the file the build actually runs, copied verbatim at build time.
- 1
"""Every host a browser actually requested, parsed from Chrome's network log. - 2
- 3
The site claims "no third-party requests" on every page. That was only checked - 4
by grepping the generated HTML for foreign hostnames, which cannot see what a - 5
script fetches at run time — and Cloudflare injects a script into every page. - 6
- 7
A warning learned the hard way: do NOT grep URLs out of the netlog. The file - 8
contains Chrome's own configuration — the HSTS preload list, safe-browsing and - 9
autofill tables — so a regex over it "finds" youtube.com and play.google.com on - 10
a page that never touched them. That would have been a confident, wrong, - 11
alarming claim. This parses real request events instead. - 12
- 13
Capture and analysis are separate: only shot.sh may reach the network and start - 14
chrome, so run it per page first, then - 15
- 16
python3 checkrequests.py /tmp/claude-996/net-*.json - 17
""" - 18
- 19
import json - 20
import os - 21
import re - 22
import sys - 23
- 24
OWN = {"sweedworks.com", "www.sweedworks.com"} - 25
- 26
# Hosts Chrome contacts on its own account at startup. Verified by capturing a - 27
# load of /robots.txt — a plain text file with no HTML, CSS or scripts — and - 28
# seeing exactly these. They would appear on any site in the world. Listed - 29
# rather than filtered silently, and deliberately narrow: a real request to a - 30
# Google host from page code would still be reported. - 31
BROWSER_STARTUP = { - 32
"accounts.google.com", - 33
"www.google.com", - 34
"clients2.google.com", - 35
"content-autofill.googleapis.com", - 36
} - 37
HOST_RE = re.compile(r"^https?://([^/:]+)") - 38
- 39
# Event types that mean a request was actually started. - 40
REQUEST_EVENTS = {"URL_REQUEST_START_JOB", "HTTP_STREAM_JOB_CONTROLLER_BOUND"} - 41
- 42
- 43
def analyse(path): - 44
with open(path, encoding="utf-8", errors="replace") as fh: - 45
raw = fh.read() - 46
- 47
# Chrome may not close the JSON if it was killed; salvage what parses. - 48
try: - 49
log = json.loads(raw) - 50
except ValueError: - 51
cut = raw.rfind("},") - 52
log = json.loads(raw[:cut + 1] + "]}") - 53
- 54
types = log.get("constants", {}).get("logEventTypes", {}) - 55
wanted = {tid for name, tid in types.items() if name in REQUEST_EVENTS} - 56
if not wanted: - 57
raise RuntimeError(f"{path}: no request event types in constants") - 58
- 59
requested = [] - 60
for ev in log.get("events", []): - 61
if ev.get("type") not in wanted: - 62
continue - 63
url = (ev.get("params") or {}).get("url") - 64
if url: - 65
requested.append(url) - 66
- 67
own, foreign, browser = set(), {}, {} - 68
for url in requested: - 69
m = HOST_RE.match(url) - 70
if not m: - 71
continue - 72
host = m.group(1).lower() - 73
if host in OWN: - 74
own.add(url.split("?")[0]) - 75
elif host in BROWSER_STARTUP: - 76
browser[host] = browser.get(host, 0) + 1 - 77
else: - 78
foreign[host] = foreign.get(host, 0) + 1 - 79
return own, foreign, browser, len(requested) - 80
- 81
- 82
def main(): - 83
logs = sys.argv[1:] - 84
if not logs: - 85
print(__doc__) - 86
return 2 - 87
- 88
problems = [] - 89
for path in logs: - 90
if not os.path.exists(path): - 91
problems.append(f"{path}: missing — was shot.sh run for it?") - 92
continue - 93
own, foreign, browser, total = analyse(path) - 94
print(f"{os.path.basename(path)} ({total} requests started)") - 95
for u in sorted(own): - 96
tag = "cloudflare" if "/cdn-cgi/" in u else "own" - 97
print(f" {tag:<10} {u}") - 98
for h, n in sorted(browser.items()): - 99
print(f" browser {h} ({n})") - 100
for h, n in sorted(foreign.items()): - 101
print(f" FOREIGN {h} ({n})") - 102
problems.append(f"{os.path.basename(path)}: requested {h}") - 103
print() - 104
- 105
if problems: - 106
print(f"{len(problems)} problem(s):") - 107
for p in problems: - 108
print(f" - {p}") - 109
return 1 - 110
print("Every request from page code went to sweedworks.com. The only other " - 111
"traffic is Chrome's own startup calls, which happen on any site.") - 112
return 0 - 113
- 114
- 115
if __name__ == "__main__": - 116
sys.exit(main())