diff --git a/internal/config/config_stdin_test.go b/internal/config/config_stdin_test.go index 17985547..17526e38 100644 --- a/internal/config/config_stdin_test.go +++ b/internal/config/config_stdin_test.go @@ -16,7 +16,7 @@ func TestConvertStdinServerConfig_StdioServer(t *testing.T) { Entrypoint: "/bin/custom", EntrypointArgs: []string{"arg1", "arg2"}, Args: []string{"--network", "host"}, - Mounts: []string{"/tmp:/tmp"}, + Mounts: []string{"/tmp:/tmp:rw"}, Env: map[string]string{ "TEST_VAR": "value123", "PASSTHROUGH": "", @@ -46,7 +46,7 @@ func TestConvertStdinServerConfig_StdioServer(t *testing.T) { // Check mounts assert.Contains(t, result.Args, "-v") - assert.Contains(t, result.Args, "/tmp:/tmp") + assert.Contains(t, result.Args, "/tmp:/tmp:rw") // Check user environment variables assert.Contains(t, result.Args, "TEST_VAR=value123") @@ -380,7 +380,7 @@ func TestConvertStdinServerConfig_ArgsOrdering(t *testing.T) { Entrypoint: "/custom/entry", EntrypointArgs: []string{"entry-arg1"}, Args: []string{"--custom-flag", "value"}, - Mounts: []string{"/host:/container"}, + Mounts: []string{"/host:/container:ro"}, Env: map[string]string{ "MY_VAR": "my-value", }, diff --git a/internal/config/rules/rules.go b/internal/config/rules/rules.go index 83f1e1e0..f32b5cd9 100644 --- a/internal/config/rules/rules.go +++ b/internal/config/rules/rules.go @@ -120,31 +120,28 @@ func TimeoutPositive(timeout int, fieldName, jsonPath string) *ValidationError { return nil } -// MountFormat validates a mount specification in the format "source:dest" or "source:dest:mode" +// MountFormat validates a mount specification in the format "source:dest:mode" // Returns nil if valid, *ValidationError if invalid -// Per MCP Gateway specification v1.7.0 section 4.1.5: +// Per MCP Gateway specification v1.8.0 section 4.1.5: // - Host path MUST be an absolute path // - Container path MUST be an absolute path -// - Mode (if provided) MUST be either "ro" (read-only) or "rw" (read-write) +// - Mode MUST be either "ro" (read-only) or "rw" (read-write) func MountFormat(mount, jsonPath string, index int) *ValidationError { log.Printf("Validating mount format: mount=%s, jsonPath=%s, index=%d", mount, jsonPath, index) parts := strings.Split(mount, ":") - if len(parts) < 2 || len(parts) > 3 { + if len(parts) != 3 { log.Printf("Mount format validation failed: invalid part count=%d", len(parts)) return &ValidationError{ Field: "mounts", - Message: fmt.Sprintf("invalid mount format '%s' (expected 'source:dest' or 'source:dest:mode')", mount), + 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' or 'source:dest:mode' where mode is 'ro' (read-only) or 'rw' (read-write)", + Suggestion: "Use format 'source:dest:mode' where mode is 'ro' (read-only) or 'rw' (read-write), e.g. '/host/path:/container/path:ro'", } } source := parts[0] dest := parts[1] - mode := "" - if len(parts) == 3 { - mode = parts[2] - } + mode := parts[2] // Validate source is not empty if source == "" { @@ -186,8 +183,8 @@ func MountFormat(mount, jsonPath string, index int) *ValidationError { } } - // Validate mode if provided - if mode != "" && mode != "ro" && mode != "rw" { + // Validate mode + if mode != "ro" && mode != "rw" { return &ValidationError{ Field: "mounts", Message: fmt.Sprintf("invalid mount mode '%s' (must be 'ro' or 'rw')", mode), diff --git a/internal/config/rules/rules_test.go b/internal/config/rules/rules_test.go index d3022965..56c9a3ef 100644 --- a/internal/config/rules/rules_test.go +++ b/internal/config/rules/rules_test.go @@ -175,11 +175,12 @@ func TestMountFormat(t *testing.T) { shouldErr: false, }, { - name: "valid mount without mode", + name: "invalid mount without mode", mount: "/host/path:/container/path", jsonPath: "mcpServers.github", index: 0, - shouldErr: false, + shouldErr: true, + errMsg: "invalid mount format", }, { name: "invalid format - too many colons", diff --git a/internal/config/validation_schema.go b/internal/config/validation_schema.go index 50bbeb4a..898e80b1 100644 --- a/internal/config/validation_schema.go +++ b/internal/config/validation_schema.go @@ -20,7 +20,7 @@ var ( // Compile regex patterns from schema for additional validation containerPattern = regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9./_-]*(:([a-zA-Z0-9._-]+|latest))?$`) urlPattern = regexp.MustCompile(`^https?://.+`) - mountPattern = regexp.MustCompile(`^[^:]+:[^:]+(:(ro|rw))?$`) + mountPattern = regexp.MustCompile(`^[^:]+:[^:]+:(ro|rw)$`) domainVarPattern = regexp.MustCompile(`^\$\{[A-Z_][A-Z0-9_]*\}$`) // logSchema is the debug logger for schema validation diff --git a/internal/config/validation_schema_test.go b/internal/config/validation_schema_test.go index b7fd9f33..2ed95f0f 100644 --- a/internal/config/validation_schema_test.go +++ b/internal/config/validation_schema_test.go @@ -392,7 +392,7 @@ func TestValidateStringPatterns(t *testing.T) { shouldErr: false, }, { - name: "valid mount without mode", + name: "invalid mount without mode", config: &StdinConfig{ MCPServers: map[string]*StdinServerConfig{ "test": { @@ -402,7 +402,7 @@ func TestValidateStringPatterns(t *testing.T) { }, }, }, - shouldErr: false, + shouldErr: true, }, { name: "valid http url pattern", diff --git a/internal/config/validation_string_patterns_test.go b/internal/config/validation_string_patterns_test.go index 3c212624..aa04c61d 100644 --- a/internal/config/validation_string_patterns_test.go +++ b/internal/config/validation_string_patterns_test.go @@ -136,9 +136,9 @@ func TestValidateStringPatternsComprehensive(t *testing.T) { shouldError: false, }, { - name: "valid mount without mode", + name: "invalid mount without mode", mounts: []string{"/host/path:/container/path"}, - shouldError: false, + shouldError: true, }, { name: "valid multiple mounts", diff --git a/internal/config/validation_test.go b/internal/config/validation_test.go index 53cc1ad6..bfa629cc 100644 --- a/internal/config/validation_test.go +++ b/internal/config/validation_test.go @@ -310,13 +310,13 @@ func TestValidateStdioServer(t *testing.T) { shouldErr: false, }, { - name: "valid mount without mode", + name: "invalid mount without mode", server: &StdinServerConfig{ Type: "stdio", Container: "test:latest", Mounts: []string{"/host:/container"}, }, - shouldErr: false, + shouldErr: true, }, { name: "invalid mount format - too many parts",