angel-agent-guard 0.2.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.
- angel_agent_guard-0.2.0/.gitignore +3 -0
- angel_agent_guard-0.2.0/LICENSE +21 -0
- angel_agent_guard-0.2.0/PKG-INFO +155 -0
- angel_agent_guard-0.2.0/README.md +126 -0
- angel_agent_guard-0.2.0/pyproject.toml +45 -0
- angel_agent_guard-0.2.0/src/angel_agent_guard/__init__.py +30 -0
- angel_agent_guard-0.2.0/src/angel_agent_guard/__main__.py +63 -0
- angel_agent_guard-0.2.0/src/angel_agent_guard/_guard.py +320 -0
- angel_agent_guard-0.2.0/src/angel_agent_guard/_http.py +42 -0
- angel_agent_guard-0.2.0/src/angel_agent_guard/_local.py +188 -0
- angel_agent_guard-0.2.0/src/angel_agent_guard/integrations/__init__.py +1 -0
- angel_agent_guard-0.2.0/src/angel_agent_guard/integrations/_texts.py +11 -0
- angel_agent_guard-0.2.0/src/angel_agent_guard/integrations/browser_use.py +42 -0
- angel_agent_guard-0.2.0/src/angel_agent_guard/integrations/crewai.py +54 -0
- angel_agent_guard-0.2.0/src/angel_agent_guard/integrations/langchain.py +52 -0
- angel_agent_guard-0.2.0/src/angel_agent_guard/py.typed +0 -0
- angel_agent_guard-0.2.0/tests/test_core.py +118 -0
- angel_agent_guard-0.2.0/tests/test_integrations.py +56 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Oroboros Design (Daouda Ndiaye)
|
|
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,155 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: angel-agent-guard
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Guard an AI agent before it acts: pre-purchase check for any shop, checkout or payment link, paid API, x402 endpoint or token, and a prompt-injection check for untrusted content. Zero dependencies; browser-use, LangChain/LangGraph and CrewAI tools included.
|
|
5
|
+
Project-URL: Homepage, https://github.com/daoudandiaye98-alt/angel-agent-guard/tree/main/python
|
|
6
|
+
Project-URL: Repository, https://github.com/daoudandiaye98-alt/angel-agent-guard
|
|
7
|
+
Project-URL: Documentation, https://angel.oroboros-design.com/docs/prompt-injection-defense/
|
|
8
|
+
Project-URL: Issues, https://github.com/daoudandiaye98-alt/angel-agent-guard/issues
|
|
9
|
+
Author: Oroboros Design
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: agent-security,agentic-commerce,ai-agent,base,browser-use,checkout,crewai,langchain,langgraph,mcp,phishing,prompt-injection,shopping-agent,usdc,x402
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Classifier: Topic :: Security
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Provides-Extra: browser-use
|
|
23
|
+
Requires-Dist: browser-use>=1.0; extra == 'browser-use'
|
|
24
|
+
Provides-Extra: crewai
|
|
25
|
+
Requires-Dist: crewai>=0.80; extra == 'crewai'
|
|
26
|
+
Provides-Extra: langchain
|
|
27
|
+
Requires-Dist: langchain-core>=0.3; extra == 'langchain'
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# angel-agent-guard (Python)
|
|
31
|
+
|
|
32
|
+
Two checks an AI agent should run before it acts:
|
|
33
|
+
|
|
34
|
+
- **`verify_before_pay(url_or_token)`** before **any purchase or paid call**. That covers a shop or product page, a checkout or payment link (Stripe, Shopify, PayPal, SaaS checkout), an unknown paid API, an x402 endpoint, and a token swap or approval. It checks:
|
|
35
|
+
- TLS and domain age
|
|
36
|
+
- look-alike domains, and redirects to payment pages on other domains
|
|
37
|
+
- which payment provider takes the money
|
|
38
|
+
- card fields and plain-http forms in the page
|
|
39
|
+
- text in the page aimed at shopping agents
|
|
40
|
+
- for x402: the payment offer itself
|
|
41
|
+
- for an ERC-20: the contract's owner powers
|
|
42
|
+
- **`guard_untrusted(text_or_url)`** before the model acts on a web page, email, document, RAG chunk or tool result written by someone else. A local pass runs first (invisible Unicode, tag smuggling, exfiltration images), then a hosted scan in 17 languages for text aimed at the model.
|
|
43
|
+
- **`quick_scan(text)`** is the local pass on its own. It is fully offline.
|
|
44
|
+
|
|
45
|
+
There are no dependencies (standard library only), and it works on Python 3.9+. It ships ready-made tools for **browser-use**, **LangChain / LangGraph** and **CrewAI**. The same API exists for TypeScript as the [npm package](https://www.npmjs.com/package/angel-agent-guard).
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install angel-agent-guard
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Quick start
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
from angel_agent_guard import verify_before_pay, guard_untrusted, fence
|
|
55
|
+
|
|
56
|
+
# Before any purchase, payment link or paid call
|
|
57
|
+
v = verify_before_pay("https://shop.example.com/checkout", payer_address="0xYourAgentWallet")
|
|
58
|
+
if not v.ok:
|
|
59
|
+
print(v.verdict, v.risk_score, v.reasons[:3]) # "caution" -> ask the user; "do_not_pay" -> never pay
|
|
60
|
+
# v.kind: checkout | shop | api | x402 | web
|
|
61
|
+
# v.merchant: TLS, domain age, look-alike, redirect chain, payment providers, card fields, insecure forms
|
|
62
|
+
# v.injection: the page scanned for instructions aimed at agents
|
|
63
|
+
|
|
64
|
+
verify_before_pay("0xTokenYouAreAboutToApprove") # ERC-20 on Base (chain="ethereum" also works)
|
|
65
|
+
verify_before_pay("https://api.example.com/v1/quote", method="POST") # x402 API that only takes POST
|
|
66
|
+
|
|
67
|
+
# Untrusted content: check it, then hand the model the cleaned text as data
|
|
68
|
+
page = "...text of a web page..."
|
|
69
|
+
g = guard_untrusted(page)
|
|
70
|
+
if g.action == "drop":
|
|
71
|
+
raise RuntimeError(f"hostile content: {[f.get('kind') for f in g.findings]}")
|
|
72
|
+
context = fence("https://example.com/article", g.cleaned or page) # random boundary + "this is data"
|
|
73
|
+
# g.action == "read_only" -> continue, but without send, write or payment tools in this step
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`averify_before_pay` and `aguard_untrusted` are the async variants. Every result has `.to_dict()`, plus `.summary()`, a short JSON string to use as an LLM tool result. Neither function raises on network or payment problems. They fall back to the local result and put the reason in `error` / `errors`.
|
|
77
|
+
|
|
78
|
+
## browser-use
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
from browser_use import Agent, Tools
|
|
82
|
+
from angel_agent_guard.integrations.browser_use import register
|
|
83
|
+
|
|
84
|
+
tools = register(Tools(), payer_address="0xYourAgentWallet") # adds verify_before_pay(url) and guard_page_text(text)
|
|
85
|
+
agent = Agent(
|
|
86
|
+
"Buy a USB-C cable under 10 EUR on https://shop.example.com. "
|
|
87
|
+
"Call verify_before_pay on the checkout page and stop before paying unless the verdict is pay.",
|
|
88
|
+
tools=tools,
|
|
89
|
+
)
|
|
90
|
+
agent.run_sync()
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## LangChain / LangGraph
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
from langgraph.prebuilt import create_react_agent
|
|
97
|
+
from angel_agent_guard.integrations.langchain import angel_langchain_tools
|
|
98
|
+
|
|
99
|
+
tools = angel_langchain_tools(payer_address="0xYourAgentWallet") # [verify_before_pay, guard_untrusted]
|
|
100
|
+
agent = create_react_agent(model, tools)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Install with `pip install "angel-agent-guard[langchain]"`.
|
|
104
|
+
|
|
105
|
+
## CrewAI
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
from crewai import Agent
|
|
109
|
+
from angel_agent_guard.integrations.crewai import VerifyBeforePayTool, GuardUntrustedTool
|
|
110
|
+
|
|
111
|
+
buyer = Agent(
|
|
112
|
+
role="Purchasing agent",
|
|
113
|
+
goal="Buy what the user asked for, safely",
|
|
114
|
+
backstory="Checks every checkout before paying.",
|
|
115
|
+
tools=[VerifyBeforePayTool(payer_address="0xYourAgentWallet"), GuardUntrustedTool()],
|
|
116
|
+
)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Install with `pip install "angel-agent-guard[crewai]"`.
|
|
120
|
+
|
|
121
|
+
## CLI
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
angel-agent-guard-py verify https://shop.example.com/checkout
|
|
125
|
+
angel-agent-guard-py verify --token 0x... --chain base
|
|
126
|
+
angel-agent-guard-py scan "text to check" # or a https URL, or - for stdin
|
|
127
|
+
angel-agent-guard-py quick "text" # offline
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Exit codes: `0` clean / pay, `1` caution / unchecked, `2` hostile / do_not_pay, `3` usage or error.
|
|
131
|
+
|
|
132
|
+
## Pricing and paying
|
|
133
|
+
|
|
134
|
+
The local checks are free. The hosted checks run on [Angel](https://angel.oroboros-design.com/docs/prompt-injection-defense/). Each agent gets 3 free calls, then pays per call in USDC on Base via x402:
|
|
135
|
+
|
|
136
|
+
| Check | Price per call |
|
|
137
|
+
|---|---|
|
|
138
|
+
| pre-purchase check | 0.25 USDC |
|
|
139
|
+
| input guard | 0.99 USDC |
|
|
140
|
+
| token risk | 0.99 USDC |
|
|
141
|
+
|
|
142
|
+
Pass `payer_address` (or set `ANGEL_PAYER_ADDRESS`) so that your wallet gets its own 3 free calls. Without it, the free calls are shared by IP. Once the free calls are used up, the result carries `payment`, which holds the x402 offer, the price and a card checkout link a human can open.
|
|
143
|
+
|
|
144
|
+
To pay automatically, pass a paying HTTP client as `client=`. This can be any object with `.request()`, for example a paying client from the [x402 Python SDK](https://pypi.org/project/x402/) built on httpx or requests. The library checks an x402 offer locally (`check_offer`: official USDC, network, amount cap, same host) but never signs anything itself.
|
|
145
|
+
|
|
146
|
+
## Limits and data
|
|
147
|
+
|
|
148
|
+
- The local pass removes invisible characters and flags the riskiest markers. It does not judge meaning. If it finds nothing, it reports `unchecked`, never `clean`.
|
|
149
|
+
- The hosted checks are deterministic: patterns, TLS, DNS, RDAP and on-chain reads, with no model. That makes them fast and repeatable, but a new attack, or a well-made fraud shop on an old domain, can pass. Keep the other layers:
|
|
150
|
+
- read external content as data
|
|
151
|
+
- give the step that reads content no dangerous tools
|
|
152
|
+
- require an approval the model cannot give itself before money moves
|
|
153
|
+
- For a hosted check, the text or URL is sent to `angel.oroboros-design.com`. Use `remote=False` for content that must not leave your machine.
|
|
154
|
+
|
|
155
|
+
Disclosure: the hosted checks are a paid service run by the author (Oroboros Design). The library is MIT-licensed.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# angel-agent-guard (Python)
|
|
2
|
+
|
|
3
|
+
Two checks an AI agent should run before it acts:
|
|
4
|
+
|
|
5
|
+
- **`verify_before_pay(url_or_token)`** before **any purchase or paid call**. That covers a shop or product page, a checkout or payment link (Stripe, Shopify, PayPal, SaaS checkout), an unknown paid API, an x402 endpoint, and a token swap or approval. It checks:
|
|
6
|
+
- TLS and domain age
|
|
7
|
+
- look-alike domains, and redirects to payment pages on other domains
|
|
8
|
+
- which payment provider takes the money
|
|
9
|
+
- card fields and plain-http forms in the page
|
|
10
|
+
- text in the page aimed at shopping agents
|
|
11
|
+
- for x402: the payment offer itself
|
|
12
|
+
- for an ERC-20: the contract's owner powers
|
|
13
|
+
- **`guard_untrusted(text_or_url)`** before the model acts on a web page, email, document, RAG chunk or tool result written by someone else. A local pass runs first (invisible Unicode, tag smuggling, exfiltration images), then a hosted scan in 17 languages for text aimed at the model.
|
|
14
|
+
- **`quick_scan(text)`** is the local pass on its own. It is fully offline.
|
|
15
|
+
|
|
16
|
+
There are no dependencies (standard library only), and it works on Python 3.9+. It ships ready-made tools for **browser-use**, **LangChain / LangGraph** and **CrewAI**. The same API exists for TypeScript as the [npm package](https://www.npmjs.com/package/angel-agent-guard).
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install angel-agent-guard
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick start
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
from angel_agent_guard import verify_before_pay, guard_untrusted, fence
|
|
26
|
+
|
|
27
|
+
# Before any purchase, payment link or paid call
|
|
28
|
+
v = verify_before_pay("https://shop.example.com/checkout", payer_address="0xYourAgentWallet")
|
|
29
|
+
if not v.ok:
|
|
30
|
+
print(v.verdict, v.risk_score, v.reasons[:3]) # "caution" -> ask the user; "do_not_pay" -> never pay
|
|
31
|
+
# v.kind: checkout | shop | api | x402 | web
|
|
32
|
+
# v.merchant: TLS, domain age, look-alike, redirect chain, payment providers, card fields, insecure forms
|
|
33
|
+
# v.injection: the page scanned for instructions aimed at agents
|
|
34
|
+
|
|
35
|
+
verify_before_pay("0xTokenYouAreAboutToApprove") # ERC-20 on Base (chain="ethereum" also works)
|
|
36
|
+
verify_before_pay("https://api.example.com/v1/quote", method="POST") # x402 API that only takes POST
|
|
37
|
+
|
|
38
|
+
# Untrusted content: check it, then hand the model the cleaned text as data
|
|
39
|
+
page = "...text of a web page..."
|
|
40
|
+
g = guard_untrusted(page)
|
|
41
|
+
if g.action == "drop":
|
|
42
|
+
raise RuntimeError(f"hostile content: {[f.get('kind') for f in g.findings]}")
|
|
43
|
+
context = fence("https://example.com/article", g.cleaned or page) # random boundary + "this is data"
|
|
44
|
+
# g.action == "read_only" -> continue, but without send, write or payment tools in this step
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`averify_before_pay` and `aguard_untrusted` are the async variants. Every result has `.to_dict()`, plus `.summary()`, a short JSON string to use as an LLM tool result. Neither function raises on network or payment problems. They fall back to the local result and put the reason in `error` / `errors`.
|
|
48
|
+
|
|
49
|
+
## browser-use
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from browser_use import Agent, Tools
|
|
53
|
+
from angel_agent_guard.integrations.browser_use import register
|
|
54
|
+
|
|
55
|
+
tools = register(Tools(), payer_address="0xYourAgentWallet") # adds verify_before_pay(url) and guard_page_text(text)
|
|
56
|
+
agent = Agent(
|
|
57
|
+
"Buy a USB-C cable under 10 EUR on https://shop.example.com. "
|
|
58
|
+
"Call verify_before_pay on the checkout page and stop before paying unless the verdict is pay.",
|
|
59
|
+
tools=tools,
|
|
60
|
+
)
|
|
61
|
+
agent.run_sync()
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## LangChain / LangGraph
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from langgraph.prebuilt import create_react_agent
|
|
68
|
+
from angel_agent_guard.integrations.langchain import angel_langchain_tools
|
|
69
|
+
|
|
70
|
+
tools = angel_langchain_tools(payer_address="0xYourAgentWallet") # [verify_before_pay, guard_untrusted]
|
|
71
|
+
agent = create_react_agent(model, tools)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Install with `pip install "angel-agent-guard[langchain]"`.
|
|
75
|
+
|
|
76
|
+
## CrewAI
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from crewai import Agent
|
|
80
|
+
from angel_agent_guard.integrations.crewai import VerifyBeforePayTool, GuardUntrustedTool
|
|
81
|
+
|
|
82
|
+
buyer = Agent(
|
|
83
|
+
role="Purchasing agent",
|
|
84
|
+
goal="Buy what the user asked for, safely",
|
|
85
|
+
backstory="Checks every checkout before paying.",
|
|
86
|
+
tools=[VerifyBeforePayTool(payer_address="0xYourAgentWallet"), GuardUntrustedTool()],
|
|
87
|
+
)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Install with `pip install "angel-agent-guard[crewai]"`.
|
|
91
|
+
|
|
92
|
+
## CLI
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
angel-agent-guard-py verify https://shop.example.com/checkout
|
|
96
|
+
angel-agent-guard-py verify --token 0x... --chain base
|
|
97
|
+
angel-agent-guard-py scan "text to check" # or a https URL, or - for stdin
|
|
98
|
+
angel-agent-guard-py quick "text" # offline
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Exit codes: `0` clean / pay, `1` caution / unchecked, `2` hostile / do_not_pay, `3` usage or error.
|
|
102
|
+
|
|
103
|
+
## Pricing and paying
|
|
104
|
+
|
|
105
|
+
The local checks are free. The hosted checks run on [Angel](https://angel.oroboros-design.com/docs/prompt-injection-defense/). Each agent gets 3 free calls, then pays per call in USDC on Base via x402:
|
|
106
|
+
|
|
107
|
+
| Check | Price per call |
|
|
108
|
+
|---|---|
|
|
109
|
+
| pre-purchase check | 0.25 USDC |
|
|
110
|
+
| input guard | 0.99 USDC |
|
|
111
|
+
| token risk | 0.99 USDC |
|
|
112
|
+
|
|
113
|
+
Pass `payer_address` (or set `ANGEL_PAYER_ADDRESS`) so that your wallet gets its own 3 free calls. Without it, the free calls are shared by IP. Once the free calls are used up, the result carries `payment`, which holds the x402 offer, the price and a card checkout link a human can open.
|
|
114
|
+
|
|
115
|
+
To pay automatically, pass a paying HTTP client as `client=`. This can be any object with `.request()`, for example a paying client from the [x402 Python SDK](https://pypi.org/project/x402/) built on httpx or requests. The library checks an x402 offer locally (`check_offer`: official USDC, network, amount cap, same host) but never signs anything itself.
|
|
116
|
+
|
|
117
|
+
## Limits and data
|
|
118
|
+
|
|
119
|
+
- The local pass removes invisible characters and flags the riskiest markers. It does not judge meaning. If it finds nothing, it reports `unchecked`, never `clean`.
|
|
120
|
+
- The hosted checks are deterministic: patterns, TLS, DNS, RDAP and on-chain reads, with no model. That makes them fast and repeatable, but a new attack, or a well-made fraud shop on an old domain, can pass. Keep the other layers:
|
|
121
|
+
- read external content as data
|
|
122
|
+
- give the step that reads content no dangerous tools
|
|
123
|
+
- require an approval the model cannot give itself before money moves
|
|
124
|
+
- For a hosted check, the text or URL is sent to `angel.oroboros-design.com`. Use `remote=False` for content that must not leave your machine.
|
|
125
|
+
|
|
126
|
+
Disclosure: the hosted checks are a paid service run by the author (Oroboros Design). The library is MIT-licensed.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.24"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "angel-agent-guard"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Guard an AI agent before it acts: pre-purchase check for any shop, checkout or payment link, paid API, x402 endpoint or token, and a prompt-injection check for untrusted content. Zero dependencies; browser-use, LangChain/LangGraph and CrewAI tools included."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
requires-python = ">=3.9"
|
|
13
|
+
authors = [{ name = "Oroboros Design" }]
|
|
14
|
+
keywords = ["ai-agent", "agent-security", "prompt-injection", "shopping-agent", "agentic-commerce", "checkout", "phishing", "x402", "usdc", "base", "browser-use", "langchain", "langgraph", "crewai", "mcp"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
21
|
+
"Topic :: Security",
|
|
22
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
23
|
+
"Typing :: Typed",
|
|
24
|
+
]
|
|
25
|
+
dependencies = []
|
|
26
|
+
|
|
27
|
+
[project.optional-dependencies]
|
|
28
|
+
browser-use = ["browser-use>=1.0"]
|
|
29
|
+
langchain = ["langchain-core>=0.3"]
|
|
30
|
+
crewai = ["crewai>=0.80"]
|
|
31
|
+
|
|
32
|
+
[project.urls]
|
|
33
|
+
Homepage = "https://github.com/daoudandiaye98-alt/angel-agent-guard/tree/main/python"
|
|
34
|
+
Repository = "https://github.com/daoudandiaye98-alt/angel-agent-guard"
|
|
35
|
+
Documentation = "https://angel.oroboros-design.com/docs/prompt-injection-defense/"
|
|
36
|
+
Issues = "https://github.com/daoudandiaye98-alt/angel-agent-guard/issues"
|
|
37
|
+
|
|
38
|
+
[project.scripts]
|
|
39
|
+
angel-agent-guard-py = "angel_agent_guard.__main__:run"
|
|
40
|
+
|
|
41
|
+
[tool.hatch.build.targets.wheel]
|
|
42
|
+
packages = ["src/angel_agent_guard"]
|
|
43
|
+
|
|
44
|
+
[tool.hatch.build.targets.sdist]
|
|
45
|
+
include = ["src/angel_agent_guard", "tests", "README.md", "LICENSE", "pyproject.toml"]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""angel-agent-guard: two checks an AI agent should run before it acts.
|
|
2
|
+
|
|
3
|
+
- guard_untrusted(text_or_url): prompt-injection check for content written by someone else
|
|
4
|
+
- verify_before_pay(url_or_token): pre-purchase check for any shop, checkout or payment link, paid API, x402 endpoint or token
|
|
5
|
+
- quick_scan(text): offline pass (invisible Unicode, tag smuggling, exfiltration images), zero dependencies
|
|
6
|
+
|
|
7
|
+
Integrations: angel_agent_guard.integrations.browser_use, .langchain, .crewai
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from ._guard import (
|
|
11
|
+
ANGEL_BASE,
|
|
12
|
+
PATHS,
|
|
13
|
+
VERSION,
|
|
14
|
+
GuardResult,
|
|
15
|
+
PaymentNeeded,
|
|
16
|
+
VerifyResult,
|
|
17
|
+
aguard_untrusted,
|
|
18
|
+
averify_before_pay,
|
|
19
|
+
guard_untrusted,
|
|
20
|
+
verify_before_pay,
|
|
21
|
+
)
|
|
22
|
+
from ._local import USDC, LocalFinding, OfferCheck, QuickScanResult, check_offer, decode_offer, fence, local_verdict, looks_like_url, quick_scan
|
|
23
|
+
|
|
24
|
+
__version__ = VERSION
|
|
25
|
+
__all__ = [
|
|
26
|
+
"VERSION", "ANGEL_BASE", "PATHS", "USDC",
|
|
27
|
+
"guard_untrusted", "verify_before_pay", "aguard_untrusted", "averify_before_pay",
|
|
28
|
+
"quick_scan", "local_verdict", "fence", "looks_like_url", "check_offer", "decode_offer",
|
|
29
|
+
"GuardResult", "VerifyResult", "PaymentNeeded", "QuickScanResult", "LocalFinding", "OfferCheck",
|
|
30
|
+
]
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""CLI: python -m angel_agent_guard (or angel-agent-guard-py) scan|verify|quick ...
|
|
2
|
+
|
|
3
|
+
Exit codes: 0 clean/pay, 1 caution/unchecked, 2 hostile/do_not_pay, 3 usage or error.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import sys
|
|
10
|
+
from typing import List
|
|
11
|
+
|
|
12
|
+
from . import guard_untrusted, quick_scan, verify_before_pay
|
|
13
|
+
from ._local import local_verdict
|
|
14
|
+
|
|
15
|
+
USAGE = """usage:
|
|
16
|
+
angel-agent-guard-py scan "<text>" | <https-url> | - prompt-injection check (local + hosted)
|
|
17
|
+
angel-agent-guard-py verify <https-url> [--post] pre-purchase check (shop, checkout, payment link, API, x402)
|
|
18
|
+
angel-agent-guard-py verify --token 0x... [--chain base|ethereum]
|
|
19
|
+
angel-agent-guard-py quick "<text>" | - offline only"""
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def main(argv: List[str]) -> int:
|
|
23
|
+
if not argv or argv[0] in ("-h", "--help"):
|
|
24
|
+
print(USAGE)
|
|
25
|
+
return 3
|
|
26
|
+
cmd, rest = argv[0], argv[1:]
|
|
27
|
+
flag = lambda name: rest[rest.index(name) + 1] if name in rest and rest.index(name) + 1 < len(rest) else None # noqa: E731
|
|
28
|
+
positional = [a for i, a in enumerate(rest) if not a.startswith("--") and (i == 0 or rest[i - 1] not in ("--token", "--chain"))]
|
|
29
|
+
if cmd in ("scan", "quick"):
|
|
30
|
+
text = sys.stdin.read() if positional[:1] == ["-"] else " ".join(positional)
|
|
31
|
+
if not text:
|
|
32
|
+
print(USAGE, file=sys.stderr)
|
|
33
|
+
return 3
|
|
34
|
+
if cmd == "quick":
|
|
35
|
+
q = quick_scan(text)
|
|
36
|
+
print(json.dumps(q.to_dict(), ensure_ascii=False, indent=2))
|
|
37
|
+
v = local_verdict(q)
|
|
38
|
+
return 2 if v == "hostile" else 1 if v == "caution" else 0
|
|
39
|
+
g = guard_untrusted(text)
|
|
40
|
+
print(json.dumps(g.to_dict(), ensure_ascii=False, indent=2))
|
|
41
|
+
return 0 if g.verdict == "clean" else 2 if g.verdict == "hostile" else 1
|
|
42
|
+
if cmd == "verify":
|
|
43
|
+
url, token = (positional[0] if positional else None), flag("--token")
|
|
44
|
+
if not url and not token:
|
|
45
|
+
print(USAGE, file=sys.stderr)
|
|
46
|
+
return 3
|
|
47
|
+
r = verify_before_pay(url=url, token=token, chain=flag("--chain") or "base", method="POST" if "--post" in rest else "GET")
|
|
48
|
+
print(json.dumps(r.to_dict(), ensure_ascii=False, indent=2, default=str))
|
|
49
|
+
return 0 if r.verdict == "pay" else 2 if r.verdict == "do_not_pay" else 1
|
|
50
|
+
print(USAGE, file=sys.stderr)
|
|
51
|
+
return 3
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def run() -> None:
|
|
55
|
+
try:
|
|
56
|
+
sys.exit(main(sys.argv[1:]))
|
|
57
|
+
except Exception as e: # pragma: no cover
|
|
58
|
+
print(str(e), file=sys.stderr)
|
|
59
|
+
sys.exit(3)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
if __name__ == "__main__":
|
|
63
|
+
run()
|