Ryan Malloy 43f36b60fb
Some checks are pending
Bump version / Bump version and create changelog with commitizen (push) Waiting to run
Tests / test (macos-latest, 3.10) (push) Waiting to run
Tests / test (macos-latest, 3.11) (push) Waiting to run
Tests / test (macos-latest, 3.12) (push) Waiting to run
Tests / test (ubuntu-latest, 3.10) (push) Waiting to run
Tests / test (ubuntu-latest, 3.11) (push) Waiting to run
Tests / test (ubuntu-latest, 3.12) (push) Waiting to run
Tests / test (windows-latest, 3.10) (push) Waiting to run
Tests / test (windows-latest, 3.11) (push) Waiting to run
Tests / test (windows-latest, 3.12) (push) Waiting to run
Tests / security (push) Waiting to run
feat: add comprehensive security, license, health, and requirements analysis tools
- Add security vulnerability scanning with OSV and GitHub advisories integration
- Add license compatibility analysis with SPDX normalization and risk assessment
- Add package health scoring across 7 categories with GitHub metrics integration
- Add requirements file analysis supporting multiple formats (requirements.txt, pyproject.toml, etc.)
- Fix search functionality MCP wrapper and error handling
- Fix Python compatibility checking parameter order issue
- Fix package recommendations NoneType handling
- Add 8 new MCP tool endpoints for enhanced analysis capabilities

This brings the total to 37 comprehensive MCP tools across 8 categories for complete PyPI package analysis and management.
2025-09-06 10:28:57 -06:00

147 lines
6.0 KiB
Python

"""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}"]
}