Skip to content

Enforce HTTPS-only for custom schema URLs (spec §4.1.4)#1400

Merged
lpcox merged 2 commits intomainfrom
copilot/fix-compliance-issue-https
Feb 25, 2026
Merged

Enforce HTTPS-only for custom schema URLs (spec §4.1.4)#1400
lpcox merged 2 commits intomainfrom
copilot/fix-compliance-issue-https

Conversation

Copy link
Contributor

Copilot AI commented Feb 25, 2026

validateCustomSchemas accepted 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.go

  • validateCustomSchemas now rejects any non-empty schema URL that doesn't start with https://, returning a ValidationError with a clear message and suggestion
// Now rejected:
"customSchemas": { "mytype": "http://example.com/schema.json" }
// → Configuration error: custom schema URL must use HTTPS, got 'http://example.com/schema.json'

// Still accepted:
"customSchemas": { "mytype": "https://example.com/schema.json" }
"customSchemas": { "mytype": "" }  // empty = skip schema validation

internal/config/custom_types_test.go

  • T-CFG-013b: Asserts validateCustomSchemas rejects http://, ftp://, and scheme-less URLs
  • T-CFG-013c: Asserts validateCustomSchemas accepts https:// URLs and empty strings

Note: existing T-CFG-010/012/014 tests use httptest.NewServer (HTTP) against validateServerConfigWithCustomSchemas, which bypasses validateCustomSchemas. Those tests remain valid as they exercise schema-fetch-and-validate logic independently of URL scheme enforcement.

Warning

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
    • Triggering command: /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
    • Triggering command: /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
    • Triggering command: /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
    • Triggering command: /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
    • Triggering command: /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 into connection.go + http_transport.go (refactor only)
  • internal/server/transport.goapplyAuthIfConfigured moved to auth.go, withResponseLogging moved to http_helpers.go
  • Commit reviewed: 387f45d3dbcb2c917a56501047a633eaf981605f

The 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:

"Custom schema URLs MUST be HTTPS URLs only (for security reasons)"

Current State:

The validateCustomSchemas function in internal/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 with https://.

Additionally, validateCustomServerConfig (lines 167–199) passes the URL directly to validateAgainstCustomSchema without checking the URL scheme.

// internal/config/validation.go:316-333 (current)
func validateCustomSchemas(customSchemas map[string]interface{}) error {
    // ...
    for typeName := range customSchemas {
        // Only checks reserved type names — no URL scheme check
        if typeName == "stdio" || typeName == "http" {
            return rules.UnsupportedType(...)
        }
    }
    return nil
}

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-010 and T-CFG-012 in internal/config/custom_types_test.go (lines 18, 21, 111) use httptest.NewServer (HTTP, not HTTPS) as the mock schema server and these tests pass — confirming HTTP URLs are accepted:

// internal/config/custom_types_test.go:18-19 (T-CFG-010)
// Create a mock HTTP server that returns a valid JSON schema for the custom type
mockSchemaServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

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-333validateCustomSchemas missing URL scheme check
  • internal/config/validation.go:167-199validateCustomServerConfig passes URL without HTTPS validation
  • internal/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 server

Suggested Remediation Tasks

Task 1: Enforce HTTPS-Only Custom Schema URLs

Description: Add URL scheme validation in validateCustomSchemas to reject any non-empty custom schema URL that does not start with https://.

Files to modify:

  • internal/config/validation.go — add HTTPS check in validateCustomSchemas

Suggested fix:

func validateCustomSchemas(customSchemas map[string]interface{}) error {
    if customSchemas == nil {
        return nil
    }
    for typeName, schemaValue := range customSchemas {
        if typeName == "stdio" || typeName == "http" {
            return rules.UnsupportedType(...)
        }
        // NEW: Enforce HTTPS-only for non-empty URLs
        if schemaURL, ok := schemaValue.(string); ok && schemaURL != "" {
            if !strings.HasPrefix(schemaURL, "https://") {
                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., '(example.com/redacted),
                }
            }
        }
    }
    return nil
}

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...

Custom agent used: agentic-workflows
GitHub Agentic Workflows (gh-aw) - Create, debug, and upgrade AI-powered workflows with intelligent prompt routing


🔒 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.

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix compliance issue with custom schema URLs Enforce HTTPS-only for custom schema URLs (spec §4.1.4) Feb 25, 2026
@lpcox lpcox marked this pull request as ready for review February 25, 2026 18:08
Copilot AI review requested due to automatic review settings February 25, 2026 18:08
@lpcox lpcox merged commit 44937a6 into main Feb 25, 2026
2 checks passed
@lpcox lpcox deleted the copilot/fix-compliance-issue-https branch February 25, 2026 18:08
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 validateCustomSchemas to 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://") {
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
if !strings.HasPrefix(schemaURL, "https://") {
if !strings.HasPrefix(strings.ToLower(schemaURL), "https://") {

Copilot uses AI. Check for mistakes.
name string
schemaURL string
}{
{"https_url", "https://example.com/schema.json"},
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
{"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"},

Copilot uses AI. Check for mistakes.
Comment on lines +331 to +341
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')",
}
}
}
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[compliance] Compliance Gap: Custom Schema URLs MUST be HTTPS Only (Section 4.1.4)

3 participants