kintopp/rijksmuseum-mcp-plus
criticalMCP server for the Rijksmuseum collections with extended metadata, semantic search, provenance analysis, similarity comparisons and spatial reasoning.
MCP server (purpose undetermined)
48 return execSync("git rev-parse --short HEAD", { encoding: "utf-8" }).trim();273 const allowedOrigins = process.env.ALLOWED_ORIGINS;
274 app.use(
275 cors({
276 origin: allowedOrigins ? allowedOrigins.split(",") : "*",
277 })
278 );// Network-exposed MCP, exploitable by any website when ALLOWED_ORIGINS is not configured.
When ALLOWED_ORIGINS is not set, the CORS middleware allows all origins (*). This means any website can make cross-origin requests to the MCP server, potentially enabling CSRF-like attacks if the server is exposed to the network.
ImpactAn attacker could trick a user's browser into making requests to the MCP server, potentially executing tools on behalf of the user if the server relies on browser-based authentication (though this server has no auth).
FixSet a restrictive default or require explicit configuration. Use an allowlist of known origins instead of wildcard.
367 app.get("/similar/:uuid", (req: express.Request, res: express.Response) => {
368 const page = similarPages.get(req.params.uuid as string);
369 if (!page) {
370 res.status(404).json({ error: "Page not found or expired (30 min TTL)" });
371 return;
372 }
373 page.lastAccess = Date.now();
374 res.type("html").send(page.html);
375 });
376
377 app.get("/enrichment-review/:uuid", (req: express.Request, res: express.Response) => {
378 const page = enrichmentReviewPages.get(req.params.uuid as string);
379 if (!page) {
380 res.status(404).json({ error: "Page not found or expired (30 min TTL)" });
381 return;
382 }
383 page.lastAccess = Date.now();
384 res.type("html").send(page.html);
385 });// Exploitable only if MCP is exposed to untrusted prompts and the attacker can make HTTP requests to the server.
The /similar/:uuid and /enrichment-review/:uuid endpoints accept a UUID parameter from the URL path and use it directly as a key to retrieve a page from an in-memory Map. There is no validation that the parameter is a valid UUID format. While this does not lead to injection (the key is used only for Map lookup), it could allow an attacker to enumerate valid UUIDs by observing response times or error messages. Additionally, if the Map were to contain sensitive data, an attacker could access it by guessing UUIDs.
ImpactAn attacker could potentially enumerate valid session/page UUIDs, leading to information disclosure of stored HTML pages. The pages are intended to be shared via UUID, so the impact is limited to unauthorized access if the UUID is guessed.
FixValidate that the :uuid parameter matches a UUID format (e.g., using a regex or uuid library) before using it as a Map key. Also consider rate-limiting to prevent brute-force enumeration.
118 const url = process.env[spec.urlEnvVar];
119 if (!url) return;
120 console.error(`Downloading ${spec.name} DB...`);
121 const dir = path.dirname(dbPath);
122 if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
123 const tmpPath = dbPath + ".tmp";
124 const controller = new AbortController();
125 const downloadTimer = setTimeout(() => controller.abort(), 330_000);
126 try {
127 const res = await fetch(url, { redirect: "follow", signal: controller.signal });// Local-only MCP, requires compromised LLM or environment variable control to exploit.
The ensureDb function fetches a URL from an environment variable (e.g., VOCAB_DB_URL) without validating the scheme or host. An attacker who can set environment variables (e.g., via a compromised CI/CD pipeline or local access) could point the URL to an arbitrary endpoint, leading to server-side request forgery (SSRF) or downloading malicious content.
ImpactAn attacker could cause the server to fetch arbitrary URLs, potentially exfiltrating data or downloading malicious files that could be loaded as databases.
FixValidate that the URL uses an allowed scheme (e.g., https://) and restrict to known hosts. Consider using a fixed base URL or verifying the URL against an allowlist.
421 app.get("/debug/memory", (_req: express.Request, res: express.Response) => {
422 res.json(captureMemorySnapshot(buildMemoryDbHandles()));
423 });// Exploitable only if MCP is exposed to untrusted prompts and the attacker can make HTTP requests to the server.
The /debug/memory endpoint is unauthenticated and exposes detailed memory statistics, including database file paths and internal state. While the comment states it is 'operational signal, not sensitive', it could leak information about the server's internal structure, such as file paths and database sizes, which could aid an attacker in crafting more targeted attacks.
ImpactAn attacker could gain insight into the server's configuration, including file paths and memory usage patterns, potentially aiding in path traversal or denial-of-service attacks.
FixAdd authentication or restrict access to internal networks. Alternatively, remove the endpoint or limit the information returned.
367 app.get("/similar/:uuid", (req: express.Request, res: express.Response) => {
368 const page = similarPages.get(req.params.uuid as string);
369 if (!page) {
370 res.status(404).json({ error: "Page not found or expired (30 min TTL)" });
371 return;
372 }
373 page.lastAccess = Date.now();
374 res.type("html").send(page.html);
375 });// Network-exposed MCP, but impact is limited to reading cached HTML pages.
The /similar/:uuid route accepts a UUID parameter and uses it directly as a key to retrieve a page from a Map. While this is not a path traversal or injection, it could allow an attacker to enumerate valid UUIDs if the Map contents are predictable. However, the impact is limited to retrieving cached HTML pages.
ImpactAn attacker could potentially access cached HTML pages if they can guess or enumerate UUIDs, but the pages are intended to be publicly accessible.
FixConsider adding rate limiting or authentication if the pages contain sensitive information. Otherwise, this is low risk.