cookieguard-browser-use 0.1.0b1__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.
- cookieguard_browser_use-0.1.0b1/.gitignore +11 -0
- cookieguard_browser_use-0.1.0b1/LICENSE +21 -0
- cookieguard_browser_use-0.1.0b1/PKG-INFO +97 -0
- cookieguard_browser_use-0.1.0b1/README.md +70 -0
- cookieguard_browser_use-0.1.0b1/examples/agent_login.py +102 -0
- cookieguard_browser_use-0.1.0b1/hatch_build.py +22 -0
- cookieguard_browser_use-0.1.0b1/pyproject.toml +85 -0
- cookieguard_browser_use-0.1.0b1/scripts/browser_smoke.py +66 -0
- cookieguard_browser_use-0.1.0b1/scripts/record_demo.py +471 -0
- cookieguard_browser_use-0.1.0b1/scripts/vendor_engine.mjs +93 -0
- cookieguard_browser_use-0.1.0b1/scripts/verify_external.py +120 -0
- cookieguard_browser_use-0.1.0b1/scripts/verify_metadata.py +66 -0
- cookieguard_browser_use-0.1.0b1/src/cookieguard_browser_use/__init__.py +22 -0
- cookieguard_browser_use-0.1.0b1/src/cookieguard_browser_use/_audit.py +289 -0
- cookieguard_browser_use-0.1.0b1/src/cookieguard_browser_use/_collector.py +388 -0
- cookieguard_browser_use-0.1.0b1/src/cookieguard_browser_use/_engine.py +256 -0
- cookieguard_browser_use-0.1.0b1/src/cookieguard_browser_use/_errors.py +44 -0
- cookieguard_browser_use-0.1.0b1/src/cookieguard_browser_use/_node/bridge.mjs +179 -0
- cookieguard_browser_use-0.1.0b1/src/cookieguard_browser_use/_node/manifest.json +40 -0
- cookieguard_browser_use-0.1.0b1/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-core/LICENSE +21 -0
- cookieguard_browser_use-0.1.0b1/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-core/README.md +95 -0
- cookieguard_browser_use-0.1.0b1/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-core/package.json +17 -0
- cookieguard_browser_use-0.1.0b1/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-core/src/index.d.ts +80 -0
- cookieguard_browser_use-0.1.0b1/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-core/src/index.js +4 -0
- cookieguard_browser_use-0.1.0b1/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-core/src/normalize.js +169 -0
- cookieguard_browser_use-0.1.0b1/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-core/src/onnx.d.ts +6 -0
- cookieguard_browser_use-0.1.0b1/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-core/src/onnx.js +37 -0
- cookieguard_browser_use-0.1.0b1/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-core/src/policy.js +182 -0
- cookieguard_browser_use-0.1.0b1/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-core/src/report.js +28 -0
- cookieguard_browser_use-0.1.0b1/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-core/src/safety.js +54 -0
- cookieguard_browser_use-0.1.0b1/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-core/vendor/engine.cjs +799 -0
- cookieguard_browser_use-0.1.0b1/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-playwright/LICENSE +21 -0
- cookieguard_browser_use-0.1.0b1/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-playwright/README.md +62 -0
- cookieguard_browser_use-0.1.0b1/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-playwright/package.json +15 -0
- cookieguard_browser_use-0.1.0b1/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-playwright/src/index.d.ts +19 -0
- cookieguard_browser_use-0.1.0b1/src/cookieguard_browser_use/_node/node_modules/@aiscihub/cookieguard-playwright/src/index.js +145 -0
- cookieguard_browser_use-0.1.0b1/src/cookieguard_browser_use/_origins.py +85 -0
- cookieguard_browser_use-0.1.0b1/src/cookieguard_browser_use/_result.py +211 -0
- cookieguard_browser_use-0.1.0b1/src/cookieguard_browser_use/demo/__init__.py +0 -0
- cookieguard_browser_use-0.1.0b1/src/cookieguard_browser_use/demo/__main__.py +58 -0
- cookieguard_browser_use-0.1.0b1/src/cookieguard_browser_use/demo/app.py +176 -0
- cookieguard_browser_use-0.1.0b1/src/cookieguard_browser_use/demo/scripted.py +141 -0
- cookieguard_browser_use-0.1.0b1/src/cookieguard_browser_use/py.typed +0 -0
- cookieguard_browser_use-0.1.0b1/tests/conftest.py +114 -0
- cookieguard_browser_use-0.1.0b1/tests/test_agent.py +107 -0
- cookieguard_browser_use-0.1.0b1/tests/test_browser.py +232 -0
- cookieguard_browser_use-0.1.0b1/tests/test_collector.py +146 -0
- cookieguard_browser_use-0.1.0b1/tests/test_config.py +135 -0
- cookieguard_browser_use-0.1.0b1/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,97 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: cookieguard-browser-use
|
|
3
|
+
Version: 0.1.0b1
|
|
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.0b1/README.md) policy engine, never from the model.
|
|
35
|
+
|
|
36
|
+
**Overview** · [Install](https://github.com/aiscihub/cookieguard-ai/blob/browser-use-v0.1.0b1/integrations/browser-use/docs/install.md) · [Policy](https://github.com/aiscihub/cookieguard-ai/blob/browser-use-v0.1.0b1/integrations/browser-use/docs/policy.md) · [How it works](https://github.com/aiscihub/cookieguard-ai/blob/browser-use-v0.1.0b1/integrations/browser-use/docs/how-it-works.md) · [Results](https://github.com/aiscihub/cookieguard-ai/blob/browser-use-v0.1.0b1/integrations/browser-use/docs/results.md) · [Limits & privacy](https://github.com/aiscihub/cookieguard-ai/blob/browser-use-v0.1.0b1/integrations/browser-use/docs/limits-and-privacy.md) · [Demo & tests](https://github.com/aiscihub/cookieguard-ai/blob/browser-use-v0.1.0b1/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.0b1/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.0b1/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.0b1/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.0b1/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.0b1/integrations/browser-use/docs/policy.md)**.
|
|
50
|
+
|
|
51
|
+
## See it run
|
|
52
|
+
|
|
53
|
+

|
|
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.0b1/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.0b1/integrations/browser-use/docs/install.md)**.
|
|
95
|
+
|
|
96
|
+
Status: `0.1.0b1` pre-release, tested with browser-use 0.13.14.
|
|
97
|
+
This is a community integration, not an official Browser Use package.
|
|
@@ -0,0 +1,70 @@
|
|
|
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
|
+

|
|
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
|
+
Status: `0.1.0b1` pre-release, tested with browser-use 0.13.14, not yet on PyPI.
|
|
70
|
+
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.0b1"
|
|
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.0b1/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.0b1/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.0b1/\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()))
|