eternity-agentshield 2.0.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.
- eternity_agentshield-2.0.0/PKG-INFO +183 -0
- eternity_agentshield-2.0.0/README.md +142 -0
- eternity_agentshield-2.0.0/agentshield/__init__.py +37 -0
- eternity_agentshield-2.0.0/agentshield/cli.py +80 -0
- eternity_agentshield-2.0.0/agentshield/client.py +168 -0
- eternity_agentshield-2.0.0/agentshield/decorators.py +102 -0
- eternity_agentshield-2.0.0/agentshield/exceptions.py +62 -0
- eternity_agentshield-2.0.0/agentshield/integrations/__init__.py +13 -0
- eternity_agentshield-2.0.0/agentshield/integrations/crewai.py +42 -0
- eternity_agentshield-2.0.0/agentshield/integrations/langchain.py +69 -0
- eternity_agentshield-2.0.0/agentshield/integrations/openai_interceptor.py +76 -0
- eternity_agentshield-2.0.0/eternity_agentshield.egg-info/PKG-INFO +183 -0
- eternity_agentshield-2.0.0/eternity_agentshield.egg-info/SOURCES.txt +18 -0
- eternity_agentshield-2.0.0/eternity_agentshield.egg-info/dependency_links.txt +1 -0
- eternity_agentshield-2.0.0/eternity_agentshield.egg-info/entry_points.txt +2 -0
- eternity_agentshield-2.0.0/eternity_agentshield.egg-info/requires.txt +14 -0
- eternity_agentshield-2.0.0/eternity_agentshield.egg-info/top_level.txt +1 -0
- eternity_agentshield-2.0.0/pyproject.toml +60 -0
- eternity_agentshield-2.0.0/setup.cfg +4 -0
- eternity_agentshield-2.0.0/setup.py +47 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: eternity-agentshield
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: ETERNITY : AgentShield 2.0 — Enterprise Security Middleware & Guardrails SDK for Autonomous AI Agents
|
|
5
|
+
Home-page: https://github.com/yashnishad-0923/agentshield
|
|
6
|
+
Author: ETERNITY Security Team
|
|
7
|
+
Author-email: ETERNITY Security Team <security@agentshield.ai>
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Homepage, https://github.com/agentshield/agentshield
|
|
10
|
+
Project-URL: Documentation, https://docs.agentshield.ai
|
|
11
|
+
Project-URL: Repository, https://github.com/agentshield/agentshield
|
|
12
|
+
Keywords: ai-agent,security,guardrails,langchain,crewai,autogen,openai,prompt-injection,cybersecurity,llm-firewall
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Information Technology
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Topic :: Security
|
|
25
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
26
|
+
Requires-Python: >=3.8
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
Provides-Extra: langchain
|
|
29
|
+
Requires-Dist: langchain-core>=0.1.0; extra == "langchain"
|
|
30
|
+
Provides-Extra: crewai
|
|
31
|
+
Requires-Dist: crewai>=0.1.0; extra == "crewai"
|
|
32
|
+
Provides-Extra: openai
|
|
33
|
+
Requires-Dist: openai>=1.0.0; extra == "openai"
|
|
34
|
+
Provides-Extra: all
|
|
35
|
+
Requires-Dist: langchain-core>=0.1.0; extra == "all"
|
|
36
|
+
Requires-Dist: crewai>=0.1.0; extra == "all"
|
|
37
|
+
Requires-Dist: openai>=1.0.0; extra == "all"
|
|
38
|
+
Dynamic: author
|
|
39
|
+
Dynamic: home-page
|
|
40
|
+
Dynamic: requires-python
|
|
41
|
+
|
|
42
|
+
# 🛡️ AgentShield Python Developer SDK (`agentshield`)
|
|
43
|
+
### Enterprise Security Middleware & Guardrails for Autonomous AI Agents
|
|
44
|
+
|
|
45
|
+
[](https://pypi.org/project/agentshield/)
|
|
46
|
+
[](https://www.python.org/downloads/)
|
|
47
|
+
[](LICENSE)
|
|
48
|
+
|
|
49
|
+
**AgentShield** is an inline action-authorization firewall for autonomous AI agents (LangChain, CrewAI, AutoGen, OpenAI Assistants). It evaluates planned tool calls across **6 analytical detection layers in under 6 milliseconds**, blocks prompt injection, prevents zero-day shell wipeouts, redacts confidential credentials in-flight, and manages human-in-the-loop approvals.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## 📦 Installation
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pip install eternity-agentshield
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
*In your Python code, import as:*
|
|
60
|
+
```python
|
|
61
|
+
from agentshield import agentshield_protect, SecurityBlockedException
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Or install locally in development mode:
|
|
65
|
+
```bash
|
|
66
|
+
pip install -e ./sdk
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## ⚡ 30-Second Quickstart
|
|
72
|
+
|
|
73
|
+
### 1. Protect Any Python Function with a 1-Line Decorator
|
|
74
|
+
```python
|
|
75
|
+
from agentshield import agentshield_protect, SecurityBlockedException
|
|
76
|
+
|
|
77
|
+
# Wrap any function that an AI agent has permission to execute
|
|
78
|
+
@agentshield_protect(agent_id="research_agent")
|
|
79
|
+
def read_project_report(file: str):
|
|
80
|
+
with open(file, "r") as f:
|
|
81
|
+
return f.read()
|
|
82
|
+
|
|
83
|
+
# Malicious or unauthorized calls raise SecurityBlockedException
|
|
84
|
+
try:
|
|
85
|
+
read_project_report(file="/etc/shadow")
|
|
86
|
+
except SecurityBlockedException as e:
|
|
87
|
+
print(f"Action Blocked: {e.primary_driver} (Risk Score: {e.risk_score}/100)")
|
|
88
|
+
print(f"Remediation: {e.recommendation}")
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 🤖 Framework Integrations
|
|
94
|
+
|
|
95
|
+
### 1. LangChain & LangGraph
|
|
96
|
+
```python
|
|
97
|
+
from langchain.tools import tool
|
|
98
|
+
from agentshield import LangChainTool
|
|
99
|
+
|
|
100
|
+
shield = LangChainTool(agent_id="support_agent")
|
|
101
|
+
|
|
102
|
+
@tool
|
|
103
|
+
def query_database(sql: str) -> str:
|
|
104
|
+
"""Executes a SQL query under AgentShield inspection."""
|
|
105
|
+
return shield.execute("database_query", {"query": sql})
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### 2. CrewAI
|
|
109
|
+
```python
|
|
110
|
+
from crewai.tools import BaseTool
|
|
111
|
+
from agentshield import CrewAITool
|
|
112
|
+
|
|
113
|
+
class SecureEmailTool(BaseTool):
|
|
114
|
+
name: str = "send_email"
|
|
115
|
+
description: str = "Dispatches customer emails with AgentShield inspection."
|
|
116
|
+
shield: CrewAITool = CrewAITool(agent_id="support_agent")
|
|
117
|
+
|
|
118
|
+
def _run(self, to: str, body: str) -> str:
|
|
119
|
+
return self.shield.run_protected("send_email", {"to": to, "body": body})
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### 3. OpenAI Function Calling & Assistants API
|
|
123
|
+
```python
|
|
124
|
+
from openai import OpenAI
|
|
125
|
+
from agentshield import AgentShieldOpenAIInterceptor
|
|
126
|
+
|
|
127
|
+
client = OpenAI()
|
|
128
|
+
interceptor = AgentShieldOpenAIInterceptor(agent_id="customer_copilot")
|
|
129
|
+
|
|
130
|
+
response = client.chat.completions.create(
|
|
131
|
+
model="gpt-4o",
|
|
132
|
+
messages=[{"role": "user", "content": "Dump the users table"}],
|
|
133
|
+
tools=tools
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# Intercept before execution
|
|
137
|
+
tool_outputs = interceptor.process_tool_calls(
|
|
138
|
+
response.choices[0].message.tool_calls,
|
|
139
|
+
user_context="Dump the users table"
|
|
140
|
+
)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## ⚙️ Configuration
|
|
146
|
+
|
|
147
|
+
Set the environment variable or configure programmatically:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
export AGENTSHIELD_API="http://localhost:8000"
|
|
151
|
+
export AGENTSHIELD_API_KEY="ag_live_your_api_key_here"
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Or in Python:
|
|
155
|
+
```python
|
|
156
|
+
import agentshield
|
|
157
|
+
|
|
158
|
+
agentshield.configure(
|
|
159
|
+
base_url="https://api.agentshield.your-company.com",
|
|
160
|
+
api_key="your-api-key"
|
|
161
|
+
)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## 🖥️ Command Line Interface (CLI)
|
|
167
|
+
|
|
168
|
+
Verify your gateway connection right from the terminal:
|
|
169
|
+
```bash
|
|
170
|
+
# Check connection
|
|
171
|
+
agentshield status
|
|
172
|
+
|
|
173
|
+
# List pending approvals in the human-in-the-loop queue
|
|
174
|
+
agentshield approvals
|
|
175
|
+
|
|
176
|
+
# Test a tool call
|
|
177
|
+
agentshield test --agent research_agent --tool read_file --args '{"file": "test.txt"}'
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## 📄 License
|
|
183
|
+
MIT License.
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# 🛡️ AgentShield Python Developer SDK (`agentshield`)
|
|
2
|
+
### Enterprise Security Middleware & Guardrails for Autonomous AI Agents
|
|
3
|
+
|
|
4
|
+
[](https://pypi.org/project/agentshield/)
|
|
5
|
+
[](https://www.python.org/downloads/)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
**AgentShield** is an inline action-authorization firewall for autonomous AI agents (LangChain, CrewAI, AutoGen, OpenAI Assistants). It evaluates planned tool calls across **6 analytical detection layers in under 6 milliseconds**, blocks prompt injection, prevents zero-day shell wipeouts, redacts confidential credentials in-flight, and manages human-in-the-loop approvals.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 📦 Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pip install eternity-agentshield
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
*In your Python code, import as:*
|
|
19
|
+
```python
|
|
20
|
+
from agentshield import agentshield_protect, SecurityBlockedException
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Or install locally in development mode:
|
|
24
|
+
```bash
|
|
25
|
+
pip install -e ./sdk
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## ⚡ 30-Second Quickstart
|
|
31
|
+
|
|
32
|
+
### 1. Protect Any Python Function with a 1-Line Decorator
|
|
33
|
+
```python
|
|
34
|
+
from agentshield import agentshield_protect, SecurityBlockedException
|
|
35
|
+
|
|
36
|
+
# Wrap any function that an AI agent has permission to execute
|
|
37
|
+
@agentshield_protect(agent_id="research_agent")
|
|
38
|
+
def read_project_report(file: str):
|
|
39
|
+
with open(file, "r") as f:
|
|
40
|
+
return f.read()
|
|
41
|
+
|
|
42
|
+
# Malicious or unauthorized calls raise SecurityBlockedException
|
|
43
|
+
try:
|
|
44
|
+
read_project_report(file="/etc/shadow")
|
|
45
|
+
except SecurityBlockedException as e:
|
|
46
|
+
print(f"Action Blocked: {e.primary_driver} (Risk Score: {e.risk_score}/100)")
|
|
47
|
+
print(f"Remediation: {e.recommendation}")
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 🤖 Framework Integrations
|
|
53
|
+
|
|
54
|
+
### 1. LangChain & LangGraph
|
|
55
|
+
```python
|
|
56
|
+
from langchain.tools import tool
|
|
57
|
+
from agentshield import LangChainTool
|
|
58
|
+
|
|
59
|
+
shield = LangChainTool(agent_id="support_agent")
|
|
60
|
+
|
|
61
|
+
@tool
|
|
62
|
+
def query_database(sql: str) -> str:
|
|
63
|
+
"""Executes a SQL query under AgentShield inspection."""
|
|
64
|
+
return shield.execute("database_query", {"query": sql})
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 2. CrewAI
|
|
68
|
+
```python
|
|
69
|
+
from crewai.tools import BaseTool
|
|
70
|
+
from agentshield import CrewAITool
|
|
71
|
+
|
|
72
|
+
class SecureEmailTool(BaseTool):
|
|
73
|
+
name: str = "send_email"
|
|
74
|
+
description: str = "Dispatches customer emails with AgentShield inspection."
|
|
75
|
+
shield: CrewAITool = CrewAITool(agent_id="support_agent")
|
|
76
|
+
|
|
77
|
+
def _run(self, to: str, body: str) -> str:
|
|
78
|
+
return self.shield.run_protected("send_email", {"to": to, "body": body})
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 3. OpenAI Function Calling & Assistants API
|
|
82
|
+
```python
|
|
83
|
+
from openai import OpenAI
|
|
84
|
+
from agentshield import AgentShieldOpenAIInterceptor
|
|
85
|
+
|
|
86
|
+
client = OpenAI()
|
|
87
|
+
interceptor = AgentShieldOpenAIInterceptor(agent_id="customer_copilot")
|
|
88
|
+
|
|
89
|
+
response = client.chat.completions.create(
|
|
90
|
+
model="gpt-4o",
|
|
91
|
+
messages=[{"role": "user", "content": "Dump the users table"}],
|
|
92
|
+
tools=tools
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
# Intercept before execution
|
|
96
|
+
tool_outputs = interceptor.process_tool_calls(
|
|
97
|
+
response.choices[0].message.tool_calls,
|
|
98
|
+
user_context="Dump the users table"
|
|
99
|
+
)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## ⚙️ Configuration
|
|
105
|
+
|
|
106
|
+
Set the environment variable or configure programmatically:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
export AGENTSHIELD_API="http://localhost:8000"
|
|
110
|
+
export AGENTSHIELD_API_KEY="ag_live_your_api_key_here"
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Or in Python:
|
|
114
|
+
```python
|
|
115
|
+
import agentshield
|
|
116
|
+
|
|
117
|
+
agentshield.configure(
|
|
118
|
+
base_url="https://api.agentshield.your-company.com",
|
|
119
|
+
api_key="your-api-key"
|
|
120
|
+
)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## 🖥️ Command Line Interface (CLI)
|
|
126
|
+
|
|
127
|
+
Verify your gateway connection right from the terminal:
|
|
128
|
+
```bash
|
|
129
|
+
# Check connection
|
|
130
|
+
agentshield status
|
|
131
|
+
|
|
132
|
+
# List pending approvals in the human-in-the-loop queue
|
|
133
|
+
agentshield approvals
|
|
134
|
+
|
|
135
|
+
# Test a tool call
|
|
136
|
+
agentshield test --agent research_agent --tool read_file --args '{"file": "test.txt"}'
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## 📄 License
|
|
142
|
+
MIT License.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""
|
|
2
|
+
================================================================================
|
|
3
|
+
🛡️ ETERNITY : AGENTSHIELD 2.0 — ENTERPRISE PYTHON SDK
|
|
4
|
+
================================================================================
|
|
5
|
+
Inline action-authorization firewall for autonomous AI agents.
|
|
6
|
+
Protects LangChain, CrewAI, AutoGen, and OpenAI function calling with sub-6ms latency.
|
|
7
|
+
================================================================================
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from .client import AgentShield, AgentShieldClient, get_default_client, configure
|
|
11
|
+
from .decorators import protect, agentshield_protect
|
|
12
|
+
from .exceptions import (
|
|
13
|
+
AgentShieldException,
|
|
14
|
+
SecurityBlockedException,
|
|
15
|
+
ApprovalPendingException,
|
|
16
|
+
ConnectionException,
|
|
17
|
+
)
|
|
18
|
+
from .integrations.langchain import AgentShieldTool as LangChainTool
|
|
19
|
+
from .integrations.crewai import AgentShieldCrewTool as CrewAITool
|
|
20
|
+
from .integrations.openai_interceptor import AgentShieldOpenAIInterceptor
|
|
21
|
+
|
|
22
|
+
__version__ = "2.0.0"
|
|
23
|
+
__all__ = [
|
|
24
|
+
"AgentShield",
|
|
25
|
+
"AgentShieldClient",
|
|
26
|
+
"get_default_client",
|
|
27
|
+
"configure",
|
|
28
|
+
"protect",
|
|
29
|
+
"agentshield_protect",
|
|
30
|
+
"SecurityBlockedException",
|
|
31
|
+
"ApprovalPendingException",
|
|
32
|
+
"AgentShieldException",
|
|
33
|
+
"ConnectionException",
|
|
34
|
+
"LangChainTool",
|
|
35
|
+
"CrewAITool",
|
|
36
|
+
"AgentShieldOpenAIInterceptor",
|
|
37
|
+
]
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AgentShield Command Line Interface (CLI).
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
if sys.platform == "win32":
|
|
10
|
+
try:
|
|
11
|
+
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
|
|
12
|
+
sys.stderr.reconfigure(encoding='utf-8', errors='replace')
|
|
13
|
+
except Exception:
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
if __package__:
|
|
17
|
+
from .client import get_default_client
|
|
18
|
+
else:
|
|
19
|
+
try:
|
|
20
|
+
from agentshield.client import get_default_client
|
|
21
|
+
except ImportError:
|
|
22
|
+
from client import get_default_client
|
|
23
|
+
|
|
24
|
+
def main():
|
|
25
|
+
parser = argparse.ArgumentParser(description="ETERNITY : AgentShield 2.0 CLI")
|
|
26
|
+
subparsers = parser.add_subparsers(dest="command", help="Available commands")
|
|
27
|
+
|
|
28
|
+
# Command: status
|
|
29
|
+
subparsers.add_parser("status", help="Check AgentShield Gateway connection health")
|
|
30
|
+
|
|
31
|
+
# Command: approvals
|
|
32
|
+
appr_parser = subparsers.add_parser("approvals", help="Inspect pending approvals")
|
|
33
|
+
appr_parser.add_argument("--resolve", type=int, help="Resolve approval by analysis ID")
|
|
34
|
+
appr_parser.add_argument("--action", choices=["APPROVE", "REJECT"], default="APPROVE", help="Action to perform")
|
|
35
|
+
|
|
36
|
+
# Command: test
|
|
37
|
+
test_parser = subparsers.add_parser("test", help="Test intercepting a tool call")
|
|
38
|
+
test_parser.add_argument("--agent", default="research_agent", help="Agent identifier")
|
|
39
|
+
test_parser.add_argument("--tool", default="read_file", help="Target tool name")
|
|
40
|
+
test_parser.add_argument("--args", default="{\"file\": \"test.txt\"}", help="JSON tool arguments")
|
|
41
|
+
|
|
42
|
+
args = parser.parse_args()
|
|
43
|
+
|
|
44
|
+
client = get_default_client()
|
|
45
|
+
|
|
46
|
+
if args.command == "status":
|
|
47
|
+
try:
|
|
48
|
+
health = client.check_health()
|
|
49
|
+
print(f"✅ Connected to AgentShield Gateway at {client.base_url}")
|
|
50
|
+
print(f" Status: {health.get('status')}")
|
|
51
|
+
except Exception as e:
|
|
52
|
+
print(f"❌ Connection failed: {e}")
|
|
53
|
+
sys.exit(1)
|
|
54
|
+
|
|
55
|
+
elif args.command == "approvals":
|
|
56
|
+
if args.resolve:
|
|
57
|
+
res = client.resolve_approval(args.resolve, action=args.action)
|
|
58
|
+
print(f"Resolved Approval #{args.resolve}: {res}")
|
|
59
|
+
else:
|
|
60
|
+
pending = client.get_pending_approvals()
|
|
61
|
+
print(f"📋 Active Pending Approvals ({len(pending)}):")
|
|
62
|
+
for p in pending:
|
|
63
|
+
print(f" • #{p.get('id')} [{p.get('agent_identifier')}] -> Tool: {p.get('tool_name')} (Risk: {p.get('risk_score')})")
|
|
64
|
+
|
|
65
|
+
elif args.command == "test":
|
|
66
|
+
try:
|
|
67
|
+
parsed_args = json.loads(args.args)
|
|
68
|
+
except Exception:
|
|
69
|
+
parsed_args = {"raw": args.args}
|
|
70
|
+
res = client.intercept(args.agent, args.tool, parsed_args)
|
|
71
|
+
print(f"🛡️ Evaluation Result:")
|
|
72
|
+
print(f" Decision: {res.get('decision')}")
|
|
73
|
+
print(f" Risk Score: {res.get('risk_score')} / 100")
|
|
74
|
+
print(f" Driver: {(res.get('explanation') or {}).get('primary_driver')}")
|
|
75
|
+
|
|
76
|
+
else:
|
|
77
|
+
parser.print_help()
|
|
78
|
+
|
|
79
|
+
if __name__ == "__main__":
|
|
80
|
+
main()
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Core AgentShield HTTP Client.
|
|
3
|
+
Handles request interception, cryptographic verification, and approval lifecycle.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import time
|
|
9
|
+
import urllib.error
|
|
10
|
+
import urllib.request
|
|
11
|
+
from typing import Any, Dict, List, Optional
|
|
12
|
+
from .exceptions import ConnectionException
|
|
13
|
+
|
|
14
|
+
class AgentShieldClient:
|
|
15
|
+
"""
|
|
16
|
+
Client for the ETERNITY : AgentShield 2.0 Security Gateway.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
base_url: Optional[str] = None,
|
|
22
|
+
api_key: Optional[str] = None,
|
|
23
|
+
timeout: float = 10.0
|
|
24
|
+
):
|
|
25
|
+
self.base_url = (
|
|
26
|
+
base_url
|
|
27
|
+
or os.environ.get("AGENTSHIELD_API")
|
|
28
|
+
or "http://localhost:8000"
|
|
29
|
+
).rstrip("/")
|
|
30
|
+
self.api_key = api_key or os.environ.get("AGENTSHIELD_API_KEY")
|
|
31
|
+
self.timeout = timeout
|
|
32
|
+
|
|
33
|
+
def _headers(self) -> Dict[str, str]:
|
|
34
|
+
headers = {
|
|
35
|
+
"Content-Type": "application/json",
|
|
36
|
+
"User-Agent": "AgentShield-Python-SDK/2.0.0"
|
|
37
|
+
}
|
|
38
|
+
if self.api_key:
|
|
39
|
+
headers["Authorization"] = f"Bearer {self.api_key}"
|
|
40
|
+
return headers
|
|
41
|
+
|
|
42
|
+
def check_health(self) -> Dict[str, Any]:
|
|
43
|
+
"""Verifies connection to the AgentShield Gateway."""
|
|
44
|
+
req = urllib.request.Request(f"{self.base_url}/health", headers=self._headers())
|
|
45
|
+
try:
|
|
46
|
+
with urllib.request.urlopen(req, timeout=self.timeout) as resp:
|
|
47
|
+
return json.loads(resp.read().decode("utf-8"))
|
|
48
|
+
except Exception as e:
|
|
49
|
+
raise ConnectionException(f"Cannot reach AgentShield at {self.base_url}: {e}")
|
|
50
|
+
|
|
51
|
+
def intercept(
|
|
52
|
+
self,
|
|
53
|
+
agent_id: str,
|
|
54
|
+
tool_name: str,
|
|
55
|
+
tool_args: Dict[str, Any],
|
|
56
|
+
context: Any = ""
|
|
57
|
+
) -> Dict[str, Any]:
|
|
58
|
+
"""
|
|
59
|
+
Intercepts and evaluates an autonomous agent's tool execution attempt.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
Dict containing:
|
|
63
|
+
- decision: 'ALLOWED' | 'BLOCKED' | 'PENDING'
|
|
64
|
+
- risk_score: float (0.0 to 100.0)
|
|
65
|
+
- risk_level: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL'
|
|
66
|
+
- execution_result: Sandboxed execution data (if ALLOWED)
|
|
67
|
+
- explanation: Explainable AI breakdown
|
|
68
|
+
- redacted_input: In-flight sanitized payload
|
|
69
|
+
- analysis_id: Unique audit trace identifier
|
|
70
|
+
"""
|
|
71
|
+
payload = {
|
|
72
|
+
"agent_id": agent_id,
|
|
73
|
+
"tool_name": tool_name,
|
|
74
|
+
"tool_args": tool_args,
|
|
75
|
+
"context": context if isinstance(context, str) else json.dumps(context)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
req = urllib.request.Request(
|
|
79
|
+
f"{self.base_url}/agent/request",
|
|
80
|
+
data=json.dumps(payload).encode("utf-8"),
|
|
81
|
+
headers=self._headers()
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
with urllib.request.urlopen(req, timeout=self.timeout) as resp:
|
|
86
|
+
return json.loads(resp.read().decode("utf-8"))
|
|
87
|
+
except urllib.error.HTTPError as e:
|
|
88
|
+
try:
|
|
89
|
+
body = json.loads(e.read().decode("utf-8"))
|
|
90
|
+
except Exception:
|
|
91
|
+
body = {"detail": str(e)}
|
|
92
|
+
return {
|
|
93
|
+
"status": "error",
|
|
94
|
+
"decision": "BLOCKED",
|
|
95
|
+
"risk_score": 100.0,
|
|
96
|
+
"risk_level": "CRITICAL",
|
|
97
|
+
"explanation": {
|
|
98
|
+
"primary_driver": "GATEWAY_HTTP_ERROR",
|
|
99
|
+
"reasons": [str(body.get("detail", body))]
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
except Exception as e:
|
|
103
|
+
raise ConnectionException(f"Failed to connect to AgentShield: {e}")
|
|
104
|
+
|
|
105
|
+
def get_pending_approvals(self) -> List[Dict[str, Any]]:
|
|
106
|
+
"""Retrieves list of active human-in-the-loop pending requests."""
|
|
107
|
+
req = urllib.request.Request(f"{self.base_url}/approvals/pending", headers=self._headers())
|
|
108
|
+
with urllib.request.urlopen(req, timeout=self.timeout) as resp:
|
|
109
|
+
return json.loads(resp.read().decode("utf-8"))
|
|
110
|
+
|
|
111
|
+
def resolve_approval(
|
|
112
|
+
self,
|
|
113
|
+
analysis_id: int,
|
|
114
|
+
action: str = "APPROVE",
|
|
115
|
+
reason: str = "Approved by operator"
|
|
116
|
+
) -> Dict[str, Any]:
|
|
117
|
+
"""Programmatically approves or rejects a pending request."""
|
|
118
|
+
payload = {"action": action.upper(), "reason": reason}
|
|
119
|
+
req = urllib.request.Request(
|
|
120
|
+
f"{self.base_url}/approvals/{analysis_id}/resolve",
|
|
121
|
+
data=json.dumps(payload).encode("utf-8"),
|
|
122
|
+
headers=self._headers()
|
|
123
|
+
)
|
|
124
|
+
with urllib.request.urlopen(req, timeout=self.timeout) as resp:
|
|
125
|
+
return json.loads(resp.read().decode("utf-8"))
|
|
126
|
+
|
|
127
|
+
def wait_for_approval(
|
|
128
|
+
self,
|
|
129
|
+
analysis_id: int,
|
|
130
|
+
timeout_seconds: int = 180,
|
|
131
|
+
poll_interval: float = 2.0
|
|
132
|
+
) -> Dict[str, Any]:
|
|
133
|
+
"""
|
|
134
|
+
Polls the gateway until a human operator authorizes or rejects the pending action.
|
|
135
|
+
"""
|
|
136
|
+
start = time.time()
|
|
137
|
+
while time.time() - start < timeout_seconds:
|
|
138
|
+
req = urllib.request.Request(f"{self.base_url}/dashboard/logs/{analysis_id}", headers=self._headers())
|
|
139
|
+
try:
|
|
140
|
+
with urllib.request.urlopen(req, timeout=self.timeout) as resp:
|
|
141
|
+
log = json.loads(resp.read().decode("utf-8"))
|
|
142
|
+
decision = log.get("decision")
|
|
143
|
+
if decision in ["APPROVED", "ALLOWED"]:
|
|
144
|
+
return {"approved": True, "execution_result": log.get("execution_result")}
|
|
145
|
+
elif decision in ["REJECTED", "BLOCKED", "EXPIRED"]:
|
|
146
|
+
return {"approved": False, "reason": f"Decision resolved as {decision}"}
|
|
147
|
+
except Exception:
|
|
148
|
+
pass
|
|
149
|
+
time.sleep(poll_interval)
|
|
150
|
+
return {"approved": False, "reason": f"Approval timed out after {timeout_seconds} seconds"}
|
|
151
|
+
|
|
152
|
+
# Global singleton client
|
|
153
|
+
_default_client: Optional[AgentShieldClient] = None
|
|
154
|
+
|
|
155
|
+
def get_default_client() -> AgentShieldClient:
|
|
156
|
+
global _default_client
|
|
157
|
+
if _default_client is None:
|
|
158
|
+
_default_client = AgentShieldClient()
|
|
159
|
+
return _default_client
|
|
160
|
+
|
|
161
|
+
def configure(base_url: Optional[str] = None, api_key: Optional[str] = None):
|
|
162
|
+
"""Configures the default AgentShield SDK client."""
|
|
163
|
+
global _default_client
|
|
164
|
+
_default_client = AgentShieldClient(base_url=base_url, api_key=api_key)
|
|
165
|
+
return _default_client
|
|
166
|
+
|
|
167
|
+
# Alias
|
|
168
|
+
AgentShield = AgentShieldClient
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Python Decorators for protecting tools and functions.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import functools
|
|
6
|
+
import inspect
|
|
7
|
+
from typing import Callable, Optional
|
|
8
|
+
from .client import get_default_client, AgentShieldClient
|
|
9
|
+
from .exceptions import SecurityBlockedException, ApprovalPendingException
|
|
10
|
+
|
|
11
|
+
def protect(
|
|
12
|
+
agent_id: str,
|
|
13
|
+
tool_name: Optional[str] = None,
|
|
14
|
+
on_blocked: str = "raise", # "raise" | "return_error"
|
|
15
|
+
client: Optional[AgentShieldClient] = None
|
|
16
|
+
):
|
|
17
|
+
"""
|
|
18
|
+
Python Function Decorator: Automatically intercepts execution with AgentShield.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
agent_id: Identifier of the autonomous agent executing the tool (e.g. 'support_agent')
|
|
22
|
+
tool_name: Optional tool name override (defaults to function name)
|
|
23
|
+
on_blocked: 'raise' (raises SecurityBlockedException) or 'return_error' (returns dict for LLM)
|
|
24
|
+
client: Optional custom AgentShieldClient instance
|
|
25
|
+
|
|
26
|
+
Example:
|
|
27
|
+
@protect(agent_id="research_agent")
|
|
28
|
+
def read_file(filepath: str):
|
|
29
|
+
...
|
|
30
|
+
"""
|
|
31
|
+
def decorator(func: Callable):
|
|
32
|
+
t_name = tool_name or func.__name__
|
|
33
|
+
cli = client or get_default_client()
|
|
34
|
+
|
|
35
|
+
@functools.wraps(func)
|
|
36
|
+
def wrapper(*args, **kwargs):
|
|
37
|
+
# 1. Bind arguments into structured dictionary
|
|
38
|
+
sig = inspect.signature(func)
|
|
39
|
+
bound = sig.bind_partial(*args, **kwargs)
|
|
40
|
+
bound.apply_defaults()
|
|
41
|
+
tool_args = bound.arguments.copy()
|
|
42
|
+
|
|
43
|
+
# Pop special internal context if passed
|
|
44
|
+
context = kwargs.pop("__context__", f"Function '{t_name}' invoked by agent '{agent_id}'.")
|
|
45
|
+
|
|
46
|
+
# 2. Intercept with AgentShield Security Gateway
|
|
47
|
+
evaluation = cli.intercept(
|
|
48
|
+
agent_id=agent_id,
|
|
49
|
+
tool_name=t_name,
|
|
50
|
+
tool_args=tool_args,
|
|
51
|
+
context=context
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
decision = evaluation.get("decision")
|
|
55
|
+
score = evaluation.get("risk_score", 0.0)
|
|
56
|
+
level = evaluation.get("risk_level", "LOW")
|
|
57
|
+
explanation = evaluation.get("explanation") or {}
|
|
58
|
+
|
|
59
|
+
# 3. Handle Decision
|
|
60
|
+
if decision == "ALLOWED":
|
|
61
|
+
# If AgentShield already executed in sandbox, return that; else run local func
|
|
62
|
+
if evaluation.get("execution_result") is not None:
|
|
63
|
+
return evaluation["execution_result"]
|
|
64
|
+
return func(*args, **kwargs)
|
|
65
|
+
|
|
66
|
+
elif decision == "PENDING":
|
|
67
|
+
analysis_id = evaluation.get("analysis_id", 0)
|
|
68
|
+
if on_blocked == "raise":
|
|
69
|
+
raise ApprovalPendingException(
|
|
70
|
+
message=f"Action held for human operator review in Approval Queue (Analysis #{analysis_id}).",
|
|
71
|
+
analysis_id=analysis_id,
|
|
72
|
+
risk_score=score
|
|
73
|
+
)
|
|
74
|
+
return {
|
|
75
|
+
"status": "pending_human_approval",
|
|
76
|
+
"analysis_id": analysis_id,
|
|
77
|
+
"message": "Action held for human operator review in AgentShield Approval Queue."
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
else:
|
|
81
|
+
# BLOCKED
|
|
82
|
+
driver = explanation.get("primary_driver", "POLICY_VIOLATION")
|
|
83
|
+
if on_blocked == "raise":
|
|
84
|
+
raise SecurityBlockedException(
|
|
85
|
+
message=f"[AgentShield Firewall] Tool '{t_name}' blocked. Reason: {driver}",
|
|
86
|
+
risk_score=score,
|
|
87
|
+
risk_level=level,
|
|
88
|
+
explanation=explanation,
|
|
89
|
+
analysis_id=evaluation.get("analysis_id")
|
|
90
|
+
)
|
|
91
|
+
return {
|
|
92
|
+
"error": "EXECUTION_DENIED_BY_SECURITY_POLICY",
|
|
93
|
+
"driver": driver,
|
|
94
|
+
"risk_score": score,
|
|
95
|
+
"recommendation": explanation.get("recommendation", "Action denied by least-privilege security policy.")
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return wrapper
|
|
99
|
+
return decorator
|
|
100
|
+
|
|
101
|
+
# Alias
|
|
102
|
+
agentshield_protect = protect
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AgentShield SDK Custom Exceptions.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Dict, Any, Optional
|
|
6
|
+
|
|
7
|
+
class AgentShieldException(Exception):
|
|
8
|
+
"""Base exception for all AgentShield errors."""
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
class ConnectionException(AgentShieldException):
|
|
12
|
+
"""Raised when the SDK cannot reach the AgentShield Gateway server."""
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
class SecurityBlockedException(AgentShieldException):
|
|
16
|
+
"""
|
|
17
|
+
Raised when an autonomous agent action is BLOCKED by AgentShield firewall.
|
|
18
|
+
Contains granular explainability telemetry, risk score, and primary driver.
|
|
19
|
+
"""
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
message: str,
|
|
23
|
+
risk_score: float,
|
|
24
|
+
risk_level: str,
|
|
25
|
+
explanation: Optional[Dict[str, Any]] = None,
|
|
26
|
+
analysis_id: Optional[int] = None
|
|
27
|
+
):
|
|
28
|
+
super().__init__(message)
|
|
29
|
+
self.risk_score = risk_score
|
|
30
|
+
self.risk_level = risk_level
|
|
31
|
+
self.explanation = explanation or {}
|
|
32
|
+
self.analysis_id = analysis_id
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def primary_driver(self) -> str:
|
|
36
|
+
return self.explanation.get("primary_driver", "SECURITY_POLICY_VIOLATION")
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def recommendation(self) -> str:
|
|
40
|
+
return self.explanation.get("recommendation", "Review agent permission policies.")
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def reasons(self) -> list:
|
|
44
|
+
return self.explanation.get("reasons", [])
|
|
45
|
+
|
|
46
|
+
def to_llm_response(self) -> Dict[str, Any]:
|
|
47
|
+
"""Formats safe feedback to return directly into the LLM conversation context."""
|
|
48
|
+
return {
|
|
49
|
+
"error": "EXECUTION_DENIED_BY_SECURITY_POLICY",
|
|
50
|
+
"driver": self.primary_driver,
|
|
51
|
+
"message": f"Action blocked: {self.args[0]}",
|
|
52
|
+
"risk_score": self.risk_score
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
class ApprovalPendingException(AgentShieldException):
|
|
56
|
+
"""
|
|
57
|
+
Raised when an action requires human review and is held in the Approval Queue.
|
|
58
|
+
"""
|
|
59
|
+
def __init__(self, message: str, analysis_id: int, risk_score: float):
|
|
60
|
+
super().__init__(message)
|
|
61
|
+
self.analysis_id = analysis_id
|
|
62
|
+
self.risk_score = risk_score
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AgentShield Integrations for external AI agent frameworks.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .langchain import AgentShieldTool
|
|
6
|
+
from .crewai import AgentShieldCrewTool
|
|
7
|
+
from .openai_interceptor import AgentShieldOpenAIInterceptor
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"AgentShieldTool",
|
|
11
|
+
"AgentShieldCrewTool",
|
|
12
|
+
"AgentShieldOpenAIInterceptor"
|
|
13
|
+
]
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CrewAI BaseTool Adapter for AgentShield.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict, Optional
|
|
6
|
+
from ..client import get_default_client, AgentShieldClient
|
|
7
|
+
|
|
8
|
+
class AgentShieldCrewTool:
|
|
9
|
+
"""
|
|
10
|
+
CrewAI Tool Interceptor.
|
|
11
|
+
Can be used inside any CrewAI BaseTool._run() method.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
agent_id: str,
|
|
17
|
+
client: Optional[AgentShieldClient] = None
|
|
18
|
+
):
|
|
19
|
+
self.agent_id = agent_id
|
|
20
|
+
self.client = client or get_default_client()
|
|
21
|
+
|
|
22
|
+
def run_protected(
|
|
23
|
+
self,
|
|
24
|
+
tool_name: str,
|
|
25
|
+
tool_args: Dict[str, Any],
|
|
26
|
+
context: str = ""
|
|
27
|
+
) -> str:
|
|
28
|
+
res = self.client.intercept(
|
|
29
|
+
agent_id=self.agent_id,
|
|
30
|
+
tool_name=tool_name,
|
|
31
|
+
tool_args=tool_args,
|
|
32
|
+
context=context
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
decision = res.get("decision")
|
|
36
|
+
if decision == "ALLOWED":
|
|
37
|
+
return str(res.get("execution_result"))
|
|
38
|
+
elif decision == "PENDING":
|
|
39
|
+
return f"HELD FOR HUMAN REVIEW: Approval ID #{res.get('analysis_id')}"
|
|
40
|
+
else:
|
|
41
|
+
driver = (res.get("explanation") or {}).get("primary_driver", "SECURITY_POLICY_VIOLATION")
|
|
42
|
+
return f"BLOCKED BY AGENTSHIELD FIREWALL: {driver}"
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""
|
|
2
|
+
LangChain & LangGraph Tool Adapter for AgentShield.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict, Optional
|
|
6
|
+
from ..client import get_default_client, AgentShieldClient
|
|
7
|
+
|
|
8
|
+
class AgentShieldTool:
|
|
9
|
+
"""
|
|
10
|
+
Drop-in tool adapter to secure any LangChain or LangGraph agent tool call.
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
shield = AgentShieldTool(agent_id="support_agent")
|
|
14
|
+
|
|
15
|
+
# In your LangChain tool function:
|
|
16
|
+
@tool
|
|
17
|
+
def query_database(query: str) -> str:
|
|
18
|
+
return shield.execute("database_query", {"query": query})
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
agent_id: str,
|
|
24
|
+
client: Optional[AgentShieldClient] = None,
|
|
25
|
+
return_direct_error: bool = True
|
|
26
|
+
):
|
|
27
|
+
self.agent_id = agent_id
|
|
28
|
+
self.client = client or get_default_client()
|
|
29
|
+
self.return_direct_error = return_direct_error
|
|
30
|
+
|
|
31
|
+
def execute(
|
|
32
|
+
self,
|
|
33
|
+
tool_name: str,
|
|
34
|
+
tool_args: Dict[str, Any],
|
|
35
|
+
context: str = ""
|
|
36
|
+
) -> Any:
|
|
37
|
+
"""
|
|
38
|
+
Intercepts tool call, evaluates security rules, and returns sanitized result.
|
|
39
|
+
"""
|
|
40
|
+
eval_result = self.client.intercept(
|
|
41
|
+
agent_id=self.agent_id,
|
|
42
|
+
tool_name=tool_name,
|
|
43
|
+
tool_args=tool_args,
|
|
44
|
+
context=context
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
decision = eval_result.get("decision")
|
|
48
|
+
|
|
49
|
+
if decision == "ALLOWED":
|
|
50
|
+
return eval_result.get("execution_result", {"status": "success", "executed": True})
|
|
51
|
+
|
|
52
|
+
elif decision == "PENDING":
|
|
53
|
+
analysis_id = eval_result.get("analysis_id")
|
|
54
|
+
return (
|
|
55
|
+
f"Action Paused: This sensitive operation (Risk Score: {eval_result.get('risk_score')}) "
|
|
56
|
+
f"requires human supervisor approval (Approval ID: #{analysis_id})."
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
else:
|
|
60
|
+
# BLOCKED
|
|
61
|
+
driver = (eval_result.get("explanation") or {}).get("primary_driver", "POLICY_VIOLATION")
|
|
62
|
+
reasons = (eval_result.get("explanation") or {}).get("reasons", [])
|
|
63
|
+
reason_str = "; ".join(reasons) if reasons else "Violated security least-privilege boundary"
|
|
64
|
+
|
|
65
|
+
if not self.return_direct_error:
|
|
66
|
+
raise RuntimeError(f"AgentShield Blocked [{driver}]: {reason_str}")
|
|
67
|
+
|
|
68
|
+
# Return graceful error string so the LLM continues safely
|
|
69
|
+
return f"Security Policy Refusal: Cannot execute '{tool_name}' ({driver}: {reason_str})."
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""
|
|
2
|
+
OpenAI Function Calling & Assistants API Interceptor.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from typing import Any, Dict, List, Optional
|
|
7
|
+
from ..client import get_default_client, AgentShieldClient
|
|
8
|
+
|
|
9
|
+
class AgentShieldOpenAIInterceptor:
|
|
10
|
+
"""
|
|
11
|
+
Inspects tool_calls generated by OpenAI Chat Completions (GPT-4o, etc.)
|
|
12
|
+
before any actual local execution occurs.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
agent_id: str,
|
|
18
|
+
client: Optional[AgentShieldClient] = None
|
|
19
|
+
):
|
|
20
|
+
self.agent_id = agent_id
|
|
21
|
+
self.client = client or get_default_client()
|
|
22
|
+
|
|
23
|
+
def process_tool_calls(
|
|
24
|
+
self,
|
|
25
|
+
tool_calls: List[Any],
|
|
26
|
+
user_context: str = ""
|
|
27
|
+
) -> List[Dict[str, Any]]:
|
|
28
|
+
"""
|
|
29
|
+
Takes raw tool_calls from OpenAI response, evaluates each through AgentShield,
|
|
30
|
+
and returns formatted tool_outputs ready to submit back to OpenAI.
|
|
31
|
+
"""
|
|
32
|
+
tool_outputs = []
|
|
33
|
+
|
|
34
|
+
for call in tool_calls:
|
|
35
|
+
call_id = getattr(call, "id", None) or call.get("id")
|
|
36
|
+
func = getattr(call, "function", None) or call.get("function", {})
|
|
37
|
+
func_name = getattr(func, "name", None) or func.get("name")
|
|
38
|
+
raw_args = getattr(func, "arguments", None) or func.get("arguments", "{}")
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
args = json.loads(raw_args) if isinstance(raw_args, str) else raw_args
|
|
42
|
+
except Exception:
|
|
43
|
+
args = {"raw_args": raw_args}
|
|
44
|
+
|
|
45
|
+
# Intercept with AgentShield
|
|
46
|
+
eval_result = self.client.intercept(
|
|
47
|
+
agent_id=self.agent_id,
|
|
48
|
+
tool_name=func_name,
|
|
49
|
+
tool_args=args,
|
|
50
|
+
context=user_context
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
decision = eval_result.get("decision")
|
|
54
|
+
|
|
55
|
+
if decision == "ALLOWED":
|
|
56
|
+
output = json.dumps(eval_result.get("execution_result", {"status": "success"}))
|
|
57
|
+
elif decision == "PENDING":
|
|
58
|
+
output = json.dumps({
|
|
59
|
+
"status": "pending_human_approval",
|
|
60
|
+
"analysis_id": eval_result.get("analysis_id"),
|
|
61
|
+
"message": "Action held for human operator review in AgentShield Approval Queue."
|
|
62
|
+
})
|
|
63
|
+
else:
|
|
64
|
+
driver = (eval_result.get("explanation") or {}).get("primary_driver", "POLICY_VIOLATION")
|
|
65
|
+
output = json.dumps({
|
|
66
|
+
"error": "SECURITY_POLICY_VIOLATION",
|
|
67
|
+
"driver": driver,
|
|
68
|
+
"risk_score": eval_result.get("risk_score")
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
tool_outputs.append({
|
|
72
|
+
"tool_call_id": call_id,
|
|
73
|
+
"output": output
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
return tool_outputs
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: eternity-agentshield
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: ETERNITY : AgentShield 2.0 — Enterprise Security Middleware & Guardrails SDK for Autonomous AI Agents
|
|
5
|
+
Home-page: https://github.com/yashnishad-0923/agentshield
|
|
6
|
+
Author: ETERNITY Security Team
|
|
7
|
+
Author-email: ETERNITY Security Team <security@agentshield.ai>
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Homepage, https://github.com/agentshield/agentshield
|
|
10
|
+
Project-URL: Documentation, https://docs.agentshield.ai
|
|
11
|
+
Project-URL: Repository, https://github.com/agentshield/agentshield
|
|
12
|
+
Keywords: ai-agent,security,guardrails,langchain,crewai,autogen,openai,prompt-injection,cybersecurity,llm-firewall
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Information Technology
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Topic :: Security
|
|
25
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
26
|
+
Requires-Python: >=3.8
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
Provides-Extra: langchain
|
|
29
|
+
Requires-Dist: langchain-core>=0.1.0; extra == "langchain"
|
|
30
|
+
Provides-Extra: crewai
|
|
31
|
+
Requires-Dist: crewai>=0.1.0; extra == "crewai"
|
|
32
|
+
Provides-Extra: openai
|
|
33
|
+
Requires-Dist: openai>=1.0.0; extra == "openai"
|
|
34
|
+
Provides-Extra: all
|
|
35
|
+
Requires-Dist: langchain-core>=0.1.0; extra == "all"
|
|
36
|
+
Requires-Dist: crewai>=0.1.0; extra == "all"
|
|
37
|
+
Requires-Dist: openai>=1.0.0; extra == "all"
|
|
38
|
+
Dynamic: author
|
|
39
|
+
Dynamic: home-page
|
|
40
|
+
Dynamic: requires-python
|
|
41
|
+
|
|
42
|
+
# 🛡️ AgentShield Python Developer SDK (`agentshield`)
|
|
43
|
+
### Enterprise Security Middleware & Guardrails for Autonomous AI Agents
|
|
44
|
+
|
|
45
|
+
[](https://pypi.org/project/agentshield/)
|
|
46
|
+
[](https://www.python.org/downloads/)
|
|
47
|
+
[](LICENSE)
|
|
48
|
+
|
|
49
|
+
**AgentShield** is an inline action-authorization firewall for autonomous AI agents (LangChain, CrewAI, AutoGen, OpenAI Assistants). It evaluates planned tool calls across **6 analytical detection layers in under 6 milliseconds**, blocks prompt injection, prevents zero-day shell wipeouts, redacts confidential credentials in-flight, and manages human-in-the-loop approvals.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## 📦 Installation
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pip install eternity-agentshield
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
*In your Python code, import as:*
|
|
60
|
+
```python
|
|
61
|
+
from agentshield import agentshield_protect, SecurityBlockedException
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Or install locally in development mode:
|
|
65
|
+
```bash
|
|
66
|
+
pip install -e ./sdk
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## ⚡ 30-Second Quickstart
|
|
72
|
+
|
|
73
|
+
### 1. Protect Any Python Function with a 1-Line Decorator
|
|
74
|
+
```python
|
|
75
|
+
from agentshield import agentshield_protect, SecurityBlockedException
|
|
76
|
+
|
|
77
|
+
# Wrap any function that an AI agent has permission to execute
|
|
78
|
+
@agentshield_protect(agent_id="research_agent")
|
|
79
|
+
def read_project_report(file: str):
|
|
80
|
+
with open(file, "r") as f:
|
|
81
|
+
return f.read()
|
|
82
|
+
|
|
83
|
+
# Malicious or unauthorized calls raise SecurityBlockedException
|
|
84
|
+
try:
|
|
85
|
+
read_project_report(file="/etc/shadow")
|
|
86
|
+
except SecurityBlockedException as e:
|
|
87
|
+
print(f"Action Blocked: {e.primary_driver} (Risk Score: {e.risk_score}/100)")
|
|
88
|
+
print(f"Remediation: {e.recommendation}")
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 🤖 Framework Integrations
|
|
94
|
+
|
|
95
|
+
### 1. LangChain & LangGraph
|
|
96
|
+
```python
|
|
97
|
+
from langchain.tools import tool
|
|
98
|
+
from agentshield import LangChainTool
|
|
99
|
+
|
|
100
|
+
shield = LangChainTool(agent_id="support_agent")
|
|
101
|
+
|
|
102
|
+
@tool
|
|
103
|
+
def query_database(sql: str) -> str:
|
|
104
|
+
"""Executes a SQL query under AgentShield inspection."""
|
|
105
|
+
return shield.execute("database_query", {"query": sql})
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### 2. CrewAI
|
|
109
|
+
```python
|
|
110
|
+
from crewai.tools import BaseTool
|
|
111
|
+
from agentshield import CrewAITool
|
|
112
|
+
|
|
113
|
+
class SecureEmailTool(BaseTool):
|
|
114
|
+
name: str = "send_email"
|
|
115
|
+
description: str = "Dispatches customer emails with AgentShield inspection."
|
|
116
|
+
shield: CrewAITool = CrewAITool(agent_id="support_agent")
|
|
117
|
+
|
|
118
|
+
def _run(self, to: str, body: str) -> str:
|
|
119
|
+
return self.shield.run_protected("send_email", {"to": to, "body": body})
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### 3. OpenAI Function Calling & Assistants API
|
|
123
|
+
```python
|
|
124
|
+
from openai import OpenAI
|
|
125
|
+
from agentshield import AgentShieldOpenAIInterceptor
|
|
126
|
+
|
|
127
|
+
client = OpenAI()
|
|
128
|
+
interceptor = AgentShieldOpenAIInterceptor(agent_id="customer_copilot")
|
|
129
|
+
|
|
130
|
+
response = client.chat.completions.create(
|
|
131
|
+
model="gpt-4o",
|
|
132
|
+
messages=[{"role": "user", "content": "Dump the users table"}],
|
|
133
|
+
tools=tools
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# Intercept before execution
|
|
137
|
+
tool_outputs = interceptor.process_tool_calls(
|
|
138
|
+
response.choices[0].message.tool_calls,
|
|
139
|
+
user_context="Dump the users table"
|
|
140
|
+
)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## ⚙️ Configuration
|
|
146
|
+
|
|
147
|
+
Set the environment variable or configure programmatically:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
export AGENTSHIELD_API="http://localhost:8000"
|
|
151
|
+
export AGENTSHIELD_API_KEY="ag_live_your_api_key_here"
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Or in Python:
|
|
155
|
+
```python
|
|
156
|
+
import agentshield
|
|
157
|
+
|
|
158
|
+
agentshield.configure(
|
|
159
|
+
base_url="https://api.agentshield.your-company.com",
|
|
160
|
+
api_key="your-api-key"
|
|
161
|
+
)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## 🖥️ Command Line Interface (CLI)
|
|
167
|
+
|
|
168
|
+
Verify your gateway connection right from the terminal:
|
|
169
|
+
```bash
|
|
170
|
+
# Check connection
|
|
171
|
+
agentshield status
|
|
172
|
+
|
|
173
|
+
# List pending approvals in the human-in-the-loop queue
|
|
174
|
+
agentshield approvals
|
|
175
|
+
|
|
176
|
+
# Test a tool call
|
|
177
|
+
agentshield test --agent research_agent --tool read_file --args '{"file": "test.txt"}'
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## 📄 License
|
|
183
|
+
MIT License.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
setup.py
|
|
4
|
+
agentshield/__init__.py
|
|
5
|
+
agentshield/cli.py
|
|
6
|
+
agentshield/client.py
|
|
7
|
+
agentshield/decorators.py
|
|
8
|
+
agentshield/exceptions.py
|
|
9
|
+
agentshield/integrations/__init__.py
|
|
10
|
+
agentshield/integrations/crewai.py
|
|
11
|
+
agentshield/integrations/langchain.py
|
|
12
|
+
agentshield/integrations/openai_interceptor.py
|
|
13
|
+
eternity_agentshield.egg-info/PKG-INFO
|
|
14
|
+
eternity_agentshield.egg-info/SOURCES.txt
|
|
15
|
+
eternity_agentshield.egg-info/dependency_links.txt
|
|
16
|
+
eternity_agentshield.egg-info/entry_points.txt
|
|
17
|
+
eternity_agentshield.egg-info/requires.txt
|
|
18
|
+
eternity_agentshield.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
agentshield
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "eternity-agentshield"
|
|
7
|
+
version = "2.0.0"
|
|
8
|
+
description = "ETERNITY : AgentShield 2.0 — Enterprise Security Middleware & Guardrails SDK for Autonomous AI Agents"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
authors = [
|
|
11
|
+
{ name = "ETERNITY Security Team", email = "security@agentshield.ai" }
|
|
12
|
+
]
|
|
13
|
+
license = { text = "MIT" }
|
|
14
|
+
requires-python = ">=3.8"
|
|
15
|
+
keywords = [
|
|
16
|
+
"ai-agent",
|
|
17
|
+
"security",
|
|
18
|
+
"guardrails",
|
|
19
|
+
"langchain",
|
|
20
|
+
"crewai",
|
|
21
|
+
"autogen",
|
|
22
|
+
"openai",
|
|
23
|
+
"prompt-injection",
|
|
24
|
+
"cybersecurity",
|
|
25
|
+
"llm-firewall"
|
|
26
|
+
]
|
|
27
|
+
classifiers = [
|
|
28
|
+
"Development Status :: 5 - Production/Stable",
|
|
29
|
+
"Intended Audience :: Developers",
|
|
30
|
+
"Intended Audience :: Information Technology",
|
|
31
|
+
"License :: OSI Approved :: MIT License",
|
|
32
|
+
"Programming Language :: Python :: 3",
|
|
33
|
+
"Programming Language :: Python :: 3.8",
|
|
34
|
+
"Programming Language :: Python :: 3.9",
|
|
35
|
+
"Programming Language :: Python :: 3.10",
|
|
36
|
+
"Programming Language :: Python :: 3.11",
|
|
37
|
+
"Programming Language :: Python :: 3.12",
|
|
38
|
+
"Programming Language :: Python :: 3.13",
|
|
39
|
+
"Topic :: Security",
|
|
40
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence"
|
|
41
|
+
]
|
|
42
|
+
dependencies = []
|
|
43
|
+
|
|
44
|
+
[project.optional-dependencies]
|
|
45
|
+
langchain = ["langchain-core>=0.1.0"]
|
|
46
|
+
crewai = ["crewai>=0.1.0"]
|
|
47
|
+
openai = ["openai>=1.0.0"]
|
|
48
|
+
all = [
|
|
49
|
+
"langchain-core>=0.1.0",
|
|
50
|
+
"crewai>=0.1.0",
|
|
51
|
+
"openai>=1.0.0"
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
[project.scripts]
|
|
55
|
+
agentshield = "agentshield.cli:main"
|
|
56
|
+
|
|
57
|
+
[project.urls]
|
|
58
|
+
Homepage = "https://github.com/agentshield/agentshield"
|
|
59
|
+
Documentation = "https://docs.agentshield.ai"
|
|
60
|
+
Repository = "https://github.com/agentshield/agentshield"
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
from setuptools import setup, find_packages
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
readme_path = os.path.join(os.path.dirname(__file__), "README.md")
|
|
6
|
+
long_description = ""
|
|
7
|
+
if os.path.exists(readme_path):
|
|
8
|
+
with open(readme_path, encoding="utf-8") as f:
|
|
9
|
+
long_description = f.read()
|
|
10
|
+
|
|
11
|
+
setup(
|
|
12
|
+
name="eternity-agentshield",
|
|
13
|
+
version="2.0.0",
|
|
14
|
+
description="ETERNITY : AgentShield 2.0 — Enterprise Security Middleware & Guardrails SDK for Autonomous AI Agents",
|
|
15
|
+
long_description=long_description,
|
|
16
|
+
long_description_content_type="text/markdown",
|
|
17
|
+
author="ETERNITY Security Team",
|
|
18
|
+
author_email="security@agentshield.ai",
|
|
19
|
+
url="https://github.com/yashnishad-0923/agentshield",
|
|
20
|
+
project_urls={
|
|
21
|
+
"Documentation": "https://github.com/yashnishad-0923/agentshield#readme",
|
|
22
|
+
"Source": "https://github.com/yashnishad-0923/agentshield",
|
|
23
|
+
"Tracker": "https://github.com/yashnishad-0923/agentshield/issues",
|
|
24
|
+
},
|
|
25
|
+
license="MIT",
|
|
26
|
+
classifiers=[
|
|
27
|
+
"Development Status :: 5 - Production/Stable",
|
|
28
|
+
"Intended Audience :: Developers",
|
|
29
|
+
"License :: OSI Approved :: MIT License",
|
|
30
|
+
"Programming Language :: Python :: 3",
|
|
31
|
+
"Programming Language :: Python :: 3.8",
|
|
32
|
+
"Programming Language :: Python :: 3.9",
|
|
33
|
+
"Programming Language :: Python :: 3.10",
|
|
34
|
+
"Programming Language :: Python :: 3.11",
|
|
35
|
+
"Programming Language :: Python :: 3.12",
|
|
36
|
+
"Topic :: Security",
|
|
37
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
38
|
+
],
|
|
39
|
+
packages=find_packages(),
|
|
40
|
+
python_requires=">=3.8",
|
|
41
|
+
install_requires=[],
|
|
42
|
+
entry_points={
|
|
43
|
+
"console_scripts": [
|
|
44
|
+
"agentshield=agentshield.cli:main",
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
)
|