agentsentinel-cli 0.7.5__tar.gz → 0.7.6__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.
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/PKG-INFO +1 -1
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/a2a_rules.py +0 -1
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/mcp_rules.py +42 -37
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/rules.py +10 -20
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/pyproject.toml +1 -1
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/.gitignore +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/DOCUMENTATION.md +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/LICENSE +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/README.md +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/__init__.py +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/a2a_report.py +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/a2a_scanner.py +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/agent_mode.py +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/agent_mode_report.py +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/ai_probe.py +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/attacks/__init__.py +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/attacks/library.py +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/cli.py +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/discover.py +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/discover_report.py +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/fingerprint.py +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/frameworks.py +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/inspect.py +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/inspect_report.py +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/mcp_client.py +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/mcp_report.py +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/probe.py +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/probe_report.py +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/report.py +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/scanner.py +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/secrets.py +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/secrets_report.py +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/secrets_rules.py +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/supply_chain_ai.py +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/supply_chain_report.py +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/supply_chain_rules.py +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/suppress.py +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/target.py +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/tmp/note.md +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/tmp/test-mcp-agent/README.md +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/tmp/test-mcp-agent/langchain_agent.py +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/tmp/test-mcp-agent/mcp_server.py +0 -0
- {agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/tmp/test-mcp-agent/requirements.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentsentinel-cli
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.6
|
|
4
4
|
Summary: Agentic security CLI — AI analyst with memory, supply chain audit, MCP audit, red-team probing, and agent discovery
|
|
5
5
|
Project-URL: Homepage, https://github.com/jaydenaung/agentsentinel-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/jaydenaung/agentsentinel-cli
|
|
@@ -114,15 +114,26 @@ def _rule_code_execution(ctx: McpContext) -> McpFinding | None:
|
|
|
114
114
|
|
|
115
115
|
|
|
116
116
|
def _rule_unbounded_input(ctx: McpContext) -> McpFinding | None:
|
|
117
|
-
"""HIGH:
|
|
117
|
+
"""HIGH: dangerous string parameters have no input constraints. (OWASP LLM01)
|
|
118
|
+
|
|
119
|
+
Only flags parameters whose names suggest they feed dangerous operations —
|
|
120
|
+
shell commands, SQL queries, file paths, URLs, code. Generic string fields
|
|
121
|
+
(name, title, message, body) are not flagged.
|
|
122
|
+
"""
|
|
123
|
+
_DANGEROUS_PARAMS = frozenset({
|
|
124
|
+
"command", "cmd", "shell", "shell_command",
|
|
125
|
+
"query", "sql", "sql_query", "expression",
|
|
126
|
+
"path", "file_path", "filepath", "filename", "directory", "dir",
|
|
127
|
+
"url", "uri", "endpoint", "webhook", "target",
|
|
128
|
+
"code", "script", "template", "prompt",
|
|
129
|
+
})
|
|
130
|
+
|
|
118
131
|
unvalidated: list[str] = []
|
|
119
132
|
for tool in ctx.server.tools:
|
|
120
|
-
|
|
121
|
-
props = schema.get("properties", {})
|
|
122
|
-
if not props and schema.get("type") == "object":
|
|
123
|
-
unvalidated.append(f"{tool.name} (no schema)")
|
|
124
|
-
continue
|
|
133
|
+
props = tool.input_schema.get("properties", {})
|
|
125
134
|
for prop_name, prop_def in props.items():
|
|
135
|
+
if prop_name.lower() not in _DANGEROUS_PARAMS:
|
|
136
|
+
continue
|
|
126
137
|
if (
|
|
127
138
|
prop_def.get("type") == "string"
|
|
128
139
|
and "maxLength" not in prop_def
|
|
@@ -130,7 +141,6 @@ def _rule_unbounded_input(ctx: McpContext) -> McpFinding | None:
|
|
|
130
141
|
and "pattern" not in prop_def
|
|
131
142
|
):
|
|
132
143
|
unvalidated.append(f"{tool.name}.{prop_name}")
|
|
133
|
-
break
|
|
134
144
|
|
|
135
145
|
if unvalidated:
|
|
136
146
|
sample = unvalidated[:5]
|
|
@@ -139,24 +149,29 @@ def _rule_unbounded_input(ctx: McpContext) -> McpFinding | None:
|
|
|
139
149
|
severity="HIGH",
|
|
140
150
|
rule_id="UNBOUNDED_INPUT",
|
|
141
151
|
message=(
|
|
142
|
-
"
|
|
143
|
-
"
|
|
152
|
+
"Dangerous parameters (command, path, query, url, code) accept unconstrained "
|
|
153
|
+
"string input. No maxLength, enum, or pattern — injection payloads pass through directly."
|
|
144
154
|
),
|
|
145
|
-
detail=f"Unconstrained inputs: {', '.join(sample)}{suffix}",
|
|
155
|
+
detail=f"Unconstrained dangerous inputs: {', '.join(sample)}{suffix}",
|
|
146
156
|
)
|
|
147
157
|
return None
|
|
148
158
|
|
|
149
159
|
|
|
150
160
|
def _rule_tool_sprawl(ctx: McpContext) -> McpFinding | None:
|
|
151
|
-
"""MEDIUM:
|
|
161
|
+
"""MEDIUM: high tool count across many categories increases blast radius. (OWASP LLM06)
|
|
162
|
+
|
|
163
|
+
Requires BOTH high count AND diverse categories. A server with 14 file-system
|
|
164
|
+
tools is a focused file manager. A server with 12 tools spanning code execution,
|
|
165
|
+
email, database, and web is a broad attack surface.
|
|
166
|
+
"""
|
|
152
167
|
categories = {t.category for t in ctx.server.tools} - {"other"}
|
|
153
|
-
if len(ctx.server.tools) > 10
|
|
168
|
+
if len(ctx.server.tools) > 10 and len(categories) >= 5:
|
|
154
169
|
return McpFinding(
|
|
155
170
|
severity="MEDIUM",
|
|
156
171
|
rule_id="TOOL_SPRAWL",
|
|
157
172
|
message=(
|
|
158
|
-
f"Server exposes {len(ctx.server.tools)} tools across {len(categories)}
|
|
159
|
-
"
|
|
173
|
+
f"Server exposes {len(ctx.server.tools)} tools across {len(categories)} "
|
|
174
|
+
"distinct categories. High cross-category diversity increases blast radius."
|
|
160
175
|
),
|
|
161
176
|
detail=f"Categories: {', '.join(sorted(categories))}",
|
|
162
177
|
)
|
|
@@ -164,33 +179,25 @@ def _rule_tool_sprawl(ctx: McpContext) -> McpFinding | None:
|
|
|
164
179
|
|
|
165
180
|
|
|
166
181
|
def _rule_vague_descriptions(ctx: McpContext) -> McpFinding | None:
|
|
167
|
-
"""MEDIUM:
|
|
168
|
-
|
|
182
|
+
"""MEDIUM: missing or single-word tool descriptions expand prompt injection surface. (OWASP LLM01)
|
|
183
|
+
|
|
184
|
+
Flags descriptions with fewer than 3 words — empty, one-word, or two-word
|
|
185
|
+
descriptions give the LLM no context about what the tool does or what it
|
|
186
|
+
should NOT do.
|
|
187
|
+
"""
|
|
188
|
+
vague = [
|
|
189
|
+
t.name for t in ctx.server.tools
|
|
190
|
+
if len(t.description.strip().split()) < 3
|
|
191
|
+
]
|
|
169
192
|
if len(vague) >= 2:
|
|
170
193
|
return McpFinding(
|
|
171
194
|
severity="MEDIUM",
|
|
172
195
|
rule_id="VAGUE_TOOL_DESCRIPTIONS",
|
|
173
196
|
message=(
|
|
174
|
-
"Multiple tools have
|
|
175
|
-
"
|
|
176
|
-
),
|
|
177
|
-
detail=f"Thin descriptions on: {', '.join(vague[:5])}{'…' if len(vague) > 5 else ''}",
|
|
178
|
-
)
|
|
179
|
-
return None
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
def _rule_missing_rate_limit(ctx: McpContext) -> McpFinding | None:
|
|
183
|
-
"""LOW: MCP protocol has no built-in rate limiting. (OWASP LLM06)"""
|
|
184
|
-
dangerous = [t.name for t in ctx.server.tools if t.is_dangerous]
|
|
185
|
-
if dangerous:
|
|
186
|
-
return McpFinding(
|
|
187
|
-
severity="LOW",
|
|
188
|
-
rule_id="MISSING_RATE_LIMIT",
|
|
189
|
-
message=(
|
|
190
|
-
"Dangerous tools detected. MCP has no built-in rate limiting — "
|
|
191
|
-
"ensure the server layer enforces per-client call limits."
|
|
197
|
+
"Multiple tools have absent or near-absent descriptions (fewer than 3 words). "
|
|
198
|
+
"Without clear descriptions the LLM cannot reason about safe tool use."
|
|
192
199
|
),
|
|
193
|
-
detail=f"
|
|
200
|
+
detail=f"Absent descriptions: {', '.join(vague[:5])}{'…' if len(vague) > 5 else ''}",
|
|
194
201
|
)
|
|
195
202
|
return None
|
|
196
203
|
|
|
@@ -206,8 +213,6 @@ _ALL_RULES = [
|
|
|
206
213
|
# MEDIUM
|
|
207
214
|
_rule_tool_sprawl,
|
|
208
215
|
_rule_vague_descriptions,
|
|
209
|
-
# LOW
|
|
210
|
-
_rule_missing_rate_limit,
|
|
211
216
|
]
|
|
212
217
|
|
|
213
218
|
_SEVERITY_WEIGHT = {"CRITICAL": 40, "HIGH": 20, "MEDIUM": 10, "LOW": 5}
|
|
@@ -145,27 +145,32 @@ def _rule_privilege_excess(agent: AgentInfo) -> Finding | None:
|
|
|
145
145
|
|
|
146
146
|
|
|
147
147
|
def _rule_dangerous_grants(agent: AgentInfo) -> Finding | None:
|
|
148
|
-
|
|
148
|
+
# Exclude code_execution tools — CODE_EXECUTION_GRANT already covers them at CRITICAL.
|
|
149
|
+
# This rule catches dangerous tools outside that category (e.g. send_email, delete_record).
|
|
150
|
+
dangerous = [
|
|
151
|
+
t.name for t in agent.tools
|
|
152
|
+
if t.is_dangerous and t.category not in _CODE_EXEC_CATEGORIES
|
|
153
|
+
]
|
|
149
154
|
if dangerous:
|
|
150
155
|
return Finding(
|
|
151
156
|
severity="HIGH",
|
|
152
157
|
rule_id="DANGEROUS_GRANTS",
|
|
153
|
-
message="Agent holds dangerous tool grants. Verify intent
|
|
158
|
+
message="Agent holds dangerous tool grants outside code execution. Verify intent.",
|
|
154
159
|
detail=f"Dangerous tools: {', '.join(dangerous)}",
|
|
155
160
|
)
|
|
156
161
|
return None
|
|
157
162
|
|
|
158
163
|
|
|
159
164
|
def _rule_tool_sprawl(agent: AgentInfo) -> Finding | None:
|
|
160
|
-
"""MEDIUM:
|
|
165
|
+
"""MEDIUM: high tool count across many categories — blast radius scales with diversity."""
|
|
161
166
|
categories = {t.category for t in agent.tools if t.category != "other"}
|
|
162
|
-
if len(agent.tools) > 10
|
|
167
|
+
if len(agent.tools) > 10 and len(categories) >= 5:
|
|
163
168
|
return Finding(
|
|
164
169
|
severity="MEDIUM",
|
|
165
170
|
rule_id="TOOL_SPRAWL",
|
|
166
171
|
message=(
|
|
167
172
|
f"Agent holds {len(agent.tools)} tools across {len(categories)} categories. "
|
|
168
|
-
"
|
|
173
|
+
"High cross-category diversity increases blast radius."
|
|
169
174
|
),
|
|
170
175
|
detail=f"Categories present: {', '.join(sorted(categories))}",
|
|
171
176
|
)
|
|
@@ -187,19 +192,6 @@ def _rule_write_without_description(agent: AgentInfo) -> Finding | None:
|
|
|
187
192
|
return None
|
|
188
193
|
|
|
189
194
|
|
|
190
|
-
def _rule_missing_rate_limit(agent: AgentInfo) -> Finding | None:
|
|
191
|
-
"""Flag dangerous tools — rate limits aren't visible in static analysis."""
|
|
192
|
-
dangerous = [t.name for t in agent.tools if t.is_dangerous]
|
|
193
|
-
if dangerous:
|
|
194
|
-
return Finding(
|
|
195
|
-
severity="LOW",
|
|
196
|
-
rule_id="MISSING_RATE_LIMIT",
|
|
197
|
-
message="Dangerous grants detected. Ensure rate limits are configured at runtime.",
|
|
198
|
-
detail=f"Tools to check: {', '.join(dangerous)}",
|
|
199
|
-
)
|
|
200
|
-
return None
|
|
201
|
-
|
|
202
|
-
|
|
203
195
|
_ALL_RULES = [
|
|
204
196
|
# CRITICAL
|
|
205
197
|
_rule_exfiltration_path,
|
|
@@ -215,8 +207,6 @@ _ALL_RULES = [
|
|
|
215
207
|
# MEDIUM
|
|
216
208
|
_rule_tool_sprawl,
|
|
217
209
|
_rule_write_without_description,
|
|
218
|
-
# LOW
|
|
219
|
-
_rule_missing_rate_limit,
|
|
220
210
|
]
|
|
221
211
|
|
|
222
212
|
_SEVERITY_WEIGHT = {"CRITICAL": 40, "HIGH": 20, "MEDIUM": 10, "LOW": 5}
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "agentsentinel-cli"
|
|
7
|
-
version = "0.7.
|
|
7
|
+
version = "0.7.6"
|
|
8
8
|
description = "Agentic security CLI — AI analyst with memory, supply chain audit, MCP audit, red-team probing, and agent discovery"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentsentinel_cli-0.7.5 → agentsentinel_cli-0.7.6}/agentsentinel_cli/supply_chain_report.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|