Mostly safe — a couple of notes worth reading.
Scanned 5/1/2026, 11:56:53 AM·Cached result·Fast Scan·45 rules·View source ↗·How we decide ↗
AIVSS Score
Low
Severity Breakdown
0
critical
3
high
44
medium
3
low
MCP Server Information
Findings
This package carries moderate risk with a B grade and 66/100 safety score, driven primarily by 42 instances of verbose error handling that could leak sensitive information to attackers. Three high-severity findings related to XXE (XML External Entity) vulnerabilities and readiness issues warrant careful review before deployment. While no critical vulnerabilities were detected, the combination of information disclosure risks and XXE concerns suggests you should thoroughly test this in a controlled environment and consider implementing additional input validation safeguards.
No known CVEs found for this package or its dependencies.
Scan Details
Want deeper analysis?
Fast scan found 50 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 50 findings
50 findings
XML parser configured without entity expansion disabled. XXE (XML external entity) attacks can read local files, exfiltrate data, and cause SSRF when the parser resolves external entities.
Evidence
| 855 | drawing_path = os.path.join(drawings_dir, drawing_file) |
| 856 | sheet_name = drawing_to_sheet.get(drawing_file, "Unknown") |
| 857 | try: |
| 858 | tree = ET.parse(drawing_path) |
| 859 | root = tree.getroot() |
| 860 | for blip in root.iter('{http://schemas.openxmlformats.org/drawingml/2006/main}blip'): |
| 861 | embed = blip.get('{http://schemas.openxmlformats.org/officeDocument/2006/relat |
Remediation
Use defusedxml in Python (drop-in replacement for stdlib XML modules). In Java, set XMLConstants.FEATURE_SECURE_PROCESSING=true and disable external-DTD features on the factory before parsing.
XML parser configured without entity expansion disabled. XXE (XML external entity) attacks can read local files, exfiltrate data, and cause SSRF when the parser resolves external entities.
Evidence
| 839 | sheet_num = rels_file.replace('sheet', '').replace('.xml.rels', '') |
| 840 | rels_path = os.path.join(worksheets_rels_dir, rels_file) |
| 841 | try: |
| 842 | tree = ET.parse(rels_path) |
| 843 | root = tree.getroot() |
| 844 | for rel in root.findall('.//{http://schemas.openxmlformats.org/package/2006/relationships}Relationship'): |
| 845 | target = rel.get('Target') |
Remediation
Use defusedxml in Python (drop-in replacement for stdlib XML modules). In Java, set XMLConstants.FEATURE_SECURE_PROCESSING=true and disable external-DTD features on the factory before parsing.
XML parser configured without entity expansion disabled. XXE (XML external entity) attacks can read local files, exfiltrate data, and cause SSRF when the parser resolves external entities.
Evidence
| 874 | sheet_name = drawing_to_sheet.get(drawing_name, "Unknown") |
| 875 | rels_path = os.path.join(drawings_rels_dir, rels_file) |
| 876 | try: |
| 877 | tree = ET.parse(rels_path) |
| 878 | root = tree.getroot() |
| 879 | for rel in root.findall('.//{http://schemas.openxmlformats.org/package/2006/relationships}Relationship'): |
| 880 | rid = rel.get('Id') |
Remediation
Use defusedxml in Python (drop-in replacement for stdlib XML modules). In Java, set XMLConstants.FEATURE_SECURE_PROCESSING=true and disable external-DTD features on the factory before parsing.
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
| 756 | "rows_filled": end_row - start_row + 1 |
| 757 | } |
| 758 | except Exception as e: |
| 759 | return {"success": False, "error": str(e)} |
| 760 | |
| 761 | @MCP.tool() |
| 762 | def list_sheet_images(sheet_name: str = 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
| 902 | finally: |
| 903 | shutil.rmtree(temp_dir, ignore_errors=True) |
| 904 | except Exception as e: |
| 905 | return {"success": False, "error": str(e)} |
| 906 | |
| 907 | @MCP.tool() |
| 908 | def delete_sheet_image(sheet_name: str, image_index: int = None, image_name: str = 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
| 1477 | "include_all_sheets": include_all_sheets |
| 1478 | } |
| 1479 | except Exception as e: |
| 1480 | return {"success": False, "error": str(e)} |
| 1481 | |
| 1482 | if __name__ == "__main__": |
| 1483 | verify_license() |
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
| 628 | return {"success": True, "message": f"区域 {range_address} 粗体已设置为 {bold}"} |
| 629 | except Exception as e: |
| 630 | return {"success": False, "error": str(e)} |
| 631 | |
| 632 | @MCP.tool() |
| 633 | def set_range_font_color(sheet_name: str, range_address: str, color: 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.
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
| 1333 | "dst_sheet": dst_sheet_name |
| 1334 | } |
| 1335 | except Exception as e: |
| 1336 | return {"success": False, "error": str(e)} |
| 1337 | |
| 1338 | @MCP.tool() |
| 1339 | def create_table(sheet_name: str = None, range_address: str = None, table_name: str = "Table1", has_headers: bool = True): |
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
| 354 | "sheets": [ws.Name for ws in wb.Worksheets] |
| 355 | } |
| 356 | except Exception as e: |
| 357 | return {"success": False, "error": str(e)} |
| 358 | |
| 359 | def hex_to_vba_color(hex_color: str) -> int: |
| 360 | """将十六进制颜色转换为VBA颜色值(BGR格式)""" |
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
| 567 | "message": f"共 {len(results)} 个单元格,其中 {len(colored_cells)} 个有字体颜色" |
| 568 | } |
| 569 | except Exception as e: |
| 570 | return {"success": False, "error": str(e)} |
| 571 | |
| 572 | @MCP.tool() |
| 573 | def set_range_color(sheet_name: str, range_address: str, color: 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.
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
| 151 | } |
| 152 | } |
| 153 | except Exception as e: |
| 154 | return {"success": False, "error": str(e)} |
| 155 | |
| 156 | @MCP.tool() |
| 157 | def get_selection(): |
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
| 1063 | return {"success": True, "message": f"已写入 {len(data)} 行,起始位置:{start_col}{start_row}"} |
| 1064 | except Exception as e: |
| 1065 | return {"success": False, "error": str(e)} |
| 1066 | |
| 1067 | @MCP.tool() |
| 1068 | def batch_create_formulas(sheet_name: str, formulas: list): |
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
| 293 | return {"success": True, "message": f"已写入 '{value}' 到 {cell}"} |
| 294 | except Exception as e: |
| 295 | return {"success": False, "error": str(e)} |
| 296 | |
| 297 | @MCP.tool() |
| 298 | def save_workbook(): |
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
| 233 | return {"success": True, "message": f"Sheet '{sheet_name}' created"} |
| 234 | except Exception as e: |
| 235 | return {"success": False, "error": str(e)} |
| 236 | |
| 237 | @MCP.tool() |
| 238 | def delete_sheet(sheet_name: 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.
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
| 411 | "message": f"单元格 {cell} 颜色: {color_name}" |
| 412 | } |
| 413 | except Exception as e: |
| 414 | return {"success": False, "error": str(e)} |
| 415 | |
| 416 | @MCP.tool() |
| 417 | def get_range_colors(sheet_name: str, range_address: 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.
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
| 1240 | wb.Save() |
| 1241 | return {"success": True, "message": f"Unmerged cells {range_address}"} |
| 1242 | except Exception as e: |
| 1243 | return {"success": False, "error": str(e)} |
| 1244 | |
| 1245 | @MCP.tool() |
| 1246 | def auto_format_table(sheet_name: str = None, range_address: str = None, style_index: int = 1): |
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
| 105 | return {"success": True, "data": data, "sheet": ws.Name} |
| 106 | except Exception as e: |
| 107 | return {"success": False, "error": str(e)} |
| 108 | |
| 109 | @MCP.tool() |
| 110 | def get_workbook_info(): |
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
| 670 | wb.Save() |
| 671 | return {"success": True, "message": f"区域 {range_address} 字体颜色已设置"} |
| 672 | except Exception as e: |
| 673 | return {"success": False, "error": str(e)} |
| 674 | |
| 675 | @MCP.tool() |
| 676 | def set_range_font_size(sheet_name: str, range_address: str, size: int): |
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
| 1297 | "style": style_name |
| 1298 | } |
| 1299 | except Exception as e: |
| 1300 | return {"success": False, "error": str(e)} |
| 1301 | |
| 1302 | @MCP.tool() |
| 1303 | def copy_sheet(src_sheet_name: str, dst_sheet_name: str, after_sheet_name: str = 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
| 218 | return {"success": True, "message": f"Formula '{formula}' set at {cell}"} |
| 219 | except Exception as e: |
| 220 | return {"success": False, "error": str(e)} |
| 221 | |
| 222 | @MCP.tool() |
| 223 | def add_new_sheet(sheet_name: 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.
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
| 1376 | "sheet": ws.Name |
| 1377 | } |
| 1378 | except Exception as e: |
| 1379 | return {"success": False, "error": str(e)} |
| 1380 | |
| 1381 | @MCP.tool() |
| 1382 | def screen_capture(sheet_name: str = None, range_address: str = None, output_path: str = 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
| 1088 | wb.Save() |
| 1089 | return {"success": True, "message": f"Created {success_count} formulas"} |
| 1090 | except Exception as e: |
| 1091 | return {"success": False, "error": str(e)} |
| 1092 | |
| 1093 | @MCP.tool() |
| 1094 | def insert_rows(sheet_name: str, row: int, count: int = 1): |
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
| 1020 | wb.Save() |
| 1021 | return {"success": True, "message": f"Colored {success_count} cells"} |
| 1022 | except Exception as e: |
| 1023 | return {"success": False, "error": str(e)} |
| 1024 | |
| 1025 | @MCP.tool() |
| 1026 | def batch_write_rows(sheet_name: str, start_row: int, data: list, start_col: str = "A"): |
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
| 333 | "sheets": [ws.Name for ws in wb.Worksheets] |
| 334 | } |
| 335 | except Exception as e: |
| 336 | return {"success": False, "error": str(e)} |
| 337 | |
| 338 | @MCP.tool() |
| 339 | def open_workbook(file_path: 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.
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
| 787 | return {"success": True, "images": images, "count": len(images)} |
| 788 | except Exception as e: |
| 789 | return {"success": False, "error": str(e)} |
| 790 | |
| 791 | @MCP.tool() |
| 792 | def extract_images_from_excel(workbook_path: str = None, output_folder: str = 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
| 940 | else: |
| 941 | return {"success": False, "error": f"未找到图片: {image_name}"} |
| 942 | except Exception as e: |
| 943 | return {"success": False, "error": str(e)} |
| 944 | |
| 945 | @MCP.tool() |
| 946 | def batch_write_cells(sheet_name: str, cells: list): |
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
| 1126 | wb.Save() |
| 1127 | return {"success": True, "message": f"Deleted {count} rows from row {row}"} |
| 1128 | except Exception as e: |
| 1129 | return {"success": False, "error": str(e)} |
| 1130 | |
| 1131 | @MCP.tool() |
| 1132 | def insert_columns(sheet_name: str, column: str, count: int = 1): |
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
| 1202 | wb.Save() |
| 1203 | return {"success": True, "message": f"Row {row} height set to {height}"} |
| 1204 | except Exception as e: |
| 1205 | return {"success": False, "error": str(e)} |
| 1206 | |
| 1207 | @MCP.tool() |
| 1208 | def merge_cells(sheet_name: str, range_address: 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.
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
| 1183 | wb.Save() |
| 1184 | return {"success": True, "message": f"Column {column} width set to {width}"} |
| 1185 | except Exception as e: |
| 1186 | return {"success": False, "error": str(e)} |
| 1187 | |
| 1188 | @MCP.tool() |
| 1189 | def set_row_height(sheet_name: str, row: int, height: float): |
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
| 1221 | wb.Save() |
| 1222 | return {"success": True, "message": f"Merged cells {range_address}"} |
| 1223 | except Exception as e: |
| 1224 | return {"success": False, "error": str(e)} |
| 1225 | |
| 1226 | @MCP.tool() |
| 1227 | def unmerge_cells(sheet_name: str, range_address: 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.
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
| 1164 | wb.Save() |
| 1165 | return {"success": True, "message": f"Deleted {count} columns from {column}"} |
| 1166 | except Exception as e: |
| 1167 | return {"success": False, "error": str(e)} |
| 1168 | |
| 1169 | @MCP.tool() |
| 1170 | def set_column_width(sheet_name: str, column: str, width: float): |
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 | wb.Save() |
| 1108 | return {"success": True, "message": f"Inserted {count} rows at row {row}"} |
| 1109 | except Exception as e: |
| 1110 | return {"success": False, "error": str(e)} |
| 1111 | |
| 1112 | @MCP.tool() |
| 1113 | def delete_rows(sheet_name: str, row: int, count: int = 1): |
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
| 199 | "message": f"用户选择区域: {range_address}" if is_user_selection else f"未选择,使用整个表格: {range_address}" |
| 200 | } |
| 201 | except Exception as e: |
| 202 | return {"success": False, "error": str(e)} |
| 203 | |
| 204 | @MCP.tool() |
| 205 | def create_formula(sheet_name: str, cell: str, formula: 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.
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
| 1424 | "sheet": ws.Name |
| 1425 | } |
| 1426 | except Exception as e: |
| 1427 | return {"success": False, "error": str(e)} |
| 1428 | |
| 1429 | @MCP.tool() |
| 1430 | def export_to_pdf(sheet_name: str = None, output_path: str = None, include_all_sheets: bool = False): |
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
| 609 | wb.Save() |
| 610 | return {"success": True, "message": f"区域 {range_address} 背景颜色已设置"} |
| 611 | except Exception as e: |
| 612 | return {"success": False, "error": str(e)} |
| 613 | |
| 614 | @MCP.tool() |
| 615 | def set_range_bold(sheet_name: str, range_address: str, bold: bool = True): |
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
| 304 | wb.Save() |
| 305 | return {"success": True, "message": "Workbook saved"} |
| 306 | except Exception as e: |
| 307 | return {"success": False, "error": str(e)} |
| 308 | |
| 309 | @MCP.tool() |
| 310 | def create_workbook(file_path: str = 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
| 507 | "message": f"单元格 {cell} 字体颜色: {color_name}" |
| 508 | } |
| 509 | except Exception as e: |
| 510 | return {"success": False, "error": str(e)} |
| 511 | |
| 512 | @MCP.tool() |
| 513 | def get_range_font_colors(sheet_name: str, range_address: 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.
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
| 266 | return {"success": True, "cell": cell, "value": value, "formula": formula} |
| 267 | except Exception as e: |
| 268 | return {"success": False, "error": str(e)} |
| 269 | |
| 270 | @MCP.tool() |
| 271 | def write_cell(sheet_name: str, cell: str, value): |
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
| 471 | "message": f"共 {len(results)} 个单元格,其中 {len(colored_cells)} 个有颜色" |
| 472 | } |
| 473 | except Exception as e: |
| 474 | return {"success": False, "error": str(e)} |
| 475 | |
| 476 | @MCP.tool() |
| 477 | def get_cell_font_color(sheet_name: str, cell: 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.
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
| 247 | return {"success": True, "message": f"Sheet '{sheet_name}' deleted"} |
| 248 | except Exception as e: |
| 249 | return {"success": False, "error": str(e)} |
| 250 | |
| 251 | @MCP.tool() |
| 252 | def read_cell(sheet_name: str, cell: 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.
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
| 719 | wb.Save() |
| 720 | return {"success": True, "message": f"区域 {range_address} 对齐方式已设置"} |
| 721 | except Exception as e: |
| 722 | return {"success": False, "error": str(e)} |
| 723 | |
| 724 | @MCP.tool() |
| 725 | def fill_formula_down(sheet_name: str, cell: str, end_row: int): |
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
| 980 | return {"success": True, "message": f"已写入 {success_count} 个单元格"} |
| 981 | except Exception as e: |
| 982 | return {"success": False, "error": str(e)} |
| 983 | |
| 984 | @MCP.tool() |
| 985 | def batch_set_colors(sheet_name: str, cells: list): |
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
| 131 | } |
| 132 | } |
| 133 | except Exception as e: |
| 134 | return {"success": False, "error": str(e)} |
| 135 | |
| 136 | @MCP.tool() |
| 137 | def get_active_sheet(): |
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
| 1145 | wb.Save() |
| 1146 | return {"success": True, "message": f"Inserted {count} columns at {column}"} |
| 1147 | except Exception as e: |
| 1148 | return {"success": False, "error": str(e)} |
| 1149 | |
| 1150 | @MCP.tool() |
| 1151 | def delete_columns(sheet_name: str, column: str, count: int = 1): |
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
| 689 | return {"success": True, "message": f"区域 {range_address} 字体大小已设置为 {size}"} |
| 690 | except Exception as e: |
| 691 | return {"success": False, "error": str(e)} |
| 692 | |
| 693 | @MCP.tool() |
| 694 | def set_range_alignment(sheet_name: str, range_address: str, horizontal: str = "center", vertical: str = "center"): |
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
| 33 | mac = get_mac_address() |
| 34 | hostname = socket.gethostname() |
| 35 | try: |
| 36 | result = subprocess.run(['wmic', 'cpu', 'get', 'ProcessorId'], capture_output=True, text=True) |
| 37 | cpu_id = result.stdout.strip().split('\n')[-1].strip() |
| 38 | except: |
| 39 | cpu_id = 'CPU-UNKNOWN' |
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
| 28 | mac = get_mac_address() |
| 29 | hostname = socket.gethostname() |
| 30 | try: |
| 31 | result = subprocess.run(['wmic', 'cpu', 'get', 'ProcessorId'], capture_output=True, text=True) |
| 32 | cpu_id = result.stdout.strip().split('\n')[-1].strip() |
| 33 | except: |
| 34 | cpu_id = 'CPU-UNKNOWN' |
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.
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
| 845 | target = rel.get('Target') |
| 846 | if target and '../drawings/' in target: |
| 847 | drawing_name = target.split('/')[-1] |
| 848 | drawing_to_sheet[drawing_name] = f"Sheet{sheet_num}" |
| 849 | except: |
| 850 | pass |
| 851 | |
| 852 | if os.path.exists(drawings_dir): |
| 853 | for drawing_file in os.listdir(drawings_dir): |
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
| 861 | embed = blip.get('{http://schemas.openxmlformats.org/officeDocument/2006/relationships}embed') |
| 862 | if embed: |
| 863 | image_num = embed.replace('rId', '') |
| 864 | sheet_image_map[image_num] = sheet_name |
| 865 | except: |
| 866 | pass |
| 867 | |
| 868 | drawings_rels_dir = os.path.join(temp_dir, 'xl', 'drawings', '_rels') |
| 869 | image_to_sheet = {} |
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
| 881 | target = rel.get('Target') |
| 882 | if target and '../media/' in target: |
| 883 | image_name = target.split('/')[-1] |
| 884 | image_to_sheet[image_name] = sheet_name |
| 885 | except: |
| 886 | pass |
| 887 | |
| 888 | images = [] |
| 889 | for filename in os.listdir(media_dir): |
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.