cookieguard-browser-use 0.1.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.
Files changed (49) hide show
  1. cookieguard_browser_use-0.1.0/.gitignore +11 -0
  2. cookieguard_browser_use-0.1.0/LICENSE +21 -0
  3. cookieguard_browser_use-0.1.0/PKG-INFO +104 -0
  4. cookieguard_browser_use-0.1.0/README.md +77 -0
  5. cookieguard_browser_use-0.1.0/examples/agent_login.py +102 -0
  6. cookieguard_browser_use-0.1.0/hatch_build.py +22 -0
  7. cookieguard_browser_use-0.1.0/pyproject.toml +85 -0
  8. cookieguard_browser_use-0.1.0/scripts/browser_smoke.py +66 -0
  9. cookieguard_browser_use-0.1.0/scripts/record_demo.py +471 -0
  10. cookieguard_browser_use-0.1.0/scripts/vendor_engine.mjs +93 -0
  11. cookieguard_browser_use-0.1.0/scripts/verify_external.py +147 -0
  12. cookieguard_browser_use-0.1.0/scripts/verify_metadata.py +66 -0
  13. cookieguard_browser_use-0.1.0/src/cookieguard_browser_use/__init__.py +22 -0
  14. cookieguard_browser_use-0.1.0/src/cookieguard_browser_use/_audit.py +289 -0
  15. cookieguard_browser_use-0.1.0/src/cookieguard_browser_use/_collector.py +388 -0
  16. cookieguard_browser_use-0.1.0/src/cookieguard_browser_use/_engine.py +256 -0
  17. cookieguard_browser_use-0.1.0/src/cookieguard_browser_use/_errors.py +44 -0
  18. cookieguard_browser_use-0.1.0/src/cookieguard_browser_use/_node/bridge.mjs +179 -0
  19. cookieguard_browser_use-0.1.0/src/cookieguard_browser_use/_node/manifest.json +40 -0
  20. cookieguard_browser_use-0.1.0/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-core/LICENSE +21 -0
  21. cookieguard_browser_use-0.1.0/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-core/README.md +95 -0
  22. cookieguard_browser_use-0.1.0/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-core/package.json +17 -0
  23. cookieguard_browser_use-0.1.0/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-core/src/index.d.ts +80 -0
  24. cookieguard_browser_use-0.1.0/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-core/src/index.js +4 -0
  25. cookieguard_browser_use-0.1.0/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-core/src/normalize.js +169 -0
  26. cookieguard_browser_use-0.1.0/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-core/src/onnx.d.ts +6 -0
  27. cookieguard_browser_use-0.1.0/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-core/src/onnx.js +37 -0
  28. cookieguard_browser_use-0.1.0/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-core/src/policy.js +182 -0
  29. cookieguard_browser_use-0.1.0/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-core/src/report.js +28 -0
  30. cookieguard_browser_use-0.1.0/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-core/src/safety.js +54 -0
  31. cookieguard_browser_use-0.1.0/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-core/vendor/engine.cjs +799 -0
  32. cookieguard_browser_use-0.1.0/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-playwright/LICENSE +21 -0
  33. cookieguard_browser_use-0.1.0/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-playwright/README.md +62 -0
  34. cookieguard_browser_use-0.1.0/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-playwright/package.json +15 -0
  35. cookieguard_browser_use-0.1.0/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-playwright/src/index.d.ts +19 -0
  36. cookieguard_browser_use-0.1.0/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-playwright/src/index.js +145 -0
  37. cookieguard_browser_use-0.1.0/src/cookieguard_browser_use/_origins.py +85 -0
  38. cookieguard_browser_use-0.1.0/src/cookieguard_browser_use/_result.py +211 -0
  39. cookieguard_browser_use-0.1.0/src/cookieguard_browser_use/demo/__init__.py +0 -0
  40. cookieguard_browser_use-0.1.0/src/cookieguard_browser_use/demo/__main__.py +58 -0
  41. cookieguard_browser_use-0.1.0/src/cookieguard_browser_use/demo/app.py +176 -0
  42. cookieguard_browser_use-0.1.0/src/cookieguard_browser_use/demo/scripted.py +141 -0
  43. cookieguard_browser_use-0.1.0/src/cookieguard_browser_use/py.typed +0 -0
  44. cookieguard_browser_use-0.1.0/tests/conftest.py +114 -0
  45. cookieguard_browser_use-0.1.0/tests/test_agent.py +107 -0
  46. cookieguard_browser_use-0.1.0/tests/test_browser.py +232 -0
  47. cookieguard_browser_use-0.1.0/tests/test_collector.py +146 -0
  48. cookieguard_browser_use-0.1.0/tests/test_config.py +135 -0
  49. cookieguard_browser_use-0.1.0/tests/test_engine.py +359 -0
@@ -0,0 +1,11 @@
1
+ # The repository root ignores `_*`; Python packages need __init__.py, _private.py, and _node/.
2
+ !_*
3
+ .venv/
4
+ dist/
5
+ build/
6
+ __pycache__/
7
+ *.egg-info/
8
+ .pytest_cache/
9
+ # Generated by scripts/vendor_engine.mjs from the CookieGuard npm packages.
10
+ src/cookieguard_browser_use/_node/node_modules/
11
+ src/cookieguard_browser_use/_node/manifest.json
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CookieGuard AI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,104 @@
1
+ Metadata-Version: 2.5
2
+ Name: cookieguard-browser-use
3
+ Version: 0.1.0
4
+ Summary: CookieGuard authentication-cookie policy checks for Browser Use agents
5
+ Project-URL: Homepage, https://github.com/aiscihub/cookieguard-ai/tree/main/integrations/browser-use
6
+ Project-URL: Repository, https://github.com/aiscihub/cookieguard-ai
7
+ Project-URL: Issues, https://github.com/aiscihub/cookieguard-ai/issues
8
+ Author: aiscihub
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: ai-agents,authentication,browser-use,cookies,security,testing
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Security
19
+ Classifier: Topic :: Software Development :: Testing
20
+ Requires-Python: >=3.11
21
+ Requires-Dist: browser-use<0.14,>=0.13.14
22
+ Requires-Dist: cdp-use<2,>=1.4.5
23
+ Requires-Dist: pydantic<3,>=2.10
24
+ Provides-Extra: test
25
+ Requires-Dist: pytest>=8.3; extra == 'test'
26
+ Description-Content-Type: text/markdown
27
+
28
+ # CookieGuard for Browser Use
29
+
30
+ **A successful agent login is not a secure session cookie.** CookieGuard for
31
+ Browser Use watches the browser your [Browser Use](https://github.com/browser-use/browser-use)
32
+ agent drives, checks the authentication cookies it receives, and returns
33
+ **PASS / FAIL / INCOMPLETE** with rule IDs, explanations, and fixes. The decision comes
34
+ from the local [CookieGuard](https://github.com/aiscihub/cookieguard-ai/blob/browser-use-v0.1.0/README.md) policy engine, never from the model.
35
+
36
+ **Overview** · [Install](https://github.com/aiscihub/cookieguard-ai/blob/browser-use-v0.1.0/integrations/browser-use/docs/install.md) · [Policy](https://github.com/aiscihub/cookieguard-ai/blob/browser-use-v0.1.0/integrations/browser-use/docs/policy.md) · [How it works](https://github.com/aiscihub/cookieguard-ai/blob/browser-use-v0.1.0/integrations/browser-use/docs/how-it-works.md) · [Results](https://github.com/aiscihub/cookieguard-ai/blob/browser-use-v0.1.0/integrations/browser-use/docs/results.md) · [Limits & privacy](https://github.com/aiscihub/cookieguard-ai/blob/browser-use-v0.1.0/integrations/browser-use/docs/limits-and-privacy.md) · [Demo & tests](https://github.com/aiscihub/cookieguard-ai/blob/browser-use-v0.1.0/integrations/browser-use/docs/demo-and-tests.md)
37
+
38
+ ## Your policy decides what fails
39
+
40
+ <picture>
41
+ <source media="(max-width: 600px) and (prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/aiscihub/cookieguard-ai/browser-use-v0.1.0/integrations/browser-use/docs/policy-dark-mobile.svg">
42
+ <source media="(max-width: 600px)" srcset="https://raw.githubusercontent.com/aiscihub/cookieguard-ai/browser-use-v0.1.0/integrations/browser-use/docs/policy-light-mobile.svg">
43
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/aiscihub/cookieguard-ai/browser-use-v0.1.0/integrations/browser-use/docs/policy-dark.svg">
44
+ <img alt="CookieGuardAudit options decide which findings fail. High-severity rules CG001 Missing HttpOnly, CG002 Missing Secure, CG006 invalid prefix, and CG007 regression fail by default. With fail_on set to medium, CG003 SameSite and CG005 lifetime fail too. CG004 Domain sharing fails only with require_host_only. CG008 and CG009 are review only. The result is FAIL (exit 1), INCOMPLETE (exit 2, which overrides FAIL), or PASS (exit 0)." src="https://raw.githubusercontent.com/aiscihub/cookieguard-ai/browser-use-v0.1.0/integrations/browser-use/docs/policy-light.svg" width="100%">
45
+ </picture>
46
+
47
+ `fail_on` sets the line: a confirmed finding at or above it fails the run; anything below
48
+ is reported. Only cookies you name in `auth_cookies` can be confirmed.
49
+ All options and a stricter example: **[Policy](https://github.com/aiscihub/cookieguard-ai/blob/browser-use-v0.1.0/integrations/browser-use/docs/policy.md)**.
50
+
51
+ ## See it run
52
+
53
+ ![A Browser Use agent logs in to two local apps. Both logins succeed. CookieGuard fails the first with CG001 Missing HttpOnly (exit 1) and passes the second (exit 0).](https://raw.githubusercontent.com/aiscihub/cookieguard-ai/browser-use-v0.1.0/integrations/browser-use/docs/cookieguard-browser-use-demo.gif)
54
+
55
+ Claude (`claude-opus-5-5`) drives a real Browser Use agent through the same login twice.
56
+ The insecure app's cookie lacks `HttpOnly`, so CookieGuard returns **FAIL — CG001** (exit 1).
57
+ The secure app gets **PASS** (exit 0). Recorded from a real run: **[Demo & tests](https://github.com/aiscihub/cookieguard-ai/blob/browser-use-v0.1.0/integrations/browser-use/docs/demo-and-tests.md)**.
58
+
59
+ ## Add it to your agent
60
+
61
+ ```python
62
+ from browser_use import Agent, BrowserProfile, BrowserSession, Tools
63
+ from cookieguard_browser_use import CookieGuardAudit
64
+
65
+ audit = CookieGuardAudit(
66
+ allowed_origins=['http://127.0.0.1:3000'], # exact origins you are authorized to test
67
+ auth_cookies=['session_id'], # the real session cookie name(s)
68
+ fail_on='high',
69
+ )
70
+ session = BrowserSession(browser_profile=BrowserProfile(
71
+ keep_alive=True, # required: Agent.run() otherwise closes the browser
72
+ enable_default_extensions=False, # avoid the bundled cookie-consent extension
73
+ allowed_domains=audit.browser_allowed_domains(),
74
+ ))
75
+ tools = Tools()
76
+ audit.register_tools(tools) # optional agent-visible check
77
+
78
+ await session.start()
79
+ await audit.start(session) # before the login
80
+ agent = Agent(task=..., llm=..., browser_session=session, tools=tools,
81
+ sensitive_data={'http://127.0.0.1': {'login_password': '...'}})
82
+ await agent.run()
83
+ result = await audit.finish() # decided by the CookieGuard engine
84
+ await session.kill()
85
+
86
+ print(result.summary)
87
+ result.assert_policy() # raises CookiePolicyError on FAIL or INCOMPLETE
88
+ ```
89
+
90
+ `assert_policy()` raises even if the agent never called the tool, or reported
91
+ success. `CookieGuardAudit` is also an async context manager; `close()` stops
92
+ collection without a result. One instance audits one workflow.
93
+
94
+ Needs Python 3.11+, Node.js 24+, and Chrome or Chromium: **[Install](https://github.com/aiscihub/cookieguard-ai/blob/browser-use-v0.1.0/integrations/browser-use/docs/install.md)**.
95
+
96
+ Install [version 0.1.0 from PyPI](https://pypi.org/project/cookieguard-browser-use/):
97
+
98
+ ```bash
99
+ pip install cookieguard-browser-use
100
+ ```
101
+
102
+ Status: `0.1.0`, tested with browser-use 0.13.14 on Linux (Python 3.11–3.13) and macOS (Python 3.12).
103
+ See [tested environments](https://github.com/aiscihub/cookieguard-ai/blob/browser-use-v0.1.0/integrations/browser-use/docs/limits-and-privacy.md#tested-environments-and-dependencies).
104
+ This is a community integration, not an official Browser Use package.
@@ -0,0 +1,77 @@
1
+ # CookieGuard for Browser Use
2
+
3
+ **A successful agent login is not a secure session cookie.** CookieGuard for
4
+ Browser Use watches the browser your [Browser Use](https://github.com/browser-use/browser-use)
5
+ agent drives, checks the authentication cookies it receives, and returns
6
+ **PASS / FAIL / INCOMPLETE** with rule IDs, explanations, and fixes. The decision comes
7
+ from the local [CookieGuard](../../README.md) policy engine, never from the model.
8
+
9
+ **Overview** · [Install](docs/install.md) · [Policy](docs/policy.md) · [How it works](docs/how-it-works.md) · [Results](docs/results.md) · [Limits & privacy](docs/limits-and-privacy.md) · [Demo & tests](docs/demo-and-tests.md)
10
+
11
+ ## Your policy decides what fails
12
+
13
+ <picture>
14
+ <source media="(max-width: 600px) and (prefers-color-scheme: dark)" srcset="docs/policy-dark-mobile.svg">
15
+ <source media="(max-width: 600px)" srcset="docs/policy-light-mobile.svg">
16
+ <source media="(prefers-color-scheme: dark)" srcset="docs/policy-dark.svg">
17
+ <img alt="CookieGuardAudit options decide which findings fail. High-severity rules CG001 Missing HttpOnly, CG002 Missing Secure, CG006 invalid prefix, and CG007 regression fail by default. With fail_on set to medium, CG003 SameSite and CG005 lifetime fail too. CG004 Domain sharing fails only with require_host_only. CG008 and CG009 are review only. The result is FAIL (exit 1), INCOMPLETE (exit 2, which overrides FAIL), or PASS (exit 0)." src="docs/policy-light.svg" width="100%">
18
+ </picture>
19
+
20
+ `fail_on` sets the line: a confirmed finding at or above it fails the run; anything below
21
+ is reported. Only cookies you name in `auth_cookies` can be confirmed.
22
+ All options and a stricter example: **[Policy](docs/policy.md)**.
23
+
24
+ ## See it run
25
+
26
+ ![A Browser Use agent logs in to two local apps. Both logins succeed. CookieGuard fails the first with CG001 Missing HttpOnly (exit 1) and passes the second (exit 0).](docs/cookieguard-browser-use-demo.gif)
27
+
28
+ Claude (`claude-opus-5-5`) drives a real Browser Use agent through the same login twice.
29
+ The insecure app's cookie lacks `HttpOnly`, so CookieGuard returns **FAIL — CG001** (exit 1).
30
+ The secure app gets **PASS** (exit 0). Recorded from a real run: **[Demo & tests](docs/demo-and-tests.md)**.
31
+
32
+ ## Add it to your agent
33
+
34
+ ```python
35
+ from browser_use import Agent, BrowserProfile, BrowserSession, Tools
36
+ from cookieguard_browser_use import CookieGuardAudit
37
+
38
+ audit = CookieGuardAudit(
39
+ allowed_origins=['http://127.0.0.1:3000'], # exact origins you are authorized to test
40
+ auth_cookies=['session_id'], # the real session cookie name(s)
41
+ fail_on='high',
42
+ )
43
+ session = BrowserSession(browser_profile=BrowserProfile(
44
+ keep_alive=True, # required: Agent.run() otherwise closes the browser
45
+ enable_default_extensions=False, # avoid the bundled cookie-consent extension
46
+ allowed_domains=audit.browser_allowed_domains(),
47
+ ))
48
+ tools = Tools()
49
+ audit.register_tools(tools) # optional agent-visible check
50
+
51
+ await session.start()
52
+ await audit.start(session) # before the login
53
+ agent = Agent(task=..., llm=..., browser_session=session, tools=tools,
54
+ sensitive_data={'http://127.0.0.1': {'login_password': '...'}})
55
+ await agent.run()
56
+ result = await audit.finish() # decided by the CookieGuard engine
57
+ await session.kill()
58
+
59
+ print(result.summary)
60
+ result.assert_policy() # raises CookiePolicyError on FAIL or INCOMPLETE
61
+ ```
62
+
63
+ `assert_policy()` raises even if the agent never called the tool, or reported
64
+ success. `CookieGuardAudit` is also an async context manager; `close()` stops
65
+ collection without a result. One instance audits one workflow.
66
+
67
+ Needs Python 3.11+, Node.js 24+, and Chrome or Chromium: **[Install](docs/install.md)**.
68
+
69
+ Install [version 0.1.0 from PyPI](https://pypi.org/project/cookieguard-browser-use/):
70
+
71
+ ```bash
72
+ pip install cookieguard-browser-use
73
+ ```
74
+
75
+ Status: `0.1.0`, tested with browser-use 0.13.14 on Linux (Python 3.11–3.13) and macOS (Python 3.12).
76
+ See [tested environments](docs/limits-and-privacy.md#tested-environments-and-dependencies).
77
+ This is a community integration, not an official Browser Use package.
@@ -0,0 +1,102 @@
1
+ """CookieGuard + a Browser Use agent logging in to a local demo app.
2
+
3
+ python examples/agent_login.py --mode insecure # scripted, no API key
4
+ ANTHROPIC_API_KEY=... python examples/agent_login.py --provider anthropic --mode insecure
5
+ BROWSER_USE_API_KEY=... python examples/agent_login.py --provider browser-use --mode secure
6
+ OPENAI_API_KEY=... python examples/agent_login.py --provider openai --model <model-id> --mode secure
7
+
8
+ Exit code: 0 PASS, 1 confirmed policy FAIL, 2 INCOMPLETE. With --mode insecure the
9
+ login succeeds and the exit code is 1: the agent reaching the account page does
10
+ not make the session cookie secure.
11
+
12
+ Model providers receive the task, page content, and CookieGuard's sanitized tool
13
+ output. Credentials stay in Browser Use's sensitive_data and are typed as
14
+ placeholders; cookie values never reach the model. The app is loopback-only.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import argparse
20
+ import asyncio
21
+ import os
22
+ import sys
23
+
24
+ # Browser Use telemetry would send the task, visited URLs, actions, and final answer.
25
+ os.environ.setdefault('ANONYMIZED_TELEMETRY', 'false')
26
+ os.environ.setdefault('BROWSER_USE_CLOUD_SYNC', 'false')
27
+
28
+ from browser_use import Agent, BrowserProfile, BrowserSession, Tools # noqa: E402
29
+
30
+ from cookieguard_browser_use import CookieGuardAudit, CookiePolicyError # noqa: E402
31
+ from cookieguard_browser_use.demo.app import DEMO_PASSWORD, DEMO_USERNAME, DemoApp # noqa: E402
32
+ from cookieguard_browser_use.demo.scripted import TASK, ScriptedLoginModel # noqa: E402
33
+
34
+ KEYS = {'anthropic': 'ANTHROPIC_API_KEY', 'openai': 'OPENAI_API_KEY', 'browser-use': 'BROWSER_USE_API_KEY'}
35
+
36
+
37
+ def make_llm(provider: str, model: str | None, origin: str):
38
+ if provider == 'scripted':
39
+ return ScriptedLoginModel(origin) # Deterministic stand-in; not an AI model.
40
+ if not os.environ.get(KEYS[provider]):
41
+ raise SystemExit(f'{KEYS[provider]} is not set; use --provider scripted to run without a model.')
42
+ if provider == 'anthropic':
43
+ from browser_use import ChatAnthropic
44
+ # Adaptive thinking makes browser-use 0.13 send tool_choice "auto"; current
45
+ # Claude models reject the forced tool_choice it would otherwise send.
46
+ # Server-side fallback: if a safety classifier declines a step (observed
47
+ # intermittently on this login task), the API reruns it on the fallback model.
48
+ return ChatAnthropic(model=model or 'claude-opus-5-5', thinking={'type': 'adaptive'},
49
+ fallbacks=[{'model': 'claude-opus-4-8'}])
50
+ if provider == 'browser-use':
51
+ from browser_use import ChatBrowserUse
52
+ return ChatBrowserUse(**({'model': model} if model else {}))
53
+ if not model:
54
+ raise SystemExit('--model is required for --provider openai.')
55
+ from browser_use import ChatOpenAI
56
+ return ChatOpenAI(model=model)
57
+
58
+
59
+ async def main(args: argparse.Namespace) -> int:
60
+ with DemoApp(args.mode) as app:
61
+ # 1. Configure the audit: exact authorized origins and the known session cookie.
62
+ audit = CookieGuardAudit(allowed_origins=[app.origin], auth_cookies=['session_id'], fail_on='high')
63
+ # keep_alive=True: Agent.run() would otherwise close the browser before audit.finish().
64
+ session = BrowserSession(browser_profile=BrowserProfile(
65
+ headless=not args.headed, keep_alive=True, enable_default_extensions=False,
66
+ allowed_domains=audit.browser_allowed_domains()))
67
+ tools = Tools()
68
+ audit.register_tools(tools) # Optional: lets the agent request a sanitized provisional check.
69
+ try:
70
+ # 2. Start the browser, then evidence collection, before the login.
71
+ await session.start()
72
+ await audit.start(session)
73
+ # 3-4. The agent performs the authorized login and workflow.
74
+ agent = Agent(
75
+ task=TASK.format(origin=app.origin), llm=make_llm(args.provider, args.model, app.origin),
76
+ browser_session=session, tools=tools, use_vision=False,
77
+ sensitive_data={audit.browser_allowed_domains()[0]: {'login_user': DEMO_USERNAME, 'login_password': DEMO_PASSWORD}})
78
+ history = await agent.run(max_steps=12)
79
+ # 5. Evaluate with the CookieGuard engine, independent of what the agent concluded.
80
+ result = await audit.finish()
81
+ finally:
82
+ await audit.close()
83
+ await session.kill()
84
+ print(f'\nAgent final answer: {history.final_result()}')
85
+ print(f'Server-side logins: {app.logins}\n')
86
+ print(result.summary)
87
+ result.write_json(f'cookieguard-{args.mode}.json')
88
+ # 6. Enforce. In pytest, just call result.assert_policy().
89
+ try:
90
+ result.assert_policy()
91
+ except CookiePolicyError as error:
92
+ print(error)
93
+ return result.exit_code
94
+
95
+
96
+ if __name__ == '__main__':
97
+ parser = argparse.ArgumentParser(description='Browser Use agent login audited by CookieGuard.')
98
+ parser.add_argument('--mode', choices=['insecure', 'secure'], default='insecure')
99
+ parser.add_argument('--provider', choices=['scripted', *KEYS], default='scripted')
100
+ parser.add_argument('--model', help='model id for the selected provider')
101
+ parser.add_argument('--headed', action='store_true')
102
+ sys.exit(asyncio.run(main(parser.parse_args())))
@@ -0,0 +1,22 @@
1
+ """Refuse to build a wheel without a complete, verified vendored engine."""
2
+
3
+ import hashlib
4
+ import json
5
+ from pathlib import Path
6
+
7
+ from hatchling.builders.hooks.plugin.interface import BuildHookInterface
8
+
9
+
10
+ class CustomBuildHook(BuildHookInterface):
11
+ def initialize(self, version, build_data):
12
+ engine = Path(self.root) / 'src' / 'cookieguard_browser_use' / '_node'
13
+ manifest_path = engine / 'manifest.json'
14
+ if not manifest_path.is_file():
15
+ raise RuntimeError('Vendored engine missing. Run: node scripts/vendor_engine.mjs --registry')
16
+ manifest = json.loads(manifest_path.read_text(encoding='utf-8'))
17
+ expected = dict(manifest['bridge'])
18
+ for package in manifest['packages']:
19
+ expected.update(package['files'])
20
+ for relative, digest in expected.items():
21
+ if hashlib.sha256((engine / relative).read_bytes()).hexdigest() != digest:
22
+ raise RuntimeError('Vendored engine does not match its manifest. Re-run scripts/vendor_engine.mjs.')
@@ -0,0 +1,85 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.27", "hatch-fancy-pypi-readme==25.1.0"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "cookieguard-browser-use"
7
+ version = "0.1.0"
8
+ description = "CookieGuard authentication-cookie policy checks for Browser Use agents"
9
+ dynamic = ["readme"]
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ requires-python = ">=3.11"
13
+ authors = [{ name = "aiscihub" }]
14
+ keywords = ["browser-use", "cookies", "security", "authentication", "ai-agents", "testing"]
15
+ classifiers = [
16
+ "Development Status :: 4 - Beta",
17
+ "Intended Audience :: Developers",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.11",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Programming Language :: Python :: 3.13",
22
+ "Topic :: Security",
23
+ "Topic :: Software Development :: Testing",
24
+ ]
25
+ # Tested with browser-use 0.13.14 / cdp-use 1.4.5. Node.js 24+ is required at
26
+ # runtime for the vendored CookieGuard engine; it is not a Python dependency.
27
+ dependencies = [
28
+ "browser-use>=0.13.14,<0.14",
29
+ "cdp-use>=1.4.5,<2",
30
+ "pydantic>=2.10,<3",
31
+ ]
32
+
33
+ [project.optional-dependencies]
34
+ test = ["pytest>=8.3"]
35
+
36
+ [project.urls]
37
+ Homepage = "https://github.com/aiscihub/cookieguard-ai/tree/main/integrations/browser-use"
38
+ Repository = "https://github.com/aiscihub/cookieguard-ai"
39
+ Issues = "https://github.com/aiscihub/cookieguard-ai/issues"
40
+
41
+ [tool.hatch.metadata.hooks.fancy-pypi-readme]
42
+ content-type = "text/markdown"
43
+ fragments = [{ path = "README.md" }]
44
+
45
+ # Only the package description gets absolute links. Keep GitHub's README relative.
46
+ # Update these release-tag pins together with the package version.
47
+ [[tool.hatch.metadata.hooks.fancy-pypi-readme.substitutions]]
48
+ pattern = '''(?P<prefix>(?:src|srcset)="|!\[[^\]]*\]\()(?P<path>docs/[^"()\s]+\.(?:svg|gif))'''
49
+ replacement = '\g<prefix>https://raw.githubusercontent.com/aiscihub/cookieguard-ai/browser-use-v0.1.0/integrations/browser-use/\g<path>'
50
+
51
+ [[tool.hatch.metadata.hooks.fancy-pypi-readme.substitutions]]
52
+ pattern = '''(?P<prefix>\]\()(?P<path>docs/[^)\s]+\.md(?:#[^)\s]*)?)'''
53
+ replacement = '\g<prefix>https://github.com/aiscihub/cookieguard-ai/blob/browser-use-v0.1.0/integrations/browser-use/\g<path>'
54
+
55
+ [[tool.hatch.metadata.hooks.fancy-pypi-readme.substitutions]]
56
+ pattern = '''(?P<prefix>\]\()\.\./\.\./(?P<path>README\.md(?:#[^)\s]*)?)'''
57
+ replacement = '\g<prefix>https://github.com/aiscihub/cookieguard-ai/blob/browser-use-v0.1.0/\g<path>'
58
+
59
+ [[tool.hatch.metadata.hooks.fancy-pypi-readme.substitutions]]
60
+ pattern = ', not yet on PyPI\.'
61
+ replacement = '.'
62
+
63
+ [tool.hatch.build.targets.wheel]
64
+ packages = ["src/cookieguard_browser_use"]
65
+ # Generated by scripts/vendor_engine.mjs and ignored by git, so list explicitly.
66
+ artifacts = [
67
+ "src/cookieguard_browser_use/_node/node_modules/**",
68
+ "src/cookieguard_browser_use/_node/manifest.json",
69
+ ]
70
+
71
+ [tool.hatch.build.targets.wheel.hooks.custom]
72
+ path = "hatch_build.py"
73
+
74
+ [tool.hatch.build.targets.sdist]
75
+ include = ["src", "tests", "examples", "scripts", "hatch_build.py", "README.md", "LICENSE", "pyproject.toml"]
76
+ artifacts = [
77
+ "src/cookieguard_browser_use/_node/node_modules/**",
78
+ "src/cookieguard_browser_use/_node/manifest.json",
79
+ ]
80
+
81
+ [tool.pytest.ini_options]
82
+ testpaths = ["tests"]
83
+ markers = [
84
+ "browser: launches a real local Chromium through Browser Use",
85
+ ]
@@ -0,0 +1,66 @@
1
+ """Bounded, step-by-step smoke check of the browser stack, for CI diagnostics.
2
+
3
+ python scripts/browser_smoke.py
4
+
5
+ Each step has its own timeout and prints a marker, so a hang or crash names
6
+ the step. No credentials or cookies are involved: it only opens the login page.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import asyncio
12
+ import os
13
+ import sys
14
+ import time
15
+
16
+ for key, value in (('ANONYMIZED_TELEMETRY', 'false'), ('BROWSER_USE_CLOUD_SYNC', 'false')):
17
+ os.environ.setdefault(key, value)
18
+
19
+
20
+ def mark(step: str, started: float) -> None:
21
+ print(f'[smoke] {step} ({time.monotonic() - started:.1f}s)', flush=True)
22
+
23
+
24
+ async def bounded(step: str, awaitable, seconds: float):
25
+ started = time.monotonic()
26
+ print(f'[smoke] {step} ...', flush=True)
27
+ result = await asyncio.wait_for(awaitable, seconds)
28
+ mark(f'{step}: ok', started)
29
+ return result
30
+
31
+
32
+ async def main() -> int:
33
+ from browser_use import BrowserProfile, BrowserSession, Tools
34
+ from browser_use.browser.watchdogs.local_browser_watchdog import LocalBrowserWatchdog
35
+
36
+ from cookieguard_browser_use import CookieGuardAudit
37
+ from cookieguard_browser_use._engine import node_major, resolve_node
38
+ from cookieguard_browser_use.demo.app import DemoApp
39
+
40
+ print(f'[smoke] python {sys.version.split()[0]} on {sys.platform}', flush=True)
41
+ print(f'[smoke] browser chosen by Browser Use: {LocalBrowserWatchdog._find_installed_browser_path()}', flush=True)
42
+ print(f'[smoke] node {resolve_node()} major {await node_major(resolve_node())}', flush=True)
43
+ with DemoApp('secure') as app:
44
+ session = BrowserSession(browser_profile=BrowserProfile(
45
+ headless=True, keep_alive=True, enable_default_extensions=False, user_data_dir=None))
46
+ try:
47
+ await bounded('BrowserSession.start', session.start(), 60)
48
+ version = await bounded('Browser.getVersion', session.cdp_client.send.Browser.getVersion(), 10)
49
+ print(f'[smoke] browser product: {version.get("product")}', flush=True)
50
+ audit = CookieGuardAudit(allowed_origins=[app.origin], auth_cookies=['session_id'])
51
+ tools = Tools()
52
+ audit.register_tools(tools)
53
+ await bounded('audit.start (engine + CDP observer)', audit.start(session), 30)
54
+ result = await bounded('navigate to demo login', tools.registry.execute_action(
55
+ 'navigate', {'url': f'{app.origin}/login'}, browser_session=session), 30)
56
+ print(f'[smoke] navigate error: {result.error}', flush=True)
57
+ audit_result = await bounded('audit.finish', audit.finish(), 30)
58
+ print(f'[smoke] audit status without a login: {audit_result.status} (INCOMPLETE expected)', flush=True)
59
+ finally:
60
+ await bounded('BrowserSession.kill', session.kill(), 30)
61
+ print('[smoke] done', flush=True)
62
+ return 0
63
+
64
+
65
+ if __name__ == '__main__':
66
+ sys.exit(asyncio.run(main()))