Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion markitdown_mcp/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,15 @@
def with_timeout(timeout_seconds: int = 30) -> Any:
"""Decorator to add timeout protection to functions using threading."""

def decorator(func: Any) -> Any:

Check notice on line 50 in markitdown_mcp/server.py

View workflow job for this annotation

GitHub Actions / Code Issue Annotations

Function "decorator" missing docstring
@functools.wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> Any:

Check notice on line 52 in markitdown_mcp/server.py

View workflow job for this annotation

GitHub Actions / Code Issue Annotations

Function "wrapper" missing docstring
import threading

result: list[Any] = [None]
exception: list[Exception | None] = [None]

def target() -> None:

Check notice on line 58 in markitdown_mcp/server.py

View workflow job for this annotation

GitHub Actions / Code Issue Annotations

Function "target" missing docstring
try:
result[0] = func(*args, **kwargs)
except Exception as e:
Expand Down Expand Up @@ -130,7 +130,7 @@
SecurityError: If XML contains dangerous constructs
"""
try:
with Path(file_path).open(encoding="utf-8", errors="ignore") as f:

Check notice on line 133 in markitdown_mcp/server.py

View workflow job for this annotation

GitHub Actions / Security Review Annotations

Consider using 'with open()' for safer file handling: with Path(file_path).open(encoding="utf-8", errors="ignore") as f
content = f.read()

# Check for dangerous XML patterns
Expand Down Expand Up @@ -181,7 +181,7 @@
SecurityError: If JSON is too deeply nested or complex
"""
try:
with Path(file_path).open(encoding="utf-8", errors="ignore") as f:

Check notice on line 184 in markitdown_mcp/server.py

View workflow job for this annotation

GitHub Actions / Security Review Annotations

Consider using 'with open()' for safer file handling: with Path(file_path).open(encoding="utf-8", errors="ignore") as f
content = f.read()

# Check file size first
Expand All @@ -198,7 +198,7 @@
raise SecurityError("Security violation: JSON recursion depth limit exceeded") from e

# Check nesting depth
def check_depth(obj: Any, current_depth: int = 0, max_depth: int = 30) -> None:

Check notice on line 201 in markitdown_mcp/server.py

View workflow job for this annotation

GitHub Actions / Code Issue Annotations

Function "check_depth" missing docstring
if current_depth > max_depth:
raise SecurityError("Security violation: JSON recursion depth limit exceeded")

Expand Down Expand Up @@ -240,7 +240,7 @@
raise SecurityError("Security violation: CSV file too large")

# Analyze CSV structure
with Path(file_path).open(encoding="utf-8", errors="ignore") as f:

Check notice on line 243 in markitdown_mcp/server.py

View workflow job for this annotation

GitHub Actions / Security Review Annotations

Consider using 'with open()' for safer file handling: with Path(file_path).open(encoding="utf-8", errors="ignore") as f
# Read first few lines to check structure
sample = f.read(1024 * 1024) # 1MB sample

Expand Down Expand Up @@ -337,7 +337,7 @@
"""

@functools.wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> Any:

Check notice on line 340 in markitdown_mcp/server.py

View workflow job for this annotation

GitHub Actions / Code Issue Annotations

Function "wrapper" missing docstring
start_time = time.time()
try:
result = func(*args, **kwargs)
Expand Down Expand Up @@ -439,7 +439,7 @@


@with_timeout(30) # type: ignore[misc]
def safe_convert_with_limits(markitdown_instance: MarkItDown, file_path: str) -> Any:

Check warning on line 442 in markitdown_mcp/server.py

View workflow job for this annotation

GitHub Actions / Code Issue Annotations

Function "safe_convert_with_limits" has high complexity (13)
"""Safely convert a file with timeout and recursion protection.

Args:
Expand Down Expand Up @@ -467,7 +467,7 @@
# Check if file might contain binary data in text format
file_path_obj = Path(validated_file_path)
if file_path_obj.exists():
with Path(validated_file_path).open("rb") as f:

Check notice on line 470 in markitdown_mcp/server.py

View workflow job for this annotation

GitHub Actions / Security Review Annotations

Consider using 'with open()' for safer file handling: with Path(validated_file_path).open("rb") as f
data = f.read(1024) # Read first 1KB to check

# If it's a text file but contains significant binary content
Expand Down Expand Up @@ -538,7 +538,6 @@
Path(sanitized_file_path).unlink(missing_ok=True)


@normalize_timing
def validate_and_sanitize_path(
file_path: str, allowed_dirs: list[str] | None = None
) -> tuple[Path, bool]:
Expand Down Expand Up @@ -886,7 +885,7 @@
id=request.id, error={"code": -32603, "message": f"Internal error: {e!s}"}
)

async def convert_file_tool(

Check warning on line 888 in markitdown_mcp/server.py

View workflow job for this annotation

GitHub Actions / Code Issue Annotations

Function "convert_file_tool" has high complexity (12)
self, request_id: JSONRPCId, arguments: dict[str, Any]
) -> MCPResponse:
"""Convert a single file to Markdown."""
Expand Down Expand Up @@ -1057,7 +1056,7 @@
},
)

async def convert_directory_tool(

Check warning on line 1059 in markitdown_mcp/server.py

View workflow job for this annotation

GitHub Actions / Code Issue Annotations

Function "convert_directory_tool" has high complexity (18)
self, request_id: JSONRPCId, arguments: dict[str, Any]
) -> MCPResponse:
"""Convert all supported files in a directory."""
Expand Down Expand Up @@ -1159,10 +1158,10 @@
markdown_content = result.text_content

# Write file asynchronously
def write_file(

Check notice on line 1161 in markitdown_mcp/server.py

View workflow job for this annotation

GitHub Actions / Code Issue Annotations

Function "write_file" missing docstring
path: str = output_path, content: str = markdown_content
) -> None:
with Path(path).open("w", encoding="utf-8") as f:

Check notice on line 1164 in markitdown_mcp/server.py

View workflow job for this annotation

GitHub Actions / Security Review Annotations

Consider using 'with open()' for safer file handling: with Path(path).open("w", encoding="utf-8") as f
f.write(content)

await asyncio.get_event_loop().run_in_executor(None, write_file)
Expand Down Expand Up @@ -1250,7 +1249,7 @@
if response.error is not None:
response_dict["error"] = response.error

print(json.dumps(response_dict), flush=True)

Check warning on line 1252 in markitdown_mcp/server.py

View workflow job for this annotation

GitHub Actions / Code Issue Annotations

Debug print statement found: print(json.dumps(response_dict), flush=True)

except json.JSONDecodeError as e:
logger.error(f"Invalid JSON received: {e}")
Expand Down
42 changes: 36 additions & 6 deletions tests/unit/test_additional_coverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,17 @@

from markitdown_mcp.server import (
MarkItDownMCPServer,
MCPRequest,
SecurityError,
validate_xml_security,
validate_json_security,
extract_text_from_binary,
normalize_timing,
safe_convert_with_limits,
sanitize_unicode_text,
with_timeout,
secure_compare,
validate_base64,
safe_convert_with_limits,
validate_file_content_security,
validate_json_security,
validate_xml_security,
with_timeout,
)


Expand Down Expand Up @@ -177,7 +178,7 @@
def test_timeout_decorator_success(self):
"""Test timeout decorator with successful operation."""
@with_timeout(timeout_seconds=1)
def quick_operation():

Check notice on line 181 in tests/unit/test_additional_coverage.py

View workflow job for this annotation

GitHub Actions / Code Issue Annotations

Function "quick_operation" missing docstring
return "success"

result = quick_operation()
Expand All @@ -186,7 +187,7 @@
def test_timeout_decorator_exception(self):
"""Test timeout decorator when function raises exception."""
@with_timeout(timeout_seconds=1)
def failing_operation():

Check notice on line 190 in tests/unit/test_additional_coverage.py

View workflow job for this annotation

GitHub Actions / Code Issue Annotations

Function "failing_operation" missing docstring
raise ValueError("Test error")

with pytest.raises(ValueError, match="Test error"):
Expand All @@ -195,12 +196,41 @@
def test_timeout_decorator_no_timeout(self):
"""Test timeout decorator with no timeout specified."""
@with_timeout()
def operation():

Check notice on line 199 in tests/unit/test_additional_coverage.py

View workflow job for this annotation

GitHub Actions / Code Issue Annotations

Function "operation" missing docstring
return "no timeout"

result = operation()
assert result == "no timeout"

def test_secure_compare(self):
"""Test constant-time comparison helper."""
assert secure_compare("same", "same") is True
assert secure_compare("same", "different") is False

def test_normalize_timing_success(self):
"""Test timing normalization decorator returns successful results."""
@normalize_timing
def operation():

Check notice on line 213 in tests/unit/test_additional_coverage.py

View workflow job for this annotation

GitHub Actions / Code Issue Annotations

Function "operation" missing docstring
return "normalized"

start_time = time.time()
result = operation()

assert result == "normalized"
assert time.time() - start_time >= 0.05

def test_normalize_timing_exception(self):
"""Test timing normalization decorator re-raises exceptions."""
@normalize_timing
def operation():
raise ValueError("normalized error")

start_time = time.time()
with pytest.raises(ValueError, match="normalized error"):
operation()

assert time.time() - start_time >= 0.05

def test_validate_base64_valid(self):
"""Test base64 validation with valid data."""
import base64
Expand Down Expand Up @@ -320,4 +350,4 @@
result = validate_file_content_security(temp_path)
assert result == temp_path # Should return original for non-special files
finally:
Path(temp_path).unlink(missing_ok=True)
Path(temp_path).unlink(missing_ok=True)
Loading