pypi-query-mcp/pypi_query_mcp/tools/security_tools.py

147 lines
6 KiB
Python
Raw Normal View History

"""Security vulnerability scanning tools for PyPI packages."""
import logging
from typing import Any, Dict, List, Optional
from ..core.exceptions import InvalidPackageNameError, NetworkError, SearchError
from ..tools.security import bulk_security_scan, scan_package_security
logger = logging.getLogger(__name__)
async def scan_pypi_package_security(
package_name: str,
version: Optional[str] = None,
include_dependencies: bool = True,
severity_filter: Optional[str] = None
) -> Dict[str, Any]:
"""
Scan a PyPI package for security vulnerabilities.
This tool performs comprehensive security vulnerability scanning of PyPI packages,
checking against multiple vulnerability databases including OSV (Open Source Vulnerabilities),
GitHub Security Advisories, and analyzing package metadata for security indicators.
Args:
package_name: Name of the package to scan for vulnerabilities
version: Specific version to scan (optional, defaults to latest version)
include_dependencies: Whether to scan package dependencies for vulnerabilities
severity_filter: Filter results by severity level (low, medium, high, critical)
Returns:
Dictionary containing comprehensive security scan results including:
- Total vulnerability count and severity breakdown
- Direct package vulnerabilities vs dependency vulnerabilities
- Risk score and level assessment (minimal, low, medium, high, critical)
- Detailed vulnerability information with IDs, descriptions, and references
- Package metadata security analysis
- Actionable security recommendations
Raises:
InvalidPackageNameError: If package name is empty or invalid
PackageNotFoundError: If package is not found on PyPI
NetworkError: For network-related errors
SearchError: If security scanning fails
"""
if not package_name or not package_name.strip():
raise InvalidPackageNameError(package_name)
logger.info(f"MCP tool: Scanning security for package {package_name}")
try:
result = await scan_package_security(
package_name=package_name,
version=version,
include_dependencies=include_dependencies,
severity_filter=severity_filter
)
logger.info(f"MCP tool: Security scan completed for {package_name} - found {result.get('security_summary', {}).get('total_vulnerabilities', 0)} vulnerabilities")
return result
except (InvalidPackageNameError, NetworkError, SearchError) as e:
logger.error(f"Error scanning security for {package_name}: {e}")
return {
"error": f"Security scan failed: {e}",
"error_type": type(e).__name__,
"package": package_name,
"version": version,
"scan_timestamp": "",
"security_summary": {
"total_vulnerabilities": 0,
"direct_vulnerabilities": 0,
"dependency_vulnerabilities": 0,
"severity_breakdown": {"critical": 0, "high": 0, "medium": 0, "low": 0, "unknown": 0},
"risk_score": 0,
"risk_level": "unknown",
},
"vulnerabilities": {"direct": [], "dependencies": []},
"metadata_analysis": {},
"recommendations": [f"❌ Security scan failed: {e}"],
"scan_details": {
"sources_checked": [],
"dependencies_scanned": False,
"scan_completion": "error",
}
}
async def bulk_scan_package_security(
package_names: List[str],
include_dependencies: bool = False,
severity_threshold: str = "medium"
) -> Dict[str, Any]:
"""
Perform bulk security scanning of multiple PyPI packages.
This tool scans multiple packages simultaneously for security vulnerabilities,
providing a consolidated report with summary statistics and prioritized
recommendations for addressing security issues across your package ecosystem.
Args:
package_names: List of package names to scan for vulnerabilities
include_dependencies: Whether to include dependency vulnerability scanning
severity_threshold: Minimum severity level to report (low, medium, high, critical)
Returns:
Dictionary containing bulk scan results including:
- Summary statistics (total packages, packages with vulnerabilities, high-risk packages)
- Detailed scan results for each package
- Prioritized recommendations for security remediation
- Scan timestamp and completion status
Raises:
ValueError: If package_names list is empty
NetworkError: For network-related errors during scanning
SearchError: If bulk scanning fails
"""
if not package_names:
raise ValueError("Package names list cannot be empty")
logger.info(f"MCP tool: Starting bulk security scan of {len(package_names)} packages")
try:
result = await bulk_security_scan(
package_names=package_names,
include_dependencies=include_dependencies,
severity_threshold=severity_threshold
)
logger.info(f"MCP tool: Bulk security scan completed - {result.get('summary', {}).get('packages_with_vulnerabilities', 0)} packages have vulnerabilities")
return result
except (ValueError, NetworkError, SearchError) as e:
logger.error(f"Error in bulk security scan: {e}")
return {
"error": f"Bulk security scan failed: {e}",
"error_type": type(e).__name__,
"summary": {
"total_packages": len(package_names),
"packages_with_vulnerabilities": 0,
"total_vulnerabilities": 0,
"high_risk_packages": [],
"scan_timestamp": ""
},
"detailed_results": {},
"recommendations": [f"❌ Bulk security scan failed: {e}"]
}