Repository navigation
fix: mcp auth TLS gate falls back to entry.url when caller omits url - #3797
dylanyunlon wants to merge 2 commits into
Conversation
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
|
Welcome to the Agent Governance Toolkit! Thanks for your first pull request. |
|
🟡 Contributor Check: MEDIUM
Automated check by AGT Contributor Check. |
77da419 to
d4297ad
Compare
PR Review Summary
Verdict: AI review comments are untrusted advisory output. The summary reports workflow-generated completion status only, not model-authored pass/fail claims. |
|
@microsoft-github-policy-service agree |
Imran Siddique (imran-siddique)
left a comment
There was a problem hiding this comment.
Ran this branch's suite locally against its own tree (PYTHONPATH=src python -m pytest tests/test_mcp_auth_enforcement.py): 29 passed. The fix is correct and the issue it cites,
#3785, is real.
Confirmed the defect on main: the gate reads if entry.require_tls and url:, so a registered entry
configured with url: http://mcp.internal/api and require_tls: true is allowed whenever the caller
calls policy.check(name, auth_method=...) without repeating the URL. The url parameter was
documented as "for logging/audit", so a caller has no reason to pass it, which makes the bypass the
default path rather than an edge case.
Two things I want to call out as done well, because they are the parts a reviewer would otherwise
have to guess at.
The precedence is the right way round. Caller URL wins over entry.url, so a caller connecting to
somewhere other than the configured address is judged on where it is actually going. Your
test_caller_url_overrides_entry_url and test_caller_https_overrides_entry_http pin both
directions of that, which matters because getting it backwards would turn a config value into an
override of live connection facts.
Keeping "both URLs empty" as allowed is also correct, and worth having stated explicitly the way
your docstring does. That case is fail-open on a missing URL, not fail-open on TLS, and conflating
the two would break add_server() with the default url="".
Overlap you and the maintainers should know about. #3793 fixes the same defect with the same
mechanism (effective_url = url or entry.url) and is functionally identical to this branch. The
difference is coverage shape: #3793 has a single parametrized test that sweeps the full nine-way
caller-URL by configured-URL matrix crossed with require_tls, and this branch has twelve named
tests that reach schemes #3793 does not (ws, wss, ftp, bare hostname with no scheme) plus an
end-to-end YAML case plus docs/mcp-auth-tls.md. The two will conflict textually, since they edit
the same lines.
This branch is the more complete one. If it lands, the piece of #3793 worth keeping is its
parametrized precedence matrix, which is a tidier statement of the same property.
Separately, #3849 fixes the sibling hole (#3814, unregistered server names skipping the gate
entirely) in the fallback branch further down the same method. It does not overlap textually with
either of these, so it can land independently.
One thing none of the three touch, so purely a note: min_tls_version is accepted on
McpServerEntry, parsed by from_yaml, and documented in docs/specs/MCP-SECURITY-GATEWAY-1.0.md
and agent-mesh/docs/zero-trust.md, but it is never read by any enforcement code in the repo. A
policy that sets min_tls_version: "1.3" gets no enforcement today. Worth its own issue.
Nothing blocking.
Carlos Hernandez (carloshvp)
left a comment
There was a problem hiding this comment.
Reviewed head d4297add54e1d92c2d1ef206c47ce892d38fee57. All 29 auth-enforcement tests pass on Python 3.11; restoring the parent source makes six of them fail. I also ran a 200-case configured-URL/caller-URL/TLS-required matrix against this PR and #3793. Both preserve caller precedence, reject non-TLS and malformed configured URLs when no caller URL is supplied, preserve the both-empty compatibility case, and continue denying none/unknown auth methods in the tested defaults.
The full gateway-conformance module could not collect because the local environment lacks agent_control_specification. I extracted and ran its unchanged TestAuthEnforcement class in isolation: all 12 S10 tests pass, including S10.12. This does not establish full gateway conformance.
I recommend this PR for #3785, given its scheme coverage, YAML regression and documentation. Treat #3793 as the alternative fix, and land #3849 separately for unregistered-server fallback. Non-blocking documentation nit: update the check() URL argument description from "for logging/audit" to explain TLS enforcement and configured-URL fallback, as #3793 does.
No blocking finding. The four reported lint findings also occur on the parent. Signoff and merge simulation against main 359a2332 check out. The later PR-title checks passed after the earlier failure.
| # URL) is allowed through; this preserves spec S10.12 | ||
| # which permits add_server() with default url="". | ||
| if entry.require_tls: | ||
| effective_url = url or entry.url |
There was a problem hiding this comment.
The implementation is correct, but the url parameter is still documented as logging and audit metadata even though it now takes precedence in the TLS decision. The guide also says this closes the bypass for an untrusted caller, but a caller that controls url can claim an HTTPS value regardless of the actual destination. Please document that url must be the trusted live destination and remove the untrusted-caller claim.
There was a problem hiding this comment.
Updated in abf1f0a. The docstring now reads:
url: The actual destination URL for this connection. When
require_tls is true, the scheme of this URL is checked
against the TLS allowlist (https, wss). If empty, the
configured entry.url is used as fallback. Pass the
trusted, live destination -- not an unverified value
from the caller.
Also removed the untrusted-caller claim from docs/mcp-auth-tls.md and replaced it with a note that url is trusted input -- a caller that controls it can claim any scheme, so verifying the actual destination is the transport layer's job, not the policy gate's.
Address review feedback: the url parameter docstring still described it as 'for logging/audit' even though it now takes precedence in the TLS decision. Updated to document that url must be the trusted live destination and that the TLS gate validates the stated destination. Removed the 'untrusted caller' claim from the security considerations section — a caller that controls the url argument can claim any scheme, so the gate does not protect against a caller supplying a false URL. Added a note clarifying the trust boundary.
Add 34 new tests (29 → 63 total) covering the full caller-URL × entry-URL × require_tls matrix, edge cases (bare hostname, uppercase scheme, whitespace URL, error messages, dynamic add/remove, multi-server independence), and YAML round-trip integration. Extend docs/mcp-auth-tls.md with a URL resolution truth table and troubleshooting section for common TLS gate error messages.
3f063a2 to
8bed3f1
Compare
|
Closing as superseded: the TLS fallback itself landed via #3793, so what remains here is the extra test matrix and docs, on unsigned commits. Thanks for the work; a signed follow-up PR carrying just the added tests would be welcome. |
Summary
Fixes #3785 — the
require_tlsgate inMcpAuthPolicy.check()never consulted the configuredentry.urlwhen the caller-suppliedurlwas empty (or omitted). A caller that simply omitsurlcould bypass the TLS requirement even when the server entry hadrequire_tls: trueand a configured non-TLSentry.url.Changes
Core fix (
mcp_auth_enforcement.py)The TLS gate now resolves an effective URL via
url or entry.urlbefore evaluating the scheme. When both sources are empty, the gate permits the connection — preserving spec S10.12 compatibility (add_server()with defaulturl=""must remain allowed).Before:
After:
Tests (
test_mcp_auth_enforcement.py)12 new tests in
TestTlsGateEntryUrlFallbackcovering:http/https/wss/ws/ftpentry.url fallback when caller omits urlrequire_tls=FalsebypassAll 29 tests pass (17 existing + 12 new).
Documentation (
docs/mcp-auth-tls.md)New doc covering URL resolution order, configuration examples (Python + YAML), allowed TLS schemes, and security considerations.
README
Added doc link under the Reference section.
Checklist
Signed-off-by: dylanyunlon dogechat@163.com