mcpprobe-scanner 0.1.0__tar.gz → 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.
- mcpprobe_scanner-0.2.0/CHANGELOG.md +129 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/CHECKS.md +26 -26
- mcpprobe_scanner-0.2.0/LICENSE +201 -0
- mcpprobe_scanner-0.2.0/NOTICE +8 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/PKG-INFO +13 -7
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/README.md +10 -5
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/mcpprobe/__init__.py +1 -1
- mcpprobe_scanner-0.2.0/mcpprobe/chains.py +103 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/mcpprobe/checks/__init__.py +2 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/mcpprobe/checks/auth.py +295 -48
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/mcpprobe/checks/base.py +59 -1
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/mcpprobe/checks/baseline.py +33 -2
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/mcpprobe/checks/capability.py +80 -2
- mcpprobe_scanner-0.2.0/mcpprobe/checks/metadata.py +598 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/mcpprobe/checks/poisoning.py +178 -36
- mcpprobe_scanner-0.2.0/mcpprobe/checks/proof.py +154 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/mcpprobe/checks/robustness.py +111 -2
- mcpprobe_scanner-0.2.0/mcpprobe/checks/transport_conformance.py +644 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/mcpprobe/cli.py +11 -0
- mcpprobe_scanner-0.2.0/mcpprobe/environment.py +51 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/mcpprobe/models.py +80 -3
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/mcpprobe/report/json_report.py +7 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/mcpprobe/report/sarif.py +21 -3
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/mcpprobe/report/terminal.py +52 -3
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/mcpprobe/scanner.py +122 -5
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/mcpprobe/transport/base.py +2 -1
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/mcpprobe/transport/evidence.py +21 -45
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/mcpprobe/transport/http.py +13 -1
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/pyproject.toml +3 -2
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/tests/conftest.py +32 -2
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/tests/fake_server.py +361 -2
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/tests/test_checks_auth.py +40 -1
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/tests/test_checks_meta.py +40 -17
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/tests/test_checks_transport.py +20 -2
- mcpprobe_scanner-0.2.0/tests/test_environment_and_evidence.py +103 -0
- mcpprobe_scanner-0.2.0/tests/test_fixture_outcomes.py +300 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/tests/test_gate_and_policy.py +2 -2
- mcpprobe_scanner-0.2.0/tests/test_meta01_corpus.py +88 -0
- mcpprobe_scanner-0.1.0/CHANGELOG.md +0 -44
- mcpprobe_scanner-0.1.0/LICENSE +0 -21
- mcpprobe_scanner-0.1.0/mcpprobe/checks/metadata.py +0 -341
- mcpprobe_scanner-0.1.0/mcpprobe/checks/transport_conformance.py +0 -429
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/.gitignore +0 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/SECURITY.md +0 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/mcpprobe/action.py +0 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/mcpprobe/authz.py +0 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/mcpprobe/catalogue.py +0 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/mcpprobe/checks/_util.py +0 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/mcpprobe/checks/active_stubs.py +0 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/mcpprobe/dnsutil.py +0 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/mcpprobe/governance.py +0 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/mcpprobe/policy.py +0 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/mcpprobe/py.typed +0 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/mcpprobe/redact.py +0 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/mcpprobe/report/__init__.py +0 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/mcpprobe/transport/__init__.py +0 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/mcpprobe/transport/fingerprint.py +0 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/mcpprobe/transport/ratelimit.py +0 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/mcpprobe/transport/stdio.py +0 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/tests/__init__.py +0 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/tests/data/sarif-schema-2.1.0.json +0 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/tests/fake_stdio_server.py +0 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/tests/test_checks_robustness.py +0 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/tests/test_docs_and_action.py +0 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/tests/test_reports_and_cli.py +0 -0
- {mcpprobe_scanner-0.1.0 → mcpprobe_scanner-0.2.0}/tests/test_transport.py +0 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses
|
|
5
|
+
[Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.2.0]
|
|
10
|
+
|
|
11
|
+
Accuracy release, driven by an independent review of a 0.1.0 scan of the project's own
|
|
12
|
+
deliberately weak demo server. 0.1.0 missed the demo's textbook tool poisoning,
|
|
13
|
+
reported four checks as passed that had tested nothing, and rated an unauthenticated
|
|
14
|
+
remote shell low. All of that is fixed and pinned by fixtures in CI.
|
|
15
|
+
|
|
16
|
+
- Licence changes from MIT to Apache-2.0 for future releases. Version 0.1.0 remains available under MIT.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- **META-01 missed obvious poisoning.** It now detects instructions aimed at the model
|
|
21
|
+
("always call this first", "call this tool before any other", "you must", "do not
|
|
22
|
+
tell the user", "ignore previous instructions"), requests to send the model's context
|
|
23
|
+
out (conversation, chat history, system prompt, previous messages), requests to
|
|
24
|
+
harvest the user's secrets, and HTML comments, in tool, argument, prompt and resource
|
|
25
|
+
descriptions. A directive combined with a context-exfiltration request is high. The
|
|
26
|
+
demo's `run_command` description is now reported high. Several 0.1 patterns that
|
|
27
|
+
fired on benign text ("Read the file at the given path", "Forward an email",
|
|
28
|
+
"Silently ignores unknown fields", "Operating system: Linux", "rather than the user's
|
|
29
|
+
timezone") were tightened; a corpus of 33 benign real-world descriptions must stay
|
|
30
|
+
below medium in the test-suite.
|
|
31
|
+
- **Checks no longer report "passed" for things they did not test.** New check status
|
|
32
|
+
`not_applicable` (with a one-sentence reason and an optional `related_to`), raised
|
|
33
|
+
through `NotApplicable`. Every early exit was audited: AUTH-03/AUTH-04 are not
|
|
34
|
+
applicable when the server needs no authentication (related to AUTH-01), TRANS-03
|
|
35
|
+
when no `Mcp-Session-Id` is issued, META-* when no tool metadata could be read,
|
|
36
|
+
ERR-01/DOS-01 when every probe was stopped at authentication, META-02 without a
|
|
37
|
+
previous baseline, TRANS-01/TRANS-02 when a 401/429 hides the behaviour. Transport
|
|
38
|
+
failures are now `error`, not a pass. Reporters count only `ran` with no findings
|
|
39
|
+
as passed; SARIF lists not-applicable checks as notifications, never as passes.
|
|
40
|
+
- **AUTH-02 passed when the metadata was missing.** Missing RFC 9728 metadata is now a
|
|
41
|
+
finding on unauthenticated servers too (medium on public hosts, low on loopback).
|
|
42
|
+
- **AUTH-01 misreported servers scanned with credentials.** When `--header` supplies
|
|
43
|
+
credentials, one extra request is sent with them removed; a 401/403 marks the server
|
|
44
|
+
protected. AUTH-04's query-string probe no longer carries the operator's own token.
|
|
45
|
+
- **Not an MCP server.** A target that answers but does not speak MCP (for example an
|
|
46
|
+
ordinary web site) no longer aborts with a generic error or produces results: the
|
|
47
|
+
report has `target_kind: "not_mcp"`, every check is not applicable with the
|
|
48
|
+
observation ("Not an MCP server: initialize -> HTTP 200 text/html"), and the CLI
|
|
49
|
+
exits with the new code 3. Unreachable targets get `target_kind: "unreachable"`
|
|
50
|
+
(exit 2, as before).
|
|
51
|
+
|
|
52
|
+
### Changed
|
|
53
|
+
|
|
54
|
+
- **META-03 rates by capability.** Tools that advertise arbitrary command execution or
|
|
55
|
+
arbitrary file read/write (a shell or file capability selected by a free-form string
|
|
56
|
+
argument) are high on their own and critical on an unauthenticated public endpoint.
|
|
57
|
+
The fix text says to remove or sandbox the tool with an allow-list; annotations come
|
|
58
|
+
after. The description states that a passive scan sees the advertised capability, not
|
|
59
|
+
whether the implementation really executes. Annotation contradictions are one finding
|
|
60
|
+
listing every affected tool. Check title updated accordingly.
|
|
61
|
+
- **One finding per check per root cause.** META-04 emits one finding listing every
|
|
62
|
+
affected tool, with each tool's complete `inputSchema` and its specific problems
|
|
63
|
+
(including arguments missing from `required` and `additionalProperties` not false).
|
|
64
|
+
- **Evidence and proof.** Every finding has `proof` (the complete object judged, built
|
|
65
|
+
from parsed data), `verify` (one runnable line, a `curl` against the real target for
|
|
66
|
+
HTTP), `affected`, `evidence_ids`, and `confidence_reason` whenever confidence is not
|
|
67
|
+
high (enforced). Evidence keeps every request and response header, with credential
|
|
68
|
+
headers redacted, and bodies up to 64 KiB (was 4 KiB).
|
|
69
|
+
- **Environment-aware fix text.** The edge is detected from response headers
|
|
70
|
+
(`apigw-requestid`/`x-amz-apigw-id` -> AWS API Gateway, CloudFront, Cloudflare
|
|
71
|
+
`cf-ray`, Vercel `x-vercel-id`) and public vs local hosts are told apart. TRANS-05 and
|
|
72
|
+
DOS-01 say where to configure TLS, headers and throttling; AUTH-01 and TRANS-01 drop
|
|
73
|
+
"bind to 127.0.0.1" on public hosts. AUTH-01 shows the exact `WWW-Authenticate` header
|
|
74
|
+
with the target's metadata URL and notes that authentication does not make a shell
|
|
75
|
+
tool safe. TRANS-01 also probes and advises on `Origin: null` and allows requests with
|
|
76
|
+
no Origin. TRANS-02 mentions version negotiation on `initialize`.
|
|
77
|
+
- **Attack chains.** New top-level `chains`: CHAIN-01 (unauthenticated + arbitrary
|
|
78
|
+
capability, + poisoned description when META-01 fired on that tool, critical) and
|
|
79
|
+
CHAIN-02 (the same on a local server that accepts foreign Origins). A chain is only
|
|
80
|
+
emitted when every constituent finding is present.
|
|
81
|
+
- Report schema 0.2 (`schema_version`, `target_kind`, `environment`, `chains`, the new
|
|
82
|
+
finding fields, `checks[].related_to`, `summary.checks_passed` /
|
|
83
|
+
`checks_not_applicable`), documented in `docs/report-schema.md`.
|
|
84
|
+
|
|
85
|
+
### Tests
|
|
86
|
+
|
|
87
|
+
- Fixtures pinned in CI: an exact replica of the public demo server (`--preset demo`),
|
|
88
|
+
the observable passive behaviour of a hardened reference server (`--preset
|
|
89
|
+
hardened`), and an ordinary HTML site (`--preset html`). The exact outcome of every
|
|
90
|
+
check against each is asserted; any change fails the suite.
|
|
91
|
+
|
|
92
|
+
## [0.1.0]
|
|
93
|
+
|
|
94
|
+
- Published on PyPI as `mcpprobe-scanner` (PyPI blocks `mcpprobe` and `mcpprobe-cli` as too similar to existing projects). The command and the Python package are both `mcpprobe`.
|
|
95
|
+
- 2026-10-27
|
|
96
|
+
|
|
97
|
+
First public release of the open-source scanner core.
|
|
98
|
+
|
|
99
|
+
### Added
|
|
100
|
+
|
|
101
|
+
- MCP client for Streamable HTTP (JSON-RPC over POST, JSON or SSE responses)
|
|
102
|
+
and stdio, written to send deliberately non-conformant requests.
|
|
103
|
+
- Protocol fingerprinting: `server/discover` (2026-07-28) first, then
|
|
104
|
+
`initialize` with 2025-11-25 and 2025-06-18, then the deprecated
|
|
105
|
+
2024-11-05 HTTP+SSE transport.
|
|
106
|
+
- Evidence log: every request/response with timestamp, method, status, a
|
|
107
|
+
header subset, bodies truncated to 4 KB, `Authorization` values and known
|
|
108
|
+
secret patterns redacted.
|
|
109
|
+
- Passive checks: AUTH-01..04, TRANS-01..05, META-01..05, ERR-01, DOS-01.
|
|
110
|
+
- Active checks INJ-01..05, SUPPLY-01/02 and SANDBOX-01 registered as gated
|
|
111
|
+
stubs (report `not_implemented`; no payloads are sent).
|
|
112
|
+
- Governance items GOV-01..05 listed as attestation-only in every report.
|
|
113
|
+
- Authorisation gate for active mode: `--authorised-by`, `--scope` and
|
|
114
|
+
ownership verification (`--verify dns-txt` via `_mcpprobe.<host>` TXT, or
|
|
115
|
+
`--verify local` for stdio); an authorisation record JSON is written next to
|
|
116
|
+
the report.
|
|
117
|
+
- Opt-out: DNS TXT `mcpprobe-opt-out=all` at `_mcpprobe.<host>` and
|
|
118
|
+
`/.well-known/mcpprobe-opt-out`, both checked before any MCP request.
|
|
119
|
+
- Rate limiting: 5 requests/second by default, hard ceiling 10.
|
|
120
|
+
- Reports: JSON (with evidence), SARIF 2.1.0 (validated against the official
|
|
121
|
+
schema in tests) and a rich terminal summary. Exit codes 0 / 1 / 2.
|
|
122
|
+
- META-02 baselines: canonical SHA-256 of tools, prompts, resources and
|
|
123
|
+
instructions with diffing against a stored baseline (rug-pull detection).
|
|
124
|
+
- Optional LLM-judge hook for META-01 (`ScanOptions.judge`); mcpprobe makes no
|
|
125
|
+
LLM or third-party network calls itself.
|
|
126
|
+
- Docker container GitHub Action, CI (ruff, strict mypy, pytest on Python
|
|
127
|
+
3.11 to 3.13) and PyPI Trusted Publishing release workflow.
|
|
128
|
+
|
|
129
|
+
[0.1.0]: https://github.com/mcpprobe-dev/mcpprobe/releases/tag/v0.1.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# mcpprobe checks catalogue
|
|
2
2
|
|
|
3
|
-
Generated from the check metadata of mcpprobe 0.
|
|
3
|
+
Generated from the check metadata of mcpprobe 0.2.0 (`mcpprobe checks --format markdown`). Do not edit by hand.
|
|
4
4
|
|
|
5
5
|
Standards: OWASP MCP Top 10 (IDs `MCP0x:2025`, still a beta list, so shown with `(beta)`), OWASP Top 10 for Agentic Applications (`ASI0x`), OWASP Top 10 for LLM Applications 2026 (`LLM0x:2026`, primary) and 2025 (`LLM0x:2025`, kept for one release cycle). Severities are defaults; a check can raise or lower the severity of an individual finding (for example loopback-only servers are reported one level lower), and heuristic checks never rate a finding Critical on their own.
|
|
6
6
|
|
|
@@ -21,7 +21,7 @@ Passive checks send only what a normal MCP client would send plus a few benign m
|
|
|
21
21
|
| [TRANS-05](#trans-05---plain-http-weak-tls-or-missing-security-headers) | passive | high | Plain HTTP, weak TLS or missing security headers | MCP01:2025 (beta) |
|
|
22
22
|
| [META-01](#meta-01---possible-tool-description-poisoning) | passive | high | Possible tool-description poisoning | MCP03:2025 (beta), ASI01, ASI04, LLM01:2026, LLM01:2025 |
|
|
23
23
|
| [META-02](#meta-02---tool-surface-changed-since-the-stored-baseline-rug-pull-indicator) | passive | medium | Tool surface changed since the stored baseline (rug-pull indicator) | MCP03:2025 (beta), ASI04 |
|
|
24
|
-
| [META-03](#meta-03---tool-
|
|
24
|
+
| [META-03](#meta-03---dangerous-tool-capability-exposed-or-annotations-contradict-it) | passive | high | Dangerous tool capability exposed or annotations contradict it | MCP02:2025 (beta), MCP05:2025 (beta), ASI02, ASI05, LLM03:2026, LLM06:2025 |
|
|
25
25
|
| [META-04](#meta-04---tool-schema-hygiene-problems) | passive | low | Tool schema hygiene problems | MCP05:2025 (beta) |
|
|
26
26
|
| [META-05](#meta-05---secret-material-in-server-metadata-or-configuration) | passive | high | Secret material in server metadata or configuration | MCP01:2025 (beta), LLM02:2026, LLM02:2025 |
|
|
27
27
|
| [ERR-01](#err-01---verbose-error-responses-leak-stack-traces-paths-or-versions) | passive | medium | Verbose error responses leak stack traces, paths or versions | MCP08:2025 (beta), LLM02:2026, LLM02:2025 |
|
|
@@ -50,13 +50,13 @@ Passive checks send only what a normal MCP client would send plus a few benign m
|
|
|
50
50
|
| Default severity | high |
|
|
51
51
|
| Transports | http |
|
|
52
52
|
| Standards | MCP07:2025 (beta), ASI03, LLM04:2026, LLM03:2026, LLM03:2025, LLM06:2025 |
|
|
53
|
-
| Evidence | The unauthenticated
|
|
53
|
+
| Evidence | The unauthenticated request (Authorization shown as not sent) and the response. |
|
|
54
54
|
|
|
55
|
-
POSTs initialize (or server/discover) and tools/list with no Authorization header. If the server answers with a tool list, anyone who can reach the endpoint can drive its tools.
|
|
55
|
+
POSTs initialize (or server/discover) and tools/list with no Authorization header. If the server answers with a tool list, anyone who can reach the endpoint can drive its tools. When the operator supplies credentials with --header, one extra request is sent with those credentials removed, so a protected server is not misreported. Passes only when the endpoint answers 401/403 without credentials.
|
|
56
56
|
|
|
57
57
|
**False positives.** Servers protected by a network boundary (VPN, mTLS at a proxy, IP allow-list) will still be flagged because the scanner only sees what the endpoint returns. Local development servers bound to loopback are reported at reduced severity but are still exposed to DNS-rebinding and browser-based attacks (see CVE-2025-49596 class).
|
|
58
58
|
|
|
59
|
-
**Fix.** Put the endpoint behind OAuth 2.1 as described in the MCP authorization spec: return 401 with WWW-Authenticate
|
|
59
|
+
**Fix.** Put the endpoint behind OAuth 2.1 as described in the MCP authorization spec: return 401 with WWW-Authenticate: Bearer resource_metadata="<RFC 9728 metadata URL>", validate each token's signature, issuer, expiry and audience, and reject requests without a valid bearer token. Authentication does not make a dangerous tool safe.
|
|
60
60
|
|
|
61
61
|
### AUTH-02 - Protected resource metadata (RFC 9728) missing or invalid
|
|
62
62
|
|
|
@@ -66,11 +66,11 @@ POSTs initialize (or server/discover) and tools/list with no Authorization heade
|
|
|
66
66
|
| Default severity | high |
|
|
67
67
|
| Transports | http |
|
|
68
68
|
| Standards | MCP07:2025 (beta) |
|
|
69
|
-
| Evidence | The 401 response headers and each metadata GET with its status
|
|
69
|
+
| Evidence | The 401 response headers and each metadata GET with its status. |
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
Parses WWW-Authenticate for resource_metadata (when the server answers 401), then tries /.well-known/oauth-protected-resource[/path] and the host root. The document must be JSON with a non-empty authorization_servers list. Without it, conformant clients cannot start the OAuth flow and users are pushed toward pasting long-lived static tokens. A server that requires no authentication and serves no metadata is reported too (medium, the discovery half of AUTH-01).
|
|
72
72
|
|
|
73
|
-
**False positives.** Servers that use a non-OAuth scheme (API keys at a gateway) will be flagged; the MCP spec still expects RFC 9728 metadata for HTTP transports.
|
|
73
|
+
**False positives.** Servers that use a non-OAuth scheme (API keys at a gateway) will be flagged; the MCP spec still expects RFC 9728 metadata for HTTP transports. A deliberately public, read-only server with no authentication will also be flagged; AUTH-01 is the root cause.
|
|
74
74
|
|
|
75
75
|
**Fix.** Serve /.well-known/oauth-protected-resource (path-aware variant preferred) with 'resource' and 'authorization_servers', and include WWW-Authenticate: Bearer resource_metadata="<url>" on every 401.
|
|
76
76
|
|
|
@@ -114,13 +114,13 @@ Mints two syntactically valid JWTs the server cannot have issued (HS256 with a r
|
|
|
114
114
|
| Default severity | medium |
|
|
115
115
|
| Transports | http |
|
|
116
116
|
| Standards | MCP07:2025 (beta) |
|
|
117
|
-
| Evidence |
|
|
117
|
+
| Evidence | Each POST with a foreign Origin header and the non-403 response. |
|
|
118
118
|
|
|
119
|
-
Sends a normal negotiation request with 'Origin: https://evil.example'. The transport spec says servers MUST validate Origin and return 403 on an invalid one; otherwise a web page can drive
|
|
119
|
+
Sends a normal negotiation request with 'Origin: https://evil.example' and one with 'Origin: null'. The transport spec says servers MUST validate Origin and return 403 on an invalid one; otherwise a web page can drive the server through the victim's browser (DNS rebinding). Requests without an Origin header are not tested: non-browser clients do not send one.
|
|
120
120
|
|
|
121
|
-
**False positives.** If the server answers 401 before looking at Origin the check is
|
|
121
|
+
**False positives.** If the server answers 401 (or 429) before looking at Origin the check is not applicable and says so. Reverse proxies that strip Origin will cause a finding against the proxied endpoint even if the application validates it.
|
|
122
122
|
|
|
123
|
-
**Fix.** Reject
|
|
123
|
+
**Fix.** Reject any request whose Origin header is present and not on an allow-list with HTTP 403, before processing the body, including 'Origin: null'. Allow requests with no Origin header.
|
|
124
124
|
|
|
125
125
|
### TRANS-02 - Unsupported MCP-Protocol-Version header not rejected
|
|
126
126
|
|
|
@@ -134,9 +134,9 @@ Sends a normal negotiation request with 'Origin: https://evil.example'. The tran
|
|
|
134
134
|
|
|
135
135
|
Sends a request with 'MCP-Protocol-Version: 1999-01-01'. The spec requires HTTP 400 (or, on 2026-07-28, an UnsupportedProtocolVersionError). Accepting any version string suggests the server ignores version negotiation entirely.
|
|
136
136
|
|
|
137
|
-
**False positives.** Pre-2025-06-18 servers never defined the header, so an older server that ignores it is noted at low severity only.
|
|
137
|
+
**False positives.** Pre-2025-06-18 servers never defined the header, so an older server that ignores it is noted at low severity only. A 401/429 before the header is read makes the check not applicable.
|
|
138
138
|
|
|
139
|
-
**Fix.** Validate MCP-Protocol-Version against the
|
|
139
|
+
**Fix.** Validate the MCP-Protocol-Version header against the versions you support and reply 400 when it is unknown. Separately, when initialize asks for a protocolVersion you do not support, answer with the latest version you do support and let the client decide; never echo back whatever the client asked for.
|
|
140
140
|
|
|
141
141
|
### TRANS-03 - Weak, predictable or unvalidated Mcp-Session-Id
|
|
142
142
|
|
|
@@ -184,7 +184,7 @@ Flags non-loopback endpoints served over plain HTTP (bearer tokens in clear), TL
|
|
|
184
184
|
|
|
185
185
|
**False positives.** Loopback plain-HTTP is reported as informational. Header checks look at the MCP endpoint only; a CDN may add headers on other paths.
|
|
186
186
|
|
|
187
|
-
**Fix.** Serve the endpoint over HTTPS with TLS 1.2+ only, add Strict-Transport-Security and X-Content-Type-Options: nosniff.
|
|
187
|
+
**Fix.** Serve the endpoint over HTTPS with TLS 1.2+ only, add Strict-Transport-Security and X-Content-Type-Options: nosniff. Where an edge (API Gateway, CloudFront, Cloudflare, Vercel) is detected the fix text says where to configure it.
|
|
188
188
|
|
|
189
189
|
### META-01 - Possible tool-description poisoning
|
|
190
190
|
|
|
@@ -196,11 +196,11 @@ Flags non-loopback endpoints served over plain HTTP (bearer tokens in clear), TL
|
|
|
196
196
|
| Standards | MCP03:2025 (beta), ASI01, ASI04, LLM01:2026, LLM01:2025 |
|
|
197
197
|
| Evidence | The tools/list (or prompts/resources/initialize) response and the matched excerpts. |
|
|
198
198
|
|
|
199
|
-
Scans every tool, prompt and resource description, every inputSchema parameter description and the server 'instructions' for: prompt-style markup (<IMPORTANT>, <system
|
|
199
|
+
Scans every tool, prompt and resource description, every inputSchema parameter description and the server 'instructions' for: prompt-style markup (<IMPORTANT>, <system>, HTML comments), instructions aimed at the model (call this first / before any other tool, you must, conceal from the user, ignore previous instructions, redirect/replace), requests to send the model's context out (conversation, chat history, system prompt, previous messages), requests to harvest secrets (the user's API key, token, password, credentials, .env), credential file paths, references to other tools with behaviour-modifying language (shadowing), URLs and e-mail addresses with send/BCC wording, base64 blobs, zero-width/bidi/tag Unicode, mixed-script homoglyphs and excessive length. Findings carry tiered confidence and are never rated Critical on heuristics alone. A model directive combined with a request to send context out is rated high. Precision is guarded by a corpus of benign real-world descriptions in the test-suite that must stay below medium.
|
|
200
200
|
|
|
201
201
|
**False positives.** Legitimate guidance such as 'IMPORTANT: call authenticate before this tool' matches the markup and prerequisite heuristics at medium confidence. URLs to documentation and mentions of sibling tools without behaviour-modifying words are reported at low severity. Review the matched excerpts; a second-opinion judge can be plugged in through the Python API (ScanOptions.judge); mcpprobe itself makes no LLM calls.
|
|
202
202
|
|
|
203
|
-
**Fix.** Remove
|
|
203
|
+
**Fix.** Rewrite the description so it only says what the tool does. Remove every instruction addressed to the model (when to call it, what to call first, what to hide from the user) and every request to pass along conversation, prompts, files or credentials. Strip invisible Unicode and HTML comments, and never reference other servers' tools. Pin and hash approved descriptions in clients (see META-02).
|
|
204
204
|
|
|
205
205
|
### META-02 - Tool surface changed since the stored baseline (rug-pull indicator)
|
|
206
206
|
|
|
@@ -218,21 +218,21 @@ Computes SHA-256 over the canonical JSON of each tool, prompt and resource defin
|
|
|
218
218
|
|
|
219
219
|
**Fix.** Treat tool definitions as code: version them, review changes, and have clients pin and re-approve definitions when the hash changes.
|
|
220
220
|
|
|
221
|
-
### META-03 -
|
|
221
|
+
### META-03 - Dangerous tool capability exposed or annotations contradict it
|
|
222
222
|
|
|
223
223
|
| | |
|
|
224
224
|
|---|---|
|
|
225
225
|
| Mode | passive (implemented) |
|
|
226
|
-
| Default severity |
|
|
226
|
+
| Default severity | high |
|
|
227
227
|
| Transports | http, stdio |
|
|
228
228
|
| Standards | MCP02:2025 (beta), MCP05:2025 (beta), ASI02, ASI05, LLM03:2026, LLM06:2025 |
|
|
229
|
-
| Evidence | The tools/list response;
|
|
229
|
+
| Evidence | The tools/list response; each affected tool's complete definition is shown as proof. |
|
|
230
230
|
|
|
231
|
-
Classifies each tool by name, description and parameter names into capability classes
|
|
231
|
+
Classifies each tool by name, description and parameter names into capability classes. Tools that advertise arbitrary command execution, arbitrary file read or arbitrary file write (a shell/file capability selected by a free-form string argument with no enum, const or pattern) are rated high on their own and critical when the remote endpoint needs no authentication. Declared annotations are cross-checked (a command tool marked readOnlyHint:true is reported), and the remaining high-impact surface (network, e-mail, credentials, payments) is summarised at low severity. Passive scanning sees the advertised capability, not whether the implementation really executes: the rating is based on the name, description and schema, and no tool is called.
|
|
232
232
|
|
|
233
|
-
**False positives.** Classification is keyword-based: a tool named 'delete_draft' that only removes the user's own draft will be classed 'delete'.
|
|
233
|
+
**False positives.** Classification is keyword-based: a tool named 'delete_draft' that only removes the user's own draft will be classed 'delete'. A tool that advertises a shell but whose implementation is a stub is still reported, because clients and models act on the advertised capability.
|
|
234
234
|
|
|
235
|
-
**Fix.**
|
|
235
|
+
**Fix.** For arbitrary command execution or file access: remove the tool, or sandbox it and replace the free-form argument with an allow-list (enum) of commands or files. Annotations come after that. Make annotations truthful (destructiveHint:true for anything that executes or deletes); they help a careful client warn the user but do nothing about the risk.
|
|
236
236
|
|
|
237
237
|
### META-04 - Tool schema hygiene problems
|
|
238
238
|
|
|
@@ -242,13 +242,13 @@ Classifies each tool by name, description and parameter names into capability cl
|
|
|
242
242
|
| Default severity | low |
|
|
243
243
|
| Transports | http, stdio |
|
|
244
244
|
| Standards | MCP05:2025 (beta) |
|
|
245
|
-
| Evidence | The tools/list response. |
|
|
245
|
+
| Evidence | The tools/list response; each affected tool's complete inputSchema is shown as proof. |
|
|
246
246
|
|
|
247
|
-
Checks tool names against [A-Za-z0-9_.-]{1,128} and for duplicates; checks that inputSchema is a JSON Schema object of type 'object'; flags free-form string arguments whose names suggest paths, commands, URLs or queries and that carry no enum, pattern, format or maxLength constraint. Missing outputSchema cannot be judged without calling the tool and is not reported.
|
|
247
|
+
Checks tool names against [A-Za-z0-9_.-]{1,128} and for duplicates; checks that inputSchema is a JSON Schema object of type 'object'; flags free-form string arguments whose names suggest paths, commands, URLs or queries and that carry no enum, pattern, format or maxLength constraint. One finding lists every affected tool, with each tool's complete inputSchema and its specific problems (including arguments missing from 'required' and additionalProperties not set to false). Missing outputSchema cannot be judged without calling the tool and is not reported.
|
|
248
248
|
|
|
249
249
|
**False positives.** An unconstrained 'query' string is often unavoidable (search tools); the finding is Low severity and meant as a review prompt, not a defect.
|
|
250
250
|
|
|
251
|
-
**Fix.**
|
|
251
|
+
**Fix.** Constrain every risky string argument (enum for a fixed set, pattern for a format, maxLength to bound size), list required arguments in 'required', set additionalProperties: false, and validate arguments on the server against the same schema instead of trusting the client to have done it.
|
|
252
252
|
|
|
253
253
|
### META-05 - Secret material in server metadata or configuration
|
|
254
254
|
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
mcpprobe
|
|
2
|
+
Copyright 2026 mcpprobe contributors
|
|
3
|
+
|
|
4
|
+
This product is licensed under the Apache License, Version 2.0 (see LICENSE).
|
|
5
|
+
Releases up to and including 0.1.0 were published under the MIT License and remain available under it.
|
|
6
|
+
|
|
7
|
+
"mcpprobe" and the mcpprobe logo are trademarks of the project. The Apache License does not grant
|
|
8
|
+
permission to use them (Section 6), except to describe the origin of the work.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: mcpprobe-scanner
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Security scanner for Model Context Protocol (MCP) servers: auth, transport, tool-metadata and poisoning checks with JSON and SARIF output.
|
|
5
5
|
Project-URL: Homepage, https://mcpprobe.tech
|
|
6
6
|
Project-URL: Scanning policy, https://mcpprobe.tech/scanning-policy
|
|
@@ -8,8 +8,9 @@ Project-URL: Source, https://github.com/mcpprobe-dev/mcpprobe
|
|
|
8
8
|
Project-URL: Issues, https://github.com/mcpprobe-dev/mcpprobe/issues
|
|
9
9
|
Author-email: mcpprobe contributors <hello@mcpprobe.tech>
|
|
10
10
|
Maintainer-email: mcpprobe contributors <hello@mcpprobe.tech>
|
|
11
|
-
License-Expression:
|
|
11
|
+
License-Expression: Apache-2.0
|
|
12
12
|
License-File: LICENSE
|
|
13
|
+
License-File: NOTICE
|
|
13
14
|
Keywords: mcp,model-context-protocol,owasp,sarif,scanner,security
|
|
14
15
|
Classifier: Development Status :: 3 - Alpha
|
|
15
16
|
Classifier: Environment :: Console
|
|
@@ -85,7 +86,7 @@ which checks ran, which were skipped and why. `mcpprobe checks` lists every chec
|
|
|
85
86
|
|---|---|---|
|
|
86
87
|
| Authentication | AUTH-01..04 | endpoint answers without credentials; missing RFC 9728 protected-resource metadata; no PKCE S256; accepts a self-minted JWT with the wrong audience; token accepted in the query string |
|
|
87
88
|
| Transport | TRANS-01..05 | `Origin` not validated (DNS rebinding); bogus `MCP-Protocol-Version` accepted; weak or forgeable `Mcp-Session-Id`; deprecated HTTP+SSE; plain HTTP, weak TLS, missing headers |
|
|
88
|
-
| Tool metadata | META-01..05 |
|
|
89
|
+
| Tool metadata | META-01..05 | instructions aimed at the model ("always call this first", "do not tell the user"), requests to send conversation, system prompt or secrets out, cross-tool shadowing, credential paths, URLs, base64, HTML comments, zero-width/bidi/homoglyph Unicode; rug-pull diff against a stored SHA-256 baseline; advertised arbitrary command execution or file access (high, critical when unauthenticated) and annotations that contradict capability; schema hygiene; secrets in descriptions or stdio env |
|
|
89
90
|
| Robustness | ERR-01, DOS-01 | stack traces and versions in errors; one oversized argument, one 64-deep nesting probe and 20 paced `tools/list` calls, never more |
|
|
90
91
|
| Active (gated stubs) | INJ-01..05, SUPPLY-01/02, SANDBOX-01 | registered and reported as `not_implemented` in this release |
|
|
91
92
|
| Governance | GOV-01..05 | listed as attestation-only: not assessable by a scan, never marked passed |
|
|
@@ -147,7 +148,8 @@ opt-outs (below), but those are courtesy mechanisms, not permission.
|
|
|
147
148
|
|---|---|
|
|
148
149
|
| `0` | No finding at or above `--fail-on` (default `high`) |
|
|
149
150
|
| `1` | At least one finding at or above `--fail-on` |
|
|
150
|
-
| `2` | Error: target unreachable
|
|
151
|
+
| `2` | Error: target unreachable, bad arguments, ownership verification failed, authorisation refused, or the target opted out |
|
|
152
|
+
| `3` | The target answered but is not an MCP server (for example an ordinary web site); every check is reported not applicable and nothing passes |
|
|
151
153
|
|
|
152
154
|
## SARIF and GitHub code scanning
|
|
153
155
|
|
|
@@ -202,8 +204,12 @@ description after approval is the classic rug pull) and update the file; use
|
|
|
202
204
|
## Reports and evidence
|
|
203
205
|
|
|
204
206
|
- `--json FILE`: the full report, including every request/response pair (timestamp,
|
|
205
|
-
method, status,
|
|
206
|
-
|
|
207
|
+
method, status, every header, bodies up to 64 KiB). Credential headers and
|
|
208
|
+
recognised secret patterns are redacted. Each finding carries a `proof` excerpt
|
|
209
|
+
built from parsed data, a runnable `verify` line and, when confidence is not high,
|
|
210
|
+
a `confidence_reason`. Checks whose preconditions are absent are reported
|
|
211
|
+
`not_applicable` with a reason and never count as passed. The schema is documented
|
|
212
|
+
in [docs/report-schema.md](docs/report-schema.md).
|
|
207
213
|
- `--sarif FILE`: SARIF 2.1.0.
|
|
208
214
|
- `-v`: descriptions and scan notes in the terminal; `-q`: no terminal output.
|
|
209
215
|
|
|
@@ -237,4 +243,4 @@ $ python -m tests.fake_server --preset vulnerable --port 8765 # deliberately v
|
|
|
237
243
|
$ mcpprobe http://127.0.0.1:8765/mcp
|
|
238
244
|
```
|
|
239
245
|
|
|
240
|
-
Security issues: see [SECURITY.md](SECURITY.md). Licence: [
|
|
246
|
+
Security issues: see [SECURITY.md](SECURITY.md). Licence: [Apache-2.0](LICENSE) from 0.2.0 (see [NOTICE](NOTICE)); 0.1.0 was released under MIT.
|
|
@@ -47,7 +47,7 @@ which checks ran, which were skipped and why. `mcpprobe checks` lists every chec
|
|
|
47
47
|
|---|---|---|
|
|
48
48
|
| Authentication | AUTH-01..04 | endpoint answers without credentials; missing RFC 9728 protected-resource metadata; no PKCE S256; accepts a self-minted JWT with the wrong audience; token accepted in the query string |
|
|
49
49
|
| Transport | TRANS-01..05 | `Origin` not validated (DNS rebinding); bogus `MCP-Protocol-Version` accepted; weak or forgeable `Mcp-Session-Id`; deprecated HTTP+SSE; plain HTTP, weak TLS, missing headers |
|
|
50
|
-
| Tool metadata | META-01..05 |
|
|
50
|
+
| Tool metadata | META-01..05 | instructions aimed at the model ("always call this first", "do not tell the user"), requests to send conversation, system prompt or secrets out, cross-tool shadowing, credential paths, URLs, base64, HTML comments, zero-width/bidi/homoglyph Unicode; rug-pull diff against a stored SHA-256 baseline; advertised arbitrary command execution or file access (high, critical when unauthenticated) and annotations that contradict capability; schema hygiene; secrets in descriptions or stdio env |
|
|
51
51
|
| Robustness | ERR-01, DOS-01 | stack traces and versions in errors; one oversized argument, one 64-deep nesting probe and 20 paced `tools/list` calls, never more |
|
|
52
52
|
| Active (gated stubs) | INJ-01..05, SUPPLY-01/02, SANDBOX-01 | registered and reported as `not_implemented` in this release |
|
|
53
53
|
| Governance | GOV-01..05 | listed as attestation-only: not assessable by a scan, never marked passed |
|
|
@@ -109,7 +109,8 @@ opt-outs (below), but those are courtesy mechanisms, not permission.
|
|
|
109
109
|
|---|---|
|
|
110
110
|
| `0` | No finding at or above `--fail-on` (default `high`) |
|
|
111
111
|
| `1` | At least one finding at or above `--fail-on` |
|
|
112
|
-
| `2` | Error: target unreachable
|
|
112
|
+
| `2` | Error: target unreachable, bad arguments, ownership verification failed, authorisation refused, or the target opted out |
|
|
113
|
+
| `3` | The target answered but is not an MCP server (for example an ordinary web site); every check is reported not applicable and nothing passes |
|
|
113
114
|
|
|
114
115
|
## SARIF and GitHub code scanning
|
|
115
116
|
|
|
@@ -164,8 +165,12 @@ description after approval is the classic rug pull) and update the file; use
|
|
|
164
165
|
## Reports and evidence
|
|
165
166
|
|
|
166
167
|
- `--json FILE`: the full report, including every request/response pair (timestamp,
|
|
167
|
-
method, status,
|
|
168
|
-
|
|
168
|
+
method, status, every header, bodies up to 64 KiB). Credential headers and
|
|
169
|
+
recognised secret patterns are redacted. Each finding carries a `proof` excerpt
|
|
170
|
+
built from parsed data, a runnable `verify` line and, when confidence is not high,
|
|
171
|
+
a `confidence_reason`. Checks whose preconditions are absent are reported
|
|
172
|
+
`not_applicable` with a reason and never count as passed. The schema is documented
|
|
173
|
+
in [docs/report-schema.md](docs/report-schema.md).
|
|
169
174
|
- `--sarif FILE`: SARIF 2.1.0.
|
|
170
175
|
- `-v`: descriptions and scan notes in the terminal; `-q`: no terminal output.
|
|
171
176
|
|
|
@@ -199,4 +204,4 @@ $ python -m tests.fake_server --preset vulnerable --port 8765 # deliberately v
|
|
|
199
204
|
$ mcpprobe http://127.0.0.1:8765/mcp
|
|
200
205
|
```
|
|
201
206
|
|
|
202
|
-
Security issues: see [SECURITY.md](SECURITY.md). Licence: [
|
|
207
|
+
Security issues: see [SECURITY.md](SECURITY.md). Licence: [Apache-2.0](LICENSE) from 0.2.0 (see [NOTICE](NOTICE)); 0.1.0 was released under MIT.
|