Mostly safe ā a couple of notes worth reading.
Scanned 7/24/2026, 11:11:49 PMĀ·Cached resultĀ·Deep ScanĀ·91 rulesĀ·How we decide ā
AIVSS Score
Low
Severity Breakdown
0
critical
2
high
88
medium
6
low
MCP Server Information
Findings
This package carries a B grade with a safety score of 67/100 and presents moderate security concerns, primarily driven by 88 medium-severity findings centered on verbose error handling that could leak sensitive information. The two high-severity issues involve ANSI escape injection vulnerabilities, and additional medium-priority problems span server configuration gaps and resource exhaustion risks. While there are no critical vulnerabilities, the volume of medium-severity findings and the injection risks warrant careful review and mitigation before deployment in production environments.
AIPer-finding remediation generated by bedrock-claude-haiku-4-5 ā 20 of 96 findings. Click any finding to read.
No known CVEs found for this package or its dependencies.
Scan Details
Done
Sign in to save scan history and re-scan automatically on new commits.
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 96 findings
96 findings
MCP server binds an HTTP transport to localhost and registers tools, but no authentication is enforced on requests. The official MCP security best practices warn that this is reachable via DNS-rebinding attacks ā a malicious web page can hit `http://127.0.0.1:<port>` from inside the user's browser and invoke tools as the user. Pick one fix: 1. Switch to stdio transport (`mcp.run(transport="stdio")`). 2. Require an `Authorization` / `Bearer` / `api_key` check on every request. 3. Bind
Evidence
| 1 | """ |
| 2 | Main entry point for the Word Document MCP Server. |
| 3 | Acts as the central controller for the MCP server that handles Word document operations. |
| 4 | Supports multiple transports: stdio, sse, and streamable-http using standalone FastMCP. |
| 5 | """ |
| 6 | |
| 7 | import os |
| 8 | import sys |
| 9 | from dotenv import load_dotenv |
| 10 | |
| 11 | # Load environment variables from .env file |
| 12 | print("Loading configuration from .env file...") |
| 13 | load_dotenv() |
| 14 | # Set required environment variable for FastMCP 2.8.1+ |
| 15 | os.environ.setdefault('FASTMCP_LOG_LEVEL', 'INFO |
RemediationAI
The problem is that the MCP server exposes an HTTP endpoint on localhost without authentication, allowing DNS-rebinding attacks where a malicious webpage can invoke tools as the user. Switch from HTTP transport to stdio transport by changing `mcp.run(transport="stdio")` in word_document_server/main.py, which eliminates the network attack surface entirely. Stdio transport communicates only through standard input/output, preventing remote exploitation. Verify the fix by confirming the server no longer listens on any TCP port and only reads from stdin/writes to stdout.
LLM consensus
MCP server binds an HTTP transport to localhost / 127.0.0.1 / [::1] and registers tools, but does not validate the request `Host` header. Even with auth, this is exploitable via DNS rebinding ā a malicious web page can make the user's browser resolve `evil.com` to `127.0.0.1`, bypassing same-origin checks. Fix: enable `hostHeaderValidation()` middleware (TS SDK ā„1.24.0), or check `req.headers.host` against an allow-list of expected hostnames. Co-fires with MCP-268 (no auth) when both gaps are p
Evidence
| 1 | """ |
| 2 | Main entry point for the Word Document MCP Server. |
| 3 | Acts as the central controller for the MCP server that handles Word document operations. |
| 4 | Supports multiple transports: stdio, sse, and streamable-http using standalone FastMCP. |
| 5 | """ |
| 6 | |
| 7 | import os |
| 8 | import sys |
| 9 | from dotenv import load_dotenv |
| 10 | |
| 11 | # Load environment variables from .env file |
| 12 | print("Loading configuration from .env file...") |
| 13 | load_dotenv() |
| 14 | # Set required environment variable for FastMCP 2.8.1+ |
| 15 | os.environ.setdefault('FASTMCP_LOG_LEVEL', 'INFO |
RemediationAI
The problem is that the HTTP server does not validate the `Host` header, allowing DNS-rebinding attacks where an attacker can resolve a malicious domain to 127.0.0.1 and bypass same-origin protections. Add host header validation middleware by calling `app.add_middleware(HostHeaderValidation, allowed_hosts=["localhost", "127.0.0.1", "[::1]"])` in the FastMCP setup, or manually check `request.headers.get("host")` against an allowlist before processing requests. This ensures only requests from expected hostnames are accepted, blocking rebinding attacks. Verify by testing that requests with a spoofed `Host` header are rejected with a 400 error.
LLM consensus
User-controlled value printed to terminal without ANSI escape sanitization. Malicious input can inject cursor-control sequences, rewrite earlier output, or hide shell commands from the operator.
Evidence
| 50 | print(f" Result: {result}") |
| 51 | |
| 52 | # Test 4: Body text in Times New Roman |
| 53 | print("Test 4: Adding body text in Times New Roman 14pt...") |
| 54 | result = await add_paragraph( |
| 55 | test_doc, |
| 56 | "This is body text that should be in Times New Roman at 14pt. " |
RemediationAI
The problem is that user-controlled `result` values are printed directly to the terminal without sanitizing ANSI escape sequences, allowing malicious input to rewrite output or hide commands. Replace `print(f" Result: {result}")` with `print(f" Result: {repr(result)}")` or use a sanitization function like `result.replace('\x1b', '').replace('\033', '')` to strip escape codes before printing. This prevents cursor-control injection attacks that could manipulate terminal display. Verify by passing a string containing escape sequences (e.g., `"\x1b[2J"`) and confirming it is printed literally, not interpreted.
User-controlled value printed to terminal without ANSI escape sanitization. Malicious input can inject cursor-control sequences, rewrite earlier output, or hide shell commands from the operator.
Evidence
| 372 | else: # stdio |
| 373 | print(f"\nš» STDIO Transport Configuration:") |
| 374 | print(f" Server runs locally with standard input/output") |
| 375 | |
| 376 | # Provide instructions for adding configuration to Claude Desktop configuration file |
| 377 | if platform.system() == "Windows": |
RemediationAI
The problem is that user-controlled configuration values are printed directly to the terminal without sanitizing ANSI escape sequences, allowing injection of cursor-control codes. Replace `print(f"\nš» STDIO Transport Configuration:")` and similar lines with sanitized output: `print(f"\nš» STDIO Transport Configuration: {repr(config_value)}")` or strip escape sequences before printing. This prevents malicious configuration values from injecting terminal control sequences. Verify by testing with a config value containing escape codes and confirming it displays as literal text.
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
| 544 | else: |
| 545 | return f"Failed to merge cells vertically. Check that indices are valid." |
| 546 | except Exception as e: |
| 547 | return f"Failed to merge cells vertically: {str(e)}" |
| 548 | |
| 549 | |
| 550 | async def set_table_cell_alignment(filename: str, table_index: int, row_index: int, col_index: int, |
RemediationAI
The problem is that the exception message `str(e)` is returned directly to the caller, leaking internal error details and library versions. Replace `return f"Failed to merge cells vertically: {str(e)}"` with `return "Failed to merge cells vertically. Please check that table indices are valid."` and log the full exception internally using `logging.exception("merge_cells_vertically failed")`. This hides implementation details while preserving debugging capability through server logs. Verify by triggering an error and confirming the response contains only a generic message, not the traceback.
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
| 451 | ) |
| 452 | return message |
| 453 | except Exception as e: |
| 454 | return f"Failed to delete footnote: {str(e)}" |
| 455 | |
| 456 | |
| 457 | # ============================================================================ |
RemediationAI
The problem is that the exception message `str(e)` is returned directly to the caller, leaking internal error details. Replace `return f"Failed to delete footnote: {str(e)}"` with `return "Failed to delete footnote. Please verify the footnote index is valid."` and add `logging.exception("delete_footnote failed")` to log the full error server-side. This prevents information disclosure while maintaining debuggability. Verify by triggering a deletion error and confirming only a generic message is returned.
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
| 288 | else: |
| 289 | return f"Failed to apply cell shading." |
| 290 | except Exception as e: |
| 291 | return f"Failed to apply cell shading: {str(e)}" |
| 292 | |
| 293 | |
| 294 | async def apply_table_alternating_rows(filename: str, table_index: int, |
RemediationAI
The problem is that the exception message `str(e)` is returned directly to the caller, exposing internal error details. Replace `return f"Failed to apply cell shading: {str(e)}"` with `return "Failed to apply cell shading. Please check the table and cell indices."` and add `logging.exception("apply_cell_shading failed")` to capture the full error server-side. This prevents information leakage while preserving server-side debugging. Verify by triggering a shading error and confirming the response contains only a generic message.
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
| 819 | else: |
| 820 | return f"Failed to set column widths." |
| 821 | except Exception as e: |
| 822 | return f"Failed to set column widths: {str(e)}" |
| 823 | |
| 824 | |
| 825 | async def set_table_width(filename: str, table_index: int, width: float, |
RemediationAI
The problem is that the exception message `str(e)` is returned directly to the caller, leaking internal details. Replace `return f"Failed to set column widths: {str(e)}"` with `return "Failed to set column widths. Please verify the table index and width values."` and add `logging.exception("set_table_width failed")` to log the full error internally. This hides implementation details from the caller while preserving server-side debugging. Verify by triggering a width-setting error and confirming only a generic message is returned.
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
| 35 | "table_count": len(doc.tables) |
| 36 | } |
| 37 | except Exception as e: |
| 38 | return {"error": f"Failed to get document properties: {str(e)}"} |
| 39 | |
| 40 | |
| 41 | def extract_document_text(doc_path: str) -> str: |
RemediationAI
The problem is that the exception message `str(e)` is returned directly to the caller, exposing internal error details. Replace `return {"error": f"Failed to get document properties: {str(e)}"}` with `return {"error": "Failed to retrieve document properties. Please verify the file path and permissions."}` and add `logging.exception("get_document_properties failed")` to log the full error server-side. This prevents information disclosure while maintaining debuggability. Verify by triggering a property-retrieval error and confirming only a generic message is returned.
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
| 743 | else: |
| 744 | return f"Failed to set column width. Check that indices are valid." |
| 745 | except Exception as e: |
| 746 | return f"Failed to set column width: {str(e)}" |
| 747 | |
| 748 | |
| 749 | async def set_table_column_widths(filename: str, table_index: int, widths: list, |
RemediationAI
The problem is that the exception message `str(e)` is returned directly to the caller, leaking internal error details. Replace `return f"Failed to set column width: {str(e)}"` with `return "Failed to set column width. Please check that indices are valid."` and add `logging.exception("set_table_column_widths failed")` to capture the full error server-side. This prevents information leakage while preserving server-side debugging. Verify by triggering a column-width error and confirming the response contains only a generic message.
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
| 75 | os.remove(metadata_path) |
| 76 | return True, "Protection removed successfully" |
| 77 | except Exception as e: |
| 78 | return False, f"Error removing protection: {str(e)}" |
RemediationAI
The problem is that the exception message `str(e)` is returned directly to the caller, exposing internal error details. Replace `return False, f"Error removing protection: {str(e)}"` with `return False, "Error removing protection. Please verify file permissions and document format."` and add `logging.exception("remove_protection failed")` to log the full error internally. This hides implementation details from the caller while preserving server-side debugging. Verify by triggering a protection-removal error and confirming only a generic message is returned.
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
| 168 | else: |
| 169 | return f"Failed to add digital signature to document {filename}" |
| 170 | except Exception as e: |
| 171 | return f"Failed to add digital signature: {str(e)}" |
| 172 | |
| 173 | async def verify_document(filename: str, password: Optional[str] = None) -> str: |
| 174 | """Verify document protection and/or digital signature. |
RemediationAI
The problem is that the exception message `str(e)` is returned directly to the caller, leaking internal error details. Replace `return f"Failed to add digital signature: {str(e)}"` with `return "Failed to add digital signature. Please verify the certificate and document format."` and add `logging.exception("add_digital_signature failed")` to log the full error server-side. This prevents information disclosure while maintaining debuggability. Verify by triggering a signature error and confirming only a generic message is returned.
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
| 107 | doc.save(filename) |
| 108 | return f"Heading '{text}' (level {level}) added to {filename}" |
| 109 | except Exception as e: |
| 110 | return f"Failed to add heading: {str(e)}" |
| 111 | |
| 112 | |
| 113 | async def add_paragraph(filename: str, text: str, style: Optional[str] = None, |
RemediationAI
The problem is that the exception message `str(e)` is returned directly to the caller, exposing internal error details. Replace `return f"Failed to add heading: {str(e)}"` with `return "Failed to add heading. Please verify the document is accessible and the style is valid."` and add `logging.exception("add_heading failed")` to capture the full error server-side. This prevents information leakage while preserving server-side debugging. Verify by triggering a heading-addition error and confirming only a generic message is returned.
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
| 382 | else: |
| 383 | return f"Failed to apply header highlighting." |
| 384 | except Exception as e: |
| 385 | return f"Failed to apply header highlighting: {str(e)}" |
| 386 | |
| 387 | |
| 388 | async def merge_table_cells(filename: str, table_index: int, start_row: int, start_col: int, |
RemediationAI
The problem is that the exception message `str(e)` is returned directly to the caller, leaking internal error details. Replace `return f"Failed to apply header highlighting: {str(e)}"` with `return "Failed to apply header highlighting. Please check the table indices."` and add `logging.exception("apply_header_highlighting failed")` to log the full error internally. This hides implementation details from the caller while preserving server-side debugging. Verify by triggering a highlighting error and confirming only a generic message is returned.
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
| 59 | return "\n".join(text) |
| 60 | except Exception as e: |
| 61 | return f"Failed to extract text: {str(e)}" |
| 62 | |
| 63 | |
| 64 | def get_document_structure(doc_path: str) -> Dict[str, Any]: |
RemediationAI
The problem is that the exception message `str(e)` is returned directly to the caller, exposing internal error details. Replace `return f"Failed to extract text: {str(e)}"` with `return "Failed to extract text. Please verify the document is accessible and properly formatted."` and add `logging.exception("extract_document_text failed")` to log the full error server-side. This prevents information disclosure while maintaining debuggability. Verify by triggering a text-extraction error and confirming only a generic message is returned.
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
| 353 | ) |
| 354 | return message |
| 355 | except Exception as e: |
| 356 | return f"Failed to add footnote: {str(e)}" |
| 357 | |
| 358 | |
| 359 | async def customize_footnote_style(filename: str, numbering_format: str = "1, 2, 3", |
RemediationAI
The problem is that the exception message `str(e)` is returned directly to the caller, leaking internal error details. Replace `return f"Failed to add footnote: {str(e)}"` with `return "Failed to add footnote. Please verify the document and footnote parameters."` and add `logging.exception("add_footnote failed")` to capture the full error server-side. This prevents information leakage while preserving server-side debugging. Verify by triggering a footnote-addition error and confirming only a generic message is returned.
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
| 40 | except IOError as e: |
| 41 | return False, f"File {filepath} is not writeable: {str(e)}" |
| 42 | except Exception as e: |
| 43 | return False, f"Unknown error checking file permissions: {str(e)}" |
| 44 | |
| 45 | |
| 46 | def create_document_copy(source_path: str, dest_path: Optional[str] = None) -> Tuple[bool, str, Optional[str]]: |
RemediationAI
The problem is that the exception message `str(e)` is returned directly to the caller, exposing internal error details. Replace both `return False, f"File {filepath} is not writeable: {str(e)}"` and `return False, f"Unknown error checking file permissions: {str(e)}"` with generic messages like `return False, "File is not accessible. Please check permissions."` and add `logging.exception("check_file_permissions failed")` to log the full error internally. This hides implementation details from the caller while preserving server-side debugging. Verify by triggering permission errors and confirming only generic messages are returned.
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
| 124 | return True, f"Document is protected with {protection_type} protection" |
| 125 | |
| 126 | except Exception as e: |
| 127 | return False, f"Error verifying protection: {str(e)}" |
| 128 | |
| 129 | |
| 130 | def is_section_editable(doc_path: str, section_name: str) -> bool: |
RemediationAI
The problem is that the exception message `str(e)` is returned directly to the caller, leaking internal error details. Replace `return False, f"Error verifying protection: {str(e)}"` with `return False, "Error verifying protection. Please ensure the document is accessible."` and add `logging.exception("verify_protection failed")` to log the full error server-side. This prevents information disclosure while maintaining debuggability. Verify by triggering a protection-verification error and confirming only a generic message is returned.
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
| 458 | else: |
| 459 | return f"No occurrences of '{find_text}' found." |
| 460 | except Exception as e: |
| 461 | return f"Failed to search and replace: {str(e)}" |
| 462 | |
| 463 | async def insert_header_near_text_tool(filename: str, target_text: str = None, header_title: str = "", position: str = 'after', header_style: str = 'Heading 1', target_paragraph_index: int = None) -> str: |
| 464 | """Insert a header (with specified style) before or after the target paragraph. Specify by text or paragraph index.""" |
RemediationAI
The problem is that the exception message `str(e)` is returned directly to the caller, exposing internal error details. Replace `return f"Failed to search and replace: {str(e)}"` with `return "Failed to search and replace. Please verify the search text and document format."` and add `logging.exception("search_and_replace failed")` to capture the full error server-side. This prevents information leakage while preserving server-side debugging. Verify by triggering a search-and-replace error and confirming only a generic message is returned.
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
| 237 | else: |
| 238 | return f"Header '{header_title}' (style: {header_style}) inserted {position} the target paragraph." |
| 239 | except Exception as e: |
| 240 | return f"Failed to insert header: {str(e)}" |
| 241 | |
| 242 | |
| 243 | def insert_line_or_paragraph_near_text(doc_path: str, target_text: str = None, line_text: str = "", position: str = 'after', line_style: str = None, target_paragraph_index: int = None) -> str: |
RemediationAI
The problem is that the exception message `str(e)` is returned directly to the caller, leaking internal error details. Replace `return f"Failed to insert header: {str(e)}"` with `return "Failed to insert header. Please verify the target text and header parameters."` and add `logging.exception("insert_header_near_text failed")` to log the full error internally. This hides implementation details from the caller while preserving server-side debugging. Verify by triggering a header-insertion error and confirming only a generic message is returned.
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
| 117 | else: |
| 118 | return f"Failed to protect document {filename} with restricted editing" |
| 119 | except Exception as e: |
| 120 | return f"Failed to add restricted editing: {str(e)}" |
| 121 | |
| 122 | async def add_digital_signature(filename: str, signer_name: str, reason: Optional[str] = None) -> str: |
| 123 | """Add a digital signature to a Word document. |
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
| 412 | return f"Footnote style and numbering customized in {filename}" |
| 413 | except Exception as e: |
| 414 | return f"Failed to customize footnote style: {str(e)}" |
| 415 | |
| 416 | |
| 417 | async def delete_footnote_from_document(filename: str, footnote_id: Optional[int] = 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
| 274 | ) |
| 275 | return message |
| 276 | except Exception as e: |
| 277 | return f"Failed to add footnote: {str(e)}" |
| 278 | |
| 279 | |
| 280 | async def add_footnote_before_text(filename: str, search_text: str, footnote_text: 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
| 270 | outfile.write(encrypted_data) |
| 271 | return f"Failed to decrypt document {filename}: {str(e)}. Encrypted file restored." |
| 272 | else: |
| 273 | return f"Failed to decrypt document {filename}: {str(e)}. Could not restore encrypted file." |
| 274 | except Exception as restore_e: |
| 275 | return f"Failed to decrypt document {filename}: {str(e)}. Also failed to restore encrypted file: {str(restore_e)}" |
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
| 76 | else: |
| 77 | return f"Failed to encrypt document {filename}: {str(e)}. Could not restore original file." |
| 78 | except Exception as restore_e: |
| 79 | return f"Failed to encrypt document {filename}: {str(e)}. Also failed to restore original file: {str(restore_e)}" |
| 80 | |
| 81 | |
| 82 | async def add_restricted_editing(filename: str, password: str, editable_sections: List[str]) -> 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
| 72 | if 'original_data' in locals(): |
| 73 | with open(filename, "wb") as outfile: |
| 74 | outfile.write(original_data) |
| 75 | return f"Failed to encrypt document {filename}: {str(e)}. Original file restored." |
| 76 | else: |
| 77 | return f"Failed to encrypt document {filename}: {str(e)}. Could not restore original file." |
| 78 | except Exception as restore_e: |
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
| 311 | ) |
| 312 | return message |
| 313 | except Exception as e: |
| 314 | return f"Failed to add footnote: {str(e)}" |
| 315 | |
| 316 | |
| 317 | async def add_footnote_enhanced(filename: str, paragraph_index: int, footnote_text: 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
| 109 | return structure |
| 110 | except Exception as e: |
| 111 | return {"error": f"Failed to get document structure: {str(e)}"} |
| 112 | |
| 113 | |
| 114 | def find_paragraph_by_text(doc, text, partial_match=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
| 493 | else: |
| 494 | return f"Failed to merge cells horizontally. Check that indices are valid." |
| 495 | except Exception as e: |
| 496 | return f"Failed to merge cells horizontally: {str(e)}" |
| 497 | |
| 498 | |
| 499 | async def merge_table_cells_vertical(filename: str, table_index: int, col_index: 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
| 62 | properties = get_document_properties(filename) |
| 63 | return json.dumps(properties, indent=2) |
| 64 | except Exception as e: |
| 65 | return f"Failed to get document info: {str(e)}" |
| 66 | |
| 67 | |
| 68 | async def get_document_text(filename: str) -> 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
| 1019 | else: |
| 1020 | return f"Failed to format cell text. Check that indices are valid." |
| 1021 | except Exception as e: |
| 1022 | return f"Failed to format cell text: {str(e)}" |
| 1023 | |
| 1024 | |
| 1025 | async def set_table_cell_padding(filename: str, table_index: int, row_index: int, col_index: 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
| 67 | shutil.copy2(source_path, dest_path) |
| 68 | return True, f"Document copied to {dest_path}", dest_path |
| 69 | except Exception as e: |
| 70 | return False, f"Failed to copy document: {str(e)}", None |
| 71 | |
| 72 | |
| 73 | def ensure_docx_extension(filename: str) -> 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
| 268 | if 'encrypted_data' in locals(): |
| 269 | with open(filename, "wb") as outfile: |
| 270 | outfile.write(encrypted_data) |
| 271 | return f"Failed to decrypt document {filename}: {str(e)}. Encrypted file restored." |
| 272 | else: |
| 273 | return f"Failed to decrypt document {filename}: {str(e)}. Could not restore encrypted file." |
| 274 | except Exception as restore_e: |
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 | doc.save(filename) |
| 301 | return f"Page break added to {filename}." |
| 302 | except Exception as e: |
| 303 | return f"Failed to add page break: {str(e)}" |
| 304 | |
| 305 | |
| 306 | async def add_table_of_contents(filename: str, title: str = "Table of Contents", max_level: int = 3) -> 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
| 44 | return f"Document {filename} created successfully" |
| 45 | except Exception as e: |
| 46 | return f"Failed to create document: {str(e)}" |
| 47 | |
| 48 | |
| 49 | async def get_document_info(filename: str) -> 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
| 389 | return f"Table of contents with {len(headings)} entries added to {filename}" |
| 390 | except Exception as e: |
| 391 | return f"Failed to add table of contents: {str(e)}" |
| 392 | |
| 393 | |
| 394 | async def delete_paragraph(filename: str, paragraph_index: int) -> 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
| 486 | temp_file = working_file + '.tmp' |
| 487 | if os.path.exists(temp_file): |
| 488 | os.remove(temp_file) |
| 489 | return False, f"Error adding footnote: {str(e)}", None |
| 490 | |
| 491 | |
| 492 | def delete_footnote_robust( |
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
| 239 | return True, f"Document signature is valid. Signed by {signature_info.get('signer')} on {signature_info.get('timestamp')}" |
| 240 | |
| 241 | except Exception as e: |
| 242 | return False, f"Error verifying signature: {str(e)}" |
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
| 442 | else: |
| 443 | return f"Failed to merge cells. Check that indices are valid." |
| 444 | except Exception as e: |
| 445 | return f"Failed to merge cells: {str(e)}" |
| 446 | |
| 447 | |
| 448 | async def merge_table_cells_horizontal(filename: str, table_index: int, row_index: 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
| 162 | return results |
| 163 | except Exception as e: |
| 164 | return {"error": f"Failed to search for text: {str(e)}"} |
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
| 169 | doc.save(filename) |
| 170 | return f"Paragraph added to {filename}" |
| 171 | except Exception as e: |
| 172 | return f"Failed to add paragraph: {str(e)}" |
| 173 | |
| 174 | |
| 175 | async def add_table(filename: str, rows: int, cols: int, data: Optional[List[List[str]]] = None) -> 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
| 188 | with docx_zip.open('word/document.xml') as xml_file: |
| 189 | return xml_file.read().decode('utf-8') |
| 190 | except Exception as e: |
| 191 | return f"Failed to extract XML: {str(e)}" |
| 192 | |
| 193 | |
| 194 | def insert_header_near_text(doc_path: str, target_text: str = None, header_title: str = "", position: str = 'after', header_style: str = 'Heading 1', target_paragraph_index: int = None) -> 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
| 220 | return message |
| 221 | except Exception as e: |
| 222 | return f"Failed to verify document: {str(e)}" |
| 223 | |
| 224 | async def unprotect_document(filename: str, password: str) -> str: |
| 225 | """Remove password protection from a Word document. |
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
| 206 | target_doc.save(target_filename) |
| 207 | return f"Successfully merged {len(source_filenames)} documents into {target_filename}" |
| 208 | except Exception as e: |
| 209 | return f"Failed to merge documents: {str(e)}" |
| 210 | |
| 211 | |
| 212 | async def get_document_xml_tool(filename: str) -> 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
| 111 | return result |
| 112 | except Exception as e: |
| 113 | return f"Failed to list documents: {str(e)}" |
| 114 | |
| 115 | |
| 116 | async def copy_document(source_filename: str, destination_filename: Optional[str] = None) -> 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
| 732 | return is_valid, message, report |
| 733 | |
| 734 | except Exception as e: |
| 735 | return False, f"Error validating document: {str(e)}", report |
| 736 | |
| 737 | |
| 738 | # ============================================================================ |
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
| 60 | result = find_text(filename, text_to_find, match_case, whole_word) |
| 61 | return json.dumps(result, indent=2) |
| 62 | except Exception as e: |
| 63 | return f"Failed to search for text: {str(e)}" |
| 64 | |
| 65 | |
| 66 | async def convert_to_pdf(filename: str, output_filename: Optional[str] = None) -> 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
| 74 | outfile.write(original_data) |
| 75 | return f"Failed to encrypt document {filename}: {str(e)}. Original file restored." |
| 76 | else: |
| 77 | return f"Failed to encrypt document {filename}: {str(e)}. Could not restore original file." |
| 78 | except Exception as restore_e: |
| 79 | return f"Failed to encrypt document {filename}: {str(e)}. Also failed to restore original file: {str(restore_e)}" |
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
| 605 | else: |
| 606 | return f"Failed to set cell alignment. Check that indices are valid." |
| 607 | except Exception as e: |
| 608 | return f"Failed to set cell alignment: {str(e)}" |
| 609 | |
| 610 | |
| 611 | async def set_table_alignment_all(filename: str, table_index: 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
| 1109 | else: |
| 1110 | return f"Failed to set cell padding. Check that indices are valid." |
| 1111 | except Exception as e: |
| 1112 | return f"Failed to set cell padding: {str(e)}" |
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
| 36 | "is_heading": paragraph.style.name.startswith("Heading") if paragraph.style else False |
| 37 | } |
| 38 | except Exception as e: |
| 39 | return {"error": f"Failed to get paragraph text: {str(e)}"} |
| 40 | |
| 41 | |
| 42 | def find_text(doc_path: str, text_to_find: str, match_case: bool = True, whole_word: bool = False) -> Dict[str, Any]: |
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
| 335 | else: |
| 336 | return f"Failed to apply alternating row shading." |
| 337 | except Exception as e: |
| 338 | return f"Failed to apply alternating row shading: {str(e)}" |
| 339 | |
| 340 | |
| 341 | async def highlight_table_header(filename: str, table_index: 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
| 425 | doc.save(filename) |
| 426 | return f"Paragraph at index {paragraph_index} deleted successfully." |
| 427 | except Exception as e: |
| 428 | return f"Failed to delete paragraph: {str(e)}" |
| 429 | |
| 430 | |
| 431 | async def search_and_replace(filename: str, find_text: str, replace_text: str) -> 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
| 934 | else: |
| 935 | return f"Failed to set table auto-fit." |
| 936 | except Exception as e: |
| 937 | return f"Failed to set table auto-fit: {str(e)}" |
| 938 | |
| 939 | |
| 940 | async def format_table_cell_text(filename: str, table_index: int, row_index: int, col_index: 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
| 237 | return f"Converted {len(footnote_references)} footnotes to endnotes in {filename}" |
| 238 | except Exception as e: |
| 239 | return f"Failed to convert footnotes to endnotes: {str(e)}" |
| 240 | |
| 241 | |
| 242 | async def add_footnote_after_text(filename: str, search_text: str, footnote_text: 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
| 38 | pass |
| 39 | return True, "" |
| 40 | except IOError as e: |
| 41 | return False, f"File {filepath} is not writeable: {str(e)}" |
| 42 | except Exception as e: |
| 43 | return False, f"Unknown error checking file permissions: {str(e)}" |
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
| 414 | else: |
| 415 | return f"{list_type.capitalize()} list with {len(new_paras)} items inserted {position} the target paragraph." |
| 416 | except Exception as e: |
| 417 | return f"Failed to insert numbered list: {str(e)}" |
| 418 | |
| 419 | |
| 420 | def is_toc_paragraph(para): |
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
| 662 | else: |
| 663 | return f"Failed to set table alignment." |
| 664 | except Exception as e: |
| 665 | return f"Failed to set table alignment: {str(e)}" |
| 666 | |
| 667 | |
| 668 | async def set_table_column_width(filename: str, table_index: int, col_index: 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
| 292 | else: |
| 293 | return f"Line/paragraph inserted {position} the target paragraph with style '{style}'." |
| 294 | except Exception as e: |
| 295 | return f"Failed to insert line/paragraph: {str(e)}" |
| 296 | |
| 297 | |
| 298 | def add_bullet_numbering(paragraph, num_id=1, level=0): |
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
| 153 | doc.save(filename) |
| 154 | return f"Endnote added to paragraph {paragraph_index} in {filename}" |
| 155 | except Exception as e: |
| 156 | return f"Failed to add endnote: {str(e)}" |
| 157 | |
| 158 | |
| 159 | async def convert_footnotes_to_endnotes_in_document(filename: str) -> 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
| 890 | else: |
| 891 | return f"Failed to set table width." |
| 892 | except Exception as e: |
| 893 | return f"Failed to set table width: {str(e)}" |
| 894 | |
| 895 | |
| 896 | async def auto_fit_table_columns(filename: str, table_index: int) -> 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
| 607 | return True, message, details |
| 608 | |
| 609 | except Exception as e: |
| 610 | return False, f"Error deleting footnote: {str(e)}", None |
| 611 | |
| 612 | |
| 613 | def validate_document_footnotes(filename: str) -> Tuple[bool, str, Dict[str, Any]]: |
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
| 186 | doc.save(filename) |
| 187 | return f"Style '{style_name}' created successfully." |
| 188 | except Exception as e: |
| 189 | return f"Failed to create style: {str(e)}" |
| 190 | |
| 191 | |
| 192 | async def format_table(filename: str, table_index: 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
| 129 | doc.save(filename) |
| 130 | return f"Text '{target_text}' formatted successfully in paragraph {paragraph_index}." |
| 131 | except Exception as e: |
| 132 | return f"Failed to format text: {str(e)}" |
| 133 | |
| 134 | |
| 135 | async def create_custom_style(filename: str, style_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
| 93 | doc.save(filename) |
| 94 | return f"Footnote added to paragraph {paragraph_index} in {filename} (simplified approach)" |
| 95 | except Exception as e: |
| 96 | return f"Failed to add footnote: {str(e)}" |
| 97 | |
| 98 | |
| 99 | async def add_endnote_to_document(filename: str, paragraph_index: int, endnote_text: str) -> 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
| 216 | else: |
| 217 | return f"Document signature is valid. Signed by {signature_info.get('signer')} on {signature_info.get('timestamp')}" |
| 218 | except Exception as e: |
| 219 | return f"Error verifying signature: {str(e)}" |
| 220 | |
| 221 | return message |
| 222 | except Exception as e: |
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
| 272 | else: |
| 273 | return f"Failed to decrypt document {filename}: {str(e)}. Could not restore encrypted file." |
| 274 | except Exception as restore_e: |
| 275 | return f"Failed to decrypt document {filename}: {str(e)}. Also failed to restore encrypted file: {str(restore_e)}" |
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
| 35 | result = get_paragraph_text(filename, paragraph_index) |
| 36 | return json.dumps(result, indent=2) |
| 37 | except Exception as e: |
| 38 | return f"Failed to get paragraph text: {str(e)}" |
| 39 | |
| 40 | |
| 41 | async def find_text_in_document(filename: str, text_to_find: str, match_case: bool = True, whole_word: bool = False) -> 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
| 69 | except ImportError: |
| 70 | return False, "Missing msoffcrypto package required for encryption/decryption" |
| 71 | except Exception as e: |
| 72 | return False, f"Error decrypting document: {str(e)}" |
| 73 | |
| 74 | # Remove the protection metadata file |
| 75 | os.remove(metadata_path) |
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
| 181 | return f"PDF conversion not supported on {system} platform" |
| 182 | |
| 183 | except Exception as e: |
| 184 | return f"Failed to convert document to PDF: {str(e)}" |
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
| 216 | doc.save(filename) |
| 217 | return f"Table ({rows}x{cols}) added to {filename}" |
| 218 | except Exception as e: |
| 219 | return f"Failed to add table: {str(e)}" |
| 220 | |
| 221 | |
| 222 | async def add_picture(filename: str, image_path: str, width: Optional[float] = None) -> 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
| 111 | convert(filename, output_filename) |
| 112 | return f"Document successfully converted to PDF: {output_filename}" |
| 113 | except (ImportError, Exception) as e: |
| 114 | return f"Failed to convert document to PDF: {str(e)}\nNote: docx2pdf requires Microsoft Word to be installed." |
| 115 | |
| 116 | elif system in ["Linux", "Darwin"]: # Linux or macOS |
| 117 | errors = [] |
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
| 230 | else: |
| 231 | return f"Failed to format table at index {table_index}." |
| 232 | except Exception as e: |
| 233 | return f"Failed to format table: {str(e)}" |
| 234 | |
| 235 | |
| 236 | async def set_table_cell_shading(filename: str, table_index: int, row_index: 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.
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
| 153 | # Also install dependencies from requirements.txt if it exists |
| 154 | requirements_path = os.path.join(base_path, 'requirements.txt') |
| 155 | if os.path.exists(requirements_path): |
| 156 | subprocess.run([pip_path, 'install', '-r', requirements_path], check=True) |
| 157 | |
| 158 | print("Requirements installed successfully!") |
| 159 | except subprocess.CalledProcessError as e: |
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
| 128 | # Create virtual environment |
| 129 | try: |
| 130 | subprocess.run([sys.executable, '-m', 'venv', venv_path], check=True) |
| 131 | print("Virtual environment created successfully!") |
| 132 | except subprocess.CalledProcessError as e: |
| 133 | print(f"Error creating virtual environment: {e}") |
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
| 148 | # Install FastMCP package (standalone library) |
| 149 | subprocess.run([pip_path, 'install', 'fastmcp'], check=True) |
| 150 | # Install python-docx package |
| 151 | subprocess.run([pip_path, 'install', 'python-docx'], check=True) |
| 152 | |
| 153 | # Also install dependencies from requirements.txt if it exists |
| 154 | requirements_path = os.path.join(base_path, 'requirements.txt') |
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
| 334 | """ |
| 335 | print("\nInstalling word-document-server from PyPI...") |
| 336 | try: |
| 337 | subprocess.run([sys.executable, "-m", "pip", "install", "word-mcp-server"], check=True) |
| 338 | print("word-mcp-server successfully installed from PyPI!") |
| 339 | return True |
| 340 | except subprocess.CalledProcessError: |
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
| 146 | print("\nInstalling requirements...") |
| 147 | try: |
| 148 | # Install FastMCP package (standalone library) |
| 149 | subprocess.run([pip_path, 'install', 'fastmcp'], check=True) |
| 150 | # Install python-docx package |
| 151 | subprocess.run([pip_path, 'install', 'python-docx'], check=True) |
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.
Dockerfile never sets a non-root `USER` directive, so the CMD runs as root by default. Any RCE or library-level vulnerability exploited inside this container gets full privileges (MCP Top-10 R3). Add `USER <non-root>` before CMD / ENTRYPOINT in the final stage ā e.g. `USER 1000`, `USER nobody`, or `USER nonroot` on distroless.
Evidence
| 1 | # Generated by https://smithery.ai. See: https://smithery.ai/docs/build/project-config |
| 2 | # syntax=docker/dockerfile:1 |
| 3 | |
| 4 | # Use official Python runtime |
| 5 | FROM python:3.11-slim |
| 6 | |
| 7 | # Set working directory |
| 8 | WORKDIR /app |
| 9 | |
| 10 | # Install build dependencies |
| 11 | RUN apt-get update \ |
| 12 | && apt-get install -y --no-install-recommends build-essential \ |
| 13 | && rm -rf /var/lib/apt/lists/* |
| 14 | |
| 15 | # Copy project files |
| 16 | COPY . /app |
| 17 | |
| 18 | # Install Python dependencies |
| 19 | RUN pip install --no-cache-dir . |
| 20 | |
| 21 | # Default command |
| 22 | ENTRYPOINT ["word_mcp_ |
Remediation
Create and switch to a non-root user before the CMD / ENTRYPOINT: RUN adduser --system --uid 1000 app USER 1000 Or reuse the base image's shipped non-root account (e.g. `USER nobody`, `USER nonroot` on distroless). Multi-stage builds only need the USER directive in the final stage.
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 | # Import necessary Python standard libraries |
| 2 | import os |
| 3 | import json |
| 4 | import subprocess |
| 5 | import sys |
| 6 | import shutil |
| 7 | import platform |
| 8 | |
| 9 | def check_prerequisites(): |
| 10 | """ |
| 11 | Check if necessary prerequisites are installed |
| 12 | |
| 13 | Returns: |
| 14 | tuple: (python_ok, uv_installed, uvx_installed, word_server_installed) |
| 15 | """ |
| 16 | # Check Python version |
| 17 | python_version = sys.version_info |
| 18 | python_ok = python_version.major >= 3 and python_version.minor >= 8 |
| 19 | |
| 20 | # |
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 `"
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 | # Office-Word-MCP-Server |
| 2 | |
| 3 | [](https://smithery.ai/server/@GongRzhe/Office-Word-MCP-Server) |
| 4 | |
| 5 | A Model Context Protocol (MCP) server for creating, reading, and manipulating Microsoft Word documents. This server enables AI assistants to work with Word documents through a standardized interface, providing rich document editing capabilities. |
| 6 | |
| 7 | <a href="https://glama.ai/mcp/servers/@GongRzhe/Office-Word-MCP-Server"> |
| 8 | <img width |
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 `"
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 | { |
| 2 | "mcpServers": { |
| 3 | "word-document-server": { |
| 4 | "command": "/Users/gongzhe/GitRepos/Office-Word-MCP-Server/.venv/bin/python", |
| 5 | "args": [ |
| 6 | "/Users/gongzhe/GitRepos/Office-Word-MCP-Server/word_mcp_server.py" |
| 7 | ], |
| 8 | "env": { |
| 9 | "PYTHONPATH": "/Users/gongzhe/GitRepos/Office-Word-MCP-Server", |
| 10 | "MCP_TRANSPORT": "stdio" |
| 11 | } |
| 12 | } |
| 13 | } |
| 14 | } |
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 `"
GitHub Actions `uses:` reference is not pinned to a 40-character commit SHA. Tags (`@v4`) and branches (`@main`) are mutable ā a compromised maintainer or a tag rewrite can substitute malicious code into your CI pipeline silently. Pin to a SHA: `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab`. For readability, include the version as a trailing comment: `# v4.1.1`. Tools like `pinact` / `ratchet` automate this. Allowed unpinned forms (excluded by the rule): - Local actions `.
Evidence
| 11 | - uses: actions/checkout@v4 |
| 12 | |
| 13 | - name: Set up Python |
| 14 | uses: actions/setup-python@v5 |
| 15 | with: |
| 16 | python-version: "3.11" |
Remediation
Pin every `uses:` to a 40-character commit SHA. Trailing comment with the version helps reviewers: `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v4.1.1` Automate the migration with `pinact` (https://github.com/suzuki-shunsuke/pinact) or `ratchet` (https://github.com/sethvargo/ratchet). Add a `pinact run --check` pre-commit hook so future PRs stay pinned. Re-pin when the action releases a new version ā Dependabot can do this automatically with `version-update-strategy: inc
GitHub Actions `uses:` reference is not pinned to a 40-character commit SHA. Tags (`@v4`) and branches (`@main`) are mutable ā a compromised maintainer or a tag rewrite can substitute malicious code into your CI pipeline silently. Pin to a SHA: `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab`. For readability, include the version as a trailing comment: `# v4.1.1`. Tools like `pinact` / `ratchet` automate this. Allowed unpinned forms (excluded by the rule): - Local actions `.
Evidence
| 8 | publish: |
| 9 | runs-on: ubuntu-latest |
| 10 | steps: |
| 11 | - uses: actions/checkout@v4 |
| 12 | |
| 13 | - name: Set up Python |
| 14 | uses: actions/setup-python@v5 |
Remediation
Pin every `uses:` to a 40-character commit SHA. Trailing comment with the version helps reviewers: `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v4.1.1` Automate the migration with `pinact` (https://github.com/suzuki-shunsuke/pinact) or `ratchet` (https://github.com/sethvargo/ratchet). Add a `pinact run --check` pre-commit hook so future PRs stay pinned. Re-pin when the action releases a new version ā Dependabot can do this automatically with `version-update-strategy: inc
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 | # Import necessary Python standard libraries |
| 2 | import os |
| 3 | import json |
| 4 | import subprocess |
| 5 | import sys |
| 6 | import shutil |
| 7 | import platform |
| 8 | |
| 9 | def check_prerequisites(): |
| 10 | """ |
| 11 | Check if necessary prerequisites are installed |
| 12 | |
| 13 | Returns: |
| 14 | tuple: (python_ok, uv_installed, uvx_installed, word_server_installed) |
| 15 | """ |
| 16 | # Check Python version |
| 17 | python_version = sys.version_info |
| 18 | python_ok = python_version.major >= 3 and python_version.minor >= 8 |
| 19 | |
| 20 | # |
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 | """ |
| 2 | Unprotect document functionality for the Word Document Server. |
| 3 | |
| 4 | This module handles removing document protection. |
| 5 | """ |
| 6 | import os |
| 7 | import json |
| 8 | import hashlib |
| 9 | import tempfile |
| 10 | import shutil |
| 11 | from typing import Tuple, Optional |
| 12 | |
| 13 | def remove_protection_info(filename: str, password: Optional[str] = None) -> Tuple[bool, str]: |
| 14 | """ |
| 15 | Remove protection information from a document and decrypt it if necessary. |
| 16 | |
| 17 | Args: |
| 18 | filename: Path to the Word document |
| 19 | password: Password to veri |
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 | """ |
| 2 | Consolidated footnote functionality for Word documents. |
| 3 | This module combines all footnote implementations with proper namespace handling and Word compliance. |
| 4 | """ |
| 5 | |
| 6 | import os |
| 7 | import zipfile |
| 8 | import tempfile |
| 9 | from typing import Optional, Tuple, Dict, Any, List |
| 10 | from lxml import etree |
| 11 | from docx import Document |
| 12 | from docx.oxml.ns import qn |
| 13 | |
| 14 | # Namespace definitions |
| 15 | W_NS = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' |
| 16 | R_NS = 'http://schemas.openxmlformats.org/officeDocument/2006/relati |
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 | """ |
| 2 | Protection tools for Word Document Server. |
| 3 | |
| 4 | These tools handle document protection features such as |
| 5 | password protection, restricted editing, and digital signatures. |
| 6 | """ |
| 7 | import os |
| 8 | import hashlib |
| 9 | import datetime |
| 10 | import io |
| 11 | from typing import List, Optional, Dict, Any |
| 12 | from docx import Document |
| 13 | import msoffcrypto |
| 14 | |
| 15 | from word_document_server.utils.file_utils import check_file_writeable, ensure_docx_extension |
| 16 | |
| 17 | |
| 18 | |
| 19 | from word_document_server.core.protection import ( |
| 20 | add_protection_info, |
| 21 | verify |
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 | """ |
| 2 | Document protection functionality for Word Document Server. |
| 3 | """ |
| 4 | import os |
| 5 | import json |
| 6 | import hashlib |
| 7 | import datetime |
| 8 | from typing import Dict, List, Tuple, Optional, Any |
| 9 | |
| 10 | |
| 11 | def add_protection_info(doc_path: str, protection_type: str, password_hash: str, |
| 12 | sections: Optional[List[str]] = None, |
| 13 | signature_info: Optional[Dict[str, Any]] = None, |
| 14 | raw_password: Optional[str] = None) -> bool: |
| 15 | """ |
| 16 | Add document protection in |
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
MCP tool file registers a tool, performs a destructive sink (fs.unlink / shutil.rmtree / DROP TABLE / DELETE FROM / TRUNCATE / UPDATE ... SET / HTTP DELETE|PUT|PATCH / subprocess / exec / spawn), and emits no audit event anywhere in the file. Without an audit event, an investigator cannot answer "who deleted record X on day Y?" ā the irreversible action leaves no trail. Closes the OWASP MCP Top 10:2025 MCP08 (Lack of Audit and Telemetry) gap. Distinct from MCP-201 (no confirmation) and MCP-283
Evidence
| 1 | # Import necessary Python standard libraries |
| 2 | import os |
| 3 | import json |
| 4 | import subprocess |
| 5 | import sys |
| 6 | import shutil |
| 7 | import platform |
| 8 | |
| 9 | def check_prerequisites(): |
| 10 | """ |
| 11 | Check if necessary prerequisites are installed |
| 12 | |
| 13 | Returns: |
| 14 | tuple: (python_ok, uv_installed, uvx_installed, word_server_installed) |
| 15 | """ |
| 16 | # Check Python version |
| 17 | python_version = sys.version_info |
| 18 | python_ok = python_version.major >= 3 and python_version.minor >= 8 |
| 19 | |
| 20 | # |
Remediation
Add a structured audit-event emit immediately after every destructive sink. Minimum schema: actor, action, target, outcome, request_id. Python: from acme.audit import audit_log @mcp.tool() def delete_record(token: str, record_id: str) -> dict: actor = verify_token(token) db.execute("DELETE FROM records WHERE id = %s", (record_id,)) audit_log( actor=actor.sub, action="delete_record", target=record_id, outcome=
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
| 103 | # Apply style if available |
| 104 | if footnote_style: |
| 105 | try: |
| 106 | ref['paragraph'].style = footnote_style |
| 107 | except: |
| 108 | pass |
| 109 | count += 1 |
| 110 | return count |
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
| 124 | fn_id = fn.get(f'{{{W_NS}}}id') |
| 125 | if fn_id: |
| 126 | try: |
| 127 | used_ids.add(int(fn_id)) |
| 128 | except ValueError: |
| 129 | pass |
| 130 | |
| 131 | # Start from 2 to avoid reserved IDs |
| 132 | candidate_id = 2 |
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
| 370 | # Copy style if possible |
| 371 | try: |
| 372 | if paragraph.style: |
| 373 | p.style = paragraph.style.name |
| 374 | except: |
| 375 | pass |
| 376 | |
| 377 | # Copy tables |
| 378 | for table in doc.tables: |
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
| 128 | except: |
| 129 | # Fall back to default grid style |
| 130 | try: |
| 131 | new_table.style = 'Table Grid' |
| 132 | except: |
| 133 | pass |
| 134 | |
| 135 | # Copy cell contents |
| 136 | for i, row in enumerate(source_table.rows): |
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
| 249 | r = int(text_color[0:2], 16) |
| 250 | g = int(text_color[2:4], 16) |
| 251 | b = int(text_color[4:6], 16) |
| 252 | run.font.color.rgb = RGBColor(r, g, b) |
| 253 | except: |
| 254 | pass # Skip if color format is invalid |
| 255 | return True |
| 256 | except Exception as e: |
| 257 | print(f"Error highlighting header row: {e}") |
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
| 182 | # Try to match the style if possible |
| 183 | try: |
| 184 | if paragraph.style and paragraph.style.name in target_doc.styles: |
| 185 | new_paragraph.style = target_doc.styles[paragraph.style.name] |
| 186 | except: |
| 187 | pass |
| 188 | |
| 189 | # Copy run formatting |
| 190 | for i, run in enumerate(paragraph.runs): |
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.