High risk. Don't ship without significant remediation.
Scanned 5/3/2026, 7:17:50 PM·Cached result·Fast Scan·88 rules·How we decide ↗
AIVSS Score
High
Severity Breakdown
0
critical
4
high
64
medium
16
low
MCP Server Information
Findings
This package presents significant security concerns with a D grade and safety score of 59/100, driven primarily by 49 verbose error findings that could expose sensitive information and 64 medium-severity issues across multiple risk categories. The presence of 4 high-severity findings including prompt injection and tool poisoning vulnerabilities, combined with 11 resource exhaustion risks, suggests the package lacks adequate input validation and error handling safeguards. Installation is not recommended without substantial remediation of these security gaps.
No known CVEs found for this package or its dependencies.
Scan Details
Want deeper analysis?
Fast scan found 84 findings using rule-based analysis. Upgrade for LLM consensus across 5 judges, AI-generated remediation, and cross-file taint analysis.
Building your own MCP server?
Same rules, same LLM judges, same grade. Private scans stay isolated to your account and never appear in the public registry. Required for code your team hasn’t shipped yet.
Showing 1–30 of 84 findings
84 findings
Telemetry system transmits tool execution data, prompts, and session information to Supabase (third-party cloud service) without explicit operator control over destination or data classification.
Evidence
| 1 | """ |
| 2 | Privacy-focused, anonymous telemetry for Blender MCP |
| 3 | Tracks tool usage, DAU/MAU, and performance metrics |
| 4 | """ |
| 5 | |
| 6 | import contextlib |
| 7 | import json |
| 8 | import logging |
| 9 | import os |
| 10 | import platform |
| 11 | import queue |
| 12 | import sys |
| 13 | import threading |
| 14 | import time |
| 15 | import uuid |
| 16 | from dataclasses import dataclass |
| 17 | from enum import Enum |
| 18 | from pathlib import Path |
| 19 | from typing import Any |
| 20 | |
| 21 | try: |
| 22 | from supabase import create_client, Client |
| 23 | HAS_SUPABASE = True |
| 24 | except ImportError: |
| 25 | HAS_SUPABASE = False |
| 26 | |
| 27 | try: |
| 28 | import tomli |
| 29 | e |
Remediation
Remove the outbound transfer. If the tool legitimately needs remote delivery, restrict destinations to a customer-owned allowlist, strip secrets before transport, and document the data flow in the server's README.
Tool uses module-level global RODIN_FREE_TRIAL_KEY (line 31) to call external Rodin API without consulting caller identity or per-request credentials.
Evidence
| 28 | "location": "View3D > Sidebar > BlenderMCP", |
| 29 | "description": "Connect Blender to Claude via MCP", |
| 30 | "category": "Interface", |
| 31 | } |
| 32 | |
| 33 | RODIN_FREE_TRIAL_KEY = "k9TcfFoEhNd9cCPP2guHAHHHkctZHIRhZDywZ1euGUXwihbYLpOjQhofby80NJez" |
Remediation
Pull the token / credential from `ctx.principal` (or the MCP auth layer equivalent) on every call. The handler must fail closed when the caller supplies no credential. Never cache a global API token at import time and reuse it across callers.
Telemetry module performs NETWORK side effects (Supabase client connection and data transmission) and FILESYSTEM side effects (reading/writing UUID and config files) that are not disclosed in module or tool descriptions as non-essential telemetry beacons.
Evidence
| 1 | """ |
| 2 | Privacy-focused, anonymous telemetry for Blender MCP |
| 3 | Tracks tool usage, DAU/MAU, and performance metrics |
| 4 | """ |
Remediation
Either remove the undeclared side effect or amend the tool description + input schema to disclose it. Add machine-readable `destructiveHint`, `networkHint`, `filesystemHint` annotations when the MCP spec supports them.
Telemetry decorator wraps tools to perform NETWORK side effects (sending telemetry data to Supabase) that are not disclosed in individual tool descriptions.
Evidence
| 1 | """ |
| 2 | Telemetry decorator for Blender MCP tools |
| 3 | """ |
Remediation
Either remove the undeclared side effect or amend the tool description + input schema to disclose it. Add machine-readable `destructiveHint`, `networkHint`, `filesystemHint` annotations when the MCP spec supports them.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 1593 | except requests.exceptions.Timeout: |
| 1594 | return {"error": "Request timed out. Check your internet connection."} |
| 1595 | except json.JSONDecodeError as e: |
| 1596 | return {"error": f"Invalid JSON response from Sketchfab API: {str(e)}"} |
| 1597 | except Exception as e: |
| 1598 | import traceback |
| 1599 | traceback.print_exc() |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 344 | return f"Code executed successfully: {result.get('result', '')}" |
| 345 | except Exception as e: |
| 346 | logger.error(f"Error executing code: {str(e)}") |
| 347 | return f"Error executing code: {str(e)}" |
| 348 | |
| 349 | @telemetry_tool("get_polyhaven_categories") |
| 350 | @mcp.tool() |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 598 | return message |
| 599 | except Exception as e: |
| 600 | logger.error(f"Error checking Sketchfab status: {str(e)}") |
| 601 | return f"Error checking Sketchfab status: {str(e)}" |
| 602 | |
| 603 | @telemetry_tool("search_sketchfab_models") |
| 604 | @mcp.tool() |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 1683 | except Exception as e: |
| 1684 | import traceback |
| 1685 | traceback.print_exc() |
| 1686 | return {"error": f"Failed to get model preview: {str(e)}"} |
| 1687 | |
| 1688 | def download_sketchfab_model(self, uid, normalize_size=False, target_size=1.0): |
| 1689 | """Download a model from Sketchfab by its UID |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 1083 | return result |
| 1084 | except Exception as e: |
| 1085 | logger.error(f"Error generating Hunyuan3D task: {str(e)}") |
| 1086 | return f"Error generating Hunyuan3D task: {str(e)}" |
| 1087 | |
| 1088 | |
| 1089 | @mcp.prompt() |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 282 | return json.dumps(result, indent=2) |
| 283 | except Exception as e: |
| 284 | logger.error(f"Error getting object info from Blender: {str(e)}") |
| 285 | return f"Error getting object info: {str(e)}" |
| 286 | |
| 287 | @telemetry_tool("get_viewport_screenshot") |
| 288 | @mcp.tool() |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 1902 | except requests.exceptions.Timeout: |
| 1903 | return {"error": "Request timed out. Check your internet connection and try again with a simpler model."} |
| 1904 | except json.JSONDecodeError as e: |
| 1905 | return {"error": f"Invalid JSON response from Sketchfab API: {str(e)}"} |
| 1906 | except Exception as e: |
| 1907 | import traceback |
| 1908 | traceback.print_exc() |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 1597 | except Exception as e: |
| 1598 | import traceback |
| 1599 | traceback.print_exc() |
| 1600 | return {"error": str(e)} |
| 1601 | |
| 1602 | def get_sketchfab_model_preview(self, uid): |
| 1603 | """Get thumbnail preview image of a Sketchfab model by its UID""" |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 1206 | data = response.json() |
| 1207 | return data |
| 1208 | except Exception as e: |
| 1209 | return {"error": str(e)} |
| 1210 | |
| 1211 | def create_rodin_job_fal_ai( |
| 1212 | self, |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 989 | return message |
| 990 | except Exception as e: |
| 991 | logger.error(f"Error checking Hunyuan3D status: {str(e)}") |
| 992 | return f"Error checking Hunyuan3D status: {str(e)}" |
| 993 | |
| 994 | @mcp.tool() |
| 995 | def generate_hunyuan3d_model( |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
LLM consensus
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 2154 | image_base64 = base64.b64encode(resImg.content).decode("ascii") |
| 2155 | data["image"] = image_base64 |
| 2156 | except Exception as e: |
| 2157 | return {"error": f"Failed to download or encode image: {str(e)}"} |
| 2158 | else: |
| 2159 | try: |
| 2160 | # Convert to Base64 format |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 449 | else: |
| 450 | return {"error": f"API request failed with status code {response.status_code}"} |
| 451 | except Exception as e: |
| 452 | return {"error": str(e)} |
| 453 | |
| 454 | def search_polyhaven_assets(self, asset_type=None, categories=None): |
| 455 | """Search for assets from Polyhaven with optional filtering""" |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 1906 | except Exception as e: |
| 1907 | import traceback |
| 1908 | traceback.print_exc() |
| 1909 | return {"error": f"Failed to download model: {str(e)}"} |
| 1910 | #endregion |
| 1911 | |
| 1912 | #region Hunyuan3D |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 427 | return formatted_output |
| 428 | except Exception as e: |
| 429 | logger.error(f"Error searching Polyhaven assets: {str(e)}") |
| 430 | return f"Error searching Polyhaven assets: {str(e)}" |
| 431 | |
| 432 | @telemetry_tool("download_polyhaven_asset") |
| 433 | @mcp.tool() |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 262 | except Exception as e: |
| 263 | print(f"Error in handler: {str(e)}") |
| 264 | traceback.print_exc() |
| 265 | return {"status": "error", "message": str(e)} |
| 266 | else: |
| 267 | return {"status": "error", "message": f"Unknown command type: {cmd_type}"} |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 791 | "imported_objects": imported_objects |
| 792 | } |
| 793 | except Exception as e: |
| 794 | return {"error": f"Failed to import model: {str(e)}"} |
| 795 | finally: |
| 796 | # Clean up temporary directory |
| 797 | with suppress(Exception): |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 803 | return {"error": f"Unsupported asset type: {asset_type}"} |
| 804 | |
| 805 | except Exception as e: |
| 806 | return {"error": f"Failed to download asset: {str(e)}"} |
| 807 | |
| 808 | def set_texture(self, object_name, texture_id): |
| 809 | """Apply a previously downloaded Polyhaven texture to an object by creating a new material""" |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 263 | return json.dumps(result, indent=2) |
| 264 | except Exception as e: |
| 265 | logger.error(f"Error getting scene info from Blender: {str(e)}") |
| 266 | return f"Error getting scene info: {str(e)}" |
| 267 | |
| 268 | @telemetry_tool("get_object_info") |
| 269 | @mcp.tool() |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 558 | return message |
| 559 | except Exception as e: |
| 560 | logger.error(f"Error checking PolyHaven status: {str(e)}") |
| 561 | return f"Error checking PolyHaven status: {str(e)}" |
| 562 | |
| 563 | @telemetry_tool("get_hyper3d_status") |
| 564 | @mcp.tool() |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 377 | return formatted_output |
| 378 | except Exception as e: |
| 379 | logger.error(f"Error getting Polyhaven categories: {str(e)}") |
| 380 | return f"Error getting Polyhaven categories: {str(e)}" |
| 381 | |
| 382 | @telemetry_tool("search_polyhaven_assets") |
| 383 | @mcp.tool() |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 1107 | except Exception as e: |
| 1108 | print(f"Error in set_texture: {str(e)}") |
| 1109 | traceback.print_exc() |
| 1110 | return {"error": f"Failed to apply texture: {str(e)}"} |
| 1111 | |
| 1112 | def get_telemetry_consent(self): |
| 1113 | """Get the current telemetry consent status""" |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 300 | except Exception as e: |
| 301 | print(f"Error in get_scene_info: {str(e)}") |
| 302 | traceback.print_exc() |
| 303 | return {"error": str(e)} |
| 304 | |
| 305 | @staticmethod |
| 306 | def _get_aabb(obj): |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 894 | return json.dumps(result) |
| 895 | except Exception as e: |
| 896 | logger.error(f"Error generating Hyper3D task: {str(e)}") |
| 897 | return f"Error generating Hyper3D task: {str(e)}" |
| 898 | |
| 899 | @telemetry_tool("poll_rodin_job_status") |
| 900 | @mcp.tool() |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 480 | else: |
| 481 | return {"error": f"API request failed with status code {response.status_code}"} |
| 482 | except Exception as e: |
| 483 | return {"error": str(e)} |
| 484 | |
| 485 | def download_polyhaven_asset(self, asset_id, asset_type, resolution="1k", file_format=None): |
| 486 | try: |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 1026 | return json.dumps(result) |
| 1027 | except Exception as e: |
| 1028 | logger.error(f"Error generating Hunyuan3D task: {str(e)}") |
| 1029 | return f"Error generating Hunyuan3D task: {str(e)}" |
| 1030 | |
| 1031 | @mcp.tool() |
| 1032 | def poll_hunyuan_job_status( |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
LLM consensus
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 416 | } |
| 417 | |
| 418 | except Exception as e: |
| 419 | return {"error": str(e)} |
| 420 | |
| 421 | def execute_code(self, code): |
| 422 | """Execute arbitrary Blender Python code""" |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 2193 | } |
| 2194 | except Exception as e: |
| 2195 | print(f"An error occurred: {e}") |
| 2196 | return {"error": str(e)} |
| 2197 | |
| 2198 | |
| 2199 | def poll_hunyuan_job_status(self, *args, **kwargs): |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 2162 | image_base64 = base64.b64encode(f.read()).decode("ascii") |
| 2163 | data["image"] = image_base64 |
| 2164 | except Exception as e: |
| 2165 | return {"error": f"Image encoding failed: {str(e)}"} |
| 2166 | |
| 2167 | response = requests.post( |
| 2168 | f"{base_url}/generate", |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 675 | logger.error(f"Error searching Sketchfab models: {str(e)}") |
| 676 | import traceback |
| 677 | logger.error(traceback.format_exc()) |
| 678 | return f"Error searching Sketchfab models: {str(e)}" |
| 679 | |
| 680 | @telemetry_tool("download_sketchfab_model") |
| 681 | @mcp.tool() |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 837 | return json.dumps(result) |
| 838 | except Exception as e: |
| 839 | logger.error(f"Error generating Hyper3D task: {str(e)}") |
| 840 | return f"Error generating Hyper3D task: {str(e)}" |
| 841 | |
| 842 | @telemetry_tool("generate_hyper3d_model_via_images") |
| 843 | @mcp.tool() |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 2097 | image_base64 = base64.b64encode(f.read()).decode("ascii") |
| 2098 | data["ImageBase64"] = image_base64 |
| 2099 | except Exception as e: |
| 2100 | return {"error": f"Image encoding failed: {str(e)}"} |
| 2101 | |
| 2102 | # Get signed headers |
| 2103 | headers, endpoint = self.get_tencent_cloud_sign_headers("POST", "/", headParams, data, service, region, secret_id, secret_key) |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 191 | except Exception as e: |
| 192 | print(f"Error executing command: {str(e)}") |
| 193 | traceback.print_exc() |
| 194 | return {"status": "error", "message": str(e)} |
| 195 | |
| 196 | def _execute_command_internal(self, command): |
| 197 | """Internal command execution with proper context""" |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 579 | return message |
| 580 | except Exception as e: |
| 581 | logger.error(f"Error checking Hyper3D status: {str(e)}") |
| 582 | return f"Error checking Hyper3D status: {str(e)}" |
| 583 | |
| 584 | @telemetry_tool("get_sketchfab_status") |
| 585 | @mcp.tool() |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 479 | return f"Failed to download asset: {result.get('message', 'Unknown error')}" |
| 480 | except Exception as e: |
| 481 | logger.error(f"Error downloading Polyhaven asset: {str(e)}") |
| 482 | return f"Error downloading Polyhaven asset: {str(e)}" |
| 483 | |
| 484 | @telemetry_tool("set_texture") |
| 485 | @mcp.tool() |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 2241 | "error": f"API request failed with status {response.status_code}: {response}" |
| 2242 | } |
| 2243 | except Exception as e: |
| 2244 | return {"error": str(e)} |
| 2245 | |
| 2246 | def import_generated_asset_hunyuan(self, *args, **kwargs): |
| 2247 | return self.import_generated_asset_hunyuan_ai(*args, **kwargs) |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 972 | return result |
| 973 | except Exception as e: |
| 974 | logger.error(f"Error generating Hyper3D task: {str(e)}") |
| 975 | return f"Error generating Hyper3D task: {str(e)}" |
| 976 | |
| 977 | @mcp.tool() |
| 978 | def get_hunyuan3d_status(ctx: Context) -> str: |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
LLM consensus
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 1471 | "succeed": True, **result |
| 1472 | } |
| 1473 | except Exception as e: |
| 1474 | return {"succeed": False, "error": str(e)} |
| 1475 | #endregion |
| 1476 | |
| 1477 | #region Sketchfab API |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 938 | return result |
| 939 | except Exception as e: |
| 940 | logger.error(f"Error generating Hyper3D task: {str(e)}") |
| 941 | return f"Error generating Hyper3D task: {str(e)}" |
| 942 | |
| 943 | @telemetry_tool("import_generated_asset") |
| 944 | @mcp.tool() |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 1055 | return result |
| 1056 | except Exception as e: |
| 1057 | logger.error(f"Error generating Hunyuan3D task: {str(e)}") |
| 1058 | return f"Error generating Hunyuan3D task: {str(e)}" |
| 1059 | |
| 1060 | @mcp.tool() |
| 1061 | def import_generated_asset_hunyuan( |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
LLM consensus
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 718 | } |
| 719 | |
| 720 | except Exception as e: |
| 721 | return {"error": f"Failed to process textures: {str(e)}"} |
| 722 | |
| 723 | elif asset_type == "models": |
| 724 | # For models, prefer glTF format if available |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 539 | return f"Failed to apply texture: {result.get('message', 'Unknown error')}" |
| 540 | except Exception as e: |
| 541 | logger.error(f"Error applying texture: {str(e)}") |
| 542 | return f"Error applying texture: {str(e)}" |
| 543 | |
| 544 | @telemetry_tool("get_polyhaven_status") |
| 545 | @mcp.tool() |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 1448 | # Clean up the file if there's an error |
| 1449 | temp_file.close() |
| 1450 | os.unlink(temp_file.name) |
| 1451 | return {"succeed": False, "error": str(e)} |
| 1452 | |
| 1453 | try: |
| 1454 | obj = self._clean_imported_glb( |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 1413 | "succeed": True, **result |
| 1414 | } |
| 1415 | except Exception as e: |
| 1416 | return {"succeed": False, "error": str(e)} |
| 1417 | |
| 1418 | def import_generated_asset_fal_ai(self, request_id: str, name: str): |
| 1419 | """Fetch the generated asset, import into blender""" |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 1386 | # Clean up the file if there's an error |
| 1387 | temp_file.close() |
| 1388 | os.unlink(temp_file.name) |
| 1389 | return {"succeed": False, "error": str(e)} |
| 1390 | |
| 1391 | break |
| 1392 | else: |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 2114 | "error": f"API request failed with status {response.status_code}: {response}" |
| 2115 | } |
| 2116 | except Exception as e: |
| 2117 | return {"error": str(e)} |
| 2118 | |
| 2119 | def create_hunyuan_job_local_site( |
| 2120 | self, |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 791 | logger.error(f"Error downloading Sketchfab model: {str(e)}") |
| 792 | import traceback |
| 793 | logger.error(traceback.format_exc()) |
| 794 | return f"Error downloading Sketchfab model: {str(e)}" |
| 795 | |
| 796 | def _process_bbox(original_bbox: list[float] | list[int] | None) -> list[int] | None: |
| 797 | if original_bbox is None: |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 2308 | return {"succeed": True, **result} |
| 2309 | except Exception as e: |
| 2310 | return {"succeed": False, "error": str(e)} |
| 2311 | finally: |
| 2312 | # Clean up temporary zip and obj, save texture and mtl |
| 2313 | try: |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 581 | "image_name": env_tex.image.name |
| 582 | } |
| 583 | except Exception as e: |
| 584 | return {"error": f"Failed to set up HDRI in Blender: {str(e)}"} |
| 585 | else: |
| 586 | return {"error": f"Requested resolution or format not available for this HDRI"} |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure — useful recon for attackers.
Evidence
| 1235 | data = response.json() |
| 1236 | return data |
| 1237 | except Exception as e: |
| 1238 | return {"error": str(e)} |
| 1239 | |
| 1240 | def poll_rodin_job_status(self, *args, **kwargs): |
| 1241 | match bpy.context.scene.blendermcp_hyper3d_mode: |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Network / IO / subprocess call without an explicit timeout. A malicious or hung upstream (HTTP host, socket peer, child process) can pin threads, exhaust connection/process pools, and make the MCP server unresponsive. Always pass a bounded timeout. v2 extends v1 with subprocess coverage (R03 from the legacy readiness audit).
Evidence
| 505 | # since Blender can't properly load HDR data directly from memory |
| 506 | with tempfile.NamedTemporaryFile(suffix=f".{file_format}", delete=False) as tmp_file: |
| 507 | # Download the file |
| 508 | response = requests.get(file_url, headers=REQ_HEADERS) |
| 509 | if response.status_code != 200: |
| 510 | return {"error": f"Failed to download HDRI: {response.status_code}"} |
Remediation
Pass timeout= on every call: - HTTP: `requests.get(url, timeout=5)`, `httpx.get(url, timeout=5.0)` - Node fetch: `AbortSignal.timeout(5000)` - Subprocess: `subprocess.run(["cmd"], timeout=30, check=True)` Pick a value short enough to fail fast and retry.
Network / IO / subprocess call without an explicit timeout. A malicious or hung upstream (HTTP host, socket peer, child process) can pin threads, exhaust connection/process pools, and make the MCP server unresponsive. Always pass a bounded timeout. v2 extends v1 with subprocess coverage (R03 from the legacy readiness audit).
Evidence
| 2149 | if image: |
| 2150 | if re.match(r'^https?://', image, re.IGNORECASE) is not None: |
| 2151 | try: |
| 2152 | resImg = requests.get(image) |
| 2153 | resImg.raise_for_status() |
| 2154 | image_base64 = base64.b64encode(resImg.content).decode("ascii") |
| 2155 | data["image"] = image_base64 |
Remediation
Pass timeout= on every call: - HTTP: `requests.get(url, timeout=5)`, `httpx.get(url, timeout=5.0)` - Node fetch: `AbortSignal.timeout(5000)` - Subprocess: `subprocess.run(["cmd"], timeout=30, check=True)` Pick a value short enough to fail fast and retry.
Network / IO / subprocess call without an explicit timeout. A malicious or hung upstream (HTTP host, socket peer, child process) can pin threads, exhaust connection/process pools, and make the MCP server unresponsive. Always pass a bounded timeout. v2 extends v1 with subprocess coverage (R03 from the legacy readiness audit).
Evidence
| 485 | def download_polyhaven_asset(self, asset_id, asset_type, resolution="1k", file_format=None): |
| 486 | try: |
| 487 | # First get the files information |
| 488 | files_response = requests.get(f"https://api.polyhaven.com/files/{asset_id}", headers=REQ_HEADERS) |
| 489 | if files_response.status_code != 200: |
| 490 | return {"error": f"Failed to get asset files: {files_response.status_code}"} |
Remediation
Pass timeout= on every call: - HTTP: `requests.get(url, timeout=5)`, `httpx.get(url, timeout=5.0)` - Node fetch: `AbortSignal.timeout(5000)` - Subprocess: `subprocess.run(["cmd"], timeout=30, check=True)` Pick a value short enough to fail fast and retry.
Network / IO / subprocess call without an explicit timeout. A malicious or hung upstream (HTTP host, socket peer, child process) can pin threads, exhaust connection/process pools, and make the MCP server unresponsive. Always pass a bounded timeout. v2 extends v1 with subprocess coverage (R03 from the legacy readiness audit).
Evidence
| 756 | os.makedirs(os.path.dirname(include_file_path), exist_ok=True) |
| 757 | |
| 758 | # Download the included file |
| 759 | include_response = requests.get(include_url, headers=REQ_HEADERS) |
| 760 | if include_response.status_code == 200: |
| 761 | with open(include_file_path, "wb") as f: |
| 762 | f.write(include_response.content) |
Remediation
Pass timeout= on every call: - HTTP: `requests.get(url, timeout=5)`, `httpx.get(url, timeout=5.0)` - Node fetch: `AbortSignal.timeout(5000)` - Subprocess: `subprocess.run(["cmd"], timeout=30, check=True)` Pick a value short enough to fail fast and retry.
Network / IO / subprocess call without an explicit timeout. A malicious or hung upstream (HTTP host, socket peer, child process) can pin threads, exhaust connection/process pools, and make the MCP server unresponsive. Always pass a bounded timeout. v2 extends v1 with subprocess coverage (R03 from the legacy readiness audit).
Evidence
| 1372 | try: |
| 1373 | # Download the content |
| 1374 | response = requests.get(i["url"], stream=True) |
| 1375 | response.raise_for_status() # Raise an exception for HTTP errors |
| 1376 | |
| 1377 | # Write the content to the temporary file |
Remediation
Pass timeout= on every call: - HTTP: `requests.get(url, timeout=5)`, `httpx.get(url, timeout=5.0)` - Node fetch: `AbortSignal.timeout(5000)` - Subprocess: `subprocess.run(["cmd"], timeout=30, check=True)` Pick a value short enough to fail fast and retry.
Network / IO / subprocess call without an explicit timeout. A malicious or hung upstream (HTTP host, socket peer, child process) can pin threads, exhaust connection/process pools, and make the MCP server unresponsive. Always pass a bounded timeout. v2 extends v1 with subprocess coverage (R03 from the legacy readiness audit).
Evidence
| 601 | # Use NamedTemporaryFile like we do for HDRIs |
| 602 | with tempfile.NamedTemporaryFile(suffix=f".{file_format}", delete=False) as tmp_file: |
| 603 | # Download the file |
| 604 | response = requests.get(file_url, headers=REQ_HEADERS) |
| 605 | if response.status_code == 200: |
| 606 | tmp_file.write(response.content) |
| 607 | tmp_path |
Remediation
Pass timeout= on every call: - HTTP: `requests.get(url, timeout=5)`, `httpx.get(url, timeout=5.0)` - Node fetch: `AbortSignal.timeout(5000)` - Subprocess: `subprocess.run(["cmd"], timeout=30, check=True)` Pick a value short enough to fail fast and retry.
Network / IO / subprocess call without an explicit timeout. A malicious or hung upstream (HTTP host, socket peer, child process) can pin threads, exhaust connection/process pools, and make the MCP server unresponsive. Always pass a bounded timeout. v2 extends v1 with subprocess coverage (R03 from the legacy readiness audit).
Evidence
| 465 | if categories: |
| 466 | params["categories"] = categories |
| 467 | |
| 468 | response = requests.get(url, params=params, headers=REQ_HEADERS) |
| 469 | if response.status_code == 200: |
| 470 | # Limit the response size to avoid overwhelming Blender |
| 471 | assets = response.json() |
Remediation
Pass timeout= on every call: - HTTP: `requests.get(url, timeout=5)`, `httpx.get(url, timeout=5.0)` - Node fetch: `AbortSignal.timeout(5000)` - Subprocess: `subprocess.run(["cmd"], timeout=30, check=True)` Pick a value short enough to fail fast and retry.
Network / IO / subprocess call without an explicit timeout. A malicious or hung upstream (HTTP host, socket peer, child process) can pin threads, exhaust connection/process pools, and make the MCP server unresponsive. Always pass a bounded timeout. v2 extends v1 with subprocess coverage (R03 from the legacy readiness audit).
Evidence
| 443 | if asset_type not in ["hdris", "textures", "models", "all"]: |
| 444 | return {"error": f"Invalid asset type: {asset_type}. Must be one of: hdris, textures, models, all"} |
| 445 | |
| 446 | response = requests.get(f"https://api.polyhaven.com/categories/{asset_type}", headers=REQ_HEADERS) |
| 447 | if response.status_code == 200: |
| 448 | return {"categories": response.json()} |
| 449 | else: |
Remediation
Pass timeout= on every call: - HTTP: `requests.get(url, timeout=5)`, `httpx.get(url, timeout=5.0)` - Node fetch: `AbortSignal.timeout(5000)` - Subprocess: `subprocess.run(["cmd"], timeout=30, check=True)` Pick a value short enough to fail fast and retry.
Network / IO / subprocess call without an explicit timeout. A malicious or hung upstream (HTTP host, socket peer, child process) can pin threads, exhaust connection/process pools, and make the MCP server unresponsive. Always pass a bounded timeout. v2 extends v1 with subprocess coverage (R03 from the legacy readiness audit).
Evidence
| 2262 | try: |
| 2263 | # Download ZIP file |
| 2264 | zip_response = requests.get(zip_file_url, stream=True) |
| 2265 | zip_response.raise_for_status() |
| 2266 | with open(zip_file_path, "wb") as f: |
| 2267 | for chunk in zip_response.iter_content(chunk_size=8192): |
Remediation
Pass timeout= on every call: - HTTP: `requests.get(url, timeout=5)`, `httpx.get(url, timeout=5.0)` - Node fetch: `AbortSignal.timeout(5000)` - Subprocess: `subprocess.run(["cmd"], timeout=30, check=True)` Pick a value short enough to fail fast and retry.
Network / IO / subprocess call without an explicit timeout. A malicious or hung upstream (HTTP host, socket peer, child process) can pin threads, exhaust connection/process pools, and make the MCP server unresponsive. Always pass a bounded timeout. v2 extends v1 with subprocess coverage (R03 from the legacy readiness audit).
Evidence
| 1434 | try: |
| 1435 | # Download the content |
| 1436 | response = requests.get(data_["model_mesh"]["url"], stream=True) |
| 1437 | response.raise_for_status() # Raise an exception for HTTP errors |
| 1438 | |
| 1439 | # Write the content to the temporary file |
Remediation
Pass timeout= on every call: - HTTP: `requests.get(url, timeout=5)`, `httpx.get(url, timeout=5.0)` - Node fetch: `AbortSignal.timeout(5000)` - Subprocess: `subprocess.run(["cmd"], timeout=30, check=True)` Pick a value short enough to fail fast and retry.
Network / IO / subprocess call without an explicit timeout. A malicious or hung upstream (HTTP host, socket peer, child process) can pin threads, exhaust connection/process pools, and make the MCP server unresponsive. Always pass a bounded timeout. v2 extends v1 with subprocess coverage (R03 from the legacy readiness audit).
Evidence
| 738 | main_file_name = file_url.split("/")[-1] |
| 739 | main_file_path = os.path.join(temp_dir, main_file_name) |
| 740 | |
| 741 | response = requests.get(file_url, headers=REQ_HEADERS) |
| 742 | if response.status_code != 200: |
| 743 | return {"error": f"Failed to download model: {response.status_code}"} |
Remediation
Pass timeout= on every call: - HTTP: `requests.get(url, timeout=5)`, `httpx.get(url, timeout=5.0)` - Node fetch: `AbortSignal.timeout(5000)` - Subprocess: `subprocess.run(["cmd"], timeout=30, check=True)` Pick a value short enough to fail fast and retry.
MCP manifest declares tools but no authentication field is present (none of: auth, authorization, bearer, oauth, mtls, apiKey, api_key, basic, token, authToken). Absence is a weak signal — confirm whether the server relies on network-layer or host-level auth, or declare the real mechanism explicitly so reviewers can audit it.
Evidence
| 1 | # BlenderMCP - Blender Model Context Protocol Integration |
| 2 | |
| 3 | BlenderMCP connects Blender to Claude AI through the Model Context Protocol (MCP), allowing Claude to directly interact with and control Blender. This integration enables prompt assisted 3D modeling, scene creation, and manipulation. |
| 4 | |
| 5 | **We have no official website. Any website you see online is unofficial and has no affiliation with this project. Use them at your own risk.** |
| 6 | |
| 7 | [Full tutorial](https://www.youtube.com/watch?v=lCyQ717DuzQ) |
| 8 | |
| 9 |
Remediation
Declare a real authentication mechanism in the manifest, matching what the running server actually enforces: - `"auth": "bearer"` with a token scheme documented for callers - `"auth": "oauth"` / `"oauth2": { ... }` for delegated flows - `"apiKey": { "header": "X-API-Key", "prefix": "..." }` - `"mtls": true` when client certificates are required If the server is intentionally unauthenticated (stdio-only, local developer tool, trusted-host network), document the assumption in the manifest via a `"
Time-of-check-to-time-of-use race. Code calls `os.path.exists` / `fs.existsSync` to check a path, then `open` / `readFileSync` / `unlink` on the same name within a few lines — without a lock or atomic-open. An attacker who can race the filesystem (symlink, file replacement) between the check and the use gets the action applied to a different target. Replace the check-then-use pattern with the action's own error handling: try the open and catch FileNotFoundError / ENOENT. For atomic creation use
Evidence
| 1 | # Code created by Siddharth Ahuja: www.github.com/ahujasid © 2025 |
| 2 | |
| 3 | import re |
| 4 | import bpy |
| 5 | import mathutils |
| 6 | import json |
| 7 | import threading |
| 8 | import socket |
| 9 | import time |
| 10 | import requests |
| 11 | import tempfile |
| 12 | import traceback |
| 13 | import os |
| 14 | import shutil |
| 15 | import zipfile |
| 16 | from bpy.props import IntProperty, BoolProperty |
| 17 | import io |
| 18 | from datetime import datetime |
| 19 | import hashlib, hmac, base64 |
| 20 | import os.path as osp |
| 21 | from contextlib import redirect_stdout, suppress |
| 22 | |
| 23 | bl_info = { |
| 24 | "name": "Blender MCP", |
| 25 | "author": "BlenderMC |
Remediation
Replace check-then-use with action-then-handle: Python: `try: with open(p) as f: ... except FileNotFoundError: ...` Node: `try { fs.readFileSync(p); } catch (e) { if (e.code === "ENOENT") ... }` For atomic file creation: Python: `os.open(p, os.O_RDWR | os.O_CREAT | os.O_EXCL)` Node: `fs.open(p, "wx")` — fails if file exists, no race. When you genuinely must check first, use `flock` (Python `fcntl`) or a similar per-process advisory lock to make the window uninteresting to a
Time-of-check-to-time-of-use race. Code calls `os.path.exists` / `fs.existsSync` to check a path, then `open` / `readFileSync` / `unlink` on the same name within a few lines — without a lock or atomic-open. An attacker who can race the filesystem (symlink, file replacement) between the check and the use gets the action applied to a different target. Replace the check-then-use pattern with the action's own error handling: try the open and catch FileNotFoundError / ENOENT. For atomic creation use
Evidence
| 1 | # blender_mcp_server.py |
| 2 | from mcp.server.fastmcp import FastMCP, Context, Image |
| 3 | import socket |
| 4 | import json |
| 5 | import asyncio |
| 6 | import logging |
| 7 | import tempfile |
| 8 | from dataclasses import dataclass |
| 9 | from contextlib import asynccontextmanager |
| 10 | from typing import AsyncIterator, Dict, Any, List |
| 11 | import os |
| 12 | from pathlib import Path |
| 13 | import base64 |
| 14 | from urllib.parse import urlparse |
| 15 | |
| 16 | # Import telemetry |
| 17 | from .telemetry import record_startup, get_telemetry |
| 18 | from .telemetry_decorator import telemetry_tool |
| 19 | |
| 20 | # Configure loggi |
Remediation
Replace check-then-use with action-then-handle: Python: `try: with open(p) as f: ... except FileNotFoundError: ...` Node: `try { fs.readFileSync(p); } catch (e) { if (e.code === "ENOENT") ... }` For atomic file creation: Python: `os.open(p, os.O_RDWR | os.O_CREAT | os.O_EXCL)` Node: `fs.open(p, "wx")` — fails if file exists, no race. When you genuinely must check first, use `flock` (Python `fcntl`) or a similar per-process advisory lock to make the window uninteresting to a
LLM consensus
Identifier whose name suggests PII (email, ssn, phone, dob, credit_card, address) is passed directly to a logging / console / print call. Logs end up in CloudWatch / Datadog / Splunk indexes accessible to a wider audience than the live data — every PII value leaked into logs becomes a separate compliance liability. Mask before logging: Python: `logger.info("login from %s", redact(email))` Node: `console.log("login", maskEmail(email))` Or move the value to a structured field that the log sh
Evidence
| 100 | # Accept new connection |
| 101 | try: |
| 102 | client, address = self.socket.accept() |
| 103 | print(f"Connected to client: {address}") |
| 104 | |
| 105 | # Handle client in a separate thread |
| 106 | client_thread = threading.Thread( |
Remediation
Mask the value before logging or move it to a structured field the log shipper strips. Per-language idioms: Python: `logger.info("login %s", redact(email))` Node: `console.log("login", maskEmail(email))` For unavoidable diagnostic logging, use a per-tenant pseudonym — `tokenize(email)` returns a stable hash; logs still group per-user without leaking the underlying address.
Silent error swallowing detected. An except clause that does pass or ... discards the exception with no log, no metric, and no trace. This blinds incident response and hides real failures.
Evidence
| 682 | pass # Use default if sRGB not available |
| 683 | else: |
| 684 | try: |
| 685 | tex_node.image.colorspace_settings.name = 'Non-Color' |
| 686 | except: |
| 687 | pass # Use default if Non-Color not available |
| 688 | |
| 689 | links.new(mapping.outputs['Vector'], tex_node.inputs['Vector']) |
Remediation
Log the exception at minimum (`logger.exception(e)`), emit a metric, or re-raise if the error is not recoverable. If you genuinely want to ignore an exception, say so with a comment.
Silent error swallowing detected. An except clause that does pass or ... discards the exception with no log, no metric, and no trace. This blinds incident response and hides real failures.
Evidence
| 909 | pass # Use default if sRGB not available |
| 910 | else: |
| 911 | try: |
| 912 | tex_node.image.colorspace_settings.name = 'Non-Color' |
| 913 | except: |
| 914 | pass # Use default if Non-Color not available |
| 915 | |
| 916 | links.new(mapping.outputs['Vector'], tex_node.inputs['Vector']) |
Remediation
Log the exception at minimum (`logger.exception(e)`), emit a metric, or re-raise if the error is not recoverable. If you genuinely want to ignore an exception, say so with a comment.
Silent error swallowing detected. An except clause that does pass or ... discards the exception with no log, no metric, and no trace. This blinds incident response and hides real failures.
Evidence
| 621 | pass |
| 622 | else: |
| 623 | try: |
| 624 | image.colorspace_settings.name = 'Non-Color' |
| 625 | except: |
| 626 | pass |
| 627 | |
| 628 | downloaded_maps[map_type] = image |
Remediation
Log the exception at minimum (`logger.exception(e)`), emit a metric, or re-raise if the error is not recoverable. If you genuinely want to ignore an exception, say so with a comment.
Silent error swallowing detected. An except clause that does pass or ... discards the exception with no log, no metric, and no trace. This blinds incident response and hides real failures.
Evidence
| 629 | # Clean up temporary file |
| 630 | try: |
| 631 | os.unlink(tmp_path) |
| 632 | except: |
| 633 | pass |
| 634 | |
| 635 | if not downloaded_maps: |
| 636 | return {"error": f"No texture maps found for the requested resolution and format"} |
Remediation
Log the exception at minimum (`logger.exception(e)`), emit a metric, or re-raise if the error is not recoverable. If you genuinely want to ignore an exception, say so with a comment.
Silent error swallowing detected. An except clause that does pass or ... discards the exception with no log, no metric, and no trace. This blinds incident response and hides real failures.
Evidence
| 571 | # Clean up temporary file |
| 572 | try: |
| 573 | tempfile._cleanup() # This will clean up all temporary files |
| 574 | except: |
| 575 | pass |
| 576 | |
| 577 | return { |
| 578 | "success": True, |
Remediation
Log the exception at minimum (`logger.exception(e)`), emit a metric, or re-raise if the error is not recoverable. If you genuinely want to ignore an exception, say so with a comment.
Silent error swallowing detected. An except clause that does pass or ... discards the exception with no log, no metric, and no trace. This blinds incident response and hides real failures.
Evidence
| 232 | # Connection is dead, close it and create a new one |
| 233 | logger.warning(f"Existing connection is no longer valid: {str(e)}") |
| 234 | try: |
| 235 | _blender_connection.disconnect() |
| 236 | except: |
| 237 | pass |
| 238 | _blender_connection = None |
| 239 | |
| 240 | # Create a new connection if needed |
Remediation
Log the exception at minimum (`logger.exception(e)`), emit a metric, or re-raise if the error is not recoverable. If you genuinely want to ignore an exception, say so with a comment.
Silent error swallowing detected. An except clause that does pass or ... discards the exception with no log, no metric, and no trace. This blinds incident response and hides real failures.
Evidence
| 904 | # Set color space based on map type |
| 905 | if map_type.lower() in ['color', 'diffuse', 'albedo']: |
| 906 | try: |
| 907 | tex_node.image.colorspace_settings.name = 'sRGB' |
| 908 | except: |
| 909 | pass # Use default if sRGB not available |
| 910 | else: |
| 911 | try: |
| 912 | tex_node.image.colorspace_settings.name = 'Non-Color' |
Remediation
Log the exception at minimum (`logger.exception(e)`), emit a metric, or re-raise if the error is not recoverable. If you genuinely want to ignore an exception, say so with a comment.
Silent error swallowing detected. An except clause that does pass or ... discards the exception with no log, no metric, and no trace. This blinds incident response and hides real failures.
Evidence
| 83 | if self.server_thread: |
| 84 | try: |
| 85 | if self.server_thread.is_alive(): |
| 86 | self.server_thread.join(timeout=1.0) |
| 87 | except: |
| 88 | pass |
| 89 | self.server_thread = None |
| 90 | |
| 91 | print("BlenderMCP server stopped") |
Remediation
Log the exception at minimum (`logger.exception(e)`), emit a metric, or re-raise if the error is not recoverable. If you genuinely want to ignore an exception, say so with a comment.
Silent error swallowing detected. An except clause that does pass or ... discards the exception with no log, no metric, and no trace. This blinds incident response and hides real failures.
Evidence
| 677 | # Set color space based on map type |
| 678 | if map_type.lower() in ['color', 'diffuse', 'albedo']: |
| 679 | try: |
| 680 | tex_node.image.colorspace_settings.name = 'sRGB' |
| 681 | except: |
| 682 | pass # Use default if sRGB not available |
| 683 | else: |
| 684 | try: |
| 685 | tex_node.image.colorspace_settings.name = 'Non-Color' |
Remediation
Log the exception at minimum (`logger.exception(e)`), emit a metric, or re-raise if the error is not recoverable. If you genuinely want to ignore an exception, say so with a comment.
Silent error swallowing detected. An except clause that does pass or ... discards the exception with no log, no metric, and no trace. This blinds incident response and hides real failures.
Evidence
| 74 | # Close socket |
| 75 | if self.socket: |
| 76 | try: |
| 77 | self.socket.close() |
| 78 | except: |
| 79 | pass |
| 80 | self.socket = None |
| 81 | |
| 82 | # Wait for thread to finish |
Remediation
Log the exception at minimum (`logger.exception(e)`), emit a metric, or re-raise if the error is not recoverable. If you genuinely want to ignore an exception, say so with a comment.
Silent error swallowing detected. An except clause that does pass or ... discards the exception with no log, no metric, and no trace. This blinds incident response and hides real failures.
Evidence
| 616 | # Set color space based on map type |
| 617 | if map_type in ['color', 'diffuse', 'albedo']: |
| 618 | try: |
| 619 | image.colorspace_settings.name = 'sRGB' |
| 620 | except: |
| 621 | pass |
| 622 | else: |
| 623 | try: |
| 624 | |
Remediation
Log the exception at minimum (`logger.exception(e)`), emit a metric, or re-raise if the error is not recoverable. If you genuinely want to ignore an exception, say so with a comment.
Silent error swallowing detected. An except clause that does pass or ... discards the exception with no log, no metric, and no trace. This blinds incident response and hides real failures.
Evidence
| 830 | # Ensure proper color space |
| 831 | if map_type.lower() in ['color', 'diffuse', 'albedo']: |
| 832 | try: |
| 833 | img.colorspace_settings.name = 'sRGB' |
| 834 | except: |
| 835 | pass |
| 836 | else: |
| 837 | try: |
| 838 | img.colorspace_settings.name = 'Non-Color' |
Remediation
Log the exception at minimum (`logger.exception(e)`), emit a metric, or re-raise if the error is not recoverable. If you genuinely want to ignore an exception, say so with a comment.
Silent error swallowing detected. An except clause that does pass or ... discards the exception with no log, no metric, and no trace. This blinds incident response and hides real failures.
Evidence
| 43 | if tomli: |
| 44 | with open(pyproject_path, "rb") as f: |
| 45 | data = tomli.load(f) |
| 46 | return data["project"]["version"] |
| 47 | except Exception: |
| 48 | pass |
| 49 | return "unknown" |
Remediation
Log the exception at minimum (`logger.exception(e)`), emit a metric, or re-raise if the error is not recoverable. If you genuinely want to ignore an exception, say so with a comment.
Silent error swallowing detected. An except clause that does pass or ... discards the exception with no log, no metric, and no trace. This blinds incident response and hides real failures.
Evidence
| 161 | "status": "error", |
| 162 | "message": str(e) |
| 163 | } |
| 164 | client.sendall(json.dumps(error_response).encode('utf-8')) |
| 165 | except: |
| 166 | pass |
| 167 | return None |
| 168 | |
| 169 | # Schedule execution in main thread |
Remediation
Log the exception at minimum (`logger.exception(e)`), emit a metric, or re-raise if the error is not recoverable. If you genuinely want to ignore an exception, say so with a comment.
Silent error swallowing detected. An except clause that does pass or ... discards the exception with no log, no metric, and no trace. This blinds incident response and hides real failures.
Evidence
| 835 | pass |
| 836 | else: |
| 837 | try: |
| 838 | img.colorspace_settings.name = 'Non-Color' |
| 839 | except: |
| 840 | pass |
| 841 | |
| 842 | # Ensure the image is packed |
| 843 | if not img.packed_file: |
Remediation
Log the exception at minimum (`logger.exception(e)`), emit a metric, or re-raise if the error is not recoverable. If you genuinely want to ignore an exception, say so with a comment.
Silent error swallowing detected. An except clause that does pass or ... discards the exception with no log, no metric, and no trace. This blinds incident response and hides real failures.
Evidence
| 178 | print(f"Error in client handler: {str(e)}") |
| 179 | finally: |
| 180 | try: |
| 181 | client.close() |
| 182 | except: |
| 183 | pass |
| 184 | print("Client handler stopped") |
| 185 | |
| 186 | def execute_command(self, command): |
Remediation
Log the exception at minimum (`logger.exception(e)`), emit a metric, or re-raise if the error is not recoverable. If you genuinely want to ignore an exception, say so with a comment.
generate_hunyuan3d_model
poll_hunyuan_job_status
get_hunyuan3d_status
import_generated_asset_hunyuan
BLENDERMCP_PT_Panel
get_scene_info