agentsentinel-cli 0.7.3__tar.gz → 0.7.5__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.3 → agentsentinel_cli-0.7.5}/PKG-INFO +1 -1
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/agentsentinel_cli/cli.py +64 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/agentsentinel_cli/discover.py +33 -10
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/pyproject.toml +1 -1
- agentsentinel_cli-0.7.5/tmp/note.md +16 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/.gitignore +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/DOCUMENTATION.md +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/LICENSE +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/README.md +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/agentsentinel_cli/__init__.py +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/agentsentinel_cli/a2a_report.py +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/agentsentinel_cli/a2a_rules.py +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/agentsentinel_cli/a2a_scanner.py +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/agentsentinel_cli/agent_mode.py +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/agentsentinel_cli/agent_mode_report.py +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/agentsentinel_cli/ai_probe.py +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/agentsentinel_cli/attacks/__init__.py +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/agentsentinel_cli/attacks/library.py +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/agentsentinel_cli/discover_report.py +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/agentsentinel_cli/fingerprint.py +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/agentsentinel_cli/frameworks.py +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/agentsentinel_cli/inspect.py +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/agentsentinel_cli/inspect_report.py +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/agentsentinel_cli/mcp_client.py +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/agentsentinel_cli/mcp_report.py +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/agentsentinel_cli/mcp_rules.py +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/agentsentinel_cli/probe.py +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/agentsentinel_cli/probe_report.py +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/agentsentinel_cli/report.py +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/agentsentinel_cli/rules.py +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/agentsentinel_cli/scanner.py +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/agentsentinel_cli/secrets.py +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/agentsentinel_cli/secrets_report.py +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/agentsentinel_cli/secrets_rules.py +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/agentsentinel_cli/supply_chain_ai.py +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/agentsentinel_cli/supply_chain_report.py +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/agentsentinel_cli/supply_chain_rules.py +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/agentsentinel_cli/suppress.py +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/agentsentinel_cli/target.py +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/tmp/test-mcp-agent/README.md +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/tmp/test-mcp-agent/langchain_agent.py +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/tmp/test-mcp-agent/mcp_server.py +0 -0
- {agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/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.5
|
|
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
|
|
@@ -119,6 +119,60 @@ def _enrich_from_platform(agents, scores_map, connect_url, api_key):
|
|
|
119
119
|
console.print(f" [dim yellow]Warning: could not connect to AgentSentinel: {exc}[/dim yellow]")
|
|
120
120
|
|
|
121
121
|
|
|
122
|
+
# ── sentinel discover helpers ────────────────────────────────────────────────
|
|
123
|
+
|
|
124
|
+
def _deep_scan_agents(agents: list, extra_headers: dict | None) -> None:
|
|
125
|
+
"""Run mcp scan rules on every confirmed network MCP server from a discover run."""
|
|
126
|
+
from agentsentinel_cli.discover import DiscoveredAgent
|
|
127
|
+
from agentsentinel_cli.mcp_client import scan_http, McpAuthRequired, McpError
|
|
128
|
+
from agentsentinel_cli.mcp_rules import McpContext, run_mcp_rules, mcp_posture_score
|
|
129
|
+
from agentsentinel_cli.mcp_report import print_mcp_result
|
|
130
|
+
from agentsentinel_cli import suppress as _suppress
|
|
131
|
+
|
|
132
|
+
network_agents = [a for a in agents if a.source == "network"]
|
|
133
|
+
if not network_agents:
|
|
134
|
+
return
|
|
135
|
+
|
|
136
|
+
console.print()
|
|
137
|
+
console.rule("[bold bright_blue]DEEP SCAN[/bold bright_blue]", style="bright_blue")
|
|
138
|
+
|
|
139
|
+
sup_rules = _suppress.load_ignore_file(Path.cwd())
|
|
140
|
+
|
|
141
|
+
for agent in network_agents:
|
|
142
|
+
base = f"http://{agent.location}"
|
|
143
|
+
scan_url = f"{base}/sse" if agent.transport == "sse" else base
|
|
144
|
+
|
|
145
|
+
# Auth-required servers need credentials — skip silently if none provided
|
|
146
|
+
if not agent.tools and not extra_headers:
|
|
147
|
+
console.print(
|
|
148
|
+
f"\n [dim]Skipping {agent.location} — auth required, "
|
|
149
|
+
f"use --auth-header to deep scan[/dim]"
|
|
150
|
+
)
|
|
151
|
+
continue
|
|
152
|
+
|
|
153
|
+
try:
|
|
154
|
+
server = scan_http(scan_url, extra_headers=extra_headers, timeout=15)
|
|
155
|
+
except McpAuthRequired:
|
|
156
|
+
console.print(
|
|
157
|
+
f"\n [dim]Skipping {agent.location} — credentials rejected[/dim]"
|
|
158
|
+
)
|
|
159
|
+
continue
|
|
160
|
+
except (McpError, Exception):
|
|
161
|
+
console.print(
|
|
162
|
+
f"\n [dim]Skipping {agent.location} — could not reconnect[/dim]"
|
|
163
|
+
)
|
|
164
|
+
continue
|
|
165
|
+
|
|
166
|
+
# auth_required: True when the server actually enforces auth (risk LOW/MEDIUM)
|
|
167
|
+
auth_required = agent.risk in ("LOW", "MEDIUM")
|
|
168
|
+
ctx = McpContext(server=server, auth_required=auth_required)
|
|
169
|
+
findings = run_mcp_rules(ctx)
|
|
170
|
+
findings, _ = _suppress.apply(findings, sup_rules)
|
|
171
|
+
score = mcp_posture_score(findings)
|
|
172
|
+
|
|
173
|
+
print_mcp_result(ctx, findings, score, scan_url)
|
|
174
|
+
|
|
175
|
+
|
|
122
176
|
# ── sentinel discover ─────────────────────────────────────────────────────────
|
|
123
177
|
|
|
124
178
|
@main.command()
|
|
@@ -136,6 +190,8 @@ def _enrich_from_platform(agents, scores_map, connect_url, api_key):
|
|
|
136
190
|
help="Custom port range, e.g. 8000-9001. Defaults to common MCP/agent ports.")
|
|
137
191
|
@click.option("--auth-header", "auth_header", default=None, metavar="HEADER",
|
|
138
192
|
help="HTTP auth header for MCP handshakes, e.g. 'Authorization: Bearer token'.")
|
|
193
|
+
@click.option("--scan", "do_scan", is_flag=True, default=False,
|
|
194
|
+
help="Deep-scan every confirmed MCP server with sentinel mcp scan rules.")
|
|
139
195
|
@click.option("--format", "fmt", type=click.Choice(["text", "json"]), default="text",
|
|
140
196
|
help="Output format.")
|
|
141
197
|
@click.option("--verbose", "-v", is_flag=True, default=False,
|
|
@@ -148,17 +204,20 @@ def discover(
|
|
|
148
204
|
subnet: str | None,
|
|
149
205
|
ports: str | None,
|
|
150
206
|
auth_header: str | None,
|
|
207
|
+
do_scan: bool,
|
|
151
208
|
fmt: str,
|
|
152
209
|
verbose: bool,
|
|
153
210
|
) -> None:
|
|
154
211
|
"""Find MCP servers and AI agent processes in your environment.
|
|
155
212
|
|
|
156
213
|
Confirms MCP servers via protocol handshake — not just open ports.
|
|
214
|
+
Add --scan to deep-audit every confirmed server in the same run.
|
|
157
215
|
|
|
158
216
|
\b
|
|
159
217
|
Examples:
|
|
160
218
|
sentinel discover local processes + ports
|
|
161
219
|
sentinel discover --host 10.0.1.45 single remote host
|
|
220
|
+
sentinel discover --host 10.0.1.45 --scan discover + deep audit
|
|
162
221
|
sentinel discover --subnet 10.0.0.0/24 full subnet scan
|
|
163
222
|
sentinel discover --subnet 10.0.0.0/24 \\
|
|
164
223
|
--auth-header 'Authorization: Bearer token' scan with credentials
|
|
@@ -195,6 +254,8 @@ def discover(
|
|
|
195
254
|
click.echo(discover_json(agents))
|
|
196
255
|
return
|
|
197
256
|
print_discover_result(agents, vectors=[f"host ({host})"], verbose=verbose)
|
|
257
|
+
if do_scan:
|
|
258
|
+
_deep_scan_agents(agents, extra_headers or None)
|
|
198
259
|
if any(a.risk == "CRITICAL" for a in agents):
|
|
199
260
|
sys.exit(1)
|
|
200
261
|
return
|
|
@@ -237,6 +298,9 @@ def discover(
|
|
|
237
298
|
|
|
238
299
|
print_discover_result(agents, vectors=vectors, verbose=verbose, subnet_stats=subnet_stats)
|
|
239
300
|
|
|
301
|
+
if do_scan:
|
|
302
|
+
_deep_scan_agents(agents, extra_headers or None)
|
|
303
|
+
|
|
240
304
|
# Exit 1 if any CRITICAL agents found (useful for CI)
|
|
241
305
|
if any(a.risk == "CRITICAL" for a in agents):
|
|
242
306
|
sys.exit(1)
|
|
@@ -379,6 +379,23 @@ def scan_subnet(
|
|
|
379
379
|
|
|
380
380
|
# ── MCP protocol prober ───────────────────────────────────────────────────────
|
|
381
381
|
|
|
382
|
+
def _auth_is_enforced(base: str, timeout: float) -> bool:
|
|
383
|
+
"""Return True only if the server actively rejects unauthenticated requests.
|
|
384
|
+
|
|
385
|
+
Probes without credentials. McpAuthRequired (401/403) → enforced.
|
|
386
|
+
Successful handshake → not enforced (server accepts anyone).
|
|
387
|
+
Any other error → assume not enforced (conservative — don't hide risk).
|
|
388
|
+
"""
|
|
389
|
+
from agentsentinel_cli.mcp_client import scan_http, McpAuthRequired, McpError
|
|
390
|
+
try:
|
|
391
|
+
scan_http(base, extra_headers=None, timeout=timeout)
|
|
392
|
+
return False
|
|
393
|
+
except McpAuthRequired:
|
|
394
|
+
return True
|
|
395
|
+
except (McpError, Exception):
|
|
396
|
+
return False
|
|
397
|
+
|
|
398
|
+
|
|
382
399
|
def _probe_mcp(
|
|
383
400
|
host: str,
|
|
384
401
|
port: int,
|
|
@@ -423,28 +440,34 @@ def _probe_mcp(
|
|
|
423
440
|
except Exception:
|
|
424
441
|
return None
|
|
425
442
|
|
|
426
|
-
# Handshake succeeded — assess risk based on actual tool content
|
|
443
|
+
# Handshake succeeded — assess risk based on actual tool content and whether
|
|
444
|
+
# the server actually enforces authentication.
|
|
427
445
|
tool_names = [t.name for t in server.tools]
|
|
428
446
|
has_dangerous = any(t.is_dangerous for t in server.tools)
|
|
429
447
|
has_write = any(t.scope == "write" for t in server.tools)
|
|
430
|
-
auth_present = bool(extra_headers)
|
|
431
448
|
|
|
432
|
-
|
|
449
|
+
# When credentials were provided, verify the server actually requires them.
|
|
450
|
+
# If it accepts a probe WITHOUT credentials too, auth is not enforced — the
|
|
451
|
+
# server is still open to anyone and the risk doesn't change.
|
|
452
|
+
auth_enforced = False
|
|
453
|
+
if extra_headers:
|
|
454
|
+
auth_enforced = _auth_is_enforced(base, timeout)
|
|
455
|
+
|
|
456
|
+
if not extra_headers or not auth_enforced:
|
|
433
457
|
if has_dangerous or has_write:
|
|
434
458
|
risk = "CRITICAL"
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
f"{', '.join(t.name for t in server.tools if t.is_dangerous or t.scope == 'write')}"
|
|
438
|
-
)
|
|
459
|
+
bad = ", ".join(t.name for t in server.tools if t.is_dangerous or t.scope == "write")
|
|
460
|
+
risk_reason = f"Unauthenticated MCP server with dangerous/write tools: {bad}"
|
|
439
461
|
else:
|
|
462
|
+
n = len(server.tools)
|
|
440
463
|
risk = "HIGH"
|
|
441
464
|
risk_reason = (
|
|
442
|
-
f"Unauthenticated MCP server — {
|
|
443
|
-
f"{'s' if len(server.tools) != 1 else ''} publicly accessible"
|
|
465
|
+
f"Unauthenticated MCP server — {n} tool{'s' if n != 1 else ''} publicly accessible"
|
|
444
466
|
)
|
|
445
467
|
else:
|
|
468
|
+
n = len(server.tools)
|
|
446
469
|
risk = "LOW"
|
|
447
|
-
risk_reason = f"MCP server (
|
|
470
|
+
risk_reason = f"MCP server (auth enforced) — {n} tool{'s' if n != 1 else ''} enumerated"
|
|
448
471
|
|
|
449
472
|
scan_url = f"{base}/sse" if server.transport == "sse" else base
|
|
450
473
|
auth_flag = (
|
|
@@ -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.5"
|
|
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"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Notes
|
|
2
|
+
ls dist/
|
|
3
|
+
|
|
4
|
+
python3.11 -m build
|
|
5
|
+
twine upload dist/agentsentinel_cli-0.7.3* --username __token__ --password $PYPI_TOKEN
|
|
6
|
+
|
|
7
|
+
lsof -i :8000
|
|
8
|
+
lsof -ti :8000
|
|
9
|
+
|
|
10
|
+
kill $(lsof -ti :8000)
|
|
11
|
+
|
|
12
|
+
kill -9 $(lsof -ti :8000)
|
|
13
|
+
|
|
14
|
+
nsenter -t <PID> -m -u -i -n -p -- /bin/bash
|
|
15
|
+
|
|
16
|
+
ps -p <PID> -o pid,ppid,command
|
|
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
|
|
File without changes
|
{agentsentinel_cli-0.7.3 → agentsentinel_cli-0.7.5}/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
|