Initial commit: LLM-DS optimizer framework with data files excluded
This commit is contained in:
306
scripts/security_scan.py
Normal file
306
scripts/security_scan.py
Normal file
@@ -0,0 +1,306 @@
|
||||
"""Security scanning script using Bandit and pip-audit.
|
||||
|
||||
This script runs security scans to identify vulnerabilities.
|
||||
Note: Requires bandit and pip-audit to be installed.
|
||||
"""
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def run_bandit(output_dir: Path) -> bool:
|
||||
"""
|
||||
Run Bandit security scanner.
|
||||
|
||||
Args:
|
||||
output_dir: Directory to save results
|
||||
|
||||
Returns:
|
||||
True if scan completed successfully
|
||||
"""
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
json_output = output_dir / "bandit_report.json"
|
||||
txt_output = output_dir / "bandit_report.txt"
|
||||
|
||||
print("Running Bandit security scanner...")
|
||||
print("=" * 80)
|
||||
|
||||
try:
|
||||
# Run Bandit with JSON and text output
|
||||
result = subprocess.run(
|
||||
[
|
||||
sys.executable, "-m", "bandit",
|
||||
"-r", "llmds",
|
||||
"-f", "json",
|
||||
"-o", str(json_output),
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
|
||||
# Also generate text report
|
||||
subprocess.run(
|
||||
[
|
||||
sys.executable, "-m", "bandit",
|
||||
"-r", "llmds",
|
||||
"-f", "txt",
|
||||
"-o", str(txt_output),
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
|
||||
# Parse results
|
||||
if json_output.exists():
|
||||
with open(json_output) as f:
|
||||
bandit_data = json.load(f)
|
||||
|
||||
# Count issues by severity
|
||||
metrics = bandit_data.get("metrics", {})
|
||||
total = metrics.get("_totals", {})
|
||||
|
||||
print(f"\nBandit Results:")
|
||||
print(f" HIGH: {total.get('SEVERITY.HIGH', 0)} issues")
|
||||
print(f" MEDIUM: {total.get('SEVERITY.MEDIUM', 0)} issues")
|
||||
print(f" LOW: {total.get('SEVERITY.LOW', 0)} issues")
|
||||
print(f" Total: {total.get('CONFIDENCE.HIGH', 0)} high confidence issues")
|
||||
|
||||
# List high severity issues
|
||||
high_severity = [
|
||||
issue for issue in bandit_data.get("results", [])
|
||||
if issue.get("issue_severity") == "HIGH"
|
||||
]
|
||||
|
||||
if high_severity:
|
||||
print(f"\n HIGH Severity Issues ({len(high_severity)}):")
|
||||
for issue in high_severity[:10]: # Show first 10
|
||||
print(f" - {issue.get('test_id')}: {issue.get('test_name')}")
|
||||
print(f" File: {issue.get('filename')}:{issue.get('line_number')}")
|
||||
|
||||
print(f"\n Full report: {txt_output}")
|
||||
print(f" JSON report: {json_output}")
|
||||
|
||||
return total.get("SEVERITY.HIGH", 0) == 0
|
||||
else:
|
||||
print(" Warning: Bandit JSON output not found")
|
||||
return False
|
||||
|
||||
except FileNotFoundError:
|
||||
print(" Error: Bandit not installed. Install with: pip install bandit[toml]")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f" Error running Bandit: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def run_pip_audit(output_dir: Path) -> bool:
|
||||
"""
|
||||
Run pip-audit to check for known vulnerabilities in dependencies.
|
||||
|
||||
Args:
|
||||
output_dir: Directory to save results
|
||||
|
||||
Returns:
|
||||
True if no HIGH/CRITICAL vulnerabilities found
|
||||
"""
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
json_output = output_dir / "pip_audit_report.json"
|
||||
txt_output = output_dir / "pip_audit_report.txt"
|
||||
|
||||
print("\nRunning pip-audit security scanner...")
|
||||
print("=" * 80)
|
||||
|
||||
try:
|
||||
# Run pip-audit
|
||||
result = subprocess.run(
|
||||
[
|
||||
sys.executable, "-m", "pip_audit",
|
||||
"--format", "json",
|
||||
"--output", str(json_output),
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
|
||||
# Also generate text output
|
||||
subprocess.run(
|
||||
[
|
||||
sys.executable, "-m", "pip_audit",
|
||||
"--format", "text",
|
||||
"--output", str(txt_output),
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
|
||||
# Parse results
|
||||
if json_output.exists():
|
||||
with open(json_output) as f:
|
||||
audit_data = json.load(f)
|
||||
|
||||
vulnerabilities = audit_data.get("vulnerabilities", [])
|
||||
high_critical = [
|
||||
v for v in vulnerabilities
|
||||
if v.get("aliases", [{}])[0].get("severity", "").upper() in ["HIGH", "CRITICAL"]
|
||||
]
|
||||
|
||||
print(f"\npip-audit Results:")
|
||||
print(f" Total vulnerabilities: {len(vulnerabilities)}")
|
||||
print(f" HIGH/CRITICAL: {len(high_critical)}")
|
||||
|
||||
if high_critical:
|
||||
print(f"\n HIGH/CRITICAL Vulnerabilities:")
|
||||
for vuln in high_critical[:10]: # Show first 10
|
||||
package = vuln.get("name", "unknown")
|
||||
severity = vuln.get("aliases", [{}])[0].get("severity", "UNKNOWN")
|
||||
print(f" - {package}: {severity}")
|
||||
if "versions" in vuln:
|
||||
print(f" Affected versions: {vuln['versions']}")
|
||||
|
||||
print(f"\n Full report: {txt_output}")
|
||||
print(f" JSON report: {json_output}")
|
||||
|
||||
return len(high_critical) == 0
|
||||
else:
|
||||
print(" Warning: pip-audit JSON output not found")
|
||||
# Check if there were errors
|
||||
if result.stderr:
|
||||
print(f" Error output: {result.stderr}")
|
||||
return False
|
||||
|
||||
except FileNotFoundError:
|
||||
print(" Error: pip-audit not installed. Install with: pip install pip-audit")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f" Error running pip-audit: {e}")
|
||||
if result.stderr:
|
||||
print(f" Error output: {result.stderr}")
|
||||
return False
|
||||
|
||||
|
||||
def generate_sbom(output_dir: Path) -> bool:
|
||||
"""
|
||||
Generate Software Bill of Materials (SBOM) using pip-audit.
|
||||
|
||||
Args:
|
||||
output_dir: Directory to save SBOM
|
||||
|
||||
Returns:
|
||||
True if SBOM generated successfully
|
||||
"""
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
sbom_output = output_dir / "sbom.json"
|
||||
|
||||
print("\nGenerating SBOM (Software Bill of Materials)...")
|
||||
print("=" * 80)
|
||||
|
||||
try:
|
||||
# Try to generate SBOM using pip-audit (if supported)
|
||||
# Note: pip-audit may need additional flags for SBOM generation
|
||||
result = subprocess.run(
|
||||
[
|
||||
sys.executable, "-m", "pip_audit",
|
||||
"--format", "json",
|
||||
"--output", str(sbom_output),
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
|
||||
if sbom_output.exists():
|
||||
print(f" SBOM generated: {sbom_output}")
|
||||
print(" Note: For CycloneDX format, consider using cyclonedx-bom or pip-tools")
|
||||
return True
|
||||
else:
|
||||
print(" Warning: SBOM generation may require additional tools")
|
||||
print(" Consider using: cyclonedx-py or pip-tools for full SBOM")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f" Error generating SBOM: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
"""Run all security scans."""
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description="Run security scans")
|
||||
parser.add_argument(
|
||||
"--output-dir",
|
||||
type=Path,
|
||||
default=Path("audit/security"),
|
||||
help="Directory for security scan results (default: audit/security)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--skip-bandit",
|
||||
action="store_true",
|
||||
help="Skip Bandit scan",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--skip-pip-audit",
|
||||
action="store_true",
|
||||
help="Skip pip-audit scan",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--skip-sbom",
|
||||
action="store_true",
|
||||
help="Skip SBOM generation",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
print("Security Scanning")
|
||||
print("=" * 80)
|
||||
print(f"Output directory: {args.output_dir}")
|
||||
print()
|
||||
|
||||
results = {}
|
||||
|
||||
# Run Bandit
|
||||
if not args.skip_bandit:
|
||||
results["bandit"] = run_bandit(args.output_dir)
|
||||
else:
|
||||
print("Skipping Bandit scan")
|
||||
|
||||
# Run pip-audit
|
||||
if not args.skip_pip_audit:
|
||||
results["pip_audit"] = run_pip_audit(args.output_dir)
|
||||
else:
|
||||
print("Skipping pip-audit scan")
|
||||
|
||||
# Generate SBOM
|
||||
if not args.skip_sbom:
|
||||
results["sbom"] = generate_sbom(args.output_dir)
|
||||
else:
|
||||
print("Skipping SBOM generation")
|
||||
|
||||
# Summary
|
||||
print("\n" + "=" * 80)
|
||||
print("Summary")
|
||||
print("=" * 80)
|
||||
|
||||
all_passed = all(results.values())
|
||||
|
||||
for tool, passed in results.items():
|
||||
status = "✓ PASSED" if passed else "✗ FAILED"
|
||||
print(f" {tool}: {status}")
|
||||
|
||||
if all_passed:
|
||||
print("\n✓ All security scans passed!")
|
||||
return 0
|
||||
else:
|
||||
print("\n✗ Some security issues found. Please review reports.")
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
|
||||
Reference in New Issue
Block a user