Enforce HTTPS-only for custom schema URLs (spec §4.1.4)#1400
Conversation
Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR enforces the HTTPS-only requirement for custom schema URLs as mandated by MCP Gateway specification §4.1.4. The change adds validation to reject non-HTTPS URLs (http://, ftp://, or scheme-less URLs) early in the configuration parsing process, improving security by preventing potential man-in-the-middle attacks on schema fetching.
Changes:
- Added HTTPS scheme validation in
validateCustomSchemasto reject custom schema URLs that don't use HTTPS - Added comprehensive test coverage for both rejection of non-HTTPS URLs and acceptance of valid HTTPS URLs
- Enhanced logging to track validation failures related to non-HTTPS schema URLs
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| internal/config/validation.go | Added HTTPS-only validation logic for custom schema URLs in validateCustomSchemas function with appropriate error messages and logging |
| internal/config/custom_types_test.go | Added T-CFG-013b and T-CFG-013c test cases to verify rejection of non-HTTPS URLs and acceptance of HTTPS URLs respectively |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } | ||
| // Enforce HTTPS-only for non-empty schema URLs (spec section 4.1.4) | ||
| if schemaURL, ok := schemaValue.(string); ok && schemaURL != "" { | ||
| if !strings.HasPrefix(schemaURL, "https://") { |
There was a problem hiding this comment.
The HTTPS validation uses case-sensitive string prefix matching with strings.HasPrefix(schemaURL, "https://"). This means URLs with uppercase schemes like "HTTPS://example.com/schema.json" or "Https://example.com/schema.json" will be rejected, even though URL schemes are case-insensitive according to RFC 3986 section 3.1.
Consider normalizing the scheme to lowercase before validation, or use case-insensitive comparison. For example:
- Use
strings.ToLower(schemaURL)before checking, or - Use
strings.HasPrefix(strings.ToLower(schemaURL), "https://")
This ensures URLs with any case variation of "https" are accepted.
| if !strings.HasPrefix(schemaURL, "https://") { | |
| if !strings.HasPrefix(strings.ToLower(schemaURL), "https://") { |
| name string | ||
| schemaURL string | ||
| }{ | ||
| {"https_url", "https://example.com/schema.json"}, |
There was a problem hiding this comment.
The test cases should include uppercase and mixed-case URL schemes to verify behavior with case variations. According to RFC 3986, URL schemes are case-insensitive, so "HTTPS://", "Https://", and "https://" should all be treated equivalently.
Add test cases for:
These should be accepted (if the validation is fixed to be case-insensitive) or at least have explicit test coverage to document the current behavior.
| {"https_url", "https://example.com/schema.json"}, | |
| {"https_url", "https://example.com/schema.json"}, | |
| {"https_uppercase_scheme", "HTTPS://example.com/schema.json"}, | |
| {"https_mixedcase_scheme", "Https://example.com/schema.json"}, |
| if schemaURL, ok := schemaValue.(string); ok && schemaURL != "" { | ||
| if !strings.HasPrefix(schemaURL, "https://") { | ||
| logValidation.Printf("Non-HTTPS schema URL in customSchemas: typeName=%s, url=%s", typeName, schemaURL) | ||
| return &rules.ValidationError{ | ||
| Field: "customSchemas." + typeName, | ||
| Message: fmt.Sprintf("custom schema URL must use HTTPS, got '%s'", schemaURL), | ||
| JSONPath: "customSchemas." + typeName, | ||
| Suggestion: "Use an HTTPS URL for the custom schema (e.g., 'https://example.com/schema.json')", | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Consider handling URLs with leading/trailing whitespace. The current validation would reject " https://example.com/schema.json " (with spaces) even though it could be valid after trimming.
If whitespace in URLs is considered invalid input and should be rejected, this is acceptable. However, if user-friendliness is desired, consider using strings.TrimSpace(schemaURL) before validation to handle accidental whitespace in configuration files.
validateCustomSchemasaccepted any URL scheme for custom schema entries, violating the MUST requirement in spec §4.1.4 that custom schema URLs be HTTPS-only.Changes
internal/config/validation.govalidateCustomSchemasnow rejects any non-empty schema URL that doesn't start withhttps://, returning aValidationErrorwith a clear message and suggestioninternal/config/custom_types_test.govalidateCustomSchemasrejectshttp://,ftp://, and scheme-less URLsvalidateCustomSchemasacceptshttps://URLs and empty stringsWarning
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
example.com/tmp/go-build654390369/b284/launcher.test /tmp/go-build654390369/b284/launcher.test -test.testlogfile=/tmp/go-build654390369/b284/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true 64/src/runtime/cgo user.name x_amd64/vet(dns block)invalid-host-that-does-not-exist-12345.com/tmp/go-build654390369/b266/config.test /tmp/go-build654390369/b266/config.test -test.testlogfile=/tmp/go-build654390369/b266/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true 64/src/runtime/cgo 64/src/bufio/bufio.go ache/go/1.25.6/x64/pkg/tool/linu--64 credential.usern/opt/hostedtoolcache/go/1.25.6/x64/pkg/tool/linux_amd64/vet abis(dns block)nonexistent.local/tmp/go-build654390369/b284/launcher.test /tmp/go-build654390369/b284/launcher.test -test.testlogfile=/tmp/go-build654390369/b284/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true 64/src/runtime/cgo user.name x_amd64/vet(dns block)slow.example.com/tmp/go-build654390369/b284/launcher.test /tmp/go-build654390369/b284/launcher.test -test.testlogfile=/tmp/go-build654390369/b284/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true 64/src/runtime/cgo user.name x_amd64/vet(dns block)this-host-does-not-exist-12345.com/tmp/go-build654390369/b293/mcp.test /tmp/go-build654390369/b293/mcp.test -test.testlogfile=/tmp/go-build654390369/b293/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true ache/go/1.25.6/x64/src/runtime/cgo .cfg ache/uv/0.10.6/x86_64/as(dns block)If you need me to access, download, or install something from one of these locations, you can either:
Original prompt
This section details on the original issue you should resolve
<issue_title>[compliance] Compliance Gap: Custom Schema URLs MUST be HTTPS Only (Section 4.1.4)</issue_title>
<issue_description>## MCP Gateway Compliance Review — 2026-02-25
Summary
Found 1 critical compliance issue during daily review of commit
387f45d(Refactor: split mcp/connection.go by concern, relocate misplaced server helpers #1373).Recent Changes Reviewed
internal/mcp/connection.go— split intoconnection.go+http_transport.go(refactor only)internal/server/transport.go—applyAuthIfConfiguredmoved toauth.go,withResponseLoggingmoved tohttp_helpers.go387f45d3dbcb2c917a56501047a633eaf981605fThe refactor itself is clean — no behavioral regressions detected. However, the review surfaced a pre-existing MUST violation in custom schema URL validation.
Critical Issues (MUST violations)
Issue 1: Custom Schema URLs Not Validated to be HTTPS-Only
Specification Section: 4.1.4 Custom Server Types
Deep Link: https://github.com/github/gh-aw/blob/main/docs/src/content/docs/reference/mcp-gateway.md#414-custom-server-types
Requirement:
Current State:
The
validateCustomSchemasfunction ininternal/config/validation.go(lines 316–333) validates only that custom type names do not conflict with reserved types ("stdio","http"). It does not validate that the schema URL starts withhttps://.Additionally,
validateCustomServerConfig(lines 167–199) passes the URL directly tovalidateAgainstCustomSchemawithout checking the URL scheme.As a result, a configuration like:
{ "customSchemas": { "mytype": "(insecure.example.com/redacted) } }is accepted without error, violating the MUST requirement.
Evidence from Tests:
The compliance tests
T-CFG-010andT-CFG-012ininternal/config/custom_types_test.go(lines 18, 21, 111) usehttptest.NewServer(HTTP, not HTTPS) as the mock schema server and these tests pass — confirming HTTP URLs are accepted:Gap:
The implementation silently accepts
(redacted) and potentially(redacted) URLs for custom schemas, which is a security risk (man-in-the-middle attacks on schema fetching) and a MUST violation per Section 4.1.4.Severity: Critical (MUST violation)
File References:
internal/config/validation.go:316-333—validateCustomSchemasmissing URL scheme checkinternal/config/validation.go:167-199—validateCustomServerConfigpasses URL without HTTPS validationinternal/config/custom_types_test.go:18— T-CFG-010 uses HTTP mock server (should expect rejection or use HTTPS)internal/config/validate_against_custom_schema_test.go:63— Test uses HTTP mock serverSuggested Remediation Tasks
Task 1: Enforce HTTPS-Only Custom Schema URLs
Description: Add URL scheme validation in
validateCustomSchemasto reject any non-empty custom schema URL that does not start withhttps://.Files to modify:
internal/config/validation.go— add HTTPS check invalidateCustomSchemasSuggested fix:
Specification Reference: https://github.com/github/gh-aw/blob/main/docs/src/content/docs/reference/mcp-gateway.md#414-custom-server-types
Estimated Effort: Small (1–2 hours incl...
🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.