From 240c561850190422ea5d8a7e6ad0fbdc6ac54c0f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 23 Jan 2026 15:58:57 +0000 Subject: [PATCH 1/2] Initial plan From cd784eede8ab19382a43a39c6881264fd06d8be9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 23 Jan 2026 16:06:48 +0000 Subject: [PATCH 2/2] Fix duplicate test function declarations and formatting issues Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> --- internal/config/validation_schema.go | 10 +-- internal/logger/rpc_helpers_test.go | 10 +-- internal/logger/rpc_logger_test.go | 108 --------------------------- 3 files changed, 10 insertions(+), 118 deletions(-) diff --git a/internal/config/validation_schema.go b/internal/config/validation_schema.go index 206ef9036..dee0dcbb1 100644 --- a/internal/config/validation_schema.go +++ b/internal/config/validation_schema.go @@ -23,7 +23,7 @@ var ( // gatewayVersion stores the version string to include in error messages gatewayVersion = "dev" - + // logSchema is the debug logger for schema validation logSchema = logger.New("config:validation_schema") ) @@ -39,7 +39,7 @@ func SetGatewayVersion(version string) { // regex patterns that use negative lookahead (not supported in JSON Schema Draft 7) func fetchAndFixSchema(url string) ([]byte, error) { logSchema.Printf("Fetching schema from URL: %s", url) - + client := &http.Client{ Timeout: 10 * time.Second, } @@ -55,7 +55,7 @@ func fetchAndFixSchema(url string) ([]byte, error) { logSchema.Printf("Schema fetch returned non-OK status: %d", resp.StatusCode) return nil, fmt.Errorf("failed to fetch schema: HTTP %d", resp.StatusCode) } - + logSchema.Print("Schema fetched successfully, applying fixes") schemaBytes, err := io.ReadAll(resp.Body) @@ -121,7 +121,7 @@ func fetchAndFixSchema(url string) ([]byte, error) { // validateJSONSchema validates the raw JSON configuration against the JSON schema func validateJSONSchema(data []byte) error { logSchema.Printf("Starting JSON schema validation: data_size=%d bytes", len(data)) - + // Fetch the schema from the remote URL (source of truth) schemaURL := "https://raw.githubusercontent.com/githubnext/gh-aw/main/docs/public/schemas/mcp-gateway-config.schema.json" schemaJSON, err := fetchAndFixSchema(schemaURL) @@ -295,7 +295,7 @@ func formatErrorContext(ve *jsonschema.ValidationError, prefix string) string { // This provides additional validation beyond the JSON schema validation func validateStringPatterns(stdinCfg *StdinConfig) error { logSchema.Printf("Validating string patterns: server_count=%d", len(stdinCfg.MCPServers)) - + // Validate server configurations for name, server := range stdinCfg.MCPServers { jsonPath := fmt.Sprintf("mcpServers.%s", name) diff --git a/internal/logger/rpc_helpers_test.go b/internal/logger/rpc_helpers_test.go index 6112c62f7..9f422c17b 100644 --- a/internal/logger/rpc_helpers_test.go +++ b/internal/logger/rpc_helpers_test.go @@ -217,13 +217,13 @@ func TestTruncateAndSanitize(t *testing.T) { name: "sanitize secrets - GitHub token", payload: "token: ghp_1234567890abcdefghijklmnopqrstuvwxyz", maxLength: 100, - want: "token: [REDACTED]", + want: "token=[REDACTED]", }, { name: "sanitize and truncate", payload: "auth bearer ghp_1234567890abcdefghijklmnopqrstuvwxyz " + strings.Repeat("x", 100), maxLength: 50, - want: "auth bearer [REDACTED] " + strings.Repeat("x", 23) + "...", + want: "auth bearer [REDACTED] " + strings.Repeat("x", 27) + "...", }, } @@ -308,18 +308,18 @@ func TestExtractEssentialFields(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := extractEssentialFields([]byte(tt.payload)) - + if tt.want == nil { assert.Nil(t, result) return } - + require.NotNil(t, result) assert.Equal(t, tt.want["jsonrpc"], result["jsonrpc"]) assert.Equal(t, tt.want["method"], result["method"]) assert.Equal(t, tt.want["id"], result["id"]) assert.Equal(t, tt.want["error"], result["error"]) - + // Special handling for params_keys since order may vary if expectedKeys, ok := tt.want["params_keys"].([]string); ok { actualKeys, ok := result["params_keys"].([]string) diff --git a/internal/logger/rpc_logger_test.go b/internal/logger/rpc_logger_test.go index cc3ddc936..3f2d78810 100644 --- a/internal/logger/rpc_logger_test.go +++ b/internal/logger/rpc_logger_test.go @@ -11,114 +11,6 @@ import ( "github.com/stretchr/testify/require" ) -func TestTruncateAndSanitize(t *testing.T) { - tests := []struct { - name string - input string - maxLength int - wantLen int // Expected length (may be less due to sanitization) - wantRedacted bool - }{ - { - name: "short message without secrets", - input: "Hello, world!", - maxLength: 50, - wantLen: 13, - wantRedacted: false, - }, - { - name: "long message gets truncated", - input: `{"method":"test","data":"` + strings.Repeat("x", 200) + `"}`, - maxLength: 100, - wantLen: 103, // 100 + "..." - wantRedacted: false, - }, - { - name: "message with token gets sanitized", - input: "Authorization: ghp_1234567890123456789012345678901234567890", - maxLength: 150, - wantLen: -1, // Variable due to redaction - wantRedacted: true, - }, - { - name: "message with password gets sanitized", - input: "password=supersecretpassword123", - maxLength: 150, - wantLen: -1, // Variable due to redaction - wantRedacted: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := truncateAndSanitize(tt.input, tt.maxLength) - - if tt.wantRedacted { - if !strings.Contains(result, "[REDACTED]") { - t.Errorf("Expected result to contain [REDACTED], got: %s", result) - } - } else { - if tt.wantLen > 0 && len(result) != tt.wantLen { - t.Errorf("Expected length %d, got %d: %s", tt.wantLen, len(result), result) - } - } - - // Ensure result is not longer than maxLength + 3 (for "...") - if !tt.wantRedacted && len(result) > tt.maxLength+3 { - t.Errorf("Result too long: %d > %d", len(result), tt.maxLength+3) - } - }) - } -} - -func TestExtractEssentialFields(t *testing.T) { - tests := []struct { - name string - payload string - wantKeys []string - }{ - { - name: "JSON-RPC request", - payload: `{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}`, - wantKeys: []string{"jsonrpc", "id", "method", "params_keys"}, - }, - { - name: "JSON-RPC response with result", - payload: `{"jsonrpc":"2.0","id":1,"result":{"tools":[]}}`, - wantKeys: []string{"jsonrpc", "id"}, - }, - { - name: "JSON-RPC response with error", - payload: `{"jsonrpc":"2.0","id":1,"error":{"code":-32600,"message":"Invalid request"}}`, - wantKeys: []string{"jsonrpc", "id", "error"}, - }, - { - name: "invalid JSON", - payload: `{invalid json}`, - wantKeys: nil, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := extractEssentialFields([]byte(tt.payload)) - - if tt.wantKeys == nil { - assert.Nil(t, result, "Expected nil result for invalid JSON") - return - } - - require.NotNil(t, result, "Expected result map, got nil") - - for _, key := range tt.wantKeys { - if _, ok := result[key]; !ok { - t.Errorf("Expected key %s not found in result: %v", key, result) - } - } - }) - } -} - func TestFormatRPCMessage(t *testing.T) { tests := []struct { name string