diff --git a/client/src/components/Sidebar.tsx b/client/src/components/Sidebar.tsx
index 13a4f24f..762678f2 100644
--- a/client/src/components/Sidebar.tsx
+++ b/client/src/components/Sidebar.tsx
@@ -15,6 +15,7 @@ import {
Copy,
CheckCheck,
Server,
+ AlertTriangle,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
@@ -42,6 +43,7 @@ import CustomHeaders from "./CustomHeaders";
import { CustomHeaders as CustomHeadersType } from "@/lib/types/customHeaders";
import { useToast } from "../lib/hooks/useToast";
import IconDisplay, { WithIcons } from "./IconDisplay";
+import { validateRedirectUrl } from "@/utils/urlValidation";
interface SidebarProps {
connectionStatus: ConnectionStatus;
@@ -782,44 +784,72 @@ const Sidebar = ({
- {connectionStatus === "connected" && serverImplementation && (
-
-
- {(serverImplementation as WithIcons).icons &&
- (serverImplementation as WithIcons).icons!.length > 0 ? (
-
- ) : (
-
- )}
- {(serverImplementation as { websiteUrl?: string })
- .websiteUrl ? (
-
- {serverImplementation.name || "MCP Server"}
-
- ) : (
-
- {serverImplementation.name || "MCP Server"}
-
- )}
-
- {serverImplementation.version && (
-
- Version: {serverImplementation.version}
+ {connectionStatus === "connected" &&
+ serverImplementation &&
+ (() => {
+ const websiteUrl = (
+ serverImplementation as { websiteUrl?: string }
+ ).websiteUrl;
+ let isValidWebsiteUrl = false;
+ if (websiteUrl) {
+ try {
+ validateRedirectUrl(websiteUrl);
+ isValidWebsiteUrl = true;
+ } catch {
+ isValidWebsiteUrl = false;
+ }
+ }
+ const serverName = serverImplementation.name || "MCP Server";
+ return (
+
+
+ {(serverImplementation as WithIcons).icons &&
+ (serverImplementation as WithIcons).icons!.length > 0 ? (
+
+ ) : (
+
+ )}
+ {websiteUrl && isValidWebsiteUrl ? (
+
+ {serverName}
+
+ ) : (
+
+ {serverName}
+
+ )}
+
+ {websiteUrl && !isValidWebsiteUrl && (
+
+
+
+
+
+ Potentially malicious websiteURL field
+
+
+
+
+ {websiteUrl}
+
+
+ )}
+ {serverImplementation.version && (
+
+ Version: {serverImplementation.version}
+
+ )}
- )}
-
- )}
+ );
+ })()}
{loggingSupported && connectionStatus === "connected" && (
diff --git a/client/src/components/__tests__/Sidebar.test.tsx b/client/src/components/__tests__/Sidebar.test.tsx
index 03e898ca..460161e5 100644
--- a/client/src/components/__tests__/Sidebar.test.tsx
+++ b/client/src/components/__tests__/Sidebar.test.tsx
@@ -1046,4 +1046,68 @@ describe("Sidebar", () => {
);
});
});
+
+ describe("Server Implementation websiteUrl", () => {
+ it("should render a warning instead of a link when websiteUrl uses an unsafe protocol", () => {
+ renderSidebar({
+ connectionStatus: "connected",
+ serverImplementation: {
+ name: "Evil Server",
+ version: "1.0.0",
+ websiteUrl: "javascript:alert('xss')",
+ },
+ });
+
+ // The warning message should be displayed
+ expect(
+ screen.getByText("Potentially malicious websiteURL field"),
+ ).toBeInTheDocument();
+
+ // The server name should still be shown
+ expect(screen.getByText("Evil Server")).toBeInTheDocument();
+
+ // No link should have been rendered for the malicious URL
+ expect(
+ screen.queryByRole("link", { name: "Evil Server" }),
+ ).not.toBeInTheDocument();
+ });
+
+ it("should render a link when websiteUrl is a valid https URL", () => {
+ renderSidebar({
+ connectionStatus: "connected",
+ serverImplementation: {
+ name: "Good Server",
+ version: "1.0.0",
+ websiteUrl: "https://example.com",
+ },
+ });
+
+ const link = screen.getByRole("link", { name: "Good Server" });
+ expect(link).toBeInTheDocument();
+ expect(link).toHaveAttribute("href", "https://example.com");
+
+ expect(
+ screen.queryByText("Potentially malicious websiteURL field"),
+ ).not.toBeInTheDocument();
+ });
+
+ it("should render a link when websiteUrl is a valid http URL", () => {
+ renderSidebar({
+ connectionStatus: "connected",
+ serverImplementation: {
+ name: "Local Server",
+ version: "1.0.0",
+ websiteUrl: "http://localhost:3000",
+ },
+ });
+
+ const link = screen.getByRole("link", { name: "Local Server" });
+ expect(link).toBeInTheDocument();
+ expect(link).toHaveAttribute("href", "http://localhost:3000");
+
+ expect(
+ screen.queryByText("Potentially malicious websiteURL field"),
+ ).not.toBeInTheDocument();
+ });
+ });
});