← Commit history

correct the analog/Akamai mechanism: HTTP 403 at the edge before JS, not a sensor that failed to execute

John Lauer ·1c94d905ef ·22d ago ·parent dc2fe71
1 file changed +71−10
gen.py+71−10
@@ -141,14 +141,14 @@ SITES = [   dict(slug="analog", name="Analog Devices (incl. Maxim, Linear)", domains=["www.analog.com"],   what="Manufacturer. Product pages, datasheets, LTspice models, eval boards.",-  defense="Akamai Bot Manager, strict. The single hardest case in this pack and the one that drove the 2026-09-14 investigation (see the root skill). TWO stacked causes, both measured: (1) browser VERSION - on CfT 148 the bmak sensor never initialised (typeof bmak undefined) and the edge denied; on CfT 152, matched to the installed Chrome, the sensor runs and the page loads; (2) egress IP - even on 152, loads from a VPN/Azure egress and denies from the bare office LAN NAT.",+  defense="Akamai Bot Manager, strict. The single hardest case in this pack and the one that drove the 2026-09-14 investigation (see the root skill). TWO stacked causes, both measured: (1) browser VERSION - on CfT 148 the edge answered HTTP 403 (AkamaiGHost) with a static Access Denied page; on CfT 152, matched to the installed Chrome, the same URL serves the real page. The 403 is decided at the HTTP layer before any page JavaScript runs, so this is a CONNECTION-fingerprint refusal, not a sensor failure (CfT 148 runs JS fine; typeof bmak undefined on a deny page is a symptom, the deny page has no sensor script); (2) egress IP - even on 152, loads from a VPN/Azure egress and denies from the bare office LAN NAT.",   lanes={    "WebFetch": ("BLOCKED", "Akamai edge"),    "curl": ("BLOCKED", "curl (92) HTTP/2 INTERNAL_ERROR in ~100 ms"),    "pup": ("PARTIAL", "CfT 152 REQUIRED (on 148 always denied). On 152: LOADS from a clean egress (AdomLapper VPN, winvm Azure, bmak:object), DENIED from the bare office LAN (arav-rog, ConfRoomROG, bmak:undefined). 2026-09-14"),    "nb": ("OK", "the user's real Chrome loads it (AD8232 Datasheet and Product Info); use nb when pup's egress is flagged"),   },-  symptoms=["'Access Denied' + 'Reference #18.3351db17...'", "typeof bmak === undefined on the denied page (rejected upstream)", "HTTP/2 stream reset to curl"],+  symptoms=["'Access Denied' + 'Reference #18.3351db17...'", "HTTP 403 from AkamaiGHost on the denied request (refused at the edge, before JS)", "HTTP/2 stream reset to curl"],   recipes=[    ("Load the site in pup", "Requires CfT matched to the installed Chrome (pup 2.0.549+, CfT 152). If it still denies with bmak undefined, the egress IP is flagged: use nb, or a box on a VPN/residential/cloud IP. Clear cookies first if it denied you earlier: pup_clear_cookies {domain:'analog.com'}."),    ("Datasheet PDF", "/media/en/technical-documentation/data-sheets/<part>.pdf; nbrowser_fetch_url once you have the link, or pup on a clean egress."),@@ -367,6 +367,45 @@ def lane_table(site):         rows.append(f"| {L} | **{r}** | {note or '-'} |")     return "\n".join(rows) +# ---------------------------------------------------------------------------+# 2026-09-14 PACED RE-VERIFICATION (layered over the 2026-09-12 sweep above).+# Conditions: pup 2.0.549, Chrome for Testing 152 (matched to the installed+# Chrome), EXTENSION lane (no debug port), consumer identity on, ONE tab at a+# time with a 7 s settle, that domain's cookies cleared first, AdomLapper on a+# corporate VPN egress. Result: 20 of 20 loaded, including the four the earlier+# BURST run called blocked. The burst was tripping velocity rules; it was not+# the sites refusing pup. The 09-12 notes are kept because they are the failure+# SIGNATURES: that is what these sites look like when the conditions above are+# not met (stale CfT, unpaced requests, a poisoned verdict cookie).+# ---------------------------------------------------------------------------+PACED = {+ "st":            "loads paced on CfT 152. On CfT 148 this was ERR_HTTP2_PROTOCOL_ERROR after the first page; the version match is what fixed it",+ "mouser":        "HOME PAGE loads paced on CfT 152 (was 'Access to this page has been denied' on 148). DataDome still scores CRAWLS: use the API for data, do not walk the catalog",+ "digikey":       "loads paced on CfT 152",+ "arrow":         "loads paced on CfT 152. Earlier BLOCKED readings came from the 20-tab burst, not from arrow",+ "analog":        "loads paced on CfT 152 (bmak:object, i.e. the real page with the sensor on it was served). On CfT 148 the edge answered HTTP 403 before any JS ran: the version match is the fix, and the refusal was on the connection fingerprint, not a sensor failure",+ "ti":            "loads paced on CfT 152",+ "microchip":     "loads paced on CfT 152",+ "nordic":        "loads paced on CfT 152",+ "snapeda":       "loads paced on CfT 152",+ "ultralibrarian":"loads paced on CfT 152",+ "componentsearchengine": "loads paced on CfT 152",+ "datasheets-com":"loads paced on CfT 152. Earlier BLOCKED reading came from the burst",+ "octopart":      "loads paced on CfT 152",+ "jlcpcb":        "loads paced on CfT 152 (LCSC too)",+ "avnet":         "loads paced on CfT 152, root and /americas/. The PR_WAF_DENY reCAPTCHA page is a VELOCITY deny: a 20-tab burst trips it and the verdict sticks until cookies are cleared",+ "newark":        "loads paced on CfT 152",+ "rs-online":     "loads paced on CfT 152",+ "infineon":      "loads paced on CfT 152",+ "nxp":           "loads paced on CfT 152",+}+for _s in SITES:+    _n = PACED.get(_s["slug"])+    if _n:+        _s["lanes"]["pup"] = ("OK", _n + " [paced re-verification 2026-09-14]")+        _s.setdefault("gotchas", []).insert(0,+          "Verified loading 2026-09-14 under: CfT 152 + extension lane + PACED single visit + that domain's cookies cleared. Change any one of those (stale CfT, a burst of tabs, a stale reject cookie) and the failure signatures below come back. The signatures are kept for recognition, not because the site is currently blocking pup.")+ def site_skill(site):     name = SKILL_PREFIX + site["slug"]     doms = ", ".join(site["domains"])@@ -450,15 +489,19 @@ Browser Extension 0.16.14 (Chrome 152). Two independent discoveries this date, each measured, that reframe the hardest Akamai sites:  **1. The Chrome-for-Testing VERSION is itself a bot signal.** pup pinned CfT 148 while the user's-real Chrome was 152. On 148, Akamai's `bmak` sensor never initialised (`typeof bmak === "undefined"`-on every load) and analog.com served Access Denied. On CfT **152** (matched to the installed Chrome)-the sensor initialises (`typeof bmak === "object"`), runs, and the page loads. It was NOT a fingerprint-match (the JA4 still differs from branded Chrome); Akamai gated on the sensor being able to EXECUTE,-which a 4-version-old build broke. Keep CfT within ~1 milestone of the installed Chrome. Several sites+real Chrome was 152. On 148, analog.com answered **HTTP 403 (AkamaiGHost)** with a static Access Denied+page; on CfT **152** (matched to the installed Chrome) the same URL serves the real page. Be precise+about where that is decided: a 403 arrives at the HTTP layer, so the edge refused the REQUEST and no+page JavaScript ever ran. CfT 148 executes JavaScript fine; that was never the issue. The discriminator+is the CONNECTION fingerprint (the TLS ClientHello measurably differs between CfT builds). Which exact+element flips the verdict has NOT been isolated. `typeof bmak === "undefined"` on a denied page is a+SYMPTOM, because the Access Denied page carries no sensor script at all. Keep CfT within ~1 milestone of the installed Chrome. Several sites that were pup-BLOCKED on 148 (st.com among them) load on 152. Diagnostic on a denial: `typeof bmak` `undefined` = rejected upstream (version or IP); `object` = the sensor ran and failed you (IP/behaviour). -**2. Egress IP reputation is a separate, stacked cause.** Same CfT 152, same code, four boxes:+**2. Egress IP reputation: SUSPECTED, not established.** The four-box comparison below was run with+the BURST method, which we now know trips velocity rules on its own, so these readings are method-suspect+and have not been reproduced paced. Treat as a hypothesis to re-test, not as fact: AdomLapper (corporate VPN egress) and winvm (Azure) LOAD analog.com; arav-rog and ConfRoomROG (bare office LAN) are DENIED. Identical browser, opposite result: the bare office NAT IP is Akamai-flagged. pup cannot fix this from inside the browser; the lever is a cleaner egress (VPN/residential/cloud) or@@ -483,6 +526,24 @@ masks a fix.  *Mouser loads its home page but scores behaviour on crawls (DataDome) - use the API for data. +## How to load ANY of these in pup (the procedure that got 20 of 20)++Measured 2026-09-14 on AdomLapper: all twenty sites in this pack loaded in pup, including the four a+previous burst run had called blocked. Three conditions, all of them required:++1. **Match Chrome for Testing to the user's INSTALLED Chrome.** Check `pup_readiness.chromeBuildId`+   against their Chrome. A stale CfT breaks Akamai's sensor outright (it never initialises) and no+   header work rescues it.+2. **Go at human pace: ONE tab at a time, with a real settle (about 7 s).** A burst of tabs trips+   velocity rules on Akamai sites (avnet, arrow, datasheets all "blocked" purely from bursting). This+   is the single most common self-inflicted block.+3. **Clear that domain's cookies before the visit.** A previous denial writes a reject verdict that+   persists and will keep denying you long after the real cause is fixed.++Then use the default extension lane (no debug port) with the consumer identity on, which is what+`pup_open_window` already does. Only when all three conditions hold and a site STILL refuses is it+worth reaching for nb or the API lane.+ ## The rule: a fetch failure on a vendor domain is a ROUTING signal  1. **One plain attempt only.** WebFetch or curl once. It is the cheapest lane and it works on TI,@@ -639,7 +700,7 @@ def main():                {"label": "Which browser for DigiKey", "prompt": "which browser lane do I need to read a DigiKey product page"},                {"label": "Mouser 200 but denied", "prompt": "why does Mouser return HTTP 200 with access denied and what do I do"},                {"label": "Add a site", "prompt": "add an electronics-sites skill for nxp.com and verify its lanes"}]-    pkg = {"name": PACK, "slug": PACK, "version": "2.1.1", "type": "skill", "title": TITLE, "description": BRIEF,+    pkg = {"name": PACK, "slug": PACK, "version": "2.2.0", "type": "skill", "title": TITLE, "description": BRIEF,            "license": "MIT", "files": files, "scripts": {"install": "./install.sh", "uninstall": "./uninstall.sh"},            "dependencies": {}, "tags": TAGS, "keywords": TAGS, "discovery_triggers": TRIGGERS, "discovery_pitch": PITCH,            "sample_prompts": PROMPTS, "hero": {"path": "docs/hero.png"}}@@ -687,7 +748,7 @@ No captcha solving, no fingerprint spoofing: this pack routes around walls, it d """     write("README.md", readme)     page = json.load(open(os.path.join(ROOT, "page.json")))-    page.update({"slug": PACK, "version": "2.1.1", "title": TITLE, "brief": BRIEF, "readme": readme, "license": "MIT",+    page.update({"slug": PACK, "version": "2.2.0", "title": TITLE, "brief": BRIEF, "readme": readme, "license": "MIT",                  "tags": TAGS,                  "hero": {"type": "image", "path": "docs/hero.png"},                  "sample_prompts": PROMPTS, "discovery_triggers": TRIGGERS, "discovery_pitch": PITCH,