1+ #! /bin/bash
2+ # Security audit script for FlowerPower
3+
4+ set -e
5+
6+ echo " 🔒 Running comprehensive security audit for FlowerPower..."
7+ echo " =================================================="
8+
9+ # Check if we're in the right directory
10+ if [[ ! -f " pyproject.toml" ]]; then
11+ echo " ❌ Error: pyproject.toml not found. Please run from the project root."
12+ exit 1
13+ fi
14+
15+ # Colors for output
16+ RED=' \033[0;31m'
17+ GREEN=' \033[0;32m'
18+ YELLOW=' \033[1;33m'
19+ NC=' \033[0m' # No Color
20+
21+ # Track results
22+ ISSUES_FOUND=0
23+ TOOLS_RUN=0
24+
25+ echo -e " \n${YELLOW} 1. Running Bandit (security linter)...${NC} "
26+ TOOLS_RUN=$(( TOOLS_RUN + 1 ))
27+ if uv run bandit -r src/ -f json -o bandit-report.json || true ; then
28+ # Parse results
29+ BANDIT_ISSUES=$( python3 -c "
30+ import json
31+ import sys
32+ try:
33+ with open('bandit-report.json') as f:
34+ data = json.load(f)
35+ high_issues = len([r for r in data['results'] if r['issue_severity'] == 'HIGH'])
36+ medium_issues = len([r for r in data['results'] if r['issue_severity'] == 'MEDIUM'])
37+ if high_issues > 0 or medium_issues > 0:
38+ print(f'Found {high_issues} high and {medium_issues} medium severity issues')
39+ sys.exit(1)
40+ else:
41+ print('No high or medium severity issues found')
42+ sys.exit(0)
43+ except FileNotFoundError:
44+ print('Bandit report not found')
45+ sys.exit(1)
46+ except Exception as e:
47+ print(f'Error parsing bandit report: {e}')
48+ sys.exit(1)
49+ " 2> /dev/null)
50+ BANDIT_EXIT_CODE=$?
51+ echo " ${BANDIT_ISSUES} "
52+ if [[ $BANDIT_EXIT_CODE -eq 1 ]]; then
53+ ISSUES_FOUND=$(( ISSUES_FOUND + 1 ))
54+ echo -e " ${RED} ❌ Bandit found security issues${NC} "
55+ else
56+ echo -e " ${GREEN} ✅ Bandit: No critical issues found${NC} "
57+ fi
58+ else
59+ echo -e " ${RED} ❌ Bandit failed to run${NC} "
60+ ISSUES_FOUND=$(( ISSUES_FOUND + 1 ))
61+ fi
62+
63+ echo -e " \n${YELLOW} 2. Running Safety (dependency vulnerability scanner)...${NC} "
64+ TOOLS_RUN=$(( TOOLS_RUN + 1 ))
65+ if uv run safety check --json --output safety-report.json || true ; then
66+ # Parse results
67+ SAFETY_ISSUES=$( python3 -c "
68+ import json
69+ import sys
70+ try:
71+ with open('safety-report.json') as f:
72+ data = json.load(f)
73+ vulnerabilities = data.get('vulnerabilities', [])
74+ if vulnerabilities:
75+ print(f'Found {len(vulnerabilities)} dependency vulnerabilities')
76+ for vuln in vulnerabilities[:3]: # Show first 3
77+ print(f' - {vuln.get(\" package_name\" , \" unknown\" )}: {vuln.get(\" vulnerability_id\" , \" unknown\" )}')
78+ sys.exit(1)
79+ else:
80+ print('No dependency vulnerabilities found')
81+ sys.exit(0)
82+ except FileNotFoundError:
83+ print('Safety report not found')
84+ sys.exit(0)
85+ except Exception as e:
86+ print(f'No vulnerabilities detected')
87+ sys.exit(0)
88+ " 2> /dev/null)
89+ SAFETY_EXIT_CODE=$?
90+ echo " ${SAFETY_ISSUES} "
91+ if [[ $SAFETY_EXIT_CODE -eq 1 ]]; then
92+ ISSUES_FOUND=$(( ISSUES_FOUND + 1 ))
93+ echo -e " ${RED} ❌ Safety found vulnerable dependencies${NC} "
94+ else
95+ echo -e " ${GREEN} ✅ Safety: No vulnerable dependencies found${NC} "
96+ fi
97+ else
98+ echo -e " ${GREEN} ✅ Safety: No vulnerable dependencies found${NC} "
99+ fi
100+
101+ echo -e " \n${YELLOW} 3. Running Ruff with security rules...${NC} "
102+ TOOLS_RUN=$(( TOOLS_RUN + 1 ))
103+ if uv run ruff check src/ --select=S --format=json --output-file=ruff-security.json || true ; then
104+ RUFF_ISSUES=$( python3 -c "
105+ import json
106+ import sys
107+ try:
108+ with open('ruff-security.json') as f:
109+ data = json.load(f)
110+ if data:
111+ print(f'Found {len(data)} security issues')
112+ sys.exit(1)
113+ else:
114+ print('No security issues found')
115+ sys.exit(0)
116+ except FileNotFoundError:
117+ print('No security issues found')
118+ sys.exit(0)
119+ except Exception as e:
120+ print('No security issues found')
121+ sys.exit(0)
122+ " 2> /dev/null)
123+ RUFF_EXIT_CODE=$?
124+ echo " ${RUFF_ISSUES} "
125+ if [[ $RUFF_EXIT_CODE -eq 1 ]]; then
126+ ISSUES_FOUND=$(( ISSUES_FOUND + 1 ))
127+ echo -e " ${RED} ❌ Ruff found security issues${NC} "
128+ else
129+ echo -e " ${GREEN} ✅ Ruff: No security issues found${NC} "
130+ fi
131+ else
132+ echo -e " ${GREEN} ✅ Ruff: No security issues found${NC} "
133+ fi
134+
135+ echo -e " \n${YELLOW} 4. Running type checking with MyPy...${NC} "
136+ TOOLS_RUN=$(( TOOLS_RUN + 1 ))
137+ if uv run mypy src/flowerpower --config-file=pyproject.toml --no-error-summary || true ; then
138+ echo -e " ${GREEN} ✅ MyPy: Type checking passed${NC} "
139+ else
140+ echo -e " ${YELLOW} ⚠️ MyPy: Type checking issues found (non-blocking)${NC} "
141+ fi
142+
143+ # Summary
144+ echo -e " \n=================================================="
145+ echo -e " ${YELLOW} Security Audit Summary${NC} "
146+ echo " =================================================="
147+ echo " Tools run: ${TOOLS_RUN} /4"
148+
149+ if [[ $ISSUES_FOUND -eq 0 ]]; then
150+ echo -e " ${GREEN} 🎉 No critical security issues found!${NC} "
151+ echo -e " ${GREEN} All security checks passed.${NC} "
152+ exit 0
153+ else
154+ echo -e " ${RED} ⚠️ Found $ISSUES_FOUND security issue(s) that require attention.${NC} "
155+ echo " "
156+ echo " Review the detailed reports:"
157+ [[ -f " bandit-report.json" ]] && echo " - Bandit report: bandit-report.json"
158+ [[ -f " safety-report.json" ]] && echo " - Safety report: safety-report.json"
159+ [[ -f " ruff-security.json" ]] && echo " - Ruff security: ruff-security.json"
160+ echo " "
161+ echo -e " ${YELLOW} Please address these issues before deploying to production.${NC} "
162+ exit 1
163+ fi
0 commit comments