agentic-stealth-browser 2.5.0__tar.gz → 2.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/CHANGELOG.md +71 -1
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/PKG-INFO +42 -14
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/README.md +30 -2
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/SECURITY.md +4 -2
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/agentic_stealth_browser.egg-info/PKG-INFO +42 -14
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/agentic_stealth_browser.egg-info/SOURCES.txt +6 -0
- agentic_stealth_browser-2.7.0/agentic_stealth_browser.egg-info/requires.txt +13 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/core/agent_browser.py +6 -1
- agentic_stealth_browser-2.7.0/docs/DATA_HANDLING.md +19 -0
- agentic_stealth_browser-2.7.0/docs/INCIDENT_RESPONSE.md +22 -0
- agentic_stealth_browser-2.7.0/docs/KEY_MANAGEMENT.md +9 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/production/adapters/__init__.py +3 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/production/adapters/agentic_stealth_mcp.py +11 -3
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/production/adapters/base.py +14 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/production/adapters/cdp_bridge.py +29 -3
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/production/adapters/playwright_mcp.py +34 -15
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/production/mcp_server.py +76 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/production/mcp_session_isolation.py +16 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/pyproject.toml +12 -12
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/recovery/anti_block_orchestrator.py +36 -9
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/stealth/advanced_stealth.py +181 -94
- agentic_stealth_browser-2.7.0/tests/stealth_harness/__init__.py +0 -0
- agentic_stealth_browser-2.7.0/tests/stealth_harness/collect.py +164 -0
- agentic_stealth_browser-2.7.0/tests/stealth_harness/test_stealth.py +137 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_adapter_agentic_stealth_mcp.py +28 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_adapter_cdp_bridge.py +25 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_adapter_playwright_mcp.py +48 -13
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_attach_over_cdp.py +5 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_recovery_state_machine.py +22 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_stealth_modules.py +9 -0
- agentic_stealth_browser-2.5.0/agentic_stealth_browser.egg-info/requires.txt +0 -13
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/.env.example +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/CODE_OF_CONDUCT.md +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/CONTRIBUTING.md +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/LICENSE +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/MANIFEST.in +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/agentic_stealth_browser.egg-info/dependency_links.txt +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/agentic_stealth_browser.egg-info/entry_points.txt +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/agentic_stealth_browser.egg-info/top_level.txt +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/ai/__init__.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/ai/ai_hooks.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/assets/hn-demo.gif +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/audit/__init__.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/audit/logger.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/behavior/__init__.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/behavior/adaptive_tuner.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/behavior/human_behavior.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/behavior/orchestration.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/behavior/persona_rotator.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/canary/__init__.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/canary/badge_gen.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/canary/dashboard_gen.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/canary/history.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/canary/py.typed +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/canary/readme_gen.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/canary/runner.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/canary/score.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/canary/sites.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/canary/templates/badge.svg.j2 +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/canary/templates/dashboard.html.j2 +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/core/__init__.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/core/account_health.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/core/account_warming.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/core/connection_pool.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/core/error_messages.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/core/feature_flags.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/core/py.typed +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/core/session_checkpoint.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/core/types.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/docs/ATTACH_OVER_CDP.md +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/docs/README.md +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/docs/agentic-stealth-browser-analysis-report.md +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/docs/canary/README.md +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/docs/canary/badge.svg +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/docs/canary/history.jsonl +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/docs/canary/index.html +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/docs/canary.md +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/docs/plans/2026-06-01-v2.4.0-attach-mode-hardening.md +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/docs/plans/2026-06-03-v2.5.0-real-backend-adapters.md +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/docs/plans/misc/GOAL_MODE_HERMES_BROWSER_DASHBOARD.md +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/docs/plans/misc/HN_POST.md +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/docs/plans/misc/OPEN_SOURCE_READINESS.md +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/docs/plans/misc/RELEASE_AUTONOMOUS_PLAN.md +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/examples/recipes/01_cloudflare_bypass.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/examples/recipes/02_linkedin_search.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/examples/recipes/03_amazon_product.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/examples/recipes/README.md +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/examples/recipes/linkedin.md +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/linkedin/__init__.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/linkedin/actions.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/mcp_security.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/plugins/__init__.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/plugins/template/__init__.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/plugins/template/plugin.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/production/Dockerfile +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/production/__init__.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/production/adapters/_jsonrpc_stdio.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/production/approval_gate.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/production/audit_enrichment.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/production/cli.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/production/dashboard_adapter_bridge.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/production/deprecations.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/production/docker-compose.yml +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/production/docker-healthcheck.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/production/hermes_dashboard.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/production/mcp_input_validator.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/production/metrics.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/production/otel_export.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/production/policy_engine.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/production/profiler.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/production/rate_limiter.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/production/sdk/__init__.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/production/sdk/client.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/production/workflow_orchestrator.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/proxy/__init__.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/proxy/proxy_manager.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/recovery/__init__.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/recovery/detectors.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/recovery/explain_blocked.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/scraping/__init__.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/scraping/scraper.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/scripts/canary_run.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/scripts/evaluate_stealth.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/scripts/generate-demo-gif.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/scripts/health_check.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/scripts/hn-demo.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/scripts/make-gif.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/scripts/migrate_v1_to_v2.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/scripts/perf_benchmark.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/scripts/setup_rbb.sh +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/sessions/__init__.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/sessions/cookie_manager.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/sessions/session_manager.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/setup.cfg +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/stealth/__init__.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/stealth/cache.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/stealth/firefox_adapter.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/stealth/headers.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/stealth/presets.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/stealth/profiles.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/stealth/tls_fingerprint.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/stealth/tls_ja3_ja4.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/debug_nowsecure.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/detection_check.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/detection_runner.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/fingerprint_scorecard.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/run_detection_tests.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_account_health.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_account_warming.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_adaptive_tuner.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_backend_adapter_contract.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_basic.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_cdp_snapshots.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_connection_pool.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_contract_agent_browser.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_cookie_manager_unit.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_dashboard_protocol_bridge.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_detectors.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_differential.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_docker_config.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_e2e_anti_block_recovery.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_e2e_protected_sites_placeholder.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_golden_master.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_hermes_dashboard.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_human_behavior_fuzz.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_integration_modules.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_linkedin_actions.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_mcp_contract.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_mcp_server_runtime.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_mcp_stealth_workflows.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_mcp_url_safety.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_metrics_collector_unit.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_mutation_stealth.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_persona_rotator.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_phase7_fixes.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_player.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_proxy_config_unit.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_proxy_manager.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_rate_limiter_unit.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_recorder.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_recovery.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_recovery_phase1.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_rehearsal_validation.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_selector_auto_heal.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_session_checkpoint.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_stealth_cache.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_stealth_coverage.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_stress_multi_account.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_tls_ja3_ja4.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_workflow_orchestrator.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_workflow_schema.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_workflow_schema_unit.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/tests/test_workflow_versioning.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/workflows/__init__.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/workflows/action_interpreter.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/workflows/library/auto-session-test.yaml +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/workflows/library/common/login.yaml +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/workflows/library/common/verify-email.yaml +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/workflows/library/linkedin/send-connection-request.yaml +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/workflows/library/meta-test.yaml +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/workflows/library/simple-test.yaml +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/workflows/library/teach-test/test-teach.yaml +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/workflows/library/upwork/add-portfolio-item.yaml +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/workflows/library/upwork/apply.yaml +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/workflows/library/upwork/edit-title.yaml +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/workflows/library/upwork/submit-proposal.yaml +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/workflows/library/upwork/update-rate.yaml +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/workflows/player.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/workflows/recorder.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/workflows/recovery.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/workflows/schema.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/workflows/selector_generator.py +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/workflows/test-echo.yaml +0 -0
- {agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/workflows/variable_resolver.py +0 -0
|
@@ -9,6 +9,75 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [2.7.0] — Enterprise Hardening (2026-07-18)
|
|
13
|
+
|
|
14
|
+
Auditability, supply-chain, and business-facing hardening from a full enterprise
|
|
15
|
+
audit. No breaking changes; no runtime behavior change on existing flows.
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- **Acceptable Use Policy** (`ACCEPTABLE_USE.md`) + a `## Legal & Acceptable Use`
|
|
19
|
+
section in the README — authorized-use scope, prohibited uses (ToS/robots/CFAA/
|
|
20
|
+
GDPR-CCPA), operator responsibility, and rate-limit good-citizenship, for an
|
|
21
|
+
anti-bot tool that enterprise/legal review can pass through procurement.
|
|
22
|
+
- **Compliance docs** (`docs/`): `INCIDENT_RESPONSE.md` (credential/key-compromise
|
|
23
|
+
playbook, triage severities, post-incident), `KEY_MANAGEMENT.md` (secret
|
|
24
|
+
inventory + env sourcing + rotation), `DATA_HANDLING.md` (what is persisted,
|
|
25
|
+
data-controller statement, retention + deletion posture).
|
|
26
|
+
- **Navigation success/failure metrics** (`core/agent_browser.py`): `safe_goto`
|
|
27
|
+
now emits `requests_success` / `requests_failed` counters and calls
|
|
28
|
+
`metrics.record_error("navigation", …)` on failure, so `MetricsCollector`'s
|
|
29
|
+
`success_rate` reflects real traffic instead of being permanently 100%.
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
- **Dependency lower bounds** (`pyproject.toml`): the seven previously-unpinned
|
|
33
|
+
runtime deps and the four dev deps now carry `>=` floors set to
|
|
34
|
+
CVE-safe/current releases (e.g. `aiohttp>=3.10.11`, `cryptography>=43.0.1`,
|
|
35
|
+
`jinja2>=3.1.5`), so a fresh install can no longer resolve a known-vulnerable
|
|
36
|
+
release. No upper caps — existing installs are unaffected.
|
|
37
|
+
- **README claims hedged** for an enterprise audience: the tagline and the
|
|
38
|
+
"zero-flag" line are now framed as a point-in-time canary snapshot, not a
|
|
39
|
+
guarantee.
|
|
40
|
+
- `pyproject.toml` version 2.6.0 → 2.7.0; MCP `clientInfo.version` handshake
|
|
41
|
+
strings in the two stdio adapters aligned to 2.7.0.
|
|
42
|
+
|
|
43
|
+
### Security
|
|
44
|
+
- **Release-metadata gate** (`.github/workflows/publish.yml`): the build job now
|
|
45
|
+
runs `twine check --strict dist/*` before upload, so malformed metadata can no
|
|
46
|
+
longer burn an immutable PyPI version.
|
|
47
|
+
- **SECURITY.md supported-versions table** corrected from a stale `0.8.x` to
|
|
48
|
+
`2.7.x` (`< 2.7` unsupported), with a note that the table is updated every
|
|
49
|
+
release.
|
|
50
|
+
|
|
51
|
+
### Testing
|
|
52
|
+
- The five live-Chromium attach tests in `tests/test_attach_over_cdp.py` are now
|
|
53
|
+
marked `@pytest.mark.e2e`, so the default (non-e2e) CI run no longer errors when
|
|
54
|
+
a browser is unavailable and the deterministic subset stays green.
|
|
55
|
+
|
|
56
|
+
## [2.6.0] — Stealth & MCP Hardening (2026-07-12)
|
|
57
|
+
|
|
58
|
+
### Added
|
|
59
|
+
- **`attach_mode` param** (`stealth/advanced_stealth.py`): `get_stealth_script(..., attach_mode=True)` skips `navigator.platform` / WebGL vendor-renderer / screen-DPR overrides when attaching to a real browser, so the injected fingerprint no longer contradicts the real OS/GPU. `core/agent_browser.py`'s `attach_over_cdp()` now passes `attach_mode=True`.
|
|
60
|
+
- **`AdapterToolError`** (`production/adapters/`): raised when an MCP tool result carries `isError: true` — previously a failed `navigate`/`click`/`fill` call reported silent success.
|
|
61
|
+
- **Canonical `connected` field** on adapter `status()` (`production/adapters/`), with a `running` back-compat alias.
|
|
62
|
+
- **CDP bridge profile resolution** (`production/adapters/cdp_bridge.py`): `launch(profile)` now accepts either a raw `ws/wss/http(s)` endpoint or a profile name resolved via `CDP_ENDPOINT_<PROFILE>` / `CDP_ENDPOINT` env, aligning its signature with the `BackendAdapter` protocol and sibling adapters.
|
|
63
|
+
- Adapter, stealth, and recovery unit tests covering the above (`tests/test_adapter_*.py`, `tests/test_stealth_modules.py`, `tests/test_recovery_state_machine.py`).
|
|
64
|
+
- `CLAUDE.md`: contributor guidance (setup, test commands mirroring CI, architecture map, conventions).
|
|
65
|
+
|
|
66
|
+
### Fixed
|
|
67
|
+
- **Per-patch isolation** (`stealth/advanced_stealth.py`): each stealth patch now runs in its own try/catch, so one API mismatch no longer aborts every patch queued after it.
|
|
68
|
+
- **Rotation failure correctness** (`recovery/anti_block_orchestrator.py`): a failed `ROTATE_*` action now returns `False` and records `_update_recovery_history(success=False)` instead of silently succeeding; per-attempt session/proxy names get a uuid suffix to avoid collisions; a redundant tentative history update was removed.
|
|
69
|
+
- **playwright-mcp pin** (`production/adapters/playwright_mcp.py`): pinned to `@playwright/mcp@0.0.78` with corrected `browser_navigate`/`browser_click`/`browser_type` schema (`element`/`target`/`text`).
|
|
70
|
+
|
|
71
|
+
### Security
|
|
72
|
+
- **WebRTC leak fix** (`stealth/advanced_stealth.py`): `onicecandidate` is now rebound per-instance instead of on the shared prototype (no cross-instance/global leak); `addEventListener('icecandidate', ...)` listeners are now filtered too; `createDataChannel` restored to a real passthrough.
|
|
73
|
+
- **PolicyEngine + ApprovalGate wiring** (`production/mcp_server.py`): both now sit in the `tools/call` dispatch path and workflow replay. Fail-open by default — no policy files means allow-all and auto-approve, so existing flows are unchanged — operators opt into enforcement via `STEALTH_MCP_POLICY` env or an approval callback.
|
|
74
|
+
- **Honesty docstring** (`production/mcp_session_isolation.py`): documents that `SessionEnforcer` is defined and tested but NOT wired into the dispatch path — inert until a multi-client transport exists (single-client stdio has no per-context token).
|
|
75
|
+
|
|
76
|
+
### Changed
|
|
77
|
+
- `core/agent_browser.py`: `attach_over_cdp()` passes `attach_mode=True` through to the stealth script generator (see Added).
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
12
81
|
## [2.5.0] — Real Dashboard Backend Adapters (#444) (2026-06-07)
|
|
13
82
|
|
|
14
83
|
### Added
|
|
@@ -405,7 +474,8 @@ See the full v0.9.0 milestone and the stacked PRs #383/#384/#385 for implementat
|
|
|
405
474
|
|
|
406
475
|
---
|
|
407
476
|
|
|
408
|
-
[unreleased]: https://github.com/shanewas/agentic-stealth-browser/compare/v2.
|
|
477
|
+
[unreleased]: https://github.com/shanewas/agentic-stealth-browser/compare/v2.6.0...HEAD
|
|
478
|
+
[2.6.0]: https://github.com/shanewas/agentic-stealth-browser/releases/tag/v2.6.0
|
|
409
479
|
[2.3.0]: https://github.com/shanewas/agentic-stealth-browser/releases/tag/v2.3.0
|
|
410
480
|
[2.1.1]: https://github.com/shanewas/agentic-stealth-browser/releases/tag/v2.1.1
|
|
411
481
|
[2.1.0]: https://github.com/shanewas/agentic-stealth-browser/releases/tag/v2.1.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentic-stealth-browser
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.7.0
|
|
4
4
|
Summary: Production-grade, human-mimicking browser automation framework for autonomous agents. Survives modern anti-bot systems.
|
|
5
5
|
Author-email: Shane W <shanewas@users.noreply.github.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -23,23 +23,23 @@ Classifier: Framework :: AsyncIO
|
|
|
23
23
|
Requires-Python: >=3.10
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
License-File: LICENSE
|
|
26
|
-
Requires-Dist: playwright
|
|
27
|
-
Requires-Dist: aiohttp
|
|
28
|
-
Requires-Dist: cryptography
|
|
29
|
-
Requires-Dist: pyyaml
|
|
30
|
-
Requires-Dist: fastapi
|
|
31
|
-
Requires-Dist: jinja2
|
|
32
|
-
Requires-Dist: uvicorn
|
|
26
|
+
Requires-Dist: playwright>=1.45
|
|
27
|
+
Requires-Dist: aiohttp>=3.10.11
|
|
28
|
+
Requires-Dist: cryptography>=43.0.1
|
|
29
|
+
Requires-Dist: pyyaml>=6.0.2
|
|
30
|
+
Requires-Dist: fastapi>=0.115.0
|
|
31
|
+
Requires-Dist: jinja2>=3.1.5
|
|
32
|
+
Requires-Dist: uvicorn>=0.32.0
|
|
33
33
|
Provides-Extra: dev
|
|
34
|
-
Requires-Dist: pytest; extra == "dev"
|
|
35
|
-
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
36
|
-
Requires-Dist: pytest-cov; extra == "dev"
|
|
37
|
-
Requires-Dist: httpx; extra == "dev"
|
|
34
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
35
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
36
|
+
Requires-Dist: pytest-cov>=5.0; extra == "dev"
|
|
37
|
+
Requires-Dist: httpx>=0.27; extra == "dev"
|
|
38
38
|
Dynamic: license-file
|
|
39
39
|
|
|
40
40
|
# Agentic Stealth Browser
|
|
41
41
|
|
|
42
|
-
**Playwright gets detected. This
|
|
42
|
+
**Playwright gets detected. This is much harder to detect.**
|
|
43
43
|
|
|
44
44
|
[](https://github.com/shanewas/agentic-stealth-browser/actions/workflows/ci.yml)
|
|
45
45
|
[](LICENSE)
|
|
@@ -51,6 +51,10 @@ Dynamic: license-file
|
|
|
51
51
|
<img src="assets/hn-demo.gif" alt="Agentic Stealth Browser Demo" width="90%">
|
|
52
52
|
</p>
|
|
53
53
|
|
|
54
|
+
## Legal & Acceptable Use
|
|
55
|
+
|
|
56
|
+
This is an anti-bot-evasion tool. Read the [Acceptable Use Policy](ACCEPTABLE_USE.md) before use — you are responsible for using it lawfully and within the target site's Terms of Service.
|
|
57
|
+
|
|
54
58
|
## What is this
|
|
55
59
|
|
|
56
60
|
A production-grade stealth browser automation library for Python, built on Playwright.
|
|
@@ -95,7 +99,8 @@ Sites don't just check your User-Agent anymore. They check *everything*:
|
|
|
95
99
|
| **Account warming** | Nothing | 14-day graduated ramp-up per account |
|
|
96
100
|
|
|
97
101
|
Result: **passes bot.sannysoft.com, pixelscan.net, and CreepJS** with zero flags in
|
|
98
|
-
headless mode (detection canaries run every 4 hours
|
|
102
|
+
headless mode as of the latest 4-hourly canary run (detection canaries run every 4 hours
|
|
103
|
+
via `docs/canary.md`).
|
|
99
104
|
|
|
100
105
|
---
|
|
101
106
|
|
|
@@ -224,6 +229,29 @@ live E2E (`RUN_E2E_ANTI_BLOCK=1` to enable).
|
|
|
224
229
|
|
|
225
230
|
---
|
|
226
231
|
|
|
232
|
+
## New in v2.6.0
|
|
233
|
+
|
|
234
|
+
- **Stealth patch isolation** — per-patch try/catch in `advanced_stealth.py`; one
|
|
235
|
+
API mismatch no longer aborts every patch queued after it
|
|
236
|
+
- **Attach-mode fingerprint coherence** — new `attach_mode` skips
|
|
237
|
+
navigator.platform / WebGL vendor-renderer / screen-DPR overrides that would
|
|
238
|
+
otherwise contradict a real attached browser's OS/GPU
|
|
239
|
+
- **WebRTC leak hardening** — per-instance `onicecandidate` rebind (no
|
|
240
|
+
prototype-global leak); the `addEventListener('icecandidate')` variant is
|
|
241
|
+
now filtered too
|
|
242
|
+
- **MCP policy/approval gates** — `PolicyEngine` + `ApprovalGate` wired into
|
|
243
|
+
the dispatch path, fail-open by default (opt-in enforcement via
|
|
244
|
+
`STEALTH_MCP_POLICY` env)
|
|
245
|
+
- **Adapter `isError` surfacing** — failed MCP tool calls now raise
|
|
246
|
+
`AdapterToolError` instead of reporting silent success; playwright-mcp
|
|
247
|
+
pinned to 0.0.78 with the corrected tool schema
|
|
248
|
+
- **Recovery rotation-failure correctness** — failed rotations now propagate
|
|
249
|
+
and record history instead of silently succeeding
|
|
250
|
+
|
|
251
|
+
See [CHANGELOG.md](CHANGELOG.md) for the full release history.
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
227
255
|
## New in v2.5.0
|
|
228
256
|
|
|
229
257
|
- **BackendAdapter protocol** — pluggable execution backends (M0–M4 shipped)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Agentic Stealth Browser
|
|
2
2
|
|
|
3
|
-
**Playwright gets detected. This
|
|
3
|
+
**Playwright gets detected. This is much harder to detect.**
|
|
4
4
|
|
|
5
5
|
[](https://github.com/shanewas/agentic-stealth-browser/actions/workflows/ci.yml)
|
|
6
6
|
[](LICENSE)
|
|
@@ -12,6 +12,10 @@
|
|
|
12
12
|
<img src="assets/hn-demo.gif" alt="Agentic Stealth Browser Demo" width="90%">
|
|
13
13
|
</p>
|
|
14
14
|
|
|
15
|
+
## Legal & Acceptable Use
|
|
16
|
+
|
|
17
|
+
This is an anti-bot-evasion tool. Read the [Acceptable Use Policy](ACCEPTABLE_USE.md) before use — you are responsible for using it lawfully and within the target site's Terms of Service.
|
|
18
|
+
|
|
15
19
|
## What is this
|
|
16
20
|
|
|
17
21
|
A production-grade stealth browser automation library for Python, built on Playwright.
|
|
@@ -56,7 +60,8 @@ Sites don't just check your User-Agent anymore. They check *everything*:
|
|
|
56
60
|
| **Account warming** | Nothing | 14-day graduated ramp-up per account |
|
|
57
61
|
|
|
58
62
|
Result: **passes bot.sannysoft.com, pixelscan.net, and CreepJS** with zero flags in
|
|
59
|
-
headless mode (detection canaries run every 4 hours
|
|
63
|
+
headless mode as of the latest 4-hourly canary run (detection canaries run every 4 hours
|
|
64
|
+
via `docs/canary.md`).
|
|
60
65
|
|
|
61
66
|
---
|
|
62
67
|
|
|
@@ -185,6 +190,29 @@ live E2E (`RUN_E2E_ANTI_BLOCK=1` to enable).
|
|
|
185
190
|
|
|
186
191
|
---
|
|
187
192
|
|
|
193
|
+
## New in v2.6.0
|
|
194
|
+
|
|
195
|
+
- **Stealth patch isolation** — per-patch try/catch in `advanced_stealth.py`; one
|
|
196
|
+
API mismatch no longer aborts every patch queued after it
|
|
197
|
+
- **Attach-mode fingerprint coherence** — new `attach_mode` skips
|
|
198
|
+
navigator.platform / WebGL vendor-renderer / screen-DPR overrides that would
|
|
199
|
+
otherwise contradict a real attached browser's OS/GPU
|
|
200
|
+
- **WebRTC leak hardening** — per-instance `onicecandidate` rebind (no
|
|
201
|
+
prototype-global leak); the `addEventListener('icecandidate')` variant is
|
|
202
|
+
now filtered too
|
|
203
|
+
- **MCP policy/approval gates** — `PolicyEngine` + `ApprovalGate` wired into
|
|
204
|
+
the dispatch path, fail-open by default (opt-in enforcement via
|
|
205
|
+
`STEALTH_MCP_POLICY` env)
|
|
206
|
+
- **Adapter `isError` surfacing** — failed MCP tool calls now raise
|
|
207
|
+
`AdapterToolError` instead of reporting silent success; playwright-mcp
|
|
208
|
+
pinned to 0.0.78 with the corrected tool schema
|
|
209
|
+
- **Recovery rotation-failure correctness** — failed rotations now propagate
|
|
210
|
+
and record history instead of silently succeeding
|
|
211
|
+
|
|
212
|
+
See [CHANGELOG.md](CHANGELOG.md) for the full release history.
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
188
216
|
## New in v2.5.0
|
|
189
217
|
|
|
190
218
|
- **BackendAdapter protocol** — pluggable execution backends (M0–M4 shipped)
|
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
| Version | Supported |
|
|
6
6
|
| ------- | ------------------ |
|
|
7
|
-
|
|
|
8
|
-
| <
|
|
7
|
+
| 2.7.x | :white_check_mark: |
|
|
8
|
+
| < 2.7 | :x: |
|
|
9
|
+
|
|
10
|
+
This table is updated as part of every release; a release is not complete until the current minor version appears here.
|
|
9
11
|
|
|
10
12
|
## Reporting a Vulnerability
|
|
11
13
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentic-stealth-browser
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.7.0
|
|
4
4
|
Summary: Production-grade, human-mimicking browser automation framework for autonomous agents. Survives modern anti-bot systems.
|
|
5
5
|
Author-email: Shane W <shanewas@users.noreply.github.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -23,23 +23,23 @@ Classifier: Framework :: AsyncIO
|
|
|
23
23
|
Requires-Python: >=3.10
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
License-File: LICENSE
|
|
26
|
-
Requires-Dist: playwright
|
|
27
|
-
Requires-Dist: aiohttp
|
|
28
|
-
Requires-Dist: cryptography
|
|
29
|
-
Requires-Dist: pyyaml
|
|
30
|
-
Requires-Dist: fastapi
|
|
31
|
-
Requires-Dist: jinja2
|
|
32
|
-
Requires-Dist: uvicorn
|
|
26
|
+
Requires-Dist: playwright>=1.45
|
|
27
|
+
Requires-Dist: aiohttp>=3.10.11
|
|
28
|
+
Requires-Dist: cryptography>=43.0.1
|
|
29
|
+
Requires-Dist: pyyaml>=6.0.2
|
|
30
|
+
Requires-Dist: fastapi>=0.115.0
|
|
31
|
+
Requires-Dist: jinja2>=3.1.5
|
|
32
|
+
Requires-Dist: uvicorn>=0.32.0
|
|
33
33
|
Provides-Extra: dev
|
|
34
|
-
Requires-Dist: pytest; extra == "dev"
|
|
35
|
-
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
36
|
-
Requires-Dist: pytest-cov; extra == "dev"
|
|
37
|
-
Requires-Dist: httpx; extra == "dev"
|
|
34
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
35
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
36
|
+
Requires-Dist: pytest-cov>=5.0; extra == "dev"
|
|
37
|
+
Requires-Dist: httpx>=0.27; extra == "dev"
|
|
38
38
|
Dynamic: license-file
|
|
39
39
|
|
|
40
40
|
# Agentic Stealth Browser
|
|
41
41
|
|
|
42
|
-
**Playwright gets detected. This
|
|
42
|
+
**Playwright gets detected. This is much harder to detect.**
|
|
43
43
|
|
|
44
44
|
[](https://github.com/shanewas/agentic-stealth-browser/actions/workflows/ci.yml)
|
|
45
45
|
[](LICENSE)
|
|
@@ -51,6 +51,10 @@ Dynamic: license-file
|
|
|
51
51
|
<img src="assets/hn-demo.gif" alt="Agentic Stealth Browser Demo" width="90%">
|
|
52
52
|
</p>
|
|
53
53
|
|
|
54
|
+
## Legal & Acceptable Use
|
|
55
|
+
|
|
56
|
+
This is an anti-bot-evasion tool. Read the [Acceptable Use Policy](ACCEPTABLE_USE.md) before use — you are responsible for using it lawfully and within the target site's Terms of Service.
|
|
57
|
+
|
|
54
58
|
## What is this
|
|
55
59
|
|
|
56
60
|
A production-grade stealth browser automation library for Python, built on Playwright.
|
|
@@ -95,7 +99,8 @@ Sites don't just check your User-Agent anymore. They check *everything*:
|
|
|
95
99
|
| **Account warming** | Nothing | 14-day graduated ramp-up per account |
|
|
96
100
|
|
|
97
101
|
Result: **passes bot.sannysoft.com, pixelscan.net, and CreepJS** with zero flags in
|
|
98
|
-
headless mode (detection canaries run every 4 hours
|
|
102
|
+
headless mode as of the latest 4-hourly canary run (detection canaries run every 4 hours
|
|
103
|
+
via `docs/canary.md`).
|
|
99
104
|
|
|
100
105
|
---
|
|
101
106
|
|
|
@@ -224,6 +229,29 @@ live E2E (`RUN_E2E_ANTI_BLOCK=1` to enable).
|
|
|
224
229
|
|
|
225
230
|
---
|
|
226
231
|
|
|
232
|
+
## New in v2.6.0
|
|
233
|
+
|
|
234
|
+
- **Stealth patch isolation** — per-patch try/catch in `advanced_stealth.py`; one
|
|
235
|
+
API mismatch no longer aborts every patch queued after it
|
|
236
|
+
- **Attach-mode fingerprint coherence** — new `attach_mode` skips
|
|
237
|
+
navigator.platform / WebGL vendor-renderer / screen-DPR overrides that would
|
|
238
|
+
otherwise contradict a real attached browser's OS/GPU
|
|
239
|
+
- **WebRTC leak hardening** — per-instance `onicecandidate` rebind (no
|
|
240
|
+
prototype-global leak); the `addEventListener('icecandidate')` variant is
|
|
241
|
+
now filtered too
|
|
242
|
+
- **MCP policy/approval gates** — `PolicyEngine` + `ApprovalGate` wired into
|
|
243
|
+
the dispatch path, fail-open by default (opt-in enforcement via
|
|
244
|
+
`STEALTH_MCP_POLICY` env)
|
|
245
|
+
- **Adapter `isError` surfacing** — failed MCP tool calls now raise
|
|
246
|
+
`AdapterToolError` instead of reporting silent success; playwright-mcp
|
|
247
|
+
pinned to 0.0.78 with the corrected tool schema
|
|
248
|
+
- **Recovery rotation-failure correctness** — failed rotations now propagate
|
|
249
|
+
and record history instead of silently succeeding
|
|
250
|
+
|
|
251
|
+
See [CHANGELOG.md](CHANGELOG.md) for the full release history.
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
227
255
|
## New in v2.5.0
|
|
228
256
|
|
|
229
257
|
- **BackendAdapter protocol** — pluggable execution backends (M0–M4 shipped)
|
|
@@ -46,6 +46,9 @@ core/py.typed
|
|
|
46
46
|
core/session_checkpoint.py
|
|
47
47
|
core/types.py
|
|
48
48
|
docs/ATTACH_OVER_CDP.md
|
|
49
|
+
docs/DATA_HANDLING.md
|
|
50
|
+
docs/INCIDENT_RESPONSE.md
|
|
51
|
+
docs/KEY_MANAGEMENT.md
|
|
49
52
|
docs/README.md
|
|
50
53
|
docs/agentic-stealth-browser-analysis-report.md
|
|
51
54
|
docs/canary.md
|
|
@@ -182,6 +185,9 @@ tests/test_workflow_orchestrator.py
|
|
|
182
185
|
tests/test_workflow_schema.py
|
|
183
186
|
tests/test_workflow_schema_unit.py
|
|
184
187
|
tests/test_workflow_versioning.py
|
|
188
|
+
tests/stealth_harness/__init__.py
|
|
189
|
+
tests/stealth_harness/collect.py
|
|
190
|
+
tests/stealth_harness/test_stealth.py
|
|
185
191
|
workflows/__init__.py
|
|
186
192
|
workflows/action_interpreter.py
|
|
187
193
|
workflows/player.py
|
|
@@ -1272,6 +1272,7 @@ class AgentBrowser:
|
|
|
1272
1272
|
self.account_warming.record_page_visit(url)
|
|
1273
1273
|
self.connection_pool.release_context(domain)
|
|
1274
1274
|
self.adaptive_tuner.record_feedback(blocked=False, platform=platform)
|
|
1275
|
+
self.metrics.increment("requests_success")
|
|
1275
1276
|
return True
|
|
1276
1277
|
except Exception as e:
|
|
1277
1278
|
# P3: Record failure to all tracking systems
|
|
@@ -1284,6 +1285,8 @@ class AgentBrowser:
|
|
|
1284
1285
|
self.logger.log_error(
|
|
1285
1286
|
"safe_goto_failed", str(e), {"url": url, "platform": platform}
|
|
1286
1287
|
)
|
|
1288
|
+
self.metrics.increment("requests_failed")
|
|
1289
|
+
self.metrics.record_error("navigation", str(e))
|
|
1287
1290
|
return False
|
|
1288
1291
|
|
|
1289
1292
|
async def load_cookies(self, cookies_path: str):
|
|
@@ -2274,7 +2277,9 @@ class AgentBrowser:
|
|
|
2274
2277
|
if apply_stealth:
|
|
2275
2278
|
session_name = (self.session or {}).get("name", "default-session")
|
|
2276
2279
|
fp_seed = f"agentic-{session_name}-canvas-v4"
|
|
2277
|
-
stealth_script = get_stealth_script(
|
|
2280
|
+
stealth_script = get_stealth_script(
|
|
2281
|
+
fingerprint_seed=fp_seed, attach_mode=True
|
|
2282
|
+
)
|
|
2278
2283
|
try:
|
|
2279
2284
|
await ctx.add_init_script(stealth_script)
|
|
2280
2285
|
stealth_installed = True
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Data Handling & Retention
|
|
2
|
+
|
|
3
|
+
## What the tool persists
|
|
4
|
+
|
|
5
|
+
- Scraped results (wherever the operator's workflow writes them)
|
|
6
|
+
- Cookies under `sessions/`
|
|
7
|
+
- Screenshots under `checkpoints/`
|
|
8
|
+
|
|
9
|
+
## Data controller
|
|
10
|
+
|
|
11
|
+
The operator running this tool is the data controller for any data it collects or persists. The maintainers are not.
|
|
12
|
+
|
|
13
|
+
## Retention recommendation
|
|
14
|
+
|
|
15
|
+
By default, retain persisted files no longer than necessary for the operator's task — 30 days is a reasonable starting point unless the operator's own policy or applicable law requires otherwise.
|
|
16
|
+
|
|
17
|
+
## Deletion
|
|
18
|
+
|
|
19
|
+
Deletion is the operator's responsibility. Documented deletion means removing the relevant files under `sessions/` and `checkpoints/`.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Security Incident Response
|
|
2
|
+
|
|
3
|
+
## Reporting
|
|
4
|
+
|
|
5
|
+
See [SECURITY.md](../SECURITY.md) for how to report a vulnerability.
|
|
6
|
+
|
|
7
|
+
## Credential / Key Compromise
|
|
8
|
+
|
|
9
|
+
1. Rotate the leaked secret.
|
|
10
|
+
2. Invalidate live dashboard sessions by rotating `DashboardSettings.secret_key`.
|
|
11
|
+
3. Delete affected session cookie files under `sessions/`.
|
|
12
|
+
4. Force re-login for all operators.
|
|
13
|
+
|
|
14
|
+
## Triage Severity
|
|
15
|
+
|
|
16
|
+
- **SEV1** — key/credential leak
|
|
17
|
+
- **SEV2** — data exposure
|
|
18
|
+
- **SEV3** — availability
|
|
19
|
+
|
|
20
|
+
## Post-Incident
|
|
21
|
+
|
|
22
|
+
Record the incident in the CHANGELOG Security section.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Key Management
|
|
2
|
+
|
|
3
|
+
| Secret | Source | Rotation procedure |
|
|
4
|
+
|---|---|---|
|
|
5
|
+
| Cookie encryption key | env var | Generate a new key, re-encrypt or discard existing session cookie files under `sessions/`, force re-login. |
|
|
6
|
+
| Dashboard `secret_key` (`DashboardSettings.secret_key`) | env var | Set a new value, restart the dashboard process — this invalidates all live dashboard sessions. |
|
|
7
|
+
| HMAC / audit keys | env var | Generate a new key, redeploy, allow a short overlap window if signed artifacts must remain verifiable during rotation. |
|
|
8
|
+
|
|
9
|
+
Keys must come from environment variables and must never be committed to the repository. Rotation should be performed immediately on suspected compromise (see [docs/INCIDENT_RESPONSE.md](INCIDENT_RESPONSE.md)) and periodically as routine hygiene.
|
{agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/production/adapters/__init__.py
RENAMED
|
@@ -9,6 +9,7 @@ Public surface:
|
|
|
9
9
|
Capability - the feature enum
|
|
10
10
|
AdapterLaunchError - cannot start (runtime)
|
|
11
11
|
AdapterCapabilityError - cannot perform a requested action
|
|
12
|
+
AdapterToolError - MCP tool call reported isError: true
|
|
12
13
|
AdapterNotFoundError - get_adapter() was called with an unknown name
|
|
13
14
|
BACKEND_REGISTRY - mutable dict[name -> Adapter class]
|
|
14
15
|
get_adapter(name) - registry lookup with friendly error
|
|
@@ -21,6 +22,7 @@ from production.adapters.base import (
|
|
|
21
22
|
AdapterCapabilityError,
|
|
22
23
|
AdapterLaunchError,
|
|
23
24
|
AdapterNotFoundError,
|
|
25
|
+
AdapterToolError,
|
|
24
26
|
BackendAdapter,
|
|
25
27
|
Capability,
|
|
26
28
|
)
|
|
@@ -103,6 +105,7 @@ __all__ = [
|
|
|
103
105
|
"AdapterCapabilityError",
|
|
104
106
|
"AdapterLaunchError",
|
|
105
107
|
"AdapterNotFoundError",
|
|
108
|
+
"AdapterToolError",
|
|
106
109
|
"BACKEND_REGISTRY",
|
|
107
110
|
"BackendAdapter",
|
|
108
111
|
"Capability",
|
|
@@ -24,6 +24,7 @@ from production.adapters._jsonrpc_stdio import JsonRpcStdioClient
|
|
|
24
24
|
from production.adapters.base import (
|
|
25
25
|
AdapterCapabilityError,
|
|
26
26
|
AdapterLaunchError,
|
|
27
|
+
AdapterToolError,
|
|
27
28
|
Capability,
|
|
28
29
|
)
|
|
29
30
|
|
|
@@ -118,7 +119,7 @@ class AgenticStealthMCPAdapter:
|
|
|
118
119
|
"protocolVersion": "2024-11-05",
|
|
119
120
|
"clientInfo": {
|
|
120
121
|
"name": "agentic-stealth-browser-dashboard",
|
|
121
|
-
"version": "2.
|
|
122
|
+
"version": "2.7.0",
|
|
122
123
|
},
|
|
123
124
|
"capabilities": {},
|
|
124
125
|
},
|
|
@@ -199,9 +200,11 @@ class AgenticStealthMCPAdapter:
|
|
|
199
200
|
return str(path)
|
|
200
201
|
|
|
201
202
|
async def status(self) -> dict[str, Any]:
|
|
203
|
+
alive = self._proc is not None and self._proc.returncode is None
|
|
202
204
|
return {
|
|
203
205
|
"backend": self.name,
|
|
204
|
-
"
|
|
206
|
+
"connected": alive,
|
|
207
|
+
"running": alive, # back-compat alias; "connected" is canonical
|
|
205
208
|
"headless": self._headless,
|
|
206
209
|
"profile": self._profile,
|
|
207
210
|
}
|
|
@@ -215,7 +218,12 @@ class AgenticStealthMCPAdapter:
|
|
|
215
218
|
response = await self._client.request(
|
|
216
219
|
"tools/call", {"name": name, "arguments": arguments}
|
|
217
220
|
)
|
|
218
|
-
|
|
221
|
+
result = response.get("result", {})
|
|
222
|
+
if result.get("isError"):
|
|
223
|
+
raise AdapterToolError(
|
|
224
|
+
f"MCP tool {name!r} failed: {result.get('content')!r}"
|
|
225
|
+
)
|
|
226
|
+
return result
|
|
219
227
|
|
|
220
228
|
def _require_capability(self, cap: Capability) -> None:
|
|
221
229
|
"""Raise AdapterCapabilityError when the active adapter does not
|
|
@@ -46,6 +46,20 @@ class AdapterCapabilityError(RuntimeError):
|
|
|
46
46
|
"""
|
|
47
47
|
|
|
48
48
|
|
|
49
|
+
class AdapterToolError(RuntimeError):
|
|
50
|
+
"""Raised when an MCP tool call completes as a valid JSON-RPC response
|
|
51
|
+
but reports a tool-level failure (``result["isError"] is True``).
|
|
52
|
+
|
|
53
|
+
MCP servers report a failed tool execution (bad selector, navigation
|
|
54
|
+
timeout, etc.) as a *successful* JSON-RPC call whose result carries
|
|
55
|
+
``isError: true`` — not as a JSON-RPC protocol-level error. Without this
|
|
56
|
+
exception, adapters would silently treat a failed navigate/click/fill/
|
|
57
|
+
screenshot as success. Distinct from AdapterLaunchError (process/handshake
|
|
58
|
+
failures) and the plain RuntimeError raised by JsonRpcStdioClient for
|
|
59
|
+
JSON-RPC protocol errors (e.g. unknown method).
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
|
|
49
63
|
class AdapterNotFoundError(LookupError):
|
|
50
64
|
"""Raised by get_adapter() when the requested name is not in
|
|
51
65
|
BACKEND_REGISTRY. Inherits from LookupError (the natural superclass for
|
{agentic_stealth_browser-2.5.0 → agentic_stealth_browser-2.7.0}/production/adapters/cdp_bridge.py
RENAMED
|
@@ -14,6 +14,7 @@ running and reachable at the CDP endpoint.
|
|
|
14
14
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
|
+
import os
|
|
17
18
|
from typing import Any, Optional
|
|
18
19
|
|
|
19
20
|
from playwright.async_api import (
|
|
@@ -72,17 +73,24 @@ class CDPBridgeAdapter:
|
|
|
72
73
|
return capability in self.capabilities()
|
|
73
74
|
|
|
74
75
|
# ------------------------------------------------------------------ launch
|
|
75
|
-
async def launch(self,
|
|
76
|
+
async def launch(self, profile: str, headless: bool = True) -> None:
|
|
76
77
|
"""Connect to an existing CDP endpoint. Does NOT spawn a new browser.
|
|
77
78
|
|
|
78
79
|
Args:
|
|
79
|
-
|
|
80
|
+
profile: either a raw ws:// / wss:// / http(s):// CDP endpoint URL
|
|
81
|
+
(back-compat with direct callers of this adapter), or a
|
|
82
|
+
profile name — consistent with the base.py contract and the
|
|
83
|
+
M2/M3 adapters — resolved to an endpoint via the
|
|
84
|
+
``CDP_ENDPOINT_<PROFILE>`` (or generic ``CDP_ENDPOINT``)
|
|
85
|
+
environment variable.
|
|
80
86
|
headless: cosmetic flag, passed to spec but the remote browser's
|
|
81
87
|
display state is determined by its own launch args.
|
|
82
88
|
|
|
83
89
|
Raises:
|
|
84
|
-
AdapterLaunchError: if the connection cannot be established
|
|
90
|
+
AdapterLaunchError: if the connection cannot be established, or
|
|
91
|
+
(for a profile name) if no endpoint is configured for it.
|
|
85
92
|
"""
|
|
93
|
+
endpoint = self._resolve_endpoint(profile)
|
|
86
94
|
self._endpoint = endpoint
|
|
87
95
|
self._playwright = await async_playwright().start()
|
|
88
96
|
try:
|
|
@@ -103,6 +111,23 @@ class CDPBridgeAdapter:
|
|
|
103
111
|
self._context = await self._browser.new_context()
|
|
104
112
|
self._owns_page = True
|
|
105
113
|
|
|
114
|
+
# ponytail: profile -> endpoint resolution is a flat env-var lookup, not
|
|
115
|
+
# a real profile registry. Fine while operators set one CDP target per
|
|
116
|
+
# profile by hand; upgrade to a config-file-backed mapping if/when the
|
|
117
|
+
# dashboard needs to drive many named CDP profiles at once.
|
|
118
|
+
@staticmethod
|
|
119
|
+
def _resolve_endpoint(profile: str) -> str:
|
|
120
|
+
if profile.startswith(("ws://", "wss://", "http://", "https://")):
|
|
121
|
+
return profile
|
|
122
|
+
env_key = f"CDP_ENDPOINT_{profile.upper().replace('-', '_')}"
|
|
123
|
+
endpoint = os.environ.get(env_key) or os.environ.get("CDP_ENDPOINT")
|
|
124
|
+
if not endpoint:
|
|
125
|
+
raise AdapterLaunchError(
|
|
126
|
+
f"No CDP endpoint configured for profile {profile!r}; "
|
|
127
|
+
f"set {env_key} or CDP_ENDPOINT to a ws:// CDP URL"
|
|
128
|
+
)
|
|
129
|
+
return endpoint
|
|
130
|
+
|
|
106
131
|
# ------------------------------------------------------------------ close
|
|
107
132
|
async def close(self) -> None:
|
|
108
133
|
"""Tear down. Honors the v2.4.1 attach contract: only closes owned pages.
|
|
@@ -180,6 +205,7 @@ class CDPBridgeAdapter:
|
|
|
180
205
|
return {
|
|
181
206
|
"backend": self.name,
|
|
182
207
|
"connected": connected,
|
|
208
|
+
"running": connected, # back-compat alias; "connected" is canonical
|
|
183
209
|
"endpoint": self._endpoint,
|
|
184
210
|
"owns_page": self._owns_page,
|
|
185
211
|
}
|