checklive.py
Compares served bytes against what was generated
136 lines. This is the file the build actually runs, copied verbatim at build time.
"""Compare what I generate against what is actually served.
Cloudflare injects a bot-detection script into HTML in transit. I missed it for
days because I only ever checked the files I wrote — the injection is invisible
from disk. This script diffs the served bytes against the local file and reports
anything added, so a change in what sits in front of this domain shows up as a
build finding rather than as a false claim on the privacy page.
Needs real network access to the live domain, so it is not part of build.sh:
run it after deploying.
python3 checklive.py
"""
import os
import re
import subprocess
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
ROOT = os.path.abspath(os.path.join(HERE, ".."))
BASE = "https://sweedworks.com"
PAGES = [("/", "index.html"),
("/vocabulary/", "vocabulary/index.html"),
("/predict/", "predict/index.html"),
("/cost/", "cost/index.html"),
("/learn/", "learn/index.html"),
("/tokens/", "tokens/index.html"),
("/about/", "about/index.html")]
# Injections we already know about and have disclosed on /about/.
KNOWN = [
(r"__CF\$cv\$params", "Cloudflare bot-detection script (disclosed)"),
(r"cdn-cgi/challenge-platform", "Cloudflare challenge platform (disclosed)"),
]
def fetch(path):
"""Headers and body to separate files.
Do not go back to `-D -` with text=True: universal-newline translation turns
the CRLF header/body separator into LF, the partition silently fails, the
body comes back empty, and every comparison below then "passes" against
nothing. This check reported all-clear that way while the injected script
was plainly there.
"""
import tempfile
with tempfile.TemporaryDirectory() as tmp:
hp = os.path.join(tmp, "h")
bp = os.path.join(tmp, "b")
subprocess.run(
["curl", "-sS", "-D", hp, "-o", bp, f"{BASE}{path}?livecheck=1"],
capture_output=True, timeout=60, check=True)
head = open(hp, encoding="utf-8", errors="replace").read()
body = open(bp, encoding="utf-8", errors="replace").read()
if not body:
raise RuntimeError(f"empty body for {path} — the check would be vacuous")
return head, body
def main():
problems, notes = [], []
for url, local in PAGES:
head, served = fetch(url)
mine = open(os.path.join(ROOT, local), encoding="utf-8").read()
# Anything in the served copy that is not in mine.
extra = served
for line in mine.splitlines():
extra = extra.replace(line, "", 1)
extra = extra.strip()
if extra:
explained = False
for pattern, label in KNOWN:
if re.search(pattern, extra):
notes.append(f"{url}: {label}, {len(extra):,} bytes added")
explained = True
break
if not explained:
problems.append(
f"{url}: UNEXPLAINED content injected in transit "
f"({len(extra):,} bytes): {extra[:200]!r}")
else:
notes.append(f"{url}: served bytes match what I generated")
if re.search(r"(?im)^set-cookie:", head):
problems.append(f"{url}: a cookie is being set — /about/ says none are")
for m in re.finditer(r"(?im)^(nel|report-to):\s*(.*)$", head):
if "cloudflare" in m.group(2).lower():
notes.append(f"{url}: {m.group(1)} header points at Cloudflare "
f"(disclosed)")
# Every internal link must actually be reachable. Checking that files exist
# on disk is not enough: the web server denies some paths, so a link can be
# perfectly valid locally and 403 to the public. That happened.
seen, checked = set(), 0
for url, local in PAGES:
src = open(os.path.join(ROOT, local), encoding="utf-8").read()
for attr in ("href", "src"):
for target in re.findall(rf'{attr}="([^"]+)"', src):
if target.startswith(("http://", "https://", "#", "mailto:",
"data:")):
continue
clean = target.split("#")[0]
if not clean.startswith("/") or clean in seen:
continue
seen.add(clean)
out = subprocess.run(
["curl", "-sS", "-o", "/dev/null", "-w", "%{http_code}",
f"{BASE}{clean}"],
capture_output=True, text=True, timeout=60)
code = out.stdout.strip()
checked += 1
if code != "200":
problems.append(f"{url}: links to {clean} which returns {code}")
notes.append(f"{checked} internal links checked, all reachable"
if not problems else f"{checked} internal links checked")
for n in notes:
print(f" note {n}")
print()
if problems:
print(f"{len(problems)} problem(s):")
for p in problems:
print(f" - {p}")
return 1
print("Live pages match what I generated, apart from disclosed injections.")
return 0
if __name__ == "__main__":
sys.exit(main())