MCP Gateway Compliance Review - 2026-02-21
Summary
Found 1 compliance issue (MUST violation) during daily review of commit e3e8080.
Recent Changes Reviewed
- Only commit
e3e8080 affected workflow/CI files; no changes to internal/, main.go, or core gateway logic in the last 10 commits
- Full compliance audit of current codebase performed against MCP Gateway Specification v1.8.0
Critical Issues (MUST violations)
1. Volume Mount Mode Is Accepted As Optional But Spec Requires It
Specification Section: 4.1.5 Volume Mounts for Stdio Servers
Deep Link: https://github.com/github/gh-aw/blob/main/docs/src/content/docs/reference/mcp-gateway.md#415-volume-mounts-for-stdio-servers
Requirement:
"Volume mounts MUST use the format: "host:container:mode""
"Each mount string MUST conform to the 'host:container:mode' format"
"The mode MUST be either 'ro' (read-only) or 'rw' (read-write)"
Current State:
In internal/config/rules/rules.go:123-131, the function is documented and validated as accepting either the 2-part "source:dest" format or the 3-part "source:dest:mode" format:
// MountFormat validates a mount specification in the format "source:dest" or "source:dest:mode"
// ...
// - Mode (if provided) MUST be either "ro" (read-only) or "rw" (read-write)
func MountFormat(mount, jsonPath string, index int) *ValidationError {
parts := strings.Split(mount, ":")
if len(parts) < 2 || len(parts) > 3 { // accepts 2 OR 3 parts
...
}
...
// Validate mode if provided (mode treated as optional)
if mode != "" && mode != "ro" && mode != "rw" {
This behavior is tested and confirmed in multiple test files:
internal/config/rules/rules_test.go:178: "valid mount without mode" passes validation
internal/config/validation_test.go:313: "valid mount without mode" passes validation
internal/config/validation_string_patterns_test.go:139: same
internal/config/validation_schema_test.go:395: same
Gap:
The specification in Section 4.1.5 unambiguously requires the 3-part "host:container:mode" format with mode being mandatory. The compliance test T-CFG-015 ("Valid volume mount format (host:container:mode)") is defined in Section 10.1.1 and describes the required format with an explicit mode component.
The implementation diverges from this by treating mode as optional, accepting "/host:/container" where the spec requires "/host:/container:ro" or "/host:/container:rw".
Security Impact:
This gap has security implications: without a required mode, a user may omit mode and rely on an undocumented default. Docker defaults to rw for mounts without a mode, meaning read-write access may be granted when read-only was intended. The spec explicitly recommends: "Read-only mounts ('ro') SHOULD be preferred when the server only needs to read data." Forcing explicit mode declaration aligns with the principle of least privilege.
Severity: Critical (MUST violation)
File References:
internal/config/rules/rules.go:123-128 — comment and function signature treating mode as optional
internal/config/rules/rules.go:131 — condition len(parts) < 2 || len(parts) > 3 accepts 2-part format
internal/config/rules/rules.go:178-180 — mode-only validation is skipped if empty
internal/config/rules/rules_test.go:178-186 — "valid mount without mode" test case
internal/config/validation_test.go:313-320 — "valid mount without mode" test case
Suggested Fix:
- Update
MountFormat in internal/config/rules/rules.go to require exactly 3 parts:
// MountFormat validates a mount specification in the format "source:dest:mode"
// Per MCP Gateway specification v1.7.0 section 4.1.5:
// - Host path MUST be an absolute path
// - Container path MUST be an absolute path
// - Mode MUST be either "ro" (read-only) or "rw" (read-write)
func MountFormat(mount, jsonPath string, index int) *ValidationError {
parts := strings.Split(mount, ":")
if len(parts) != 3 { // require exactly 3 parts
return &ValidationError{
Field: "mounts",
Message: fmt.Sprintf("invalid mount format '%s' (expected 'source:dest:mode')", mount),
JSONPath: fmt.Sprintf("%s.mounts[%d]", jsonPath, index),
Suggestion: "Use format 'source:dest:mode' where mode is 'ro' (read-only) or 'rw' (read-write), e.g. '/host/path:/container/path:ro'",
}
}
...
-
Update the related test cases to remove "valid mount without mode" cases and instead add tests that verify 2-part mounts are rejected.
-
Update the error message suggestion in rules.go:136 to only show the 3-part format.
Compliance Status
| Section |
Status |
| ✅ Configuration Format (Section 4.1) |
Compliant |
| ✅ Variable Expression Rendering (Section 4.2) |
Compliant |
✅ command field rejected (Section 3.2.1) |
Compliant |
| ✅ Containerization requirement (Section 3.2.1) |
Compliant |
| ✅ Payload directory validation (Section 4.1.3.1) |
Compliant |
| ❌ Volume mount mode required (Section 4.1.5) |
Non-compliant (MUST violation) |
| ✅ HTTP server mounts rejected (Section 4.1.5) |
Compliant |
✅ Health endpoint specVersion/gatewayVersion (Section 8.1.1) |
Compliant ("1.8.0") |
✅ /close endpoint idempotency — HTTP 410 (Section 5.1.3) |
Compliant |
✅ /close endpoint requires auth (Section 5.1.3 / Appendix D.4) |
Compliant |
✅ /health endpoint auth-exempt (Section 7.4) |
Compliant |
| ✅ Stdout config output — type, url, headers (Section 5.4) |
Compliant |
| ✅ HTTP connection failure not silently ignored (Section 5.2.2) |
Compliant |
| ✅ Server isolation (Section 6) |
Compliant |
| ✅ Authentication (Section 7) |
Compliant |
Remediation Task
Task 1: Require Explicit Mount Mode in Volume Mount Validation
Description: Update MountFormat validation to require exactly 3 components (host:container:mode) instead of accepting the 2-part form. Remove "valid mount without mode" test cases and add cases confirming 2-part mounts are rejected.
Files:
internal/config/rules/rules.go — change length check and make mode required
internal/config/rules/rules_test.go — update test cases
internal/config/validation_test.go — update test cases
internal/config/validation_string_patterns_test.go — update test cases
internal/config/validation_schema_test.go — update test cases
Specification Reference: https://github.com/github/gh-aw/blob/main/docs/src/content/docs/reference/mcp-gateway.md#415-volume-mounts-for-stdio-servers
Compliance Tests Addressed: T-CFG-015, T-CFG-016, T-CFG-017
Estimated Effort: Small (1-2 hours)
References
Generated by Daily Compliance Checker
MCP Gateway Compliance Review - 2026-02-21
Summary
Found 1 compliance issue (MUST violation) during daily review of commit
e3e8080.Recent Changes Reviewed
e3e8080affected workflow/CI files; no changes tointernal/,main.go, or core gateway logic in the last 10 commitsCritical Issues (MUST violations)
1. Volume Mount Mode Is Accepted As Optional But Spec Requires It
Specification Section: 4.1.5 Volume Mounts for Stdio Servers
Deep Link: https://github.com/github/gh-aw/blob/main/docs/src/content/docs/reference/mcp-gateway.md#415-volume-mounts-for-stdio-servers
Requirement:
Current State:
In
internal/config/rules/rules.go:123-131, the function is documented and validated as accepting either the 2-part"source:dest"format or the 3-part"source:dest:mode"format:This behavior is tested and confirmed in multiple test files:
internal/config/rules/rules_test.go:178:"valid mount without mode"passes validationinternal/config/validation_test.go:313:"valid mount without mode"passes validationinternal/config/validation_string_patterns_test.go:139: sameinternal/config/validation_schema_test.go:395: sameGap:
The specification in Section 4.1.5 unambiguously requires the 3-part
"host:container:mode"format with mode being mandatory. The compliance test T-CFG-015 ("Valid volume mount format (host:container:mode)") is defined in Section 10.1.1 and describes the required format with an explicit mode component.The implementation diverges from this by treating mode as optional, accepting
"/host:/container"where the spec requires"/host:/container:ro"or"/host:/container:rw".Security Impact:
This gap has security implications: without a required mode, a user may omit mode and rely on an undocumented default. Docker defaults to
rwfor mounts without a mode, meaning read-write access may be granted when read-only was intended. The spec explicitly recommends: "Read-only mounts ('ro') SHOULD be preferred when the server only needs to read data." Forcing explicit mode declaration aligns with the principle of least privilege.Severity: Critical (MUST violation)
File References:
internal/config/rules/rules.go:123-128— comment and function signature treating mode as optionalinternal/config/rules/rules.go:131— conditionlen(parts) < 2 || len(parts) > 3accepts 2-part formatinternal/config/rules/rules.go:178-180— mode-only validation is skipped if emptyinternal/config/rules/rules_test.go:178-186— "valid mount without mode" test caseinternal/config/validation_test.go:313-320— "valid mount without mode" test caseSuggested Fix:
MountFormatininternal/config/rules/rules.goto require exactly 3 parts:Update the related test cases to remove "valid mount without mode" cases and instead add tests that verify 2-part mounts are rejected.
Update the error message suggestion in
rules.go:136to only show the 3-part format.Compliance Status
commandfield rejected (Section 3.2.1)specVersion/gatewayVersion(Section 8.1.1)"1.8.0")/closeendpoint idempotency — HTTP 410 (Section 5.1.3)/closeendpoint requires auth (Section 5.1.3 / Appendix D.4)/healthendpoint auth-exempt (Section 7.4)Remediation Task
Task 1: Require Explicit Mount Mode in Volume Mount Validation
Description: Update
MountFormatvalidation to require exactly 3 components (host:container:mode) instead of accepting the 2-part form. Remove "valid mount without mode" test cases and add cases confirming 2-part mounts are rejected.Files:
internal/config/rules/rules.go— change length check and make mode requiredinternal/config/rules/rules_test.go— update test casesinternal/config/validation_test.go— update test casesinternal/config/validation_string_patterns_test.go— update test casesinternal/config/validation_schema_test.go— update test casesSpecification Reference: https://github.com/github/gh-aw/blob/main/docs/src/content/docs/reference/mcp-gateway.md#415-volume-mounts-for-stdio-servers
Compliance Tests Addressed: T-CFG-015, T-CFG-016, T-CFG-017
Estimated Effort: Small (1-2 hours)
References
e3e8080(HEAD)