From 7cc3c75d56a8981043103cac0a490a1b588a5548 Mon Sep 17 00:00:00 2001 From: Rakhi Dutta Date: Mon, 6 Apr 2026 00:02:49 +0100 Subject: [PATCH 1/6] fix: handle non-JSON responses and query params in REST tools (#3855, #3857) Backend: - Add _handle_json_parse_error() for graceful fallback when REST APIs return HTML, plain text, XML, or responses with encoding issues - Catch json.JSONDecodeError, orjson.JSONDecodeError, UnicodeDecodeError, and AttributeError during response parsing (httpx uses stdlib json, not orjson) - Add configurable REST_RESPONSE_TEXT_MAX_LENGTH (default: 5000, range: 1000-100000) to truncate non-JSON response text and limit sensitive data exposure - Fix query param handling: GET requests merge URL params with input args (URL params take precedence on conflicts); POST/PUT/PATCH/DELETE preserve query params in URL for signed URL support (Azure SAS, AWS presigned, etc.) - Add jq filter validation to detect email addresses mistakenly used as filters Frontend (admin_ui): - Fix runToolTest/runToolValidation to use tools/call JSON-RPC method instead of tool name as method (MCP protocol compliance) - Add invokeTool() function for "Invoke" button in Tools table - Port all JS changes to modular admin_ui/ structure (Vite bundled) Closes #3855 Closes #3857 Signed-off-by: Rakhi Dutta Signed-off-by: Mihai Criveti --- .env.example | 5 + mcpgateway/admin_ui/admin.js | 2 + mcpgateway/admin_ui/tools.js | 199 ++++++- mcpgateway/config.py | 8 + mcpgateway/services/tool_service.py | 93 +++- tests/unit/js/tools.test.js | 7 +- .../mcpgateway/services/test_tool_service.py | 506 ++++++++++++++++++ 7 files changed, 801 insertions(+), 19 deletions(-) diff --git a/.env.example b/.env.example index a213f767a50..b8d4407299c 100644 --- a/.env.example +++ b/.env.example @@ -2136,6 +2136,11 @@ OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317 # TOOL_CONCURRENT_LIMIT=10 # GATEWAY_TOOL_NAME_SEPARATOR=- +# Maximum length of response text returned for non-JSON REST API responses +# Longer responses are truncated to prevent exposing excessive sensitive data +# Default: 5000 characters, Range: 1000-100000 +# REST_RESPONSE_TEXT_MAX_LENGTH=5000 + # Prompt Configuration # PROMPT_CACHE_SIZE=100 # MAX_PROMPT_SIZE=102400 diff --git a/mcpgateway/admin_ui/admin.js b/mcpgateway/admin_ui/admin.js index a6b798c1c95..14bcc856f0a 100644 --- a/mcpgateway/admin_ui/admin.js +++ b/mcpgateway/admin_ui/admin.js @@ -485,6 +485,7 @@ Admin.showUsageStatsModal = showUsageStatsModal; import { editTool, initToolSelect, + invokeTool, testTool, enrichTool, generateToolTestCases, @@ -496,6 +497,7 @@ import { Admin.editTool = editTool; Admin.initToolSelect = initToolSelect; +Admin.invokeTool = invokeTool; Admin.testTool = testTool; Admin.enrichTool = enrichTool; Admin.generateToolTestCases = generateToolTestCases; diff --git a/mcpgateway/admin_ui/tools.js b/mcpgateway/admin_ui/tools.js index 376e2a1107b..7ded08b379b 100644 --- a/mcpgateway/admin_ui/tools.js +++ b/mcpgateway/admin_ui/tools.js @@ -2916,8 +2916,11 @@ export const runToolValidation = async function (testIndex) { const payload = { jsonrpc: "2.0", id: Date.now(), - method: AppState.currentTestTool.name, - params, + method: "tools/call", + params: { + name: AppState.currentTestTool.name, + arguments: params, + }, }; // Parse custom headers from the passthrough headers field @@ -3199,14 +3202,16 @@ export const runToolTest = async function () { const runButton = document.querySelector('button[onclick="runToolTest()"]'); if (!form || !AppState.currentTestTool) { - console.error("Tool test form or current tool not found"); + console.error("Tool test form or current tool not found", { + form: !!form, + currentTestTool: AppState.currentTestTool, + }); showErrorMessage("Tool test form not available"); return; } // Prevent multiple concurrent test runs if (runButton && runButton.disabled) { - console.log("Tool test already running"); return; } @@ -3329,8 +3334,11 @@ export const runToolTest = async function () { const payload = { jsonrpc: "2.0", id: Date.now(), - method: AppState.currentTestTool.name, - params, + method: "tools/call", + params: { + name: AppState.currentTestTool.name, + arguments: params, + }, }; // Parse custom headers from the passthrough headers field @@ -3502,3 +3510,182 @@ export const cleanupToolTestModal = function () { console.error("Error cleaning up tool test modal:", error); } }; + +// =================================================================== +// TOOL INVOCATION (opens test modal by tool name) +// =================================================================== + +/** + * Fetch tool details from the API by name. + * @param {string} toolName - The name of the tool to fetch + * @returns {Promise} The tool object + */ +async function fetchToolDetails(toolName) { + const response = await fetchWithTimeout( + `${window.ROOT_PATH}/admin/tools/${encodeURIComponent(toolName)}`, + { + headers: { + Accept: "application/json", + "Content-Type": "application/json", + }, + } + ); + + if (!response.ok) { + const errorText = await response.text(); + throw new Error( + `Failed to fetch tool details (${response.status}): ${errorText}` + ); + } + + return await response.json(); +} + +/** + * Create a form input field based on schema. + * @param {string} key - The field name + * @param {Object} schema - The field schema + * @param {boolean} isRequired - Whether the field is required + * @returns {HTMLElement} The input element + */ +function createFormInput(key, schema, isRequired) { + let input; + const baseInputClass = + "mt-1 block w-full rounded-md border-gray-300 dark:border-gray-600 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:bg-gray-700 dark:text-gray-200"; + + if (schema.enum) { + input = document.createElement("select"); + input.className = baseInputClass; + schema.enum.forEach((option) => { + const opt = document.createElement("option"); + opt.value = option; + opt.textContent = option; + if (option === schema.default) { + opt.selected = true; + } + input.appendChild(opt); + }); + } else if (schema.type === "boolean") { + input = document.createElement("input"); + input.type = "checkbox"; + input.className = + "h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded dark:bg-gray-700"; + if (schema.default === true) { + input.checked = true; + } + } else if (schema.type === "number" || schema.type === "integer") { + input = document.createElement("input"); + input.type = "number"; + input.className = baseInputClass; + if (schema.default !== undefined) { + input.value = schema.default; + } + } else { + input = document.createElement("input"); + input.type = "text"; + input.className = baseInputClass; + if (schema.default !== undefined) { + input.value = schema.default; + } + } + + input.name = key; + if (isRequired) { + input.required = true; + } + + return input; +} + +/** + * Generate form fields from tool input schema. + * @param {Object} tool - The tool object with input_schema + */ +function generateToolFormFields(tool) { + const formFields = safeGetElement("tool-test-form-fields"); + if (!formFields) return; + + // Clear existing fields safely + while (formFields.firstChild) { + formFields.removeChild(formFields.firstChild); + } + + if (!tool.input_schema || !tool.input_schema.properties) { + const noParams = document.createElement("p"); + noParams.className = "text-sm text-gray-500 dark:text-gray-400"; + noParams.textContent = "This tool has no input parameters."; + formFields.appendChild(noParams); + return; + } + + const properties = tool.input_schema.properties; + const required = tool.input_schema.required || []; + + for (const [key, schema] of Object.entries(properties)) { + const isRequired = required.includes(key); + const fieldDiv = document.createElement("div"); + + const label = document.createElement("label"); + label.className = + "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"; + label.textContent = `${key}${isRequired ? " *" : ""}`; + fieldDiv.appendChild(label); + + if (schema.description) { + const desc = document.createElement("p"); + desc.className = "text-xs text-gray-500 dark:text-gray-400 mb-1"; + desc.textContent = schema.description; + fieldDiv.appendChild(desc); + } + + const input = createFormInput(key, schema, isRequired); + fieldDiv.appendChild(input); + formFields.appendChild(fieldDiv); + } +} + +/** + * Open tool test modal and fetch tool details from API. + * Called by the "Invoke" button in the Tools table. + * @param {string} toolName - The name of the tool to test + */ +export const invokeTool = async function (toolName) { + try { + const tool = await fetchToolDetails(toolName); + + // Store tool details in AppState for runToolTest to access + AppState.currentTestTool = tool; + + // Populate modal title and description + const titleEl = safeGetElement("tool-test-modal-title"); + const descEl = safeGetElement("tool-test-modal-description"); + if (titleEl) { + titleEl.textContent = `Test Tool: ${tool.displayName || tool.name}`; + } + if (descEl) { + descEl.textContent = tool.description || ""; + } + + // Clear previous results + const resultContainer = safeGetElement("tool-test-result"); + if (resultContainer) { + resultContainer.textContent = ""; + } + + // Show the modal + const modal = safeGetElement("tool-test-modal"); + if (modal) { + modal.classList.remove("hidden"); + } + + // Generate form fields based on input schema + if (typeof window.renderToolTestForm === "function") { + window.renderToolTestForm(tool); + } else { + generateToolFormFields(tool); + } + } catch (error) { + console.error("Error invoking tool:", error); + showErrorMessage("Failed to open tool test modal: " + error.message); + } +}; diff --git a/mcpgateway/config.py b/mcpgateway/config.py index 6cba8d704c2..069d49716ed 100644 --- a/mcpgateway/config.py +++ b/mcpgateway/config.py @@ -1603,6 +1603,14 @@ def parse_issuers(cls, v: Any) -> list[str]: max_tool_retries: int = 3 tool_rate_limit: int = 100 # requests per minute tool_concurrent_limit: int = 10 + rest_response_text_max_length: int = Field( + default=5000, + ge=1000, + le=100000, + description="Maximum length of response text to return for non-JSON REST API responses. " + "Longer responses are truncated to prevent exposing excessive sensitive data. " + "Default: 5000 characters. Range: 1000-100000.", + ) # Content Security - Size Limits content_max_resource_size: int = Field(default=102400, ge=1024, le=10485760, description="Maximum size in bytes for resource content (default: 100KB)") # 100KB # Minimum 1KB # Maximum 10MB diff --git a/mcpgateway/services/tool_service.py b/mcpgateway/services/tool_service.py index acb54e4e1e3..8b01683e9c6 100644 --- a/mcpgateway/services/tool_service.py +++ b/mcpgateway/services/tool_service.py @@ -20,6 +20,7 @@ import binascii from datetime import datetime, timezone from functools import lru_cache +import json # NOTE: httpx uses stdlib json, not orjson, so response.json() raises json.JSONDecodeError import logging import os import re @@ -294,6 +295,33 @@ def _decrypt_tool_headers_for_runtime(headers: Optional[Dict[str, Any]]) -> Dict return {key: _decrypt_tool_header_value(value) for key, value in headers.items()} +def _handle_json_parse_error(response, error, is_error_response: bool = False) -> dict: + """Handle JSON parsing failures with graceful fallback to raw text. + + Args: + response: The HTTP response object with .text attribute + error: The exception that was raised during JSON parsing + is_error_response: If True, logs as "error response", else "response" + + Returns: + Dictionary with response_text key containing the raw response text + (truncated to REST_RESPONSE_TEXT_MAX_LENGTH if longer to avoid exposing sensitive data), + or error details if response body is empty/None + """ + msg = "error response" if is_error_response else "response" + if not response.text: + logger.warning(f"Failed to parse JSON {msg}: {error}. Response body was empty.") + return {"error": "Empty response body"} + + max_length = settings.rest_response_text_max_length + text = response.text[:max_length] if len(response.text) > max_length else response.text + if len(response.text) > max_length: + logger.warning(f"Failed to parse JSON {msg}: {error}. Response truncated from {len(response.text)} to {max_length} characters.") + else: + logger.warning(f"Failed to parse JSON {msg}: {error}") + return {"response_text": text} + + @lru_cache(maxsize=256) def _compile_jq_filter(jq_filter: str): """Cache compiled jq filter program. @@ -421,6 +449,18 @@ def extract_using_jq(data, jq_filter=""): if not jq_filter or jq_filter == "": return data + # Validate that jq_filter looks like a valid jq expression + jq_filter_str = str(jq_filter).strip() + if not jq_filter_str: + return data + + # Check if it looks like an email address (common mistake when jsonpath_filter + # field contains corrupted data). Intentionally simple regex to avoid false + # positives with valid jq expressions like .foo|.bar + if re.match(r"^[^.\[\]|]+@[^.\[\]|]+\.[^.\[\]|]+$", jq_filter_str): + logger.warning(f"Invalid jq filter (email address): {jq_filter_str}. Treating as empty filter.") + return data + # Track if input was originally a string (for error handling) was_string = isinstance(data, str) @@ -4271,6 +4311,7 @@ async def invoke_tool( if isinstance(hdr_val, str) and _INVALID_HEADER_VALUE_CHARS.search(hdr_val): raise ToolInvocationError(f"Tool '{name}': header_mapping produced value with illegal characters for header '{hdr_name}'") + # Use the tool's request_type rather than defaulting to POST (using local variable) method = tool_request_type.upper() if tool_request_type else "POST" with create_child_span("tool.gateway_call", {"tool.name": name, "tool.id": tool_id, "tool.integration_type": "REST"}): @@ -4279,13 +4320,23 @@ async def invoke_tool( if method == "GET": # For GET: merge extracted URL query params into payload; everything sent as query string if not tool_query_mapping: + conflicts = set(payload.keys()) & set(query_params.keys()) + if conflicts: + logger.warning( + f"REST tool GET request has conflicting parameters between URL and input arguments. " + f"URL query params will take precedence for: {', '.join(sorted(conflicts))}. " + f"Tool: {name}" + ) payload.update(query_params) response = await asyncio.wait_for(self._http_client.get(final_url, params=payload, headers=headers), timeout=effective_timeout) else: - # For POST/PUT/PATCH/DELETE: merge query params into the JSON body - # (preserves backward compatibility with existing tool configurations) - if not tool_query_mapping: - payload.update(query_params) + # For POST/PUT/PATCH/DELETE: Preserve query params in URL, only send input args in body. + # This is critical for signed URLs (Azure SAS, AWS presigned URLs, webhook signatures). + # When query_mapping is used, query params are already in payload; otherwise keep them in URL. + if not tool_query_mapping and query_params: + # Reconstruct URL with query params + from urllib.parse import urlencode + final_url = f"{final_url}?{urlencode(query_params)}" response = await asyncio.wait_for(self._http_client.request(method, final_url, json=payload, headers=headers), timeout=effective_timeout) except (asyncio.TimeoutError, httpx.TimeoutException): rest_elapsed_ms = (time.time() - rest_start_time) * 1000 @@ -4315,17 +4366,39 @@ async def invoke_tool( await self._run_timeout_post_invoke(name, effective_timeout, global_context, context_table, plugin_manager) raise ToolTimeoutError(f"Tool invocation timed out after {effective_timeout}s") - response.raise_for_status() + try: + response.raise_for_status() + except httpx.HTTPStatusError: + # Non-2xx response — parse body (may be HTML, plain text, XML, etc.) + try: + result = response.json() + except (json.JSONDecodeError, orjson.JSONDecodeError, UnicodeDecodeError, AttributeError) as e: + result = _handle_json_parse_error(response, e, is_error_response=True) + if "error" in result: + error_val = result["error"] + elif "response_text" in result: + error_val = f"HTTP {response.status_code}: {result['response_text']}" + else: + error_val = f"HTTP {response.status_code}" + tool_result = ToolResult( + content=[TextContent(type="text", text=error_val if isinstance(error_val, str) else orjson.dumps(error_val).decode())], + is_error=True, + structured_content={"status_code": response.status_code}, + ) + # Don't mark as successful — success remains False # Handle 204 No Content responses that have no body - if response.status_code == 204: + if tool_result is not None and tool_result.is_error: + pass # Already handled by HTTPStatusError above + elif response.status_code == 204: tool_result = ToolResult(content=[TextContent(type="text", text="Request completed successfully (No Content)")]) success = True elif response.status_code not in [200, 201, 202, 206]: + # Non-standard 2xx codes (203, 205, 207, etc.) treated as errors try: result = response.json() - except orjson.JSONDecodeError: - result = {"response_text": response.text} if response.text else {} + except (json.JSONDecodeError, orjson.JSONDecodeError, UnicodeDecodeError, AttributeError) as e: + result = _handle_json_parse_error(response, e, is_error_response=True) error_val = result["error"] if "error" in result else "Tool error encountered" tool_result = ToolResult( content=[TextContent(type="text", text=error_val if isinstance(error_val, str) else orjson.dumps(error_val).decode())], @@ -4335,8 +4408,8 @@ async def invoke_tool( else: try: result = response.json() - except orjson.JSONDecodeError: - result = {"response_text": response.text} if response.text else {} + except (json.JSONDecodeError, orjson.JSONDecodeError, UnicodeDecodeError, AttributeError) as e: + result = _handle_json_parse_error(response, e, is_error_response=False) logger.debug(f"REST API tool response: {result}") filtered_response = extract_using_jq(result, tool_jsonpath_filter) # Check if extract_using_jq returned an error (list of TextContent objects) diff --git a/tests/unit/js/tools.test.js b/tests/unit/js/tools.test.js index 27cfd6fab8b..d026cb5102a 100644 --- a/tests/unit/js/tools.test.js +++ b/tests/unit/js/tools.test.js @@ -1768,7 +1768,8 @@ describe("runToolTest - additional", () => { ); await runToolTest(); - expect(consoleSpy).toHaveBeenCalledWith("Tool test already running"); + // When button is disabled, runToolTest returns silently without making a request + expect(fetchWithTimeout).not.toHaveBeenCalled(); consoleSpy.mockRestore(); }); @@ -2189,7 +2190,7 @@ describe("runToolTest - scalar parameter types", () => { const { runToolTest } = await import("../../../mcpgateway/admin_ui/tools.js"); await runToolTest(); const body = JSON.parse(fetchWithTimeout.mock.calls.at(-1)[1].body); - expect(body.params[paramKey]).toEqual(expected); + expect(body.params.arguments[paramKey]).toEqual(expected); consoleSpy.mockRestore(); }); @@ -2244,7 +2245,7 @@ describe("runToolTest - array parameter types", () => { const { runToolTest } = await import("../../../mcpgateway/admin_ui/tools.js"); await runToolTest(); const body = JSON.parse(fetchWithTimeout.mock.calls.at(-1)[1].body); - expect(body.params[paramKey]).toEqual(expected); + expect(body.params.arguments[paramKey]).toEqual(expected); consoleSpy.mockRestore(); }); diff --git a/tests/unit/mcpgateway/services/test_tool_service.py b/tests/unit/mcpgateway/services/test_tool_service.py index 1da14e8c48f..e93b8d10679 100644 --- a/tests/unit/mcpgateway/services/test_tool_service.py +++ b/tests/unit/mcpgateway/services/test_tool_service.py @@ -4851,6 +4851,512 @@ def test_empty_filter_bypasses_cache(self): assert result is data +# --------------------------------------------------------------------------- # +# Tests for REST Tool Improvements (non-JSON, query params) # +# --------------------------------------------------------------------------- # + + +class TestJqFilterEmailValidation: + """Tests for JQ filter email address validation (#3855).""" + + def test_extract_using_jq_rejects_email_addresses(self): + """Simple email addresses are detected and ignored as jq filters.""" + data = {"key": "value", "user": "test"} + + result = extract_using_jq(data, "user@example.com") + assert result == data, "Simple email addresses should be ignored as jq filters" + + result = extract_using_jq(data, "admin@test.org") + assert result == data + + result = extract_using_jq(data, "testuser@domain.net") + assert result == data + + def test_extract_using_jq_accepts_valid_filters(self): + """Valid jq filters still work after email validation.""" + data = {"key": "value", "nested": {"field": 123}} + + result = extract_using_jq(data, ".key") + assert result == ["value"] + + result = extract_using_jq(data, ".nested.field") + assert result == [123] + + def test_extract_using_jq_empty_whitespace_filters(self): + """Empty/whitespace filters are handled.""" + data = {"key": "value"} + + result = extract_using_jq(data, "") + assert result == data + + result = extract_using_jq(data, " ") + assert result == data + + result = extract_using_jq(data, "\t\n") + assert result == data + + +class TestRestToolQueryParamHandling: + """Tests for query parameter handling in REST tools (#3857).""" + + @pytest.mark.asyncio + async def test_rest_tool_get_merges_query_params(self, tool_service, mock_tool, mock_global_config_obj, test_db): + """GET requests merge URL query params with input arguments.""" + mock_tool.integration_type = "REST" + mock_tool.request_type = "GET" + mock_tool.url = "https://api.example.com/search?api_key=secret123" + mock_tool.jsonpath_filter = "" + mock_tool.auth_value = None + + setup_db_execute_mock(test_db, mock_tool, mock_global_config_obj) + + mock_response = AsyncMock() + mock_response.raise_for_status = Mock() + mock_response.status_code = 200 + mock_response.json = Mock(return_value={"results": []}) + + tool_service._http_client.get = AsyncMock(return_value=mock_response) + + mock_metrics_buffer = Mock() + mock_metrics_buffer.record_tool_metric = Mock() + with patch("mcpgateway.services.tool_service.metrics_buffer", mock_metrics_buffer): + await tool_service.invoke_tool(test_db, "test_tool", {"q": "test query", "limit": 10}, request_headers=None) + + call_args = tool_service._http_client.get.call_args + assert call_args[0][0] == "https://api.example.com/search" + params = call_args[1]["params"] + assert params["api_key"] == "secret123" + assert params["q"] == "test query" + assert params["limit"] == 10 + + @pytest.mark.asyncio + async def test_rest_tool_post_preserves_query_params_in_url(self, tool_service, mock_tool, mock_global_config_obj, test_db): + """POST requests preserve query params in URL (signed URL support).""" + mock_tool.integration_type = "REST" + mock_tool.request_type = "POST" + mock_tool.url = "https://storage.example.com/upload?signature=xyz&expires=123" + mock_tool.jsonpath_filter = "" + mock_tool.auth_value = None + + setup_db_execute_mock(test_db, mock_tool, mock_global_config_obj) + + mock_response = AsyncMock() + mock_response.raise_for_status = Mock() + mock_response.status_code = 200 + mock_response.json = Mock(return_value={"success": True}) + + tool_service._http_client.request = AsyncMock(return_value=mock_response) + + mock_metrics_buffer = Mock() + mock_metrics_buffer.record_tool_metric = Mock() + with patch("mcpgateway.services.tool_service.metrics_buffer", mock_metrics_buffer): + await tool_service.invoke_tool(test_db, "test_tool", {"filename": "test.txt", "content": "data"}, request_headers=None) + + call_args = tool_service._http_client.request.call_args + url = call_args[0][1] + assert "signature=xyz" in url + assert "expires=123" in url + + body = call_args[1]["json"] + assert body == {"filename": "test.txt", "content": "data"} + + @pytest.mark.asyncio + async def test_rest_tool_put_preserves_query_params(self, tool_service, mock_tool, mock_global_config_obj, test_db): + """PUT requests preserve query params in URL.""" + mock_tool.integration_type = "REST" + mock_tool.request_type = "PUT" + mock_tool.url = "https://api.example.com/resource?token=abc123" + mock_tool.jsonpath_filter = "" + mock_tool.auth_value = None + + setup_db_execute_mock(test_db, mock_tool, mock_global_config_obj) + + mock_response = AsyncMock() + mock_response.raise_for_status = Mock() + mock_response.status_code = 200 + mock_response.json = Mock(return_value={"updated": True}) + + tool_service._http_client.request = AsyncMock(return_value=mock_response) + + mock_metrics_buffer = Mock() + mock_metrics_buffer.record_tool_metric = Mock() + with patch("mcpgateway.services.tool_service.metrics_buffer", mock_metrics_buffer): + await tool_service.invoke_tool(test_db, "test_tool", {"data": "updated"}, request_headers=None) + + call_args = tool_service._http_client.request.call_args + url = call_args[0][1] + assert "token=abc123" in url + + @pytest.mark.asyncio + async def test_rest_tool_patch_preserves_query_params(self, tool_service, mock_tool, mock_global_config_obj, test_db): + """PATCH requests preserve query params in URL.""" + mock_tool.integration_type = "REST" + mock_tool.request_type = "PATCH" + mock_tool.url = "https://api.example.com/resource?version=v2" + mock_tool.jsonpath_filter = "" + mock_tool.auth_value = None + + setup_db_execute_mock(test_db, mock_tool, mock_global_config_obj) + + mock_response = AsyncMock() + mock_response.raise_for_status = Mock() + mock_response.status_code = 200 + mock_response.json = Mock(return_value={"patched": True}) + + tool_service._http_client.request = AsyncMock(return_value=mock_response) + + mock_metrics_buffer = Mock() + mock_metrics_buffer.record_tool_metric = Mock() + with patch("mcpgateway.services.tool_service.metrics_buffer", mock_metrics_buffer): + await tool_service.invoke_tool(test_db, "test_tool", {"field": "value"}, request_headers=None) + + call_args = tool_service._http_client.request.call_args + url = call_args[0][1] + assert "version=v2" in url + body = call_args[1]["json"] + assert body == {"field": "value"} + + @pytest.mark.asyncio + async def test_rest_tool_delete_preserves_query_params(self, tool_service, mock_tool, mock_global_config_obj, test_db): + """DELETE requests preserve query params in URL.""" + mock_tool.integration_type = "REST" + mock_tool.request_type = "DELETE" + mock_tool.url = "https://api.example.com/resource?cascade=true" + mock_tool.jsonpath_filter = "" + mock_tool.auth_value = None + + setup_db_execute_mock(test_db, mock_tool, mock_global_config_obj) + + mock_response = AsyncMock() + mock_response.raise_for_status = Mock() + mock_response.status_code = 204 + mock_response.text = "" + mock_response.json = Mock(return_value={}) + + tool_service._http_client.request = AsyncMock(return_value=mock_response) + + mock_metrics_buffer = Mock() + mock_metrics_buffer.record_tool_metric = Mock() + with patch("mcpgateway.services.tool_service.metrics_buffer", mock_metrics_buffer): + await tool_service.invoke_tool(test_db, "test_tool", {"confirm": "yes"}, request_headers=None) + + call_args = tool_service._http_client.request.call_args + url = call_args[0][1] + assert "cascade=true" in url + + @pytest.mark.asyncio + async def test_rest_tool_get_param_conflict_logs_warning(self, tool_service, mock_tool, mock_global_config_obj, test_db, caplog): + """GET request logs warning when input args conflict with URL query params.""" + import logging + + caplog.set_level(logging.WARNING) + + mock_tool.integration_type = "REST" + mock_tool.request_type = "GET" + mock_tool.url = "https://api.example.com/search?api_key=url_value&safe=true" + mock_tool.jsonpath_filter = "" + mock_tool.auth_value = None + + setup_db_execute_mock(test_db, mock_tool, mock_global_config_obj) + + mock_response = AsyncMock() + mock_response.raise_for_status = Mock() + mock_response.status_code = 200 + mock_response.json = Mock(return_value={"results": []}) + + tool_service._http_client.get = AsyncMock(return_value=mock_response) + + mock_metrics_buffer = Mock() + mock_metrics_buffer.record_tool_metric = Mock() + with patch("mcpgateway.services.tool_service.metrics_buffer", mock_metrics_buffer): + await tool_service.invoke_tool(test_db, "test_tool", {"api_key": "input_value", "q": "search"}, request_headers=None) + + assert "conflicting parameters" in caplog.text.lower() + assert "api_key" in caplog.text + + call_args = tool_service._http_client.get.call_args + params = call_args[1]["params"] + assert params["api_key"] == "url_value" # URL value wins + assert params["safe"] == "true" + assert params["q"] == "search" + + @pytest.mark.asyncio + async def test_rest_tool_get_empty_url_params(self, tool_service, mock_tool, mock_global_config_obj, test_db): + """GET request with no URL query params works correctly.""" + mock_tool.integration_type = "REST" + mock_tool.request_type = "GET" + mock_tool.url = "https://api.example.com/search" + mock_tool.jsonpath_filter = "" + mock_tool.auth_value = None + + setup_db_execute_mock(test_db, mock_tool, mock_global_config_obj) + + mock_response = AsyncMock() + mock_response.raise_for_status = Mock() + mock_response.status_code = 200 + mock_response.json = Mock(return_value={"results": []}) + + tool_service._http_client.get = AsyncMock(return_value=mock_response) + + mock_metrics_buffer = Mock() + mock_metrics_buffer.record_tool_metric = Mock() + with patch("mcpgateway.services.tool_service.metrics_buffer", mock_metrics_buffer): + await tool_service.invoke_tool(test_db, "test_tool", {"q": "test"}, request_headers=None) + + call_args = tool_service._http_client.get.call_args + params = call_args[1]["params"] + assert params == {"q": "test"} + + +class TestRestToolNonJsonResponses: + """Tests for handling non-JSON responses from REST tools (#3855).""" + + @pytest.mark.asyncio + async def test_rest_tool_handles_html_error_response(self, tool_service, mock_tool, mock_global_config_obj, test_db, caplog): + """REST tool handles HTML error pages gracefully without crashing.""" + import httpx + + mock_tool.integration_type = "REST" + mock_tool.request_type = "GET" + mock_tool.jsonpath_filter = "" + mock_tool.auth_value = None + + setup_db_execute_mock(test_db, mock_tool, mock_global_config_obj) + + mock_response = AsyncMock() + # raise_for_status must actually raise for 500 to exercise the real code path + mock_request = Mock(spec=httpx.Request) + mock_request.url = "https://api.example.com/test" + mock_response.raise_for_status = Mock(side_effect=httpx.HTTPStatusError("Server Error", request=mock_request, response=mock_response)) + mock_response.status_code = 500 + mock_response.text = "Internal Server Error" + import json + + mock_response.json = Mock(side_effect=json.JSONDecodeError("Expecting value", "", 0)) + + tool_service._http_client.get = AsyncMock(return_value=mock_response) + + mock_metrics_buffer = Mock() + mock_metrics_buffer.record_tool_metric = Mock() + with patch("mcpgateway.services.tool_service.metrics_buffer", mock_metrics_buffer): + result = await tool_service.invoke_tool(test_db, "test_tool", {}, request_headers=None) + + assert result.is_error is True + # Status code must be preserved in structured_content for retry plugin + assert result.structured_content == {"status_code": 500} + # Error message must include the HTTP status code + assert "500" in result.content[0].text + assert "Failed to parse JSON error response" in caplog.text + + @pytest.mark.asyncio + async def test_rest_tool_handles_plain_text_response(self, tool_service, mock_tool, mock_global_config_obj, test_db, caplog): + """REST tool handles plain text responses without crashing.""" + mock_tool.integration_type = "REST" + mock_tool.request_type = "GET" + mock_tool.jsonpath_filter = "" + mock_tool.auth_value = None + + setup_db_execute_mock(test_db, mock_tool, mock_global_config_obj) + + mock_response = AsyncMock() + mock_response.raise_for_status = Mock() + mock_response.status_code = 200 + mock_response.text = "Plain text response" + import json + + mock_response.json = Mock(side_effect=json.JSONDecodeError("Expecting value", "", 0)) + + tool_service._http_client.get = AsyncMock(return_value=mock_response) + + mock_metrics_buffer = Mock() + mock_metrics_buffer.record_tool_metric = Mock() + with patch("mcpgateway.services.tool_service.metrics_buffer", mock_metrics_buffer): + result = await tool_service.invoke_tool(test_db, "test_tool", {}, request_headers=None) + + assert result.content[0].text is not None + assert "Failed to parse JSON response" in caplog.text + + @pytest.mark.asyncio + async def test_rest_tool_handles_xml_response(self, tool_service, mock_tool, mock_global_config_obj, test_db, caplog): + """REST tool handles XML responses without crashing.""" + mock_tool.integration_type = "REST" + mock_tool.request_type = "GET" + mock_tool.jsonpath_filter = "" + mock_tool.auth_value = None + + setup_db_execute_mock(test_db, mock_tool, mock_global_config_obj) + + mock_response = AsyncMock() + mock_response.raise_for_status = Mock() + mock_response.status_code = 200 + mock_response.text = 'value' + import json + + mock_response.json = Mock(side_effect=json.JSONDecodeError("Expecting value", "", 0)) + + tool_service._http_client.get = AsyncMock(return_value=mock_response) + + mock_metrics_buffer = Mock() + mock_metrics_buffer.record_tool_metric = Mock() + with patch("mcpgateway.services.tool_service.metrics_buffer", mock_metrics_buffer): + result = await tool_service.invoke_tool(test_db, "test_tool", {}, request_headers=None) + + assert result.content[0].text is not None + assert "Failed to parse JSON response" in caplog.text + + @pytest.mark.asyncio + async def test_rest_tool_handles_unicode_decode_error(self, tool_service, mock_tool, mock_global_config_obj, test_db, caplog): + """REST tool handles invalid UTF-8 encoding without crashing.""" + mock_tool.integration_type = "REST" + mock_tool.request_type = "GET" + mock_tool.jsonpath_filter = "" + mock_tool.auth_value = None + + setup_db_execute_mock(test_db, mock_tool, mock_global_config_obj) + + mock_response = AsyncMock() + mock_response.raise_for_status = Mock() + mock_response.status_code = 200 + mock_response.text = "Invalid encoding content" + mock_response.json = Mock(side_effect=UnicodeDecodeError("utf-8", b"", 0, 1, "invalid")) + + tool_service._http_client.get = AsyncMock(return_value=mock_response) + + mock_metrics_buffer = Mock() + mock_metrics_buffer.record_tool_metric = Mock() + with patch("mcpgateway.services.tool_service.metrics_buffer", mock_metrics_buffer): + result = await tool_service.invoke_tool(test_db, "test_tool", {}, request_headers=None) + + assert result.content[0].text is not None + assert "Failed to parse JSON response" in caplog.text + + @pytest.mark.asyncio + async def test_rest_tool_handles_empty_response_body(self, tool_service, mock_tool, mock_global_config_obj, test_db, caplog): + """REST tool handles empty response body with JSON parse error.""" + mock_tool.integration_type = "REST" + mock_tool.request_type = "GET" + mock_tool.jsonpath_filter = "" + mock_tool.auth_value = None + + setup_db_execute_mock(test_db, mock_tool, mock_global_config_obj) + + mock_response = AsyncMock() + mock_response.raise_for_status = Mock() + mock_response.status_code = 200 + mock_response.text = "" + import json + + mock_response.json = Mock(side_effect=json.JSONDecodeError("Expecting value", "", 0)) + + tool_service._http_client.get = AsyncMock(return_value=mock_response) + + mock_metrics_buffer = Mock() + mock_metrics_buffer.record_tool_metric = Mock() + with patch("mcpgateway.services.tool_service.metrics_buffer", mock_metrics_buffer): + result = await tool_service.invoke_tool(test_db, "test_tool", {}, request_headers=None) + + assert result.content[0].text is not None + result_text = result.content[0].text + assert "Empty response body" in result_text + assert "Response body was empty" in caplog.text + + @pytest.mark.asyncio + async def test_rest_tool_truncates_large_response_text(self, tool_service, mock_tool, mock_global_config_obj, test_db, caplog): + """REST tool truncates response text exceeding REST_RESPONSE_TEXT_MAX_LENGTH.""" + mock_tool.integration_type = "REST" + mock_tool.request_type = "GET" + mock_tool.jsonpath_filter = "" + mock_tool.auth_value = None + + setup_db_execute_mock(test_db, mock_tool, mock_global_config_obj) + + large_text = "X" * 10000 + mock_response = AsyncMock() + mock_response.raise_for_status = Mock() + mock_response.status_code = 200 + mock_response.text = large_text + import json + + mock_response.json = Mock(side_effect=json.JSONDecodeError("Expecting value", "", 0)) + + tool_service._http_client.get = AsyncMock(return_value=mock_response) + + mock_metrics_buffer = Mock() + mock_metrics_buffer.record_tool_metric = Mock() + with patch("mcpgateway.services.tool_service.metrics_buffer", mock_metrics_buffer): + result = await tool_service.invoke_tool(test_db, "test_tool", {}, request_headers=None) + + assert result.content[0].text is not None + result_data = orjson.loads(result.content[0].text) + assert "response_text" in result_data + assert len(result_data["response_text"]) == settings.rest_response_text_max_length + assert f"Response truncated from {len(large_text)} to {settings.rest_response_text_max_length} characters" in caplog.text + + @pytest.mark.asyncio + async def test_rest_tool_does_not_truncate_small_response(self, tool_service, mock_tool, mock_global_config_obj, test_db, caplog): + """REST tool does not truncate response text below the limit.""" + mock_tool.integration_type = "REST" + mock_tool.request_type = "GET" + mock_tool.jsonpath_filter = "" + mock_tool.auth_value = None + + setup_db_execute_mock(test_db, mock_tool, mock_global_config_obj) + + small_text = "Small response text" + mock_response = AsyncMock() + mock_response.raise_for_status = Mock() + mock_response.status_code = 200 + mock_response.text = small_text + import json + + mock_response.json = Mock(side_effect=json.JSONDecodeError("Expecting value", "", 0)) + + tool_service._http_client.get = AsyncMock(return_value=mock_response) + + mock_metrics_buffer = Mock() + mock_metrics_buffer.record_tool_metric = Mock() + with patch("mcpgateway.services.tool_service.metrics_buffer", mock_metrics_buffer): + result = await tool_service.invoke_tool(test_db, "test_tool", {}, request_headers=None) + + result_data = orjson.loads(result.content[0].text) + assert result_data["response_text"] == small_text + assert "Response truncated" not in caplog.text + + @pytest.mark.asyncio + async def test_rest_tool_truncation_respects_config_value(self, tool_service, mock_tool, mock_global_config_obj, test_db, caplog): + """REST tool truncation uses the configured REST_RESPONSE_TEXT_MAX_LENGTH value.""" + mock_tool.integration_type = "REST" + mock_tool.request_type = "GET" + mock_tool.jsonpath_filter = "" + mock_tool.auth_value = None + + setup_db_execute_mock(test_db, mock_tool, mock_global_config_obj) + + large_text = "Y" * 6000 + mock_response = AsyncMock() + mock_response.raise_for_status = Mock() + mock_response.status_code = 200 + mock_response.text = large_text + import json + + mock_response.json = Mock(side_effect=json.JSONDecodeError("Expecting value", "", 0)) + + tool_service._http_client.get = AsyncMock(return_value=mock_response) + + mock_metrics_buffer = Mock() + mock_metrics_buffer.record_tool_metric = Mock() + + with ( + patch("mcpgateway.services.tool_service.metrics_buffer", mock_metrics_buffer), + patch.object(settings, "rest_response_text_max_length", 2000), + ): + result = await tool_service.invoke_tool(test_db, "test_tool", {}, request_headers=None) + + result_data = orjson.loads(result.content[0].text) + assert len(result_data["response_text"]) == 2000 + assert "Response truncated from 6000 to 2000 characters" in caplog.text + + class TestSchemaValidatorCaching: """Tests for JSON Schema validator caching (#1809).""" From c0ad12f9650ade1a9b3e95069f42dbc36431f869 Mon Sep 17 00:00:00 2001 From: Rakhi Dutta Date: Thu, 9 Apr 2026 14:58:35 +0530 Subject: [PATCH 2/6] export funcyion in tools.js Signed-off-by: Rakhi Dutta --- mcpgateway/admin_ui/tools.js | 6 +- tests/unit/js/tools.test.js | 446 +++++++++++++++++++++++++++++++++++ 2 files changed, 449 insertions(+), 3 deletions(-) diff --git a/mcpgateway/admin_ui/tools.js b/mcpgateway/admin_ui/tools.js index 7ded08b379b..7a5603bddff 100644 --- a/mcpgateway/admin_ui/tools.js +++ b/mcpgateway/admin_ui/tools.js @@ -3520,7 +3520,7 @@ export const cleanupToolTestModal = function () { * @param {string} toolName - The name of the tool to fetch * @returns {Promise} The tool object */ -async function fetchToolDetails(toolName) { +export async function fetchToolDetails(toolName) { const response = await fetchWithTimeout( `${window.ROOT_PATH}/admin/tools/${encodeURIComponent(toolName)}`, { @@ -3548,7 +3548,7 @@ async function fetchToolDetails(toolName) { * @param {boolean} isRequired - Whether the field is required * @returns {HTMLElement} The input element */ -function createFormInput(key, schema, isRequired) { +export function createFormInput(key, schema, isRequired) { let input; const baseInputClass = "mt-1 block w-full rounded-md border-gray-300 dark:border-gray-600 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:bg-gray-700 dark:text-gray-200"; @@ -3601,7 +3601,7 @@ function createFormInput(key, schema, isRequired) { * Generate form fields from tool input schema. * @param {Object} tool - The tool object with input_schema */ -function generateToolFormFields(tool) { +export function generateToolFormFields(tool) { const formFields = safeGetElement("tool-test-form-fields"); if (!formFields) return; diff --git a/tests/unit/js/tools.test.js b/tests/unit/js/tools.test.js index d026cb5102a..f4c2b971ebe 100644 --- a/tests/unit/js/tools.test.js +++ b/tests/unit/js/tools.test.js @@ -18,6 +18,10 @@ import { validateTool, cleanupToolTestState, cleanupToolTestModal, + fetchToolDetails, + createFormInput, + generateToolFormFields, + invokeTool, } from "../../../mcpgateway/admin_ui/tools.js"; import { fetchWithTimeout } from "../../../mcpgateway/admin_ui/utils"; import { openModal, closeModal } from "../../../mcpgateway/admin_ui/modals"; @@ -3114,3 +3118,445 @@ describe("initToolSelect - Select All respects search filter", () => { vi.unstubAllGlobals(); }); }); + +// --------------------------------------------------------------------------- +// fetchToolDetails +// --------------------------------------------------------------------------- +describe("fetchToolDetails", () => { + test("successfully fetches tool details", async () => { + window.ROOT_PATH = ""; + + const mockTool = { + name: "test-tool", + displayName: "Test Tool", + description: "A test tool", + input_schema: { + properties: { + param1: { type: "string" }, + }, + }, + }; + + fetchWithTimeout.mockResolvedValue({ + ok: true, + json: () => Promise.resolve(mockTool), + }); + + const result = await fetchToolDetails("test-tool"); + + expect(fetchWithTimeout).toHaveBeenCalledWith( + "/admin/tools/test-tool", + expect.objectContaining({ + headers: { + Accept: "application/json", + "Content-Type": "application/json", + }, + }) + ); + expect(result).toEqual(mockTool); + }); + + test("handles error when fetch fails", async () => { + window.ROOT_PATH = ""; + + fetchWithTimeout.mockResolvedValue({ + ok: false, + status: 404, + text: () => Promise.resolve("Not found"), + }); + + await expect(fetchToolDetails("nonexistent-tool")).rejects.toThrow( + "Failed to fetch tool details (404): Not found" + ); + }); + + test("encodes tool name in URL", async () => { + window.ROOT_PATH = ""; + + fetchWithTimeout.mockResolvedValue({ + ok: true, + json: () => Promise.resolve({}), + }); + + await fetchToolDetails("tool/with/slashes"); + + expect(fetchWithTimeout).toHaveBeenCalledWith( + "/admin/tools/tool%2Fwith%2Fslashes", + expect.any(Object) + ); + }); +}); + +// --------------------------------------------------------------------------- +// createFormInput +// --------------------------------------------------------------------------- +describe("createFormInput", () => { + test("creates select input for enum schema", () => { + const schema = { + type: "string", + enum: ["option1", "option2", "option3"], + default: "option2", + }; + + const input = createFormInput("testField", schema, false); + + expect(input.tagName).toBe("SELECT"); + expect(input.name).toBe("testField"); + expect(input.required).toBe(false); + expect(input.options.length).toBe(3); + expect(input.options[0].value).toBe("option1"); + expect(input.options[1].selected).toBe(true); // default + }); + + test("creates checkbox for boolean schema", () => { + const schema = { + type: "boolean", + default: true, + }; + + const input = createFormInput("boolField", schema, false); + + expect(input.tagName).toBe("INPUT"); + expect(input.type).toBe("checkbox"); + expect(input.name).toBe("boolField"); + expect(input.checked).toBe(true); + }); + + test("creates number input for number schema", () => { + const schema = { + type: "number", + default: 42, + }; + + const input = createFormInput("numField", schema, true); + + expect(input.tagName).toBe("INPUT"); + expect(input.type).toBe("number"); + expect(input.name).toBe("numField"); + expect(input.value).toBe("42"); + expect(input.required).toBe(true); + }); + + test("creates number input for integer schema", () => { + const schema = { + type: "integer", + default: 10, + }; + + const input = createFormInput("intField", schema, false); + + expect(input.tagName).toBe("INPUT"); + expect(input.type).toBe("number"); + expect(input.name).toBe("intField"); + expect(input.value).toBe("10"); + }); + + test("creates text input for string schema", () => { + const schema = { + type: "string", + default: "default value", + }; + + const input = createFormInput("strField", schema, true); + + expect(input.tagName).toBe("INPUT"); + expect(input.type).toBe("text"); + expect(input.name).toBe("strField"); + expect(input.value).toBe("default value"); + expect(input.required).toBe(true); + }); + + test("creates text input for unspecified type", () => { + const schema = {}; + + const input = createFormInput("unknownField", schema, false); + + expect(input.tagName).toBe("INPUT"); + expect(input.type).toBe("text"); + expect(input.name).toBe("unknownField"); + }); +}); + +// --------------------------------------------------------------------------- +// generateToolFormFields +// --------------------------------------------------------------------------- +describe("generateToolFormFields", () => { + test("generates form fields from tool schema", () => { + const formFields = document.createElement("div"); + formFields.id = "tool-test-form-fields"; + document.body.appendChild(formFields); + + const tool = { + name: "test-tool", + input_schema: { + properties: { + param1: { + type: "string", + description: "First parameter", + }, + param2: { + type: "number", + description: "Second parameter", + }, + }, + required: ["param1"], + }, + }; + + generateToolFormFields(tool); + + const fields = formFields.children; + expect(fields.length).toBe(2); + + // Check first field (required) + const firstField = fields[0]; + const firstLabel = firstField.querySelector("label"); + expect(firstLabel.textContent).toBe("param1 *"); + const firstDesc = firstField.querySelector("p"); + expect(firstDesc.textContent).toBe("First parameter"); + const firstInput = firstField.querySelector('input[name="param1"]'); + expect(firstInput).toBeTruthy(); + expect(firstInput.required).toBe(true); + + // Check second field (not required) + const secondField = fields[1]; + const secondLabel = secondField.querySelector("label"); + expect(secondLabel.textContent).toBe("param2"); + const secondInput = secondField.querySelector('input[name="param2"]'); + expect(secondInput).toBeTruthy(); + expect(secondInput.required).toBe(false); + }); + + test("displays message when tool has no parameters", () => { + const formFields = document.createElement("div"); + formFields.id = "tool-test-form-fields"; + document.body.appendChild(formFields); + + const tool = { + name: "test-tool", + input_schema: {}, + }; + + generateToolFormFields(tool); + + expect(formFields.children.length).toBe(1); + expect(formFields.children[0].textContent).toBe( + "This tool has no input parameters." + ); + }); + + test("clears existing fields before generating new ones", () => { + const formFields = document.createElement("div"); + formFields.id = "tool-test-form-fields"; + const existingChild = document.createElement("div"); + existingChild.textContent = "Old content"; + formFields.appendChild(existingChild); + document.body.appendChild(formFields); + + const tool = { + name: "test-tool", + input_schema: { + properties: { + newParam: { type: "string" }, + }, + }, + }; + + generateToolFormFields(tool); + + expect(formFields.children.length).toBe(1); + expect(formFields.textContent).not.toContain("Old content"); + }); + + test("returns early if form fields element not found", () => { + const tool = { + name: "test-tool", + input_schema: { + properties: { + param1: { type: "string" }, + }, + }, + }; + + // Should not throw error + expect(() => generateToolFormFields(tool)).not.toThrow(); + }); +}); + +// --------------------------------------------------------------------------- +// invokeTool +// --------------------------------------------------------------------------- +describe("invokeTool", () => { + test("successfully opens tool test modal", async () => { + window.ROOT_PATH = ""; + const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {}); + + const mockTool = { + name: "test-tool", + displayName: "Test Tool", + description: "A test tool", + input_schema: { + properties: { + param1: { type: "string" }, + }, + }, + }; + + fetchWithTimeout.mockResolvedValue({ + ok: true, + json: () => Promise.resolve(mockTool), + }); + + const titleEl = document.createElement("div"); + titleEl.id = "tool-test-modal-title"; + document.body.appendChild(titleEl); + + const descEl = document.createElement("div"); + descEl.id = "tool-test-modal-description"; + document.body.appendChild(descEl); + + const resultContainer = document.createElement("div"); + resultContainer.id = "tool-test-result"; + document.body.appendChild(resultContainer); + + const modal = document.createElement("div"); + modal.id = "tool-test-modal"; + modal.classList.add("hidden"); + document.body.appendChild(modal); + + const formFields = document.createElement("div"); + formFields.id = "tool-test-form-fields"; + document.body.appendChild(formFields); + + await invokeTool("test-tool"); + + expect(fetchWithTimeout).toHaveBeenCalled(); + expect(titleEl.textContent).toBe("Test Tool: Test Tool"); + expect(descEl.textContent).toBe("A test tool"); + expect(modal.classList.contains("hidden")).toBe(false); + expect(formFields.children.length).toBeGreaterThan(0); + + consoleSpy.mockRestore(); + }); + + test("uses tool name when displayName is not available", async () => { + window.ROOT_PATH = ""; + const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {}); + + const mockTool = { + name: "test-tool", + description: "A test tool", + input_schema: {}, + }; + + fetchWithTimeout.mockResolvedValue({ + ok: true, + json: () => Promise.resolve(mockTool), + }); + + const titleEl = document.createElement("div"); + titleEl.id = "tool-test-modal-title"; + document.body.appendChild(titleEl); + + const descEl = document.createElement("div"); + descEl.id = "tool-test-modal-description"; + document.body.appendChild(descEl); + + const resultContainer = document.createElement("div"); + resultContainer.id = "tool-test-result"; + document.body.appendChild(resultContainer); + + const modal = document.createElement("div"); + modal.id = "tool-test-modal"; + modal.classList.add("hidden"); + document.body.appendChild(modal); + + const formFields = document.createElement("div"); + formFields.id = "tool-test-form-fields"; + document.body.appendChild(formFields); + + await invokeTool("test-tool"); + + expect(titleEl.textContent).toBe("Test Tool: test-tool"); + + consoleSpy.mockRestore(); + }); + + test("handles fetch error gracefully", async () => { + window.ROOT_PATH = ""; + const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {}); + const { showErrorMessage } = await import( + "../../../mcpgateway/admin_ui/utils" + ); + + fetchWithTimeout.mockResolvedValue({ + ok: false, + status: 404, + text: () => Promise.resolve("Not found"), + }); + + await invokeTool("nonexistent-tool"); + + expect(consoleSpy).toHaveBeenCalledWith( + "Error invoking tool:", + expect.any(Error) + ); + expect(showErrorMessage).toHaveBeenCalledWith( + expect.stringContaining("Failed to open tool test modal") + ); + + consoleSpy.mockRestore(); + }); + + test("calls window.renderToolTestForm if available", async () => { + window.ROOT_PATH = ""; + const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {}); + + const mockTool = { + name: "test-tool", + displayName: "Test Tool", + description: "A test tool", + input_schema: { + properties: { + param1: { type: "string" }, + }, + }, + }; + + fetchWithTimeout.mockResolvedValue({ + ok: true, + json: () => Promise.resolve(mockTool), + }); + + const titleEl = document.createElement("div"); + titleEl.id = "tool-test-modal-title"; + document.body.appendChild(titleEl); + + const descEl = document.createElement("div"); + descEl.id = "tool-test-modal-description"; + document.body.appendChild(descEl); + + const resultContainer = document.createElement("div"); + resultContainer.id = "tool-test-result"; + document.body.appendChild(resultContainer); + + const modal = document.createElement("div"); + modal.id = "tool-test-modal"; + modal.classList.add("hidden"); + document.body.appendChild(modal); + + const formFields = document.createElement("div"); + formFields.id = "tool-test-form-fields"; + document.body.appendChild(formFields); + + const mockRenderToolTestForm = vi.fn(); + window.renderToolTestForm = mockRenderToolTestForm; + + await invokeTool("test-tool"); + + expect(mockRenderToolTestForm).toHaveBeenCalledWith(mockTool); + + delete window.renderToolTestForm; + consoleSpy.mockRestore(); + }); +}); From 7c4115b7ddddd48ba3e5ad6b2844dd4b6c5212ce Mon Sep 17 00:00:00 2001 From: Gabriel Costa Date: Mon, 13 Apr 2026 11:16:16 +0100 Subject: [PATCH 3/6] pre-commit Signed-off-by: Gabriel Costa --- .secrets.baseline | 678 +++++++++++++++--- .../mcpgateway/services/test_tool_service.py | 9 +- 2 files changed, 589 insertions(+), 98 deletions(-) diff --git a/.secrets.baseline b/.secrets.baseline index 3569c2ec04f..80a8de3276e 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -1,9 +1,9 @@ { "exclude": { - "files": "^.secrets.baseline|package-lock.json|Cargo.lock|scripts/sign_image.sh|scripts/zap|sonar-project.properties|uv.lock|go.sum|mcpgateway/sri_hashes.json|^.secrets.baseline$", + "files": "^.secrets.baseline$|package-lock.json|Cargo.lock|scripts/sign_image.sh|scripts/zap|sonar-project.properties|uv.lock", "lines": null }, - "generated_at": "2026-04-14T13:09:46Z", + "generated_at": "2026-04-13T12:03:04Z", "plugins_used": [ { "name": "AWSKeyDetector" @@ -200,7 +200,7 @@ "hashed_secret": "f4793151b0607198d4de9b1ca458d3e25adf1cb7", "is_secret": false, "is_verified": false, - "line_number": 112, + "line_number": 111, "type": "Secret Keyword", "verified_result": null }, @@ -208,7 +208,7 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, - "line_number": 188, + "line_number": 187, "type": "Secret Keyword", "verified_result": null }, @@ -216,7 +216,7 @@ "hashed_secret": "7b4455a56fbf1d198e45e04c437488514645a82c", "is_secret": false, "is_verified": false, - "line_number": 190, + "line_number": 189, "type": "Secret Keyword", "verified_result": null }, @@ -224,7 +224,7 @@ "hashed_secret": "90bd1b48e958257948487b90bee080ba5ed00caa", "is_secret": false, "is_verified": false, - "line_number": 262, + "line_number": 261, "type": "Hex High Entropy String", "verified_result": null }, @@ -232,7 +232,7 @@ "hashed_secret": "48ffbad96aa9c2b33f9486f5a3c2108198acb518", "is_secret": false, "is_verified": false, - "line_number": 263, + "line_number": 262, "type": "Hex High Entropy String", "verified_result": null } @@ -242,7 +242,11 @@ "hashed_secret": "b4673e578b9b30fe8bba1b555b7b59883444c697", "is_secret": false, "is_verified": false, +<<<<<<< HEAD "line_number": 760, +======= + "line_number": 555, +>>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null }, @@ -250,7 +254,11 @@ "hashed_secret": "4a0a2df96d4c9a13a282268cab33ac4b8cbb2c72", "is_secret": false, "is_verified": false, +<<<<<<< HEAD "line_number": 848, +======= + "line_number": 643, +>>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null }, @@ -258,7 +266,11 @@ "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", "is_secret": false, "is_verified": false, +<<<<<<< HEAD "line_number": 1198, +======= + "line_number": 993, +>>>>>>> c529146ab (pre-commit) "type": "Basic Auth Credentials", "verified_result": null }, @@ -266,7 +278,11 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, +<<<<<<< HEAD "line_number": 2564, +======= + "line_number": 2359, +>>>>>>> c529146ab (pre-commit) "type": "Basic Auth Credentials", "verified_result": null }, @@ -274,7 +290,11 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, +<<<<<<< HEAD "line_number": 2655, +======= + "line_number": 2450, +>>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null }, @@ -282,7 +302,11 @@ "hashed_secret": "ac371b6dcce28a86c90d12bc57d946a800eebf17", "is_secret": false, "is_verified": false, +<<<<<<< HEAD "line_number": 2698, +======= + "line_number": 2493, +>>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null }, @@ -290,7 +314,11 @@ "hashed_secret": "0b6ec68df700dec4dcd64babd0eda1edccddace1", "is_secret": false, "is_verified": false, +<<<<<<< HEAD "line_number": 2703, +======= + "line_number": 2498, +>>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null }, @@ -298,7 +326,11 @@ "hashed_secret": "4ad6f0082ee224001beb3ca5c3e81c8ceea5ed86", "is_secret": false, "is_verified": false, +<<<<<<< HEAD "line_number": 2708, +======= + "line_number": 2503, +>>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null } @@ -344,7 +376,11 @@ "hashed_secret": "844c398e469ef3fb919da3778944365ab2175fb7", "is_secret": false, "is_verified": false, +<<<<<<< HEAD "line_number": 463, +======= + "line_number": 377, +>>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null }, @@ -352,7 +388,11 @@ "hashed_secret": "319037749ce37e577db0b3628c7f90e333544391", "is_secret": false, "is_verified": false, +<<<<<<< HEAD "line_number": 887, +======= + "line_number": 801, +>>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null }, @@ -360,7 +400,11 @@ "hashed_secret": "6ae2832e494d1098e8901fe156083e39399a24f1", "is_secret": false, "is_verified": false, +<<<<<<< HEAD "line_number": 889, +======= + "line_number": 803, +>>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null }, @@ -368,7 +412,11 @@ "hashed_secret": "43fc45734b96bcb1b6cef373e949eb3524ae199b", "is_secret": false, "is_verified": false, +<<<<<<< HEAD "line_number": 1580, +======= + "line_number": 1494, +>>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null }, @@ -376,7 +424,11 @@ "hashed_secret": "9d989e8d27dc9e0ec3389fc855f142c3d40f0c50", "is_secret": false, "is_verified": false, +<<<<<<< HEAD "line_number": 1791, +======= + "line_number": 1704, +>>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null }, @@ -384,7 +436,11 @@ "hashed_secret": "d3ac7a4ef1a838b4134f2f6e7f3c0d249d74b674", "is_secret": false, "is_verified": false, +<<<<<<< HEAD "line_number": 6121, +======= + "line_number": 6072, +>>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null }, @@ -392,7 +448,11 @@ "hashed_secret": "5932862bcd24dd27d0dc0407ec94fe9d6ea24aeb", "is_secret": false, "is_verified": false, +<<<<<<< HEAD "line_number": 6618, +======= + "line_number": 6569, +>>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null }, @@ -400,7 +460,11 @@ "hashed_secret": "c77c805e32f173e4321ee9187de9c29cb3804513", "is_secret": false, "is_verified": false, +<<<<<<< HEAD "line_number": 6630, +======= + "line_number": 6581, +>>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null }, @@ -408,7 +472,11 @@ "hashed_secret": "8fe3df8a68ddd0d4ab2214186cbb8e38ccd0e06a", "is_secret": false, "is_verified": false, +<<<<<<< HEAD "line_number": 6702, +======= + "line_number": 6653, +>>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null }, @@ -416,7 +484,11 @@ "hashed_secret": "93ac8946882128457cd9e283b30ca851945e6690", "is_secret": false, "is_verified": false, +<<<<<<< HEAD "line_number": 7772, +======= + "line_number": 7747, +>>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null } @@ -2028,7 +2100,7 @@ "hashed_secret": "9ae4d2858bd2df0cf19722488adae925ff3c4bdb", "is_secret": false, "is_verified": false, - "line_number": 173, + "line_number": 165, "type": "Secret Keyword", "verified_result": null } @@ -2066,7 +2138,11 @@ "hashed_secret": "25910f981e85ca04baf359199dd0bd4a3ae738b6", "is_secret": false, "is_verified": false, +<<<<<<< HEAD "line_number": 850, +======= + "line_number": 852, +>>>>>>> c529146ab (pre-commit) "type": "AWS Access Key", "verified_result": null } @@ -4102,7 +4178,7 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, - "line_number": 4240, + "line_number": 4256, "type": "Secret Keyword", "verified_result": null }, @@ -4110,7 +4186,7 @@ "hashed_secret": "559b05f1b2863e725b76e216ac3dadecbf92e244", "is_secret": false, "is_verified": false, - "line_number": 4841, + "line_number": 4864, "type": "Secret Keyword", "verified_result": null }, @@ -4118,7 +4194,7 @@ "hashed_secret": "a8af4759392d4f7496d613174f33afe2074a4b8d", "is_secret": false, "is_verified": false, - "line_number": 4843, + "line_number": 4866, "type": "Secret Keyword", "verified_result": null }, @@ -4126,7 +4202,7 @@ "hashed_secret": "85b60d811d16ff56b3654587d4487f713bfa33b7", "is_secret": false, "is_verified": false, - "line_number": 15169, + "line_number": 15018, "type": "Secret Keyword", "verified_result": null } @@ -4830,7 +4906,11 @@ "hashed_secret": "ff37a98a9963d347e9749a5c1b3936a4a245a6ff", "is_secret": false, "is_verified": false, +<<<<<<< HEAD "line_number": 2228, +======= + "line_number": 2229, +>>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null } @@ -4876,7 +4956,7 @@ "hashed_secret": "f2b14f68eb995facb3a1c35287b778d5bd785511", "is_secret": false, "is_verified": false, - "line_number": 175, + "line_number": 171, "type": "Secret Keyword", "verified_result": null }, @@ -4884,7 +4964,7 @@ "hashed_secret": "1073ab6cda4b991cd29f9e83a307f34004ae9327", "is_secret": false, "is_verified": false, - "line_number": 181, + "line_number": 177, "type": "Secret Keyword", "verified_result": null }, @@ -4892,7 +4972,7 @@ "hashed_secret": "d4fe581561f18ee5006254a7e53f53cbed780bc2", "is_secret": false, "is_verified": false, - "line_number": 268, + "line_number": 259, "type": "Secret Keyword", "verified_result": null } @@ -4968,7 +5048,7 @@ "hashed_secret": "d3ecb0d890368d7659ee54010045b835dacb8efe", "is_secret": false, "is_verified": false, - "line_number": 611, + "line_number": 610, "type": "Secret Keyword", "verified_result": null } @@ -4978,7 +5058,7 @@ "hashed_secret": "c377074d6473f35a91001981355da793dc808ffd", "is_secret": false, "is_verified": false, - "line_number": 4220, + "line_number": 4111, "type": "Hex High Entropy String", "verified_result": null }, @@ -4986,7 +5066,7 @@ "hashed_secret": "6367c48dd193d56ea7b0baad25b19455e529f5ee", "is_secret": false, "is_verified": false, - "line_number": 5333, + "line_number": 5224, "type": "Secret Keyword", "verified_result": null }, @@ -4994,7 +5074,7 @@ "hashed_secret": "f2b14f68eb995facb3a1c35287b778d5bd785511", "is_secret": false, "is_verified": false, - "line_number": 5497, + "line_number": 5388, "type": "Secret Keyword", "verified_result": null }, @@ -5002,7 +5082,7 @@ "hashed_secret": "f42a3fabe1e9bed059d727f47eb752e3aa61b977", "is_secret": false, "is_verified": false, - "line_number": 5554, + "line_number": 5445, "type": "Secret Keyword", "verified_result": null }, @@ -5010,7 +5090,7 @@ "hashed_secret": "b85788b459aa4d67e1070930dae6d0827756aadb", "is_secret": false, "is_verified": false, - "line_number": 5592, + "line_number": 5483, "type": "Secret Keyword", "verified_result": null }, @@ -5018,7 +5098,7 @@ "hashed_secret": "52dcc83ec1e54426ad58a64854d1eb8d5f5d9685", "is_secret": false, "is_verified": false, - "line_number": 5593, + "line_number": 5484, "type": "Secret Keyword", "verified_result": null } @@ -5182,7 +5262,7 @@ "hashed_secret": "b4e44716dbbf57be3dae2f819d96795a85d06652", "is_secret": false, "is_verified": false, - "line_number": 12498, + "line_number": 12461, "type": "Secret Keyword", "verified_result": null } @@ -5434,7 +5514,7 @@ "hashed_secret": "a4b48a81cdab1e1a5dd37907d6c85ca1c61ddc7c", "is_secret": false, "is_verified": false, - "line_number": 572, + "line_number": 568, "type": "Secret Keyword", "verified_result": null } @@ -5519,6 +5599,24 @@ "verified_result": null } ], + "plugins/encoded_exfil_detection/README.md": [ + { + "hashed_secret": "f7f877c24a4ebef314009a2e79314797bae91bb6", + "is_secret": false, + "is_verified": false, + "line_number": 149, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "5f2fe4f03b2314fa3a217f1e1f0ab2e3f5b1b49f", + "is_secret": false, + "is_verified": false, + "line_number": 178, + "type": "Secret Keyword", + "verified_result": null + } + ], "plugins/examples/custom_auth_example/README.md": [ { "hashed_secret": "423529858c0ba74fbbe141bce50b20f3c0d3b9f6", @@ -5649,6 +5747,16 @@ "verified_result": null } ], + "plugins/pii_filter/README.md": [ + { + "hashed_secret": "25910f981e85ca04baf359199dd0bd4a3ae738b6", + "is_secret": false, + "is_verified": false, + "line_number": 344, + "type": "AWS Access Key", + "verified_result": null + } + ], "plugins/resource_filter/README.md": [ { "hashed_secret": "048fe8f760ed90d8058cacc2da41ed7a7eb76669", @@ -5695,6 +5803,372 @@ "verified_result": null } ], + "plugins_rust/encoded_exfil_detection/benches/encoded_exfil_detection.rs": [ + { + "hashed_secret": "9e53fffcf6d106f85fff53416e2ae687e1346aea", + "is_secret": false, + "is_verified": false, + "line_number": 27, + "type": "Hex High Entropy String", + "verified_result": null + }, + { + "hashed_secret": "67844048772af8b97258a5f1fe00b0043216f739", + "is_secret": false, + "is_verified": false, + "line_number": 49, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "648b3cbf65960d678821b2ae53740d890e1be100", + "is_secret": false, + "is_verified": false, + "line_number": 128, + "type": "Secret Keyword", + "verified_result": null + } + ], + "plugins_rust/encoded_exfil_detection/src/lib.rs": [ + { + "hashed_secret": "1d278d3c888d1a2fa7eed622bfc02927ce4049af", + "is_secret": false, + "is_verified": false, + "line_number": 573, + "type": "Base64 High Entropy String", + "verified_result": null + }, + { + "hashed_secret": "d7da4707f9793b357da965eb04f06ceae7d7bfa6", + "is_secret": false, + "is_verified": false, + "line_number": 574, + "type": "Base64 High Entropy String", + "verified_result": null + }, + { + "hashed_secret": "0962e90797cdf7ff77c8a0a9477a7ca4f493465f", + "is_secret": false, + "is_verified": false, + "line_number": 892, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "ca3f3092640b07c051f03e52690fda1bcee3f60d", + "is_secret": false, + "is_verified": false, + "line_number": 913, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "532fdeccb155dce5b528ba039b9a5d201b817e3b", + "is_secret": false, + "is_verified": false, + "line_number": 932, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "cf743b3a58a4d0f91c1d7f5825c0b1b5f7758174", + "is_secret": false, + "is_verified": false, + "line_number": 969, + "type": "Base64 High Entropy String", + "verified_result": null + }, + { + "hashed_secret": "ff1a7ebc241b8eefe9e75e13f20cc22c365ab626", + "is_secret": false, + "is_verified": false, + "line_number": 969, + "type": "Secret Keyword", + "verified_result": null + } + ], + "plugins_rust/secrets_detection/benches/secrets_detection.rs": [ + { + "hashed_secret": "86de8c52637ec530fe39b0a8471da9b8764d5242", + "is_secret": false, + "is_verified": false, + "line_number": 53, + "type": "AWS Access Key", + "verified_result": null + }, + { + "hashed_secret": "5b8c02feb3811310ff452e9a812b9f98873f36bf", + "is_secret": false, + "is_verified": false, + "line_number": 69, + "type": "SoftLayer Credentials", + "verified_result": null + }, + { + "hashed_secret": "0fa6996ddd42e0f9db3f1c04d06453026d56da0f", + "is_secret": false, + "is_verified": false, + "line_number": 73, + "type": "SoftLayer Credentials", + "verified_result": null + }, + { + "hashed_secret": "de56e523c4cf688e56a7df48cf01e2a21db5b5a4", + "is_secret": false, + "is_verified": false, + "line_number": 77, + "type": "AWS Access Key", + "verified_result": null + } + ], + "plugins_rust/secrets_detection/compare_performance.py": [ + { + "hashed_secret": "86de8c52637ec530fe39b0a8471da9b8764d5242", + "is_secret": false, + "is_verified": false, + "line_number": 67, + "type": "AWS Access Key", + "verified_result": null + }, + { + "hashed_secret": "5b8c02feb3811310ff452e9a812b9f98873f36bf", + "is_secret": false, + "is_verified": false, + "line_number": 71, + "type": "SoftLayer Credentials", + "verified_result": null + }, + { + "hashed_secret": "0fa6996ddd42e0f9db3f1c04d06453026d56da0f", + "is_secret": false, + "is_verified": false, + "line_number": 72, + "type": "SoftLayer Credentials", + "verified_result": null + } + ], + "plugins_rust/secrets_detection/examples/heavy_workload.rs": [ + { + "hashed_secret": "86de8c52637ec530fe39b0a8471da9b8764d5242", + "is_secret": false, + "is_verified": false, + "line_number": 34, + "type": "AWS Access Key", + "verified_result": null + }, + { + "hashed_secret": "5b8c02feb3811310ff452e9a812b9f98873f36bf", + "is_secret": false, + "is_verified": false, + "line_number": 38, + "type": "SoftLayer Credentials", + "verified_result": null + }, + { + "hashed_secret": "0fa6996ddd42e0f9db3f1c04d06453026d56da0f", + "is_secret": false, + "is_verified": false, + "line_number": 39, + "type": "SoftLayer Credentials", + "verified_result": null + }, + { + "hashed_secret": "de56e523c4cf688e56a7df48cf01e2a21db5b5a4", + "is_secret": false, + "is_verified": false, + "line_number": 40, + "type": "AWS Access Key", + "verified_result": null + }, + { + "hashed_secret": "be4fc4886bd949b369d5e092eb87494f12e57e5b", + "is_secret": false, + "is_verified": false, + "line_number": 42, + "type": "Private Key", + "verified_result": null + } + ], + "plugins_rust/secrets_detection/src/patterns.rs": [ + { + "hashed_secret": "86de8c52637ec530fe39b0a8471da9b8764d5242", + "is_secret": false, + "is_verified": false, + "line_number": 94, + "type": "AWS Access Key", + "verified_result": null + }, + { + "hashed_secret": "de56e523c4cf688e56a7df48cf01e2a21db5b5a4", + "is_secret": false, + "is_verified": false, + "line_number": 98, + "type": "AWS Access Key", + "verified_result": null + }, + { + "hashed_secret": "a5f223264a7296f00627a4c5c00a9957c67f5479", + "is_secret": false, + "is_verified": false, + "line_number": 238, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "be4fc4886bd949b369d5e092eb87494f12e57e5b", + "is_secret": false, + "is_verified": false, + "line_number": 282, + "type": "Private Key", + "verified_result": null + }, + { + "hashed_secret": "daefe0b4345a654580dcad25c7c11ff4c944a8c0", + "is_secret": false, + "is_verified": false, + "line_number": 286, + "type": "Private Key", + "verified_result": null + }, + { + "hashed_secret": "f0778f3e140a61d5bbbed5430773e52af2f5fba4", + "is_secret": false, + "is_verified": false, + "line_number": 290, + "type": "Private Key", + "verified_result": null + }, + { + "hashed_secret": "27c6929aef41ae2bcadac15ca6abcaff72cda9cd", + "is_secret": false, + "is_verified": false, + "line_number": 294, + "type": "Private Key", + "verified_result": null + }, + { + "hashed_secret": "75f3be87351a63f1c2f677632838f9e2be066891", + "is_secret": false, + "is_verified": false, + "line_number": 336, + "type": "Hex High Entropy String", + "verified_result": null + }, + { + "hashed_secret": "a19310f994b3a8974f03cddd1de4b862266947e8", + "is_secret": false, + "is_verified": false, + "line_number": 340, + "type": "Hex High Entropy String", + "verified_result": null + }, + { + "hashed_secret": "b1775a785f09a6ebaf2dc33d6eaeb98974d9cdb8", + "is_secret": false, + "is_verified": false, + "line_number": 344, + "type": "Hex High Entropy String", + "verified_result": null + }, + { + "hashed_secret": "7326d685c1328a4b748e7c239ba227c8a7afc988", + "is_secret": false, + "is_verified": false, + "line_number": 350, + "type": "Hex High Entropy String", + "verified_result": null + }, + { + "hashed_secret": "e0eca9acea92cdf77e594f0d3367116b9adffeec", + "is_secret": false, + "is_verified": false, + "line_number": 354, + "type": "Base64 High Entropy String", + "verified_result": null + }, + { + "hashed_secret": "e4d0f04f03d9183f843c9d764592ba5bcd17f356", + "is_secret": false, + "is_verified": false, + "line_number": 369, + "type": "Base64 High Entropy String", + "verified_result": null + }, + { + "hashed_secret": "b256e49b893f8b2eeb2384801910c70ed17a2191", + "is_secret": false, + "is_verified": false, + "line_number": 411, + "type": "Secret Keyword", + "verified_result": null + } + ], + "plugins_rust/secrets_detection/src/scanner.rs": [ + { + "hashed_secret": "50b68587c1c40248753c5e9190dcc3dd73aa142a", + "is_secret": false, + "is_verified": false, + "line_number": 243, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "4e8dd640c77be10809ea27a4a6e668d9cba82014", + "is_secret": false, + "is_verified": false, + "line_number": 248, + "type": "SoftLayer Credentials", + "verified_result": null + }, + { + "hashed_secret": "721b09413c9b91bf3278f966b62c7a03ae2ef9a0", + "is_secret": false, + "is_verified": false, + "line_number": 248, + "type": "Base64 High Entropy String", + "verified_result": null + }, + { + "hashed_secret": "be4fc4886bd949b369d5e092eb87494f12e57e5b", + "is_secret": false, + "is_verified": false, + "line_number": 292, + "type": "Private Key", + "verified_result": null + }, + { + "hashed_secret": "de56e523c4cf688e56a7df48cf01e2a21db5b5a4", + "is_secret": false, + "is_verified": false, + "line_number": 726, + "type": "AWS Access Key", + "verified_result": null + }, + { + "hashed_secret": "2e3810897e838a5edcfd57b5fb117927fdc922da", + "is_secret": false, + "is_verified": false, + "line_number": 740, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "7f1d81c1d9cf850d6a1dbd32d5f110b6732fe60e", + "is_secret": false, + "is_verified": false, + "line_number": 740, + "type": "Base64 High Entropy String", + "verified_result": null + }, + { + "hashed_secret": "86de8c52637ec530fe39b0a8471da9b8764d5242", + "is_secret": false, + "is_verified": false, + "line_number": 799, + "type": "AWS Access Key", + "verified_result": null + } + ], "podman-compose-sonarqube.yaml": [ { "hashed_secret": "345e9ea7c857e75dedd9edb24c232e1cab297c19", @@ -5906,7 +6380,7 @@ "hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", "is_secret": false, "is_verified": false, - "line_number": 153, + "line_number": 115, "type": "Basic Auth Credentials", "verified_result": null }, @@ -5914,7 +6388,7 @@ "hashed_secret": "206c80413b9a96c1312cc346b7d2517b84463edd", "is_secret": false, "is_verified": false, - "line_number": 202, + "line_number": 164, "type": "Secret Keyword", "verified_result": null } @@ -5954,7 +6428,7 @@ "hashed_secret": "ba9cdae9b74942b8ac45ec20dfc3803a07fe6de9", "is_secret": false, "is_verified": false, - "line_number": 79, + "line_number": 80, "type": "Secret Keyword", "verified_result": null }, @@ -5962,7 +6436,7 @@ "hashed_secret": "4dfad2d5130a5bcaf2716c495de92da13f4389e0", "is_secret": false, "is_verified": false, - "line_number": 764, + "line_number": 758, "type": "Secret Keyword", "verified_result": null } @@ -6858,7 +7332,7 @@ "hashed_secret": "1a91d62f7ca67399625a4368a6ab5d4a3baa6073", "is_secret": false, "is_verified": false, - "line_number": 115, + "line_number": 98, "type": "Secret Keyword", "verified_result": null }, @@ -6866,7 +7340,7 @@ "hashed_secret": "dbdab9be92cacdae6a97e8601332bfaa8545800f", "is_secret": false, "is_verified": false, - "line_number": 116, + "line_number": 99, "type": "Secret Keyword", "verified_result": null }, @@ -6874,7 +7348,7 @@ "hashed_secret": "4ea8d2335b430796cf3f500368c5b0f5b1dc90f5", "is_secret": false, "is_verified": false, - "line_number": 213, + "line_number": 151, "type": "Secret Keyword", "verified_result": null } @@ -7099,6 +7573,16 @@ "verified_result": null } ], + "tests/unit/mcpgateway/plugins/plugins/pii_filter/test_pii_filter.py": [ + { + "hashed_secret": "25910f981e85ca04baf359199dd0bd4a3ae738b6", + "is_secret": false, + "is_verified": false, + "line_number": 505, + "type": "AWS Access Key", + "verified_result": null + } + ], "tests/unit/mcpgateway/plugins/plugins/tools_telemetry_exporter/test_tools_telemetry_exporter.py": [ { "hashed_secret": "e8af0e18ff4805f4efd84f58b0fa69e3780f35a4", @@ -7938,7 +8422,7 @@ "hashed_secret": "99834bc4eff3f1e1c1e4692d2476b593b501d045", "is_secret": false, "is_verified": false, - "line_number": 5045, + "line_number": 5033, "type": "Secret Keyword", "verified_result": null }, @@ -7946,7 +8430,7 @@ "hashed_secret": "f2b14f68eb995facb3a1c35287b778d5bd785511", "is_secret": false, "is_verified": false, - "line_number": 5050, + "line_number": 5038, "type": "Secret Keyword", "verified_result": null }, @@ -7954,7 +8438,7 @@ "hashed_secret": "1a91d62f7ca67399625a4368a6ab5d4a3baa6073", "is_secret": false, "is_verified": false, - "line_number": 6587, + "line_number": 6575, "type": "Secret Keyword", "verified_result": null }, @@ -7962,7 +8446,7 @@ "hashed_secret": "2a23dd4f2e8b50315e601a2867ea7b7bf5a43b8c", "is_secret": false, "is_verified": false, - "line_number": 7184, + "line_number": 7172, "type": "Secret Keyword", "verified_result": null }, @@ -7970,7 +8454,7 @@ "hashed_secret": "ed3e0017cb8e4b06a59af1a441f62cbe58d2ef59", "is_secret": false, "is_verified": false, - "line_number": 7203, + "line_number": 7191, "type": "Secret Keyword", "verified_result": null }, @@ -7978,7 +8462,7 @@ "hashed_secret": "b55c6dc4705dba8b151c59566175ad845d5a9104", "is_secret": false, "is_verified": false, - "line_number": 7409, + "line_number": 7397, "type": "Secret Keyword", "verified_result": null }, @@ -7986,7 +8470,7 @@ "hashed_secret": "3654bd5ef523a79741766b7c3f22228fd43c3836", "is_secret": false, "is_verified": false, - "line_number": 7428, + "line_number": 7416, "type": "Secret Keyword", "verified_result": null } @@ -8576,7 +9060,7 @@ "hashed_secret": "d63b39580934e062f89aae63426d2f2c77c3e258", "is_secret": false, "is_verified": false, - "line_number": 509, + "line_number": 503, "type": "Base64 High Entropy String", "verified_result": null }, @@ -8584,7 +9068,7 @@ "hashed_secret": "586a55a9b8b97f0cd88e24ce8279ebc955949688", "is_secret": false, "is_verified": false, - "line_number": 510, + "line_number": 504, "type": "Secret Keyword", "verified_result": null }, @@ -8592,7 +9076,7 @@ "hashed_secret": "00cafd126182e8a9e7c01bb2f0dfd00496be724f", "is_secret": false, "is_verified": false, - "line_number": 526, + "line_number": 520, "type": "Secret Keyword", "verified_result": null }, @@ -8600,7 +9084,7 @@ "hashed_secret": "7b1552c7c7ffb8bd70b5666e5997c8e017630aab", "is_secret": false, "is_verified": false, - "line_number": 1941, + "line_number": 1935, "type": "Base64 High Entropy String", "verified_result": null }, @@ -8608,7 +9092,7 @@ "hashed_secret": "9fb7fe1217aed442b04c0f5e43b5d5a7d3287097", "is_secret": false, "is_verified": false, - "line_number": 2994, + "line_number": 2871, "type": "Secret Keyword", "verified_result": null }, @@ -8616,7 +9100,7 @@ "hashed_secret": "72cb70dbbafe97e5ea13ad88acd65d08389439b0", "is_secret": false, "is_verified": false, - "line_number": 3622, + "line_number": 3499, "type": "Secret Keyword", "verified_result": null }, @@ -8624,7 +9108,7 @@ "hashed_secret": "ee977806d7286510da8b9a7492ba58e2484c0ecc", "is_secret": false, "is_verified": false, - "line_number": 6376, + "line_number": 6305, "type": "Secret Keyword", "verified_result": null }, @@ -8632,7 +9116,7 @@ "hashed_secret": "f2e7745f43b0ef0e2c2faf61d6c6a28be2965750", "is_secret": false, "is_verified": false, - "line_number": 6868, + "line_number": 6797, "type": "Secret Keyword", "verified_result": null }, @@ -8640,7 +9124,7 @@ "hashed_secret": "4a249743d4d2241bd2ae085b4fe654d089488295", "is_secret": false, "is_verified": false, - "line_number": 8215, + "line_number": 8007, "type": "Secret Keyword", "verified_result": null }, @@ -8648,7 +9132,7 @@ "hashed_secret": "0c8d051d3c7eada5d31b53d9936fce6bcc232ae2", "is_secret": false, "is_verified": false, - "line_number": 8357, + "line_number": 8145, "type": "Secret Keyword", "verified_result": null }, @@ -8656,7 +9140,7 @@ "hashed_secret": "f2b14f68eb995facb3a1c35287b778d5bd785511", "is_secret": false, "is_verified": false, - "line_number": 8733, + "line_number": 8521, "type": "Secret Keyword", "verified_result": null } @@ -8764,7 +9248,7 @@ "hashed_secret": "206c80413b9a96c1312cc346b7d2517b84463edd", "is_secret": false, "is_verified": false, - "line_number": 1605, + "line_number": 1604, "type": "Secret Keyword", "verified_result": null }, @@ -8772,7 +9256,7 @@ "hashed_secret": "5cbd0bf2db07a8f50fa9bbcc5ac720b1911c6380", "is_secret": false, "is_verified": false, - "line_number": 1777, + "line_number": 1776, "type": "Secret Keyword", "verified_result": null }, @@ -8780,7 +9264,7 @@ "hashed_secret": "a10b98d7340036e9c8c301704f623eddd733cc1a", "is_secret": false, "is_verified": false, - "line_number": 2782, + "line_number": 2781, "type": "Hex High Entropy String", "verified_result": null }, @@ -8788,7 +9272,7 @@ "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f", "is_secret": false, "is_verified": false, - "line_number": 5216, + "line_number": 5189, "type": "Secret Keyword", "verified_result": null }, @@ -8796,7 +9280,7 @@ "hashed_secret": "fe1bae27cb7c1fb823f496f286e78f1d2ae87734", "is_secret": false, "is_verified": false, - "line_number": 5869, + "line_number": 5842, "type": "Secret Keyword", "verified_result": null }, @@ -8804,7 +9288,7 @@ "hashed_secret": "2878cbdbbcfa6feafc04b8889f5ecc8c470ba32e", "is_secret": false, "is_verified": false, - "line_number": 5933, + "line_number": 5906, "type": "Secret Keyword", "verified_result": null }, @@ -8812,7 +9296,7 @@ "hashed_secret": "a0281cd072cea8e80e7866b05dc124815760b6c9", "is_secret": false, "is_verified": false, - "line_number": 6185, + "line_number": 6158, "type": "Secret Keyword", "verified_result": null }, @@ -8820,7 +9304,7 @@ "hashed_secret": "a0f4ea7d91495df92bbac2e2149dfb850fe81396", "is_secret": false, "is_verified": false, - "line_number": 9168, + "line_number": 9141, "type": "Secret Keyword", "verified_result": null }, @@ -8828,7 +9312,7 @@ "hashed_secret": "a75a7c7b31474f3f04f3a395228ded8d61ee1ae3", "is_secret": false, "is_verified": false, - "line_number": 9217, + "line_number": 9190, "type": "Secret Keyword", "verified_result": null }, @@ -8836,7 +9320,7 @@ "hashed_secret": "02c593fd9af8254b859d426a76b6cd42847fbec1", "is_secret": false, "is_verified": false, - "line_number": 9256, + "line_number": 9229, "type": "Secret Keyword", "verified_result": null }, @@ -8844,7 +9328,7 @@ "hashed_secret": "1ded3053d0363079a4e681a3b700435d6d880290", "is_secret": false, "is_verified": false, - "line_number": 9313, + "line_number": 9286, "type": "Secret Keyword", "verified_result": null }, @@ -8852,7 +9336,7 @@ "hashed_secret": "c00dbbc9dadfbe1e232e93a729dd4752fade0abf", "is_secret": false, "is_verified": false, - "line_number": 14411, + "line_number": 14062, "type": "Secret Keyword", "verified_result": null }, @@ -8860,7 +9344,7 @@ "hashed_secret": "f2b14f68eb995facb3a1c35287b778d5bd785511", "is_secret": false, "is_verified": false, - "line_number": 17168, + "line_number": 16819, "type": "Secret Keyword", "verified_result": null }, @@ -8868,7 +9352,7 @@ "hashed_secret": "a4b48a81cdab1e1a5dd37907d6c85ca1c61ddc7c", "is_secret": false, "is_verified": false, - "line_number": 17187, + "line_number": 16838, "type": "Secret Keyword", "verified_result": null }, @@ -8876,7 +9360,7 @@ "hashed_secret": "dc8002865f92070749b264e76045b04fa3b8de71", "is_secret": false, "is_verified": false, - "line_number": 20845, + "line_number": 20496, "type": "Secret Keyword", "verified_result": null } @@ -8886,7 +9370,7 @@ "hashed_secret": "dff6d4ff5dc357cf451d1855ab9cbda562645c9f", "is_secret": false, "is_verified": false, - "line_number": 239, + "line_number": 244, "type": "Secret Keyword", "verified_result": null } @@ -8914,7 +9398,7 @@ "hashed_secret": "b5920aa41ccde1341077fdb2636afc16d6da0e1a", "is_secret": false, "is_verified": false, - "line_number": 190, + "line_number": 180, "type": "Secret Keyword", "verified_result": null }, @@ -8922,7 +9406,7 @@ "hashed_secret": "467be688b21f5d43370f5dc983fe5ed58dea4b00", "is_secret": false, "is_verified": false, - "line_number": 216, + "line_number": 206, "type": "Secret Keyword", "verified_result": null }, @@ -8930,7 +9414,7 @@ "hashed_secret": "d955debea8e960a293ecbb257048680898cc9d94", "is_secret": false, "is_verified": false, - "line_number": 231, + "line_number": 221, "type": "Secret Keyword", "verified_result": null }, @@ -8938,7 +9422,7 @@ "hashed_secret": "4ee9e7b8482f6e0282b0117eb81651c98b537589", "is_secret": false, "is_verified": false, - "line_number": 265, + "line_number": 255, "type": "Secret Keyword", "verified_result": null }, @@ -8946,7 +9430,7 @@ "hashed_secret": "500886a66d19ebfe2d5c0ee86cf4612f9458ee4f", "is_secret": false, "is_verified": false, - "line_number": 338, + "line_number": 328, "type": "Secret Keyword", "verified_result": null }, @@ -8954,7 +9438,7 @@ "hashed_secret": "f2169f1a808382eb7b521206debaef01c91f1b60", "is_secret": false, "is_verified": false, - "line_number": 377, + "line_number": 367, "type": "Secret Keyword", "verified_result": null }, @@ -8962,7 +9446,7 @@ "hashed_secret": "cd61d9f689e9992aec40aeec7210b45a681309f9", "is_secret": false, "is_verified": false, - "line_number": 411, + "line_number": 401, "type": "Secret Keyword", "verified_result": null }, @@ -8970,7 +9454,7 @@ "hashed_secret": "ae97d9e9201a9bc1806069d80e5809ab745a91e8", "is_secret": false, "is_verified": false, - "line_number": 446, + "line_number": 436, "type": "Secret Keyword", "verified_result": null }, @@ -8978,7 +9462,7 @@ "hashed_secret": "eb2344b0e2d6529ced74f8b09be362a460defa15", "is_secret": false, "is_verified": false, - "line_number": 483, + "line_number": 473, "type": "Secret Keyword", "verified_result": null }, @@ -8986,7 +9470,7 @@ "hashed_secret": "89be7e8e88bc7b0cdb5ddaa01dab1cd1728541e2", "is_secret": false, "is_verified": false, - "line_number": 547, + "line_number": 537, "type": "Secret Keyword", "verified_result": null }, @@ -8994,7 +9478,7 @@ "hashed_secret": "5a539b200edec3aaa5167e1cde5d8fd0e8cf4aee", "is_secret": false, "is_verified": false, - "line_number": 560, + "line_number": 550, "type": "Secret Keyword", "verified_result": null }, @@ -9002,7 +9486,7 @@ "hashed_secret": "8be6db326ca95f74590bd09c9c328fc9a3db3b6c", "is_secret": false, "is_verified": false, - "line_number": 698, + "line_number": 688, "type": "Secret Keyword", "verified_result": null }, @@ -9010,7 +9494,7 @@ "hashed_secret": "2368055dc74fca43f2f9c849dbbaaa04616670cf", "is_secret": false, "is_verified": false, - "line_number": 747, + "line_number": 737, "type": "Secret Keyword", "verified_result": null }, @@ -9018,7 +9502,7 @@ "hashed_secret": "8ce647089e0614d3b924f4a0b8fb78268562ec7c", "is_secret": false, "is_verified": false, - "line_number": 1436, + "line_number": 1426, "type": "Secret Keyword", "verified_result": null }, @@ -9026,7 +9510,7 @@ "hashed_secret": "dc6f6cc49f767ae38db110a9e6c1adf5c651ecbf", "is_secret": false, "is_verified": false, - "line_number": 1480, + "line_number": 1470, "type": "Secret Keyword", "verified_result": null }, @@ -9034,7 +9518,7 @@ "hashed_secret": "7939ea1cdfc6d73115b0d736fa04012be2b25b18", "is_secret": false, "is_verified": false, - "line_number": 1571, + "line_number": 1561, "type": "Secret Keyword", "verified_result": null }, @@ -9042,7 +9526,7 @@ "hashed_secret": "91014bcbf183cf77807b4bd24f2e8652f67b56a0", "is_secret": false, "is_verified": false, - "line_number": 2079, + "line_number": 2069, "type": "Secret Keyword", "verified_result": null }, @@ -9050,7 +9534,7 @@ "hashed_secret": "61ac75f7e6677b79c219ce819d954e05f61b5d2c", "is_secret": false, "is_verified": false, - "line_number": 2097, + "line_number": 2087, "type": "Secret Keyword", "verified_result": null }, @@ -9058,7 +9542,7 @@ "hashed_secret": "8223fafd35f399e55c3cf611c3d6ebbf43d68d71", "is_secret": false, "is_verified": false, - "line_number": 2743, + "line_number": 2733, "type": "Secret Keyword", "verified_result": null }, @@ -9066,7 +9550,7 @@ "hashed_secret": "dfd99b5f25f839608a3c275c0f8ceb363f8f0bc0", "is_secret": false, "is_verified": false, - "line_number": 3538, + "line_number": 3510, "type": "Secret Keyword", "verified_result": null }, @@ -9074,7 +9558,7 @@ "hashed_secret": "5038e18712161fca54e52805726d3c70b296eff6", "is_secret": false, "is_verified": false, - "line_number": 3647, + "line_number": 3619, "type": "Secret Keyword", "verified_result": null }, @@ -9082,7 +9566,7 @@ "hashed_secret": "f5cd573c18bf811f82a886ceb6866b05d5ef02cd", "is_secret": false, "is_verified": false, - "line_number": 3725, + "line_number": 3697, "type": "Secret Keyword", "verified_result": null }, @@ -9090,7 +9574,7 @@ "hashed_secret": "79bead8e6d65862a00cffaa12ccde1189ec34d29", "is_secret": false, "is_verified": false, - "line_number": 4108, + "line_number": 4080, "type": "Secret Keyword", "verified_result": null } @@ -9230,7 +9714,7 @@ "hashed_secret": "5ef3af6cd9b5aa907fa618fac829baaec6763b42", "is_secret": false, "is_verified": false, - "line_number": 200, + "line_number": 203, "type": "Secret Keyword", "verified_result": null } @@ -9750,7 +10234,7 @@ "hashed_secret": "55d2534ed6ad4f269b428160428fa2f6f541ba7b", "is_secret": false, "is_verified": false, - "line_number": 136, + "line_number": 156, "type": "Base64 High Entropy String", "verified_result": null }, @@ -9758,7 +10242,7 @@ "hashed_secret": "cf743b3a58a4d0f91c1d7f5825c0b1b5f7758174", "is_secret": false, "is_verified": false, - "line_number": 504, + "line_number": 538, "type": "Base64 High Entropy String", "verified_result": null }, @@ -9766,7 +10250,7 @@ "hashed_secret": "8e42b03e460b2cf358ffbcf4da3bc5d14a22c86e", "is_secret": false, "is_verified": false, - "line_number": 548, + "line_number": 582, "type": "Base64 High Entropy String", "verified_result": null }, @@ -9774,7 +10258,7 @@ "hashed_secret": "2093dd9cf307518cfe1d2fa5a3985d6fec4e995e", "is_secret": false, "is_verified": false, - "line_number": 561, + "line_number": 595, "type": "Base64 High Entropy String", "verified_result": null }, @@ -9782,7 +10266,7 @@ "hashed_secret": "caa924f200b35ceb6f0e33878faff75203bdccb4", "is_secret": false, "is_verified": false, - "line_number": 930, + "line_number": 971, "type": "Secret Keyword", "verified_result": null }, @@ -9790,7 +10274,7 @@ "hashed_secret": "f16da2820437f3c703ff5b95c813f310ce8e67a4", "is_secret": false, "is_verified": false, - "line_number": 1179, + "line_number": 1288, "type": "Secret Keyword", "verified_result": null } @@ -9810,7 +10294,7 @@ "hashed_secret": "86de8c52637ec530fe39b0a8471da9b8764d5242", "is_secret": false, "is_verified": false, - "line_number": 141, + "line_number": 665, "type": "AWS Access Key", "verified_result": null } @@ -9838,7 +10322,7 @@ "hashed_secret": "9d4e1e23bd5b727046a9e3b4b7db57bd8d6ee684", "is_secret": false, "is_verified": false, - "line_number": 10447, + "line_number": 10450, "type": "Basic Auth Credentials", "verified_result": null } diff --git a/tests/unit/mcpgateway/services/test_tool_service.py b/tests/unit/mcpgateway/services/test_tool_service.py index e93b8d10679..73a70feddc3 100644 --- a/tests/unit/mcpgateway/services/test_tool_service.py +++ b/tests/unit/mcpgateway/services/test_tool_service.py @@ -5069,7 +5069,14 @@ async def test_rest_tool_get_param_conflict_logs_warning(self, tool_service, moc mock_metrics_buffer = Mock() mock_metrics_buffer.record_tool_metric = Mock() with patch("mcpgateway.services.tool_service.metrics_buffer", mock_metrics_buffer): - await tool_service.invoke_tool(test_db, "test_tool", {"api_key": "input_value", "q": "search"}, request_headers=None) + await tool_service.invoke_tool( + test_db, + "test_tool", + { + "api_key": "input_value", # pragma: allowlist secret + "q": "search" + }, + request_headers=None) assert "conflicting parameters" in caplog.text.lower() assert "api_key" in caplog.text From 158277e25a0993f67c1ae2ed1f8ef7225e59ab6f Mon Sep 17 00:00:00 2001 From: Rakhi Dutta Date: Tue, 14 Apr 2026 17:55:13 +0530 Subject: [PATCH 4/6] resolve rebase conflicts - merge mapping features with signed URL support Signed-off-by: Rakhi Dutta --- mcpgateway/services/tool_service.py | 78 ++++++++++--------- .../mcpgateway/services/test_tool_service.py | 45 +++++++---- 2 files changed, 71 insertions(+), 52 deletions(-) diff --git a/mcpgateway/services/tool_service.py b/mcpgateway/services/tool_service.py index 8b01683e9c6..e7c1afc11b9 100644 --- a/mcpgateway/services/tool_service.py +++ b/mcpgateway/services/tool_service.py @@ -4286,31 +4286,33 @@ async def invoke_tool( else: raise ToolInvocationError(f"Required URL parameter '{param}' not found in arguments") - # --- Extract query params from URL (after substitution) --- - parsed = urlparse(final_url) - final_url = f"{parsed.scheme}://{parsed.netloc}{parsed.path}" - - query_params = {k: v[0] for k, v in parse_qs(parsed.query).items()} - - if tool_query_mapping: - # Only mapped payload keys (renamed) are kept, merged on top of URL query params. - # Unmapped payload keys are intentionally dropped (mapping acts as an allowlist). - payload = apply_mapping_into_target(payload, tool_query_mapping, query_params) - # Reject non-scalar values that would be inappropriate as query parameters. - for qk, qv in payload.items(): - if isinstance(qv, (dict, list)): - raise ToolInvocationError(f"Tool '{name}': query_mapping produced non-scalar value for parameter '{qk}'") - - # Headers are mapped from the original arguments (not the path-param-reduced payload) - # to preserve all available data for header injection. - if tool_header_mapping: - _validate_header_mapping_targets(tool_header_mapping, name) - headers = apply_mapping_into_target(arguments.copy(), tool_header_mapping, headers) - # Reject header values containing CRLF or null bytes to prevent header injection. - for hdr_name, hdr_val in headers.items(): - if isinstance(hdr_val, str) and _INVALID_HEADER_VALUE_CHARS.search(hdr_val): - raise ToolInvocationError(f"Tool '{name}': header_mapping produced value with illegal characters for header '{hdr_name}'") - + # --- Extract query params from URL if query_mapping or header_mapping is used --- + # When mappings are present (not None), we strip query params from URL and apply transformations. + # When mappings are absent (None), behavior differs by method (see below). + query_params = {} + if tool_query_mapping is not None or tool_header_mapping is not None: + parsed = urlparse(final_url) + final_url = f"{parsed.scheme}://{parsed.netloc}{parsed.path}" + query_params = {k: v[0] for k, v in parse_qs(parsed.query).items()} + + if tool_query_mapping: + # Only mapped payload keys (renamed) are kept, merged on top of URL query params. + # Unmapped payload keys are intentionally dropped (mapping acts as an allowlist). + payload = apply_mapping_into_target(payload, tool_query_mapping, query_params) + # Reject non-scalar values that would be inappropriate as query parameters. + for qk, qv in payload.items(): + if isinstance(qv, (dict, list)): + raise ToolInvocationError(f"Tool '{name}': query_mapping produced non-scalar value for parameter '{qk}'") + + # Headers are mapped from the original arguments (not the path-param-reduced payload) + # to preserve all available data for header injection. + if tool_header_mapping: + _validate_header_mapping_targets(tool_header_mapping, name) + headers = apply_mapping_into_target(arguments.copy(), tool_header_mapping, headers) + # Reject header values containing CRLF or null bytes to prevent header injection. + for hdr_name, hdr_val in headers.items(): + if isinstance(hdr_val, str) and _INVALID_HEADER_VALUE_CHARS.search(hdr_val): + raise ToolInvocationError(f"Tool '{name}': header_mapping produced value with illegal characters for header '{hdr_name}'") # Use the tool's request_type rather than defaulting to POST (using local variable) method = tool_request_type.upper() if tool_request_type else "POST" @@ -4318,8 +4320,13 @@ async def invoke_tool( rest_start_time = time.time() try: if method == "GET": - # For GET: merge extracted URL query params into payload; everything sent as query string - if not tool_query_mapping: + # For GET: Extract and merge URL query params with input arguments + if tool_query_mapping is None and tool_header_mapping is None: + # When no mappings (both None), extract query params from URL + parsed = urlparse(final_url) + final_url = f"{parsed.scheme}://{parsed.netloc}{parsed.path}" + query_params = {k: v[0] for k, v in parse_qs(parsed.query).items()} + conflicts = set(payload.keys()) & set(query_params.keys()) if conflicts: logger.warning( @@ -4327,16 +4334,17 @@ async def invoke_tool( f"URL query params will take precedence for: {', '.join(sorted(conflicts))}. " f"Tool: {name}" ) - payload.update(query_params) + + payload.update(query_params) response = await asyncio.wait_for(self._http_client.get(final_url, params=payload, headers=headers), timeout=effective_timeout) else: - # For POST/PUT/PATCH/DELETE: Preserve query params in URL, only send input args in body. - # This is critical for signed URLs (Azure SAS, AWS presigned URLs, webhook signatures). - # When query_mapping is used, query params are already in payload; otherwise keep them in URL. - if not tool_query_mapping and query_params: - # Reconstruct URL with query params - from urllib.parse import urlencode - final_url = f"{final_url}?{urlencode(query_params)}" + # For POST/PUT/PATCH/DELETE: Different behavior based on mapping presence + if tool_query_mapping is not None or tool_header_mapping is not None: + # When mappings are used (not None), query params were already extracted and mapped + # Merge them into the JSON body for backward compatibility with mapped tools + payload.update(query_params) + # else: No mappings (both None) - preserve query params in URL for signed URL support + # (Azure SAS, AWS presigned URLs, webhook signatures, etc.) response = await asyncio.wait_for(self._http_client.request(method, final_url, json=payload, headers=headers), timeout=effective_timeout) except (asyncio.TimeoutError, httpx.TimeoutException): rest_elapsed_ms = (time.time() - rest_start_time) * 1000 diff --git a/tests/unit/mcpgateway/services/test_tool_service.py b/tests/unit/mcpgateway/services/test_tool_service.py index 73a70feddc3..3c5b157795d 100644 --- a/tests/unit/mcpgateway/services/test_tool_service.py +++ b/tests/unit/mcpgateway/services/test_tool_service.py @@ -2219,12 +2219,12 @@ async def test_invoke_tool_rest_parameter_substitution(self, tool_service, mock_ async def test_invoke_tool_rest_post_with_path_query_and_body_params(self, tool_service, mock_tool, mock_global_config_obj, test_db): """Test POST request with path parameters, query parameters (with templates), and body parameters. - This test demonstrates the complete parameter handling: + This test demonstrates the complete parameter handling (no mappings = signed URL mode): - Path parameters (e.g., {user_id}) are substituted into the URL path - Query parameters can also use templates (e.g., ?api_key={api_key}) - Static query parameters (e.g., ?version=v2) are preserved as-is - - Query parameters are merged into the JSON body for POST requests - - Remaining payload goes to the JSON body alongside merged query params + - Query parameters are preserved in URL for POST (signed URL support) + - Remaining payload goes to the JSON body (without query params) """ mock_tool.integration_type = "REST" mock_tool.request_type = "POST" @@ -2252,16 +2252,16 @@ async def test_invoke_tool_rest_post_with_path_query_and_body_params(self, tool_ await tool_service.invoke_tool(test_db, "test_tool", payload, request_headers=None) - # Verify parameter handling for POST: + # Verify parameter handling for POST (no mappings = signed URL support): # 1. Path parameter substituted: /users/456/posts # 2. Query param template substituted: api_key=secret123 # 3. Static query param preserved: version=v2 - # 4. Query params merged into JSON body (backward-compatible behavior for POST) + # 4. Query params STAY in URL (not merged into body) for signed URL support # 5. Body params: title and content (user_id and api_key removed after path/query substitution) tool_service._http_client.request.assert_called_once_with( "POST", - "http://example.com/api/users/456/posts", # Path param substituted, query string stripped - json={"title": "New Post", "content": "Hello World", "api_key": "secret123", "version": "v2"}, # Body + merged query params + "http://example.com/api/users/456/posts?api_key=secret123&version=v2", # Path param substituted, query params preserved + json={"title": "New Post", "content": "Hello World"}, # Only body params headers=mock_tool.headers, ) @@ -2324,11 +2324,11 @@ async def test_invoke_tool_rest_put_with_query_params(self, tool_service, mock_t await tool_service.invoke_tool(test_db, "test_tool", payload, request_headers=None) - # Query params merged into JSON body, same as POST + # Query params preserved in URL for signed URL support (no mappings) tool_service._http_client.request.assert_called_once_with( "PUT", - "http://example.com/api/items/1", - json={"name": "updated", "version": "v2"}, + "http://example.com/api/items/1?version=v2", + json={"name": "updated"}, headers=mock_tool.headers, ) @@ -4335,10 +4335,12 @@ async def test_run_timeout_post_invoke_noop_without_plugin_manager(self, tool_se {"existing": "1", "mapped_query": "test"}, {"Content-Type": "application/json", "X-Mapped-Query": "test"}, ), + # When both mappings are None, query params are preserved in URL (signed URL support) + # Only input args go in the body. Use {} to merge query params into body. ( None, None, - {"query": "test", "existing": "1"}, + {"query": "test"}, # Only input args, URL query params stay in URL {"Content-Type": "application/json"}, ), ( @@ -4396,12 +4398,21 @@ async def test_invoke_tool_rest_headers_and_query_maps_applied( ): await tool_service.invoke_tool(test_db, "test_tool", {"query": "test"}, request_headers=None) - tool_service._http_client.request.assert_called_once_with( - "POST", - "http://example.com/tools/test", - json=expected_json, - headers=expected_headers, - ) + # When both mappings are None, query params stay in URL (signed URL support) + if tool_query_mapping is None and tool_header_mapping is None: + tool_service._http_client.request.assert_called_once_with( + "POST", + "http://example.com/tools/test?existing=1", # Query params preserved in URL + json=expected_json, + headers=expected_headers, + ) + else: + tool_service._http_client.request.assert_called_once_with( + "POST", + "http://example.com/tools/test", + json=expected_json, + headers=expected_headers, + ) @pytest.mark.asyncio async def test_invoke_tool_rest_get_with_query_mapping(self, tool_service, mock_tool, mock_global_config_obj, test_db): From 31aef153ab5abe278f72ebe28af532571c19648f Mon Sep 17 00:00:00 2001 From: Rakhi Dutta Date: Tue, 14 Apr 2026 18:21:44 +0530 Subject: [PATCH 5/6] rebase conflict issue Signed-off-by: Rakhi Dutta --- mcpgateway/services/tool_service.py | 20 +++++++++++-------- .../mcpgateway/services/test_tool_service.py | 17 +++++++++++----- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/mcpgateway/services/tool_service.py b/mcpgateway/services/tool_service.py index e7c1afc11b9..abe212ec1d7 100644 --- a/mcpgateway/services/tool_service.py +++ b/mcpgateway/services/tool_service.py @@ -4287,10 +4287,14 @@ async def invoke_tool( raise ToolInvocationError(f"Required URL parameter '{param}' not found in arguments") # --- Extract query params from URL if query_mapping or header_mapping is used --- - # When mappings are present (not None), we strip query params from URL and apply transformations. - # When mappings are absent (None), behavior differs by method (see below). + # When mappings are present (not None/empty), we strip query params from URL and apply transformations. + # When mappings are absent (None/empty), preserve query params in URL for signed URLs. query_params = {} - if tool_query_mapping is not None or tool_header_mapping is not None: + # Treat empty dict same as None (no mapping configured) + has_query_mapping = tool_query_mapping is not None and tool_query_mapping != {} + has_header_mapping = tool_header_mapping is not None and tool_header_mapping != {} + + if has_query_mapping or has_header_mapping: parsed = urlparse(final_url) final_url = f"{parsed.scheme}://{parsed.netloc}{parsed.path}" query_params = {k: v[0] for k, v in parse_qs(parsed.query).items()} @@ -4321,8 +4325,8 @@ async def invoke_tool( try: if method == "GET": # For GET: Extract and merge URL query params with input arguments - if tool_query_mapping is None and tool_header_mapping is None: - # When no mappings (both None), extract query params from URL + if not has_query_mapping and not has_header_mapping: + # When no mappings (None or empty), extract query params from URL parsed = urlparse(final_url) final_url = f"{parsed.scheme}://{parsed.netloc}{parsed.path}" query_params = {k: v[0] for k, v in parse_qs(parsed.query).items()} @@ -4339,11 +4343,11 @@ async def invoke_tool( response = await asyncio.wait_for(self._http_client.get(final_url, params=payload, headers=headers), timeout=effective_timeout) else: # For POST/PUT/PATCH/DELETE: Different behavior based on mapping presence - if tool_query_mapping is not None or tool_header_mapping is not None: - # When mappings are used (not None), query params were already extracted and mapped + if has_query_mapping or has_header_mapping: + # When mappings are used (not None/empty), query params were already extracted and mapped # Merge them into the JSON body for backward compatibility with mapped tools payload.update(query_params) - # else: No mappings (both None) - preserve query params in URL for signed URL support + # else: No mappings (None or empty) - preserve query params in URL for signed URL support # (Azure SAS, AWS presigned URLs, webhook signatures, etc.) response = await asyncio.wait_for(self._http_client.request(method, final_url, json=payload, headers=headers), timeout=effective_timeout) except (asyncio.TimeoutError, httpx.TimeoutException): diff --git a/tests/unit/mcpgateway/services/test_tool_service.py b/tests/unit/mcpgateway/services/test_tool_service.py index 3c5b157795d..3b6a94c8e3a 100644 --- a/tests/unit/mcpgateway/services/test_tool_service.py +++ b/tests/unit/mcpgateway/services/test_tool_service.py @@ -4335,8 +4335,8 @@ async def test_run_timeout_post_invoke_noop_without_plugin_manager(self, tool_se {"existing": "1", "mapped_query": "test"}, {"Content-Type": "application/json", "X-Mapped-Query": "test"}, ), - # When both mappings are None, query params are preserved in URL (signed URL support) - # Only input args go in the body. Use {} to merge query params into body. + # When both mappings are None or empty, query params are preserved in URL (signed URL support) + # Only input args go in the body. ( None, None, @@ -4355,10 +4355,11 @@ async def test_run_timeout_post_invoke_noop_without_plugin_manager(self, tool_se {"query": "test", "existing": "1"}, {"Content-Type": "application/json", "X-Query": "test"}, ), + # Empty dict mappings also preserve query params in URL (same as None) ( {}, {}, - {"query": "test", "existing": "1"}, + {"query": "test"}, # Only input args, URL query params stay in URL {"Content-Type": "application/json"}, ), ], @@ -4398,8 +4399,13 @@ async def test_invoke_tool_rest_headers_and_query_maps_applied( ): await tool_service.invoke_tool(test_db, "test_tool", {"query": "test"}, request_headers=None) - # When both mappings are None, query params stay in URL (signed URL support) - if tool_query_mapping is None and tool_header_mapping is None: + # When both mappings are None or empty dict, query params stay in URL (signed URL support) + # When mappings have actual values, query params are extracted and merged into body + has_query_mapping = tool_query_mapping is not None and tool_query_mapping != {} + has_header_mapping = tool_header_mapping is not None and tool_header_mapping != {} + + if not has_query_mapping and not has_header_mapping: + # No mappings (None or empty) - query params preserved in URL tool_service._http_client.request.assert_called_once_with( "POST", "http://example.com/tools/test?existing=1", # Query params preserved in URL @@ -4407,6 +4413,7 @@ async def test_invoke_tool_rest_headers_and_query_maps_applied( headers=expected_headers, ) else: + # Mappings present - query params extracted and merged into body tool_service._http_client.request.assert_called_once_with( "POST", "http://example.com/tools/test", From a27ee1b02d9d95565c73ca5072c24b403426bcb1 Mon Sep 17 00:00:00 2001 From: Brian Hussey Date: Tue, 14 Apr 2026 15:08:46 +0100 Subject: [PATCH 6/6] Update .secrets.baseline Signed-off-by: Brian Hussey --- .secrets.baseline | 680 +++++++--------------------------------------- 1 file changed, 98 insertions(+), 582 deletions(-) diff --git a/.secrets.baseline b/.secrets.baseline index 80a8de3276e..77a86a53674 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -1,9 +1,9 @@ { "exclude": { - "files": "^.secrets.baseline$|package-lock.json|Cargo.lock|scripts/sign_image.sh|scripts/zap|sonar-project.properties|uv.lock", + "files": "^.secrets.baseline|package-lock.json|Cargo.lock|scripts/sign_image.sh|scripts/zap|sonar-project.properties|uv.lock|go.sum|mcpgateway/sri_hashes.json|^.secrets.baseline$", "lines": null }, - "generated_at": "2026-04-13T12:03:04Z", + "generated_at": "2026-04-14T14:08:10Z", "plugins_used": [ { "name": "AWSKeyDetector" @@ -200,7 +200,7 @@ "hashed_secret": "f4793151b0607198d4de9b1ca458d3e25adf1cb7", "is_secret": false, "is_verified": false, - "line_number": 111, + "line_number": 112, "type": "Secret Keyword", "verified_result": null }, @@ -208,7 +208,7 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, - "line_number": 187, + "line_number": 188, "type": "Secret Keyword", "verified_result": null }, @@ -216,7 +216,7 @@ "hashed_secret": "7b4455a56fbf1d198e45e04c437488514645a82c", "is_secret": false, "is_verified": false, - "line_number": 189, + "line_number": 190, "type": "Secret Keyword", "verified_result": null }, @@ -224,7 +224,7 @@ "hashed_secret": "90bd1b48e958257948487b90bee080ba5ed00caa", "is_secret": false, "is_verified": false, - "line_number": 261, + "line_number": 262, "type": "Hex High Entropy String", "verified_result": null }, @@ -232,7 +232,7 @@ "hashed_secret": "48ffbad96aa9c2b33f9486f5a3c2108198acb518", "is_secret": false, "is_verified": false, - "line_number": 262, + "line_number": 263, "type": "Hex High Entropy String", "verified_result": null } @@ -242,11 +242,7 @@ "hashed_secret": "b4673e578b9b30fe8bba1b555b7b59883444c697", "is_secret": false, "is_verified": false, -<<<<<<< HEAD "line_number": 760, -======= - "line_number": 555, ->>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null }, @@ -254,11 +250,7 @@ "hashed_secret": "4a0a2df96d4c9a13a282268cab33ac4b8cbb2c72", "is_secret": false, "is_verified": false, -<<<<<<< HEAD "line_number": 848, -======= - "line_number": 643, ->>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null }, @@ -266,11 +258,7 @@ "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", "is_secret": false, "is_verified": false, -<<<<<<< HEAD "line_number": 1198, -======= - "line_number": 993, ->>>>>>> c529146ab (pre-commit) "type": "Basic Auth Credentials", "verified_result": null }, @@ -278,11 +266,7 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, -<<<<<<< HEAD "line_number": 2564, -======= - "line_number": 2359, ->>>>>>> c529146ab (pre-commit) "type": "Basic Auth Credentials", "verified_result": null }, @@ -290,11 +274,7 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, -<<<<<<< HEAD "line_number": 2655, -======= - "line_number": 2450, ->>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null }, @@ -302,11 +282,7 @@ "hashed_secret": "ac371b6dcce28a86c90d12bc57d946a800eebf17", "is_secret": false, "is_verified": false, -<<<<<<< HEAD "line_number": 2698, -======= - "line_number": 2493, ->>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null }, @@ -314,11 +290,7 @@ "hashed_secret": "0b6ec68df700dec4dcd64babd0eda1edccddace1", "is_secret": false, "is_verified": false, -<<<<<<< HEAD "line_number": 2703, -======= - "line_number": 2498, ->>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null }, @@ -326,11 +298,7 @@ "hashed_secret": "4ad6f0082ee224001beb3ca5c3e81c8ceea5ed86", "is_secret": false, "is_verified": false, -<<<<<<< HEAD "line_number": 2708, -======= - "line_number": 2503, ->>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null } @@ -376,11 +344,7 @@ "hashed_secret": "844c398e469ef3fb919da3778944365ab2175fb7", "is_secret": false, "is_verified": false, -<<<<<<< HEAD "line_number": 463, -======= - "line_number": 377, ->>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null }, @@ -388,11 +352,7 @@ "hashed_secret": "319037749ce37e577db0b3628c7f90e333544391", "is_secret": false, "is_verified": false, -<<<<<<< HEAD "line_number": 887, -======= - "line_number": 801, ->>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null }, @@ -400,11 +360,7 @@ "hashed_secret": "6ae2832e494d1098e8901fe156083e39399a24f1", "is_secret": false, "is_verified": false, -<<<<<<< HEAD "line_number": 889, -======= - "line_number": 803, ->>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null }, @@ -412,11 +368,7 @@ "hashed_secret": "43fc45734b96bcb1b6cef373e949eb3524ae199b", "is_secret": false, "is_verified": false, -<<<<<<< HEAD "line_number": 1580, -======= - "line_number": 1494, ->>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null }, @@ -424,11 +376,7 @@ "hashed_secret": "9d989e8d27dc9e0ec3389fc855f142c3d40f0c50", "is_secret": false, "is_verified": false, -<<<<<<< HEAD "line_number": 1791, -======= - "line_number": 1704, ->>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null }, @@ -436,11 +384,7 @@ "hashed_secret": "d3ac7a4ef1a838b4134f2f6e7f3c0d249d74b674", "is_secret": false, "is_verified": false, -<<<<<<< HEAD "line_number": 6121, -======= - "line_number": 6072, ->>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null }, @@ -448,11 +392,7 @@ "hashed_secret": "5932862bcd24dd27d0dc0407ec94fe9d6ea24aeb", "is_secret": false, "is_verified": false, -<<<<<<< HEAD "line_number": 6618, -======= - "line_number": 6569, ->>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null }, @@ -460,11 +400,7 @@ "hashed_secret": "c77c805e32f173e4321ee9187de9c29cb3804513", "is_secret": false, "is_verified": false, -<<<<<<< HEAD "line_number": 6630, -======= - "line_number": 6581, ->>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null }, @@ -472,11 +408,7 @@ "hashed_secret": "8fe3df8a68ddd0d4ab2214186cbb8e38ccd0e06a", "is_secret": false, "is_verified": false, -<<<<<<< HEAD "line_number": 6702, -======= - "line_number": 6653, ->>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null }, @@ -484,11 +416,7 @@ "hashed_secret": "93ac8946882128457cd9e283b30ca851945e6690", "is_secret": false, "is_verified": false, -<<<<<<< HEAD "line_number": 7772, -======= - "line_number": 7747, ->>>>>>> c529146ab (pre-commit) "type": "Secret Keyword", "verified_result": null } @@ -2100,7 +2028,7 @@ "hashed_secret": "9ae4d2858bd2df0cf19722488adae925ff3c4bdb", "is_secret": false, "is_verified": false, - "line_number": 165, + "line_number": 173, "type": "Secret Keyword", "verified_result": null } @@ -2138,11 +2066,7 @@ "hashed_secret": "25910f981e85ca04baf359199dd0bd4a3ae738b6", "is_secret": false, "is_verified": false, -<<<<<<< HEAD "line_number": 850, -======= - "line_number": 852, ->>>>>>> c529146ab (pre-commit) "type": "AWS Access Key", "verified_result": null } @@ -4178,7 +4102,7 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, - "line_number": 4256, + "line_number": 4240, "type": "Secret Keyword", "verified_result": null }, @@ -4186,7 +4110,7 @@ "hashed_secret": "559b05f1b2863e725b76e216ac3dadecbf92e244", "is_secret": false, "is_verified": false, - "line_number": 4864, + "line_number": 4841, "type": "Secret Keyword", "verified_result": null }, @@ -4194,7 +4118,7 @@ "hashed_secret": "a8af4759392d4f7496d613174f33afe2074a4b8d", "is_secret": false, "is_verified": false, - "line_number": 4866, + "line_number": 4843, "type": "Secret Keyword", "verified_result": null }, @@ -4202,7 +4126,7 @@ "hashed_secret": "85b60d811d16ff56b3654587d4487f713bfa33b7", "is_secret": false, "is_verified": false, - "line_number": 15018, + "line_number": 15169, "type": "Secret Keyword", "verified_result": null } @@ -4906,11 +4830,7 @@ "hashed_secret": "ff37a98a9963d347e9749a5c1b3936a4a245a6ff", "is_secret": false, "is_verified": false, -<<<<<<< HEAD - "line_number": 2228, -======= - "line_number": 2229, ->>>>>>> c529146ab (pre-commit) + "line_number": 2236, "type": "Secret Keyword", "verified_result": null } @@ -4956,7 +4876,7 @@ "hashed_secret": "f2b14f68eb995facb3a1c35287b778d5bd785511", "is_secret": false, "is_verified": false, - "line_number": 171, + "line_number": 175, "type": "Secret Keyword", "verified_result": null }, @@ -4964,7 +4884,7 @@ "hashed_secret": "1073ab6cda4b991cd29f9e83a307f34004ae9327", "is_secret": false, "is_verified": false, - "line_number": 177, + "line_number": 181, "type": "Secret Keyword", "verified_result": null }, @@ -4972,7 +4892,7 @@ "hashed_secret": "d4fe581561f18ee5006254a7e53f53cbed780bc2", "is_secret": false, "is_verified": false, - "line_number": 259, + "line_number": 268, "type": "Secret Keyword", "verified_result": null } @@ -5048,7 +4968,7 @@ "hashed_secret": "d3ecb0d890368d7659ee54010045b835dacb8efe", "is_secret": false, "is_verified": false, - "line_number": 610, + "line_number": 611, "type": "Secret Keyword", "verified_result": null } @@ -5058,7 +4978,7 @@ "hashed_secret": "c377074d6473f35a91001981355da793dc808ffd", "is_secret": false, "is_verified": false, - "line_number": 4111, + "line_number": 4220, "type": "Hex High Entropy String", "verified_result": null }, @@ -5066,7 +4986,7 @@ "hashed_secret": "6367c48dd193d56ea7b0baad25b19455e529f5ee", "is_secret": false, "is_verified": false, - "line_number": 5224, + "line_number": 5333, "type": "Secret Keyword", "verified_result": null }, @@ -5074,7 +4994,7 @@ "hashed_secret": "f2b14f68eb995facb3a1c35287b778d5bd785511", "is_secret": false, "is_verified": false, - "line_number": 5388, + "line_number": 5497, "type": "Secret Keyword", "verified_result": null }, @@ -5082,7 +5002,7 @@ "hashed_secret": "f42a3fabe1e9bed059d727f47eb752e3aa61b977", "is_secret": false, "is_verified": false, - "line_number": 5445, + "line_number": 5554, "type": "Secret Keyword", "verified_result": null }, @@ -5090,7 +5010,7 @@ "hashed_secret": "b85788b459aa4d67e1070930dae6d0827756aadb", "is_secret": false, "is_verified": false, - "line_number": 5483, + "line_number": 5592, "type": "Secret Keyword", "verified_result": null }, @@ -5098,7 +5018,7 @@ "hashed_secret": "52dcc83ec1e54426ad58a64854d1eb8d5f5d9685", "is_secret": false, "is_verified": false, - "line_number": 5484, + "line_number": 5593, "type": "Secret Keyword", "verified_result": null } @@ -5262,7 +5182,7 @@ "hashed_secret": "b4e44716dbbf57be3dae2f819d96795a85d06652", "is_secret": false, "is_verified": false, - "line_number": 12461, + "line_number": 12498, "type": "Secret Keyword", "verified_result": null } @@ -5514,7 +5434,7 @@ "hashed_secret": "a4b48a81cdab1e1a5dd37907d6c85ca1c61ddc7c", "is_secret": false, "is_verified": false, - "line_number": 568, + "line_number": 572, "type": "Secret Keyword", "verified_result": null } @@ -5599,24 +5519,6 @@ "verified_result": null } ], - "plugins/encoded_exfil_detection/README.md": [ - { - "hashed_secret": "f7f877c24a4ebef314009a2e79314797bae91bb6", - "is_secret": false, - "is_verified": false, - "line_number": 149, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "5f2fe4f03b2314fa3a217f1e1f0ab2e3f5b1b49f", - "is_secret": false, - "is_verified": false, - "line_number": 178, - "type": "Secret Keyword", - "verified_result": null - } - ], "plugins/examples/custom_auth_example/README.md": [ { "hashed_secret": "423529858c0ba74fbbe141bce50b20f3c0d3b9f6", @@ -5747,16 +5649,6 @@ "verified_result": null } ], - "plugins/pii_filter/README.md": [ - { - "hashed_secret": "25910f981e85ca04baf359199dd0bd4a3ae738b6", - "is_secret": false, - "is_verified": false, - "line_number": 344, - "type": "AWS Access Key", - "verified_result": null - } - ], "plugins/resource_filter/README.md": [ { "hashed_secret": "048fe8f760ed90d8058cacc2da41ed7a7eb76669", @@ -5803,372 +5695,6 @@ "verified_result": null } ], - "plugins_rust/encoded_exfil_detection/benches/encoded_exfil_detection.rs": [ - { - "hashed_secret": "9e53fffcf6d106f85fff53416e2ae687e1346aea", - "is_secret": false, - "is_verified": false, - "line_number": 27, - "type": "Hex High Entropy String", - "verified_result": null - }, - { - "hashed_secret": "67844048772af8b97258a5f1fe00b0043216f739", - "is_secret": false, - "is_verified": false, - "line_number": 49, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "648b3cbf65960d678821b2ae53740d890e1be100", - "is_secret": false, - "is_verified": false, - "line_number": 128, - "type": "Secret Keyword", - "verified_result": null - } - ], - "plugins_rust/encoded_exfil_detection/src/lib.rs": [ - { - "hashed_secret": "1d278d3c888d1a2fa7eed622bfc02927ce4049af", - "is_secret": false, - "is_verified": false, - "line_number": 573, - "type": "Base64 High Entropy String", - "verified_result": null - }, - { - "hashed_secret": "d7da4707f9793b357da965eb04f06ceae7d7bfa6", - "is_secret": false, - "is_verified": false, - "line_number": 574, - "type": "Base64 High Entropy String", - "verified_result": null - }, - { - "hashed_secret": "0962e90797cdf7ff77c8a0a9477a7ca4f493465f", - "is_secret": false, - "is_verified": false, - "line_number": 892, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "ca3f3092640b07c051f03e52690fda1bcee3f60d", - "is_secret": false, - "is_verified": false, - "line_number": 913, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "532fdeccb155dce5b528ba039b9a5d201b817e3b", - "is_secret": false, - "is_verified": false, - "line_number": 932, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "cf743b3a58a4d0f91c1d7f5825c0b1b5f7758174", - "is_secret": false, - "is_verified": false, - "line_number": 969, - "type": "Base64 High Entropy String", - "verified_result": null - }, - { - "hashed_secret": "ff1a7ebc241b8eefe9e75e13f20cc22c365ab626", - "is_secret": false, - "is_verified": false, - "line_number": 969, - "type": "Secret Keyword", - "verified_result": null - } - ], - "plugins_rust/secrets_detection/benches/secrets_detection.rs": [ - { - "hashed_secret": "86de8c52637ec530fe39b0a8471da9b8764d5242", - "is_secret": false, - "is_verified": false, - "line_number": 53, - "type": "AWS Access Key", - "verified_result": null - }, - { - "hashed_secret": "5b8c02feb3811310ff452e9a812b9f98873f36bf", - "is_secret": false, - "is_verified": false, - "line_number": 69, - "type": "SoftLayer Credentials", - "verified_result": null - }, - { - "hashed_secret": "0fa6996ddd42e0f9db3f1c04d06453026d56da0f", - "is_secret": false, - "is_verified": false, - "line_number": 73, - "type": "SoftLayer Credentials", - "verified_result": null - }, - { - "hashed_secret": "de56e523c4cf688e56a7df48cf01e2a21db5b5a4", - "is_secret": false, - "is_verified": false, - "line_number": 77, - "type": "AWS Access Key", - "verified_result": null - } - ], - "plugins_rust/secrets_detection/compare_performance.py": [ - { - "hashed_secret": "86de8c52637ec530fe39b0a8471da9b8764d5242", - "is_secret": false, - "is_verified": false, - "line_number": 67, - "type": "AWS Access Key", - "verified_result": null - }, - { - "hashed_secret": "5b8c02feb3811310ff452e9a812b9f98873f36bf", - "is_secret": false, - "is_verified": false, - "line_number": 71, - "type": "SoftLayer Credentials", - "verified_result": null - }, - { - "hashed_secret": "0fa6996ddd42e0f9db3f1c04d06453026d56da0f", - "is_secret": false, - "is_verified": false, - "line_number": 72, - "type": "SoftLayer Credentials", - "verified_result": null - } - ], - "plugins_rust/secrets_detection/examples/heavy_workload.rs": [ - { - "hashed_secret": "86de8c52637ec530fe39b0a8471da9b8764d5242", - "is_secret": false, - "is_verified": false, - "line_number": 34, - "type": "AWS Access Key", - "verified_result": null - }, - { - "hashed_secret": "5b8c02feb3811310ff452e9a812b9f98873f36bf", - "is_secret": false, - "is_verified": false, - "line_number": 38, - "type": "SoftLayer Credentials", - "verified_result": null - }, - { - "hashed_secret": "0fa6996ddd42e0f9db3f1c04d06453026d56da0f", - "is_secret": false, - "is_verified": false, - "line_number": 39, - "type": "SoftLayer Credentials", - "verified_result": null - }, - { - "hashed_secret": "de56e523c4cf688e56a7df48cf01e2a21db5b5a4", - "is_secret": false, - "is_verified": false, - "line_number": 40, - "type": "AWS Access Key", - "verified_result": null - }, - { - "hashed_secret": "be4fc4886bd949b369d5e092eb87494f12e57e5b", - "is_secret": false, - "is_verified": false, - "line_number": 42, - "type": "Private Key", - "verified_result": null - } - ], - "plugins_rust/secrets_detection/src/patterns.rs": [ - { - "hashed_secret": "86de8c52637ec530fe39b0a8471da9b8764d5242", - "is_secret": false, - "is_verified": false, - "line_number": 94, - "type": "AWS Access Key", - "verified_result": null - }, - { - "hashed_secret": "de56e523c4cf688e56a7df48cf01e2a21db5b5a4", - "is_secret": false, - "is_verified": false, - "line_number": 98, - "type": "AWS Access Key", - "verified_result": null - }, - { - "hashed_secret": "a5f223264a7296f00627a4c5c00a9957c67f5479", - "is_secret": false, - "is_verified": false, - "line_number": 238, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "be4fc4886bd949b369d5e092eb87494f12e57e5b", - "is_secret": false, - "is_verified": false, - "line_number": 282, - "type": "Private Key", - "verified_result": null - }, - { - "hashed_secret": "daefe0b4345a654580dcad25c7c11ff4c944a8c0", - "is_secret": false, - "is_verified": false, - "line_number": 286, - "type": "Private Key", - "verified_result": null - }, - { - "hashed_secret": "f0778f3e140a61d5bbbed5430773e52af2f5fba4", - "is_secret": false, - "is_verified": false, - "line_number": 290, - "type": "Private Key", - "verified_result": null - }, - { - "hashed_secret": "27c6929aef41ae2bcadac15ca6abcaff72cda9cd", - "is_secret": false, - "is_verified": false, - "line_number": 294, - "type": "Private Key", - "verified_result": null - }, - { - "hashed_secret": "75f3be87351a63f1c2f677632838f9e2be066891", - "is_secret": false, - "is_verified": false, - "line_number": 336, - "type": "Hex High Entropy String", - "verified_result": null - }, - { - "hashed_secret": "a19310f994b3a8974f03cddd1de4b862266947e8", - "is_secret": false, - "is_verified": false, - "line_number": 340, - "type": "Hex High Entropy String", - "verified_result": null - }, - { - "hashed_secret": "b1775a785f09a6ebaf2dc33d6eaeb98974d9cdb8", - "is_secret": false, - "is_verified": false, - "line_number": 344, - "type": "Hex High Entropy String", - "verified_result": null - }, - { - "hashed_secret": "7326d685c1328a4b748e7c239ba227c8a7afc988", - "is_secret": false, - "is_verified": false, - "line_number": 350, - "type": "Hex High Entropy String", - "verified_result": null - }, - { - "hashed_secret": "e0eca9acea92cdf77e594f0d3367116b9adffeec", - "is_secret": false, - "is_verified": false, - "line_number": 354, - "type": "Base64 High Entropy String", - "verified_result": null - }, - { - "hashed_secret": "e4d0f04f03d9183f843c9d764592ba5bcd17f356", - "is_secret": false, - "is_verified": false, - "line_number": 369, - "type": "Base64 High Entropy String", - "verified_result": null - }, - { - "hashed_secret": "b256e49b893f8b2eeb2384801910c70ed17a2191", - "is_secret": false, - "is_verified": false, - "line_number": 411, - "type": "Secret Keyword", - "verified_result": null - } - ], - "plugins_rust/secrets_detection/src/scanner.rs": [ - { - "hashed_secret": "50b68587c1c40248753c5e9190dcc3dd73aa142a", - "is_secret": false, - "is_verified": false, - "line_number": 243, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "4e8dd640c77be10809ea27a4a6e668d9cba82014", - "is_secret": false, - "is_verified": false, - "line_number": 248, - "type": "SoftLayer Credentials", - "verified_result": null - }, - { - "hashed_secret": "721b09413c9b91bf3278f966b62c7a03ae2ef9a0", - "is_secret": false, - "is_verified": false, - "line_number": 248, - "type": "Base64 High Entropy String", - "verified_result": null - }, - { - "hashed_secret": "be4fc4886bd949b369d5e092eb87494f12e57e5b", - "is_secret": false, - "is_verified": false, - "line_number": 292, - "type": "Private Key", - "verified_result": null - }, - { - "hashed_secret": "de56e523c4cf688e56a7df48cf01e2a21db5b5a4", - "is_secret": false, - "is_verified": false, - "line_number": 726, - "type": "AWS Access Key", - "verified_result": null - }, - { - "hashed_secret": "2e3810897e838a5edcfd57b5fb117927fdc922da", - "is_secret": false, - "is_verified": false, - "line_number": 740, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "7f1d81c1d9cf850d6a1dbd32d5f110b6732fe60e", - "is_secret": false, - "is_verified": false, - "line_number": 740, - "type": "Base64 High Entropy String", - "verified_result": null - }, - { - "hashed_secret": "86de8c52637ec530fe39b0a8471da9b8764d5242", - "is_secret": false, - "is_verified": false, - "line_number": 799, - "type": "AWS Access Key", - "verified_result": null - } - ], "podman-compose-sonarqube.yaml": [ { "hashed_secret": "345e9ea7c857e75dedd9edb24c232e1cab297c19", @@ -6380,7 +5906,7 @@ "hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", "is_secret": false, "is_verified": false, - "line_number": 115, + "line_number": 153, "type": "Basic Auth Credentials", "verified_result": null }, @@ -6388,7 +5914,7 @@ "hashed_secret": "206c80413b9a96c1312cc346b7d2517b84463edd", "is_secret": false, "is_verified": false, - "line_number": 164, + "line_number": 202, "type": "Secret Keyword", "verified_result": null } @@ -6428,7 +5954,7 @@ "hashed_secret": "ba9cdae9b74942b8ac45ec20dfc3803a07fe6de9", "is_secret": false, "is_verified": false, - "line_number": 80, + "line_number": 79, "type": "Secret Keyword", "verified_result": null }, @@ -6436,7 +5962,7 @@ "hashed_secret": "4dfad2d5130a5bcaf2716c495de92da13f4389e0", "is_secret": false, "is_verified": false, - "line_number": 758, + "line_number": 764, "type": "Secret Keyword", "verified_result": null } @@ -7332,7 +6858,7 @@ "hashed_secret": "1a91d62f7ca67399625a4368a6ab5d4a3baa6073", "is_secret": false, "is_verified": false, - "line_number": 98, + "line_number": 115, "type": "Secret Keyword", "verified_result": null }, @@ -7340,7 +6866,7 @@ "hashed_secret": "dbdab9be92cacdae6a97e8601332bfaa8545800f", "is_secret": false, "is_verified": false, - "line_number": 99, + "line_number": 116, "type": "Secret Keyword", "verified_result": null }, @@ -7348,7 +6874,7 @@ "hashed_secret": "4ea8d2335b430796cf3f500368c5b0f5b1dc90f5", "is_secret": false, "is_verified": false, - "line_number": 151, + "line_number": 213, "type": "Secret Keyword", "verified_result": null } @@ -7573,16 +7099,6 @@ "verified_result": null } ], - "tests/unit/mcpgateway/plugins/plugins/pii_filter/test_pii_filter.py": [ - { - "hashed_secret": "25910f981e85ca04baf359199dd0bd4a3ae738b6", - "is_secret": false, - "is_verified": false, - "line_number": 505, - "type": "AWS Access Key", - "verified_result": null - } - ], "tests/unit/mcpgateway/plugins/plugins/tools_telemetry_exporter/test_tools_telemetry_exporter.py": [ { "hashed_secret": "e8af0e18ff4805f4efd84f58b0fa69e3780f35a4", @@ -8422,7 +7938,7 @@ "hashed_secret": "99834bc4eff3f1e1c1e4692d2476b593b501d045", "is_secret": false, "is_verified": false, - "line_number": 5033, + "line_number": 5045, "type": "Secret Keyword", "verified_result": null }, @@ -8430,7 +7946,7 @@ "hashed_secret": "f2b14f68eb995facb3a1c35287b778d5bd785511", "is_secret": false, "is_verified": false, - "line_number": 5038, + "line_number": 5050, "type": "Secret Keyword", "verified_result": null }, @@ -8438,7 +7954,7 @@ "hashed_secret": "1a91d62f7ca67399625a4368a6ab5d4a3baa6073", "is_secret": false, "is_verified": false, - "line_number": 6575, + "line_number": 6587, "type": "Secret Keyword", "verified_result": null }, @@ -8446,7 +7962,7 @@ "hashed_secret": "2a23dd4f2e8b50315e601a2867ea7b7bf5a43b8c", "is_secret": false, "is_verified": false, - "line_number": 7172, + "line_number": 7184, "type": "Secret Keyword", "verified_result": null }, @@ -8454,7 +7970,7 @@ "hashed_secret": "ed3e0017cb8e4b06a59af1a441f62cbe58d2ef59", "is_secret": false, "is_verified": false, - "line_number": 7191, + "line_number": 7203, "type": "Secret Keyword", "verified_result": null }, @@ -8462,7 +7978,7 @@ "hashed_secret": "b55c6dc4705dba8b151c59566175ad845d5a9104", "is_secret": false, "is_verified": false, - "line_number": 7397, + "line_number": 7409, "type": "Secret Keyword", "verified_result": null }, @@ -8470,7 +7986,7 @@ "hashed_secret": "3654bd5ef523a79741766b7c3f22228fd43c3836", "is_secret": false, "is_verified": false, - "line_number": 7416, + "line_number": 7428, "type": "Secret Keyword", "verified_result": null } @@ -9060,7 +8576,7 @@ "hashed_secret": "d63b39580934e062f89aae63426d2f2c77c3e258", "is_secret": false, "is_verified": false, - "line_number": 503, + "line_number": 509, "type": "Base64 High Entropy String", "verified_result": null }, @@ -9068,7 +8584,7 @@ "hashed_secret": "586a55a9b8b97f0cd88e24ce8279ebc955949688", "is_secret": false, "is_verified": false, - "line_number": 504, + "line_number": 510, "type": "Secret Keyword", "verified_result": null }, @@ -9076,7 +8592,7 @@ "hashed_secret": "00cafd126182e8a9e7c01bb2f0dfd00496be724f", "is_secret": false, "is_verified": false, - "line_number": 520, + "line_number": 526, "type": "Secret Keyword", "verified_result": null }, @@ -9084,7 +8600,7 @@ "hashed_secret": "7b1552c7c7ffb8bd70b5666e5997c8e017630aab", "is_secret": false, "is_verified": false, - "line_number": 1935, + "line_number": 1941, "type": "Base64 High Entropy String", "verified_result": null }, @@ -9092,7 +8608,7 @@ "hashed_secret": "9fb7fe1217aed442b04c0f5e43b5d5a7d3287097", "is_secret": false, "is_verified": false, - "line_number": 2871, + "line_number": 2994, "type": "Secret Keyword", "verified_result": null }, @@ -9100,7 +8616,7 @@ "hashed_secret": "72cb70dbbafe97e5ea13ad88acd65d08389439b0", "is_secret": false, "is_verified": false, - "line_number": 3499, + "line_number": 3622, "type": "Secret Keyword", "verified_result": null }, @@ -9108,7 +8624,7 @@ "hashed_secret": "ee977806d7286510da8b9a7492ba58e2484c0ecc", "is_secret": false, "is_verified": false, - "line_number": 6305, + "line_number": 6907, "type": "Secret Keyword", "verified_result": null }, @@ -9116,7 +8632,7 @@ "hashed_secret": "f2e7745f43b0ef0e2c2faf61d6c6a28be2965750", "is_secret": false, "is_verified": false, - "line_number": 6797, + "line_number": 7399, "type": "Secret Keyword", "verified_result": null }, @@ -9124,7 +8640,7 @@ "hashed_secret": "4a249743d4d2241bd2ae085b4fe654d089488295", "is_secret": false, "is_verified": false, - "line_number": 8007, + "line_number": 8746, "type": "Secret Keyword", "verified_result": null }, @@ -9132,7 +8648,7 @@ "hashed_secret": "0c8d051d3c7eada5d31b53d9936fce6bcc232ae2", "is_secret": false, "is_verified": false, - "line_number": 8145, + "line_number": 8888, "type": "Secret Keyword", "verified_result": null }, @@ -9140,7 +8656,7 @@ "hashed_secret": "f2b14f68eb995facb3a1c35287b778d5bd785511", "is_secret": false, "is_verified": false, - "line_number": 8521, + "line_number": 9264, "type": "Secret Keyword", "verified_result": null } @@ -9248,7 +8764,7 @@ "hashed_secret": "206c80413b9a96c1312cc346b7d2517b84463edd", "is_secret": false, "is_verified": false, - "line_number": 1604, + "line_number": 1605, "type": "Secret Keyword", "verified_result": null }, @@ -9256,7 +8772,7 @@ "hashed_secret": "5cbd0bf2db07a8f50fa9bbcc5ac720b1911c6380", "is_secret": false, "is_verified": false, - "line_number": 1776, + "line_number": 1777, "type": "Secret Keyword", "verified_result": null }, @@ -9264,7 +8780,7 @@ "hashed_secret": "a10b98d7340036e9c8c301704f623eddd733cc1a", "is_secret": false, "is_verified": false, - "line_number": 2781, + "line_number": 2782, "type": "Hex High Entropy String", "verified_result": null }, @@ -9272,7 +8788,7 @@ "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f", "is_secret": false, "is_verified": false, - "line_number": 5189, + "line_number": 5216, "type": "Secret Keyword", "verified_result": null }, @@ -9280,7 +8796,7 @@ "hashed_secret": "fe1bae27cb7c1fb823f496f286e78f1d2ae87734", "is_secret": false, "is_verified": false, - "line_number": 5842, + "line_number": 5869, "type": "Secret Keyword", "verified_result": null }, @@ -9288,7 +8804,7 @@ "hashed_secret": "2878cbdbbcfa6feafc04b8889f5ecc8c470ba32e", "is_secret": false, "is_verified": false, - "line_number": 5906, + "line_number": 5933, "type": "Secret Keyword", "verified_result": null }, @@ -9296,7 +8812,7 @@ "hashed_secret": "a0281cd072cea8e80e7866b05dc124815760b6c9", "is_secret": false, "is_verified": false, - "line_number": 6158, + "line_number": 6185, "type": "Secret Keyword", "verified_result": null }, @@ -9304,7 +8820,7 @@ "hashed_secret": "a0f4ea7d91495df92bbac2e2149dfb850fe81396", "is_secret": false, "is_verified": false, - "line_number": 9141, + "line_number": 9168, "type": "Secret Keyword", "verified_result": null }, @@ -9312,7 +8828,7 @@ "hashed_secret": "a75a7c7b31474f3f04f3a395228ded8d61ee1ae3", "is_secret": false, "is_verified": false, - "line_number": 9190, + "line_number": 9217, "type": "Secret Keyword", "verified_result": null }, @@ -9320,7 +8836,7 @@ "hashed_secret": "02c593fd9af8254b859d426a76b6cd42847fbec1", "is_secret": false, "is_verified": false, - "line_number": 9229, + "line_number": 9256, "type": "Secret Keyword", "verified_result": null }, @@ -9328,7 +8844,7 @@ "hashed_secret": "1ded3053d0363079a4e681a3b700435d6d880290", "is_secret": false, "is_verified": false, - "line_number": 9286, + "line_number": 9313, "type": "Secret Keyword", "verified_result": null }, @@ -9336,7 +8852,7 @@ "hashed_secret": "c00dbbc9dadfbe1e232e93a729dd4752fade0abf", "is_secret": false, "is_verified": false, - "line_number": 14062, + "line_number": 14411, "type": "Secret Keyword", "verified_result": null }, @@ -9344,7 +8860,7 @@ "hashed_secret": "f2b14f68eb995facb3a1c35287b778d5bd785511", "is_secret": false, "is_verified": false, - "line_number": 16819, + "line_number": 17168, "type": "Secret Keyword", "verified_result": null }, @@ -9352,7 +8868,7 @@ "hashed_secret": "a4b48a81cdab1e1a5dd37907d6c85ca1c61ddc7c", "is_secret": false, "is_verified": false, - "line_number": 16838, + "line_number": 17187, "type": "Secret Keyword", "verified_result": null }, @@ -9360,7 +8876,7 @@ "hashed_secret": "dc8002865f92070749b264e76045b04fa3b8de71", "is_secret": false, "is_verified": false, - "line_number": 20496, + "line_number": 20845, "type": "Secret Keyword", "verified_result": null } @@ -9370,7 +8886,7 @@ "hashed_secret": "dff6d4ff5dc357cf451d1855ab9cbda562645c9f", "is_secret": false, "is_verified": false, - "line_number": 244, + "line_number": 239, "type": "Secret Keyword", "verified_result": null } @@ -9398,7 +8914,7 @@ "hashed_secret": "b5920aa41ccde1341077fdb2636afc16d6da0e1a", "is_secret": false, "is_verified": false, - "line_number": 180, + "line_number": 190, "type": "Secret Keyword", "verified_result": null }, @@ -9406,7 +8922,7 @@ "hashed_secret": "467be688b21f5d43370f5dc983fe5ed58dea4b00", "is_secret": false, "is_verified": false, - "line_number": 206, + "line_number": 216, "type": "Secret Keyword", "verified_result": null }, @@ -9414,7 +8930,7 @@ "hashed_secret": "d955debea8e960a293ecbb257048680898cc9d94", "is_secret": false, "is_verified": false, - "line_number": 221, + "line_number": 231, "type": "Secret Keyword", "verified_result": null }, @@ -9422,7 +8938,7 @@ "hashed_secret": "4ee9e7b8482f6e0282b0117eb81651c98b537589", "is_secret": false, "is_verified": false, - "line_number": 255, + "line_number": 265, "type": "Secret Keyword", "verified_result": null }, @@ -9430,7 +8946,7 @@ "hashed_secret": "500886a66d19ebfe2d5c0ee86cf4612f9458ee4f", "is_secret": false, "is_verified": false, - "line_number": 328, + "line_number": 338, "type": "Secret Keyword", "verified_result": null }, @@ -9438,7 +8954,7 @@ "hashed_secret": "f2169f1a808382eb7b521206debaef01c91f1b60", "is_secret": false, "is_verified": false, - "line_number": 367, + "line_number": 377, "type": "Secret Keyword", "verified_result": null }, @@ -9446,7 +8962,7 @@ "hashed_secret": "cd61d9f689e9992aec40aeec7210b45a681309f9", "is_secret": false, "is_verified": false, - "line_number": 401, + "line_number": 411, "type": "Secret Keyword", "verified_result": null }, @@ -9454,7 +8970,7 @@ "hashed_secret": "ae97d9e9201a9bc1806069d80e5809ab745a91e8", "is_secret": false, "is_verified": false, - "line_number": 436, + "line_number": 446, "type": "Secret Keyword", "verified_result": null }, @@ -9462,7 +8978,7 @@ "hashed_secret": "eb2344b0e2d6529ced74f8b09be362a460defa15", "is_secret": false, "is_verified": false, - "line_number": 473, + "line_number": 483, "type": "Secret Keyword", "verified_result": null }, @@ -9470,7 +8986,7 @@ "hashed_secret": "89be7e8e88bc7b0cdb5ddaa01dab1cd1728541e2", "is_secret": false, "is_verified": false, - "line_number": 537, + "line_number": 547, "type": "Secret Keyword", "verified_result": null }, @@ -9478,7 +8994,7 @@ "hashed_secret": "5a539b200edec3aaa5167e1cde5d8fd0e8cf4aee", "is_secret": false, "is_verified": false, - "line_number": 550, + "line_number": 560, "type": "Secret Keyword", "verified_result": null }, @@ -9486,7 +9002,7 @@ "hashed_secret": "8be6db326ca95f74590bd09c9c328fc9a3db3b6c", "is_secret": false, "is_verified": false, - "line_number": 688, + "line_number": 698, "type": "Secret Keyword", "verified_result": null }, @@ -9494,7 +9010,7 @@ "hashed_secret": "2368055dc74fca43f2f9c849dbbaaa04616670cf", "is_secret": false, "is_verified": false, - "line_number": 737, + "line_number": 747, "type": "Secret Keyword", "verified_result": null }, @@ -9502,7 +9018,7 @@ "hashed_secret": "8ce647089e0614d3b924f4a0b8fb78268562ec7c", "is_secret": false, "is_verified": false, - "line_number": 1426, + "line_number": 1436, "type": "Secret Keyword", "verified_result": null }, @@ -9510,7 +9026,7 @@ "hashed_secret": "dc6f6cc49f767ae38db110a9e6c1adf5c651ecbf", "is_secret": false, "is_verified": false, - "line_number": 1470, + "line_number": 1480, "type": "Secret Keyword", "verified_result": null }, @@ -9518,7 +9034,7 @@ "hashed_secret": "7939ea1cdfc6d73115b0d736fa04012be2b25b18", "is_secret": false, "is_verified": false, - "line_number": 1561, + "line_number": 1571, "type": "Secret Keyword", "verified_result": null }, @@ -9526,7 +9042,7 @@ "hashed_secret": "91014bcbf183cf77807b4bd24f2e8652f67b56a0", "is_secret": false, "is_verified": false, - "line_number": 2069, + "line_number": 2079, "type": "Secret Keyword", "verified_result": null }, @@ -9534,7 +9050,7 @@ "hashed_secret": "61ac75f7e6677b79c219ce819d954e05f61b5d2c", "is_secret": false, "is_verified": false, - "line_number": 2087, + "line_number": 2097, "type": "Secret Keyword", "verified_result": null }, @@ -9542,7 +9058,7 @@ "hashed_secret": "8223fafd35f399e55c3cf611c3d6ebbf43d68d71", "is_secret": false, "is_verified": false, - "line_number": 2733, + "line_number": 2743, "type": "Secret Keyword", "verified_result": null }, @@ -9550,7 +9066,7 @@ "hashed_secret": "dfd99b5f25f839608a3c275c0f8ceb363f8f0bc0", "is_secret": false, "is_verified": false, - "line_number": 3510, + "line_number": 3538, "type": "Secret Keyword", "verified_result": null }, @@ -9558,7 +9074,7 @@ "hashed_secret": "5038e18712161fca54e52805726d3c70b296eff6", "is_secret": false, "is_verified": false, - "line_number": 3619, + "line_number": 3647, "type": "Secret Keyword", "verified_result": null }, @@ -9566,7 +9082,7 @@ "hashed_secret": "f5cd573c18bf811f82a886ceb6866b05d5ef02cd", "is_secret": false, "is_verified": false, - "line_number": 3697, + "line_number": 3725, "type": "Secret Keyword", "verified_result": null }, @@ -9574,7 +9090,7 @@ "hashed_secret": "79bead8e6d65862a00cffaa12ccde1189ec34d29", "is_secret": false, "is_verified": false, - "line_number": 4080, + "line_number": 4108, "type": "Secret Keyword", "verified_result": null } @@ -9714,7 +9230,7 @@ "hashed_secret": "5ef3af6cd9b5aa907fa618fac829baaec6763b42", "is_secret": false, "is_verified": false, - "line_number": 203, + "line_number": 200, "type": "Secret Keyword", "verified_result": null } @@ -10234,7 +9750,7 @@ "hashed_secret": "55d2534ed6ad4f269b428160428fa2f6f541ba7b", "is_secret": false, "is_verified": false, - "line_number": 156, + "line_number": 136, "type": "Base64 High Entropy String", "verified_result": null }, @@ -10242,7 +9758,7 @@ "hashed_secret": "cf743b3a58a4d0f91c1d7f5825c0b1b5f7758174", "is_secret": false, "is_verified": false, - "line_number": 538, + "line_number": 504, "type": "Base64 High Entropy String", "verified_result": null }, @@ -10250,7 +9766,7 @@ "hashed_secret": "8e42b03e460b2cf358ffbcf4da3bc5d14a22c86e", "is_secret": false, "is_verified": false, - "line_number": 582, + "line_number": 548, "type": "Base64 High Entropy String", "verified_result": null }, @@ -10258,7 +9774,7 @@ "hashed_secret": "2093dd9cf307518cfe1d2fa5a3985d6fec4e995e", "is_secret": false, "is_verified": false, - "line_number": 595, + "line_number": 561, "type": "Base64 High Entropy String", "verified_result": null }, @@ -10266,7 +9782,7 @@ "hashed_secret": "caa924f200b35ceb6f0e33878faff75203bdccb4", "is_secret": false, "is_verified": false, - "line_number": 971, + "line_number": 930, "type": "Secret Keyword", "verified_result": null }, @@ -10274,7 +9790,7 @@ "hashed_secret": "f16da2820437f3c703ff5b95c813f310ce8e67a4", "is_secret": false, "is_verified": false, - "line_number": 1288, + "line_number": 1179, "type": "Secret Keyword", "verified_result": null } @@ -10294,7 +9810,7 @@ "hashed_secret": "86de8c52637ec530fe39b0a8471da9b8764d5242", "is_secret": false, "is_verified": false, - "line_number": 665, + "line_number": 141, "type": "AWS Access Key", "verified_result": null } @@ -10322,7 +9838,7 @@ "hashed_secret": "9d4e1e23bd5b727046a9e3b4b7db57bd8d6ee684", "is_secret": false, "is_verified": false, - "line_number": 10450, + "line_number": 10447, "type": "Basic Auth Credentials", "verified_result": null }