Skip to content

RELEASE-4 W4: acceptance-complete offline stealth probe plus informational detectors (G-D) - #45

Open
AminDhouib wants to merge 1 commit into
audit/release-fix-bfrom
audit/release-4-w4
Open

RELEASE-4 W4: acceptance-complete offline stealth probe plus informational detectors (G-D)#45
AminDhouib wants to merge 1 commit into
audit/release-fix-bfrom
audit/release-4-w4

Conversation

@AminDhouib

Copy link
Copy Markdown
Member

RELEASE-4 (W4) — stacked on #43 (audit/release-fix-b), sibling of #44 (W2)

Closes the biggest honesty gap in the scorecard: the headline claim is undetectable browser automation and the repo had zero runtime stealth tests (only a launch-flag sanitizer).

Offline gating tier (stealth marker; gate lane -m "stealth and not online", the lane #44 wired): an armed passive collector (tests/fixture_app/stealth_probe.html) served by the ONE existing fixture mechanism, releasing its closed-schema result only after an exact ordered CDP prerequisite transcript (Runtime.enable → Page.enable → Network.enable → DOM.getDocument → Runtime.evaluate(nonce) → Page.captureScreenshot) driven through the project's own tab.send(uc.cdp.*) seam. A versioned signal table of 10 gating predicates covers navigator.webdriver, CDP-leak globals, plugins, languages, window.chrome members, Notification/permissions coherence, per-OS platform, UA client hints, patched Function.prototype.toString, and non-zero outer dimensions. Env-dependent signals (WebGL renderer) are collected informational only, never gated.

Teeth, two ways: (1) a vanilla control — the same product spawn_browser path with the stealth arg-filter neutralized (the single intentional treatment, restored in finally) plus --enable-automationnavigator.webdriver === true, asserted detected; (2) per-collector deterministic controls feeding each predicate its forbidden observation. Also asserts live-process evidence: Chrome's cmdline[0] equals the exact resolved binary and carries no --enable-automation/--test-type.

Non-gating online tier (online marker): CreepJS + bot.incolumitas, hard invariants only, skips on network error, excluded from every default run and the gate.

⚠️ Product stealth defect found — F-770 (read before believing any "stealth" claim)

Under headless — the posture the offline gate runs — the product's default User-Agent still advertises HeadlessChrome. nodriver does not mask the UA token. Per plan_RELEASE's zero-src rule this is pinned as a strict xfail, not hidden by weakening the probe. Release-claim integrity: an xfailed invariant does not satisfy the stealth claim — the headless UA vector remains detectable and needs a fix owner (or an explicitly qualified claim) before G-D can be honestly asserted for the headless posture.

Local evidence: offline lane 14 passed / 1 xfailed / 2 deselected (real headless Chrome; product passes all 10 gating predicates; vanilla control detected). Gate lane contains zero public URLs (the 2 deselected are exactly the online tests). Unit suite unchanged. ruff / ty (76 baseline) / vulture / budgets / suppression-owners clean. Zero src/ edits.

Reviewer judgment call: the control routes through the product's own spawn path with the arg-filter neutralized, rather than a raw nodriver.uc.start — chosen to honor "no second Chrome-launch path" while still producing a genuinely leaking browser. Easy to swap if you prefer the raw launch.

Merge order: after #42#43. Independent of #44 (file-disjoint; both add the same stealth marker line). True merge commits, please.

…al detectors (G-D)

plan_RELEASE W4. Lands the deterministic offline stealth-invariant gate plus the
non-gating informational online tier, asserting the product's headline
"undetectable" promise (G-D) that was previously asserted nowhere.

- tests/fixture_app/stealth_probe.html: armed passive signal collector served by
  the ONE fixture mechanism (release_gate_harness.serve_fixture_app). Publishes a
  single closed-schema window.__STEALTH_PROBE_RESULT__ and one completion event;
  collects nothing until explicitly released.
- tests/test_stealth.py: versioned predicate table; closed-schema validator with
  deterministic negative controls; per-collector-family sensitivity controls;
  the exact ordered CDP prerequisite transcript (Runtime.enable -> Page.enable ->
  Network.enable -> DOM.getDocument -> Runtime.evaluate(nonce) ->
  Page.captureScreenshot) driven via the project's own tab.send(uc.cdp.*) seam;
  process-flag evidence with W2's exact-binary identity; a redacted result
  artifact; and a real vanilla-control that must be detected. The vanilla control
  is the SAME product spawn path with the stealth arg-filter neutralized (the one
  intentional treatment) -- no second Chrome-launch path.
- pyproject.toml: adds the `stealth` (gating) and `online` (non-gating) markers.
  Gate lane selects `-m "stealth and not online"`; unit lane `-m "not
  integration"` excludes the whole module.

Zero src/ edits. One product predicate does not pass and is pinned honestly as a
strict xfail (F-770): under headless the product UA still advertises
"HeadlessChrome"; nodriver's default stealth does not mask it. An xfailed
invariant does NOT satisfy the stealth release claim -- the headless UA vector
remains detectable and is flagged for a fix owner.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@AminDhouib

Copy link
Copy Markdown
Member Author

CI status: the stealth tier itself is green. The ubuntu integration job reports 68 passed, 1 xfailed — every offline stealth test passes on Linux CI, including the vanilla-control sensitivity check and the F-770 strict xfail (it xfails as designed, so the gap is recorded, not hidden).

The single failure is inherited, not W4's: test_e2e_transport.py dies with FileNotFoundError: console launcher ... not found — the W1 harness bug where resolve_launcher followed the POSIX venv bin/python symlink out of the venv. That is already fixed on the W2 branch (c56de0b, PR #44); this branch is stacked on audit/release-fix-b, which predates it.

No action needed here: merging #44 before #45 (or rebasing #45 onto it) makes this green. Deliberately not cherry-picking the same fix onto both branches to avoid duplicate commits colliding at merge.

AminDhouib added a commit that referenced this pull request Jul 29, 2026
…n (F-780)

#43, #45 and #47 are red on the legacy `Browser Integration Tests
(Chrome + Xvfb)` check while main and #42 are green on the same check.
Read at face value that says FIX-B broke browser integration. It did
not, and the reason is provable without reading a single CI log.

test.yml runs `pytest -m integration --timeout=120`. The harness it
drives declares INIT=60, LIST=130, SPAWN=120, WARMUP=150 with 4 attempts
-- under a comment saying "the pytest --timeout is the outer net". Two
of those single-step bounds are LARGER than the whole job's per-test
budget, and BACKEND_READY_TIMEOUT is exactly equal to it. A 120s outer
net cannot contain a 150s inner step. The job is structurally incapable
of finishing that test; no timing luck is involved.

It only started failing at FIX-B because W1 landed the transport journey
already xfail'd (B1 was open), and an xfail costs nothing on the clock.
FIX-B C2 correctly removed the marker once B1 was fixed, so from that
commit the test actually runs -- and immediately hits the wall.

The controlled comparison is #46: it contains FIX-B transitively, runs
the SAME `-m integration` selection on the SAME OS under the release
gate's 180s budget, and is green 23/23.

Deliberately NOT fixed. W2 deletes this job, so patching a doomed file
on three in-review branches is churn that also mutates PRs under human
review. Recorded instead, because the trap is expensive: the merge queue
will show red at #43 -> #45 -> #47 until W2 lands, and someone will
otherwise go hunting for a regression that was never there.

Not added to RELEASE_CONTRACT.md on purpose -- the contract describes
what a user receives, not our merge plumbing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant