diff --git a/.gitea/workflows/security.yml b/.gitea/workflows/security.yml index 61a8e2a..fccd04b 100644 --- a/.gitea/workflows/security.yml +++ b/.gitea/workflows/security.yml @@ -28,9 +28,6 @@ jobs: with: token: ${{ secrets.CI_BOT_TOKEN }} - - name: Install jq (for parsing JSON) - run: sudo apt-get update && sudo apt-get install -y jq - - name: Set up Python uses: actions/setup-python@v4 with: @@ -39,41 +36,24 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip --root-user-action=ignore - # Pin safety to a v2 line where `check --json` is stable - pip install --root-user-action=ignore "safety>=2,<3" bandit semgrep + pip install --root-user-action=ignore safety bandit semgrep - name: Run Safety check run: | - if [ -f requirements.txt ]; then - safety check -r requirements.txt --json --output safety-report.json || true - else - echo "[]" > safety-report.json - fi - if [ -f requirements-dev.txt ]; then - safety check -r requirements-dev.txt --json --output safety-dev-report.json || true - else - echo "[]" > safety-dev-report.json - fi + safety check -r requirements.txt --json --output safety-report.json || true + safety check -r requirements-dev.txt --json --output safety-dev-report.json || true - name: Run Bandit security scan run: | - if [ -d src ]; then - bandit -r src/ -f json -o bandit-report.json || true - else - echo '{"results":[]}' > bandit-report.json - fi + bandit -r src/ -f json -o bandit-report.json || true - name: Run Semgrep security scan run: | - if [ -d src ]; then - semgrep --config=auto src/ --json --output=semgrep-report.json || true - else - echo '{"results":[]}' > semgrep-report.json - fi + semgrep --config=auto src/ --json --output=semgrep-report.json || true - name: Upload security reports uses: actions/upload-artifact@v3 - with: + with: name: security-reports-${{ github.run_number }} path: | safety-report.json @@ -85,43 +65,28 @@ jobs: run: | echo "๐Ÿ” Checking for critical vulnerabilities..." - # --- Safety results (robust to array/object formats) --- - summarize_safety () { - f="$1" - if [ -f "$f" ]; then - count=$(jq -r ' - if type=="array" then length - else ((.vulnerabilities // []) | length) - end - ' "$f" 2>/dev/null || echo "0") - - if [ "${count:-0}" -gt 0 ]; then - echo "โš ๏ธ Found $count dependency vulnerabilities in $f" - jq -r ' - if type=="array" then - .[] | "- \(.package_name // .name) \(.installed_version // ""): \(.vulnerability_id // .advisory // "N/A")" - else - (.vulnerabilities // [])[] | "- \(.package_name) \(.installed_version): \(.vulnerability_id)" - end - ' "$f" || true - else - echo "โœ… No dependency vulnerabilities found in $f" - fi + # Check Safety results + if [ -f safety-report.json ]; then + critical_count=$(jq '.vulnerabilities | length' safety-report.json 2>/dev/null || echo "0") + if [ "$critical_count" -gt 0 ]; then + echo "โš ๏ธ Found $critical_count dependency vulnerabilities" + jq '.vulnerabilities[] | "- \(.package_name) \(.installed_version): \(.vulnerability_id)"' safety-report.json + else + echo "โœ… No dependency vulnerabilities found" fi - } - summarize_safety safety-report.json - summarize_safety safety-dev-report.json + fi - # --- Bandit results (count HIGH severity properly) --- + # Check Bandit results if [ -f bandit-report.json ]; then - high_severity=$(jq '[.results[]? | select(.issue_severity=="HIGH")] | length' bandit-report.json 2>/dev/null || echo "0") - if [ "${high_severity:-0}" -gt 0 ]; then + high_severity=$(jq '.results[] | select(.issue_severity == "HIGH") | length' bandit-report.json 2>/dev/null | wc -l) + if [ "$high_severity" -gt 0 ]; then echo "โš ๏ธ Found $high_severity high-severity security issues" else echo "โœ… No high-severity security issues found" fi fi + # License compliance check license-check: name: License Compliance @@ -133,9 +98,6 @@ jobs: with: token: ${{ secrets.CI_BOT_TOKEN }} - - name: Install jq (for parsing JSON) - run: sudo apt-get update && sudo apt-get install -y jq - - name: Set up Python uses: actions/setup-python@v4 with: @@ -145,9 +107,7 @@ jobs: run: | python -m pip install --upgrade pip --root-user-action=ignore pip install --root-user-action=ignore pip-licenses - if [ -f requirements.txt ]; then - pip install --root-user-action=ignore -r requirements.txt - fi + pip install --root-user-action=ignore -r requirements.txt - name: Check licenses run: | @@ -155,10 +115,11 @@ jobs: pip-licenses --format=json --output-file=licenses.json pip-licenses --format=markdown --output-file=licenses.md - # Check for potentially problematic licenses + # Check for problematic licenses problematic_licenses=("GPL" "AGPL" "LGPL") + for license in "${problematic_licenses[@]}"; do - if grep -iq "$license" licenses.json; then + if grep -i "$license" licenses.json; then echo "โš ๏ธ Found potentially problematic license: $license" fi done @@ -167,7 +128,7 @@ jobs: - name: Upload license report uses: actions/upload-artifact@v3 - with: + with: name: license-report-${{ github.run_number }} path: | licenses.json @@ -184,26 +145,25 @@ jobs: with: token: ${{ secrets.CI_BOT_TOKEN }} - - name: Install jq (for parsing JSON) - run: sudo apt-get update && sudo apt-get install -y jq - - name: Set up Python uses: actions/setup-python@v4 with: python-version: ${{ env.PYTHON_VERSION }} + - name: Install pip-check-updates equivalent + run: | + python -m pip install --upgrade pip --root-user-action=ignore + pip install --root-user-action=ignore pip-review + - name: Check for outdated packages run: | echo "๐Ÿ“ฆ Checking for outdated packages..." - if [ -f requirements.txt ]; then - python -m pip install --upgrade pip --root-user-action=ignore - pip install --root-user-action=ignore -r requirements.txt - fi + pip install --root-user-action=ignore -r requirements.txt pip list --outdated --format=json > outdated-packages.json || true - if [ -s outdated-packages.json ] && [ "$(cat outdated-packages.json)" != "[]" ]; then + if [ -s outdated-packages.json ]; then echo "๐Ÿ“‹ Outdated packages found:" - jq -r '.[] | "- \(.name): \(.version) -> \(.latest_version)"' outdated-packages.json + cat outdated-packages.json | jq -r '.[] | "- \(.name): \(.version) -> \(.latest_version)"' else echo "โœ… All packages are up to date" fi @@ -213,39 +173,45 @@ jobs: run: | if [ -s outdated-packages.json ] && [ "$(cat outdated-packages.json)" != "[]" ]; then echo "๐Ÿ“ Creating dependency update issue..." + + # Create issue body cat > issue-body.md << 'EOF' -## ๐Ÿ“ฆ Dependency Updates Available + ## ๐Ÿ“ฆ Dependency Updates Available -The following packages have updates available: -EOF - jq -r '.[] | "- **\(.name)**: \(.version) โ†’ \(.latest_version)"' outdated-packages.json >> issue-body.md + The following packages have updates available: + + EOF + + cat outdated-packages.json | jq -r '.[] | "- **\(.name)**: \(.version) โ†’ \(.latest_version)"' >> issue-body.md + cat >> issue-body.md << 'EOF' -## ๐Ÿ” Security Impact + ## ๐Ÿ” Security Impact -Please review each update for: -- Security fixes -- Breaking changes -- Compatibility issues + Please review each update for: + - Security fixes + - Breaking changes + - Compatibility issues -## โœ… Action Items + ## โœ… Action Items -- [ ] Review changelog for each package -- [ ] Test updates in development environment -- [ ] Update requirements.txt -- [ ] Run full test suite -- [ ] Deploy to staging for validation + - [ ] Review changelog for each package + - [ ] Test updates in development environment + - [ ] Update requirements.txt + - [ ] Run full test suite + - [ ] Deploy to staging for validation ---- -*This issue was automatically created by the security workflow.* -EOF + --- + *This issue was automatically created by the security workflow.* + EOF + echo "Issue body created. In a real implementation, you would create a Gitea issue here." cat issue-body.md fi - name: Upload dependency reports uses: actions/upload-artifact@v3 - with: + with: name: dependency-reports-${{ github.run_number }} path: | outdated-packages.json @@ -271,47 +237,33 @@ EOF run: | python -m pip install --upgrade pip --root-user-action=ignore pip install --root-user-action=ignore radon xenon vulture - if [ -f requirements.txt ]; then - pip install --root-user-action=ignore -r requirements.txt - fi + pip install --root-user-action=ignore -r requirements.txt - name: Calculate code complexity run: | echo "๐Ÿ“Š Calculating code complexity..." - if [ -d src ]; then - radon cc src/ --json > complexity-report.json - radon mi src/ --json > maintainability-report.json - echo "๐Ÿ” Complexity Summary:" - radon cc src/ --average - echo "๐Ÿ”ง Maintainability Summary:" - radon mi src/ - else - echo "{}" > complexity-report.json - echo "{}" > maintainability-report.json - echo "No src/ directory found; skipping detailed radon output." - fi + radon cc src/ --json > complexity-report.json + radon mi src/ --json > maintainability-report.json + + echo "๐Ÿ” Complexity Summary:" + radon cc src/ --average + + echo "๐Ÿ”ง Maintainability Summary:" + radon mi src/ - name: Find dead code run: | echo "๐Ÿงน Checking for dead code..." - if [ -d src ]; then - vulture src/ --json > dead-code-report.json || true - else - echo "[]" > dead-code-report.json - fi + vulture src/ --json > dead-code-report.json || true - name: Check for code smells run: | echo "๐Ÿ‘ƒ Checking for code smells..." - if [ -d src ]; then - xenon --max-absolute B --max-modules A --max-average A src/ || true - else - echo "No src/ directory found; skipping xenon." - fi + xenon --max-absolute B --max-modules A --max-average A src/ || true - name: Upload quality reports uses: actions/upload-artifact@v3 - with: + with: name: code-quality-reports-${{ github.run_number }} path: | complexity-report.json @@ -322,13 +274,10 @@ EOF security-summary: name: Security Summary runs-on: ubuntu-latest - needs: [dependency-scan, license-check, code-quality] + needs: [dependency-scan, docker-security-scan, license-check, code-quality] if: always() steps: - - name: Install jq (for parsing JSON) - run: sudo apt-get update && sudo apt-get install -y jq - - name: Download all artifacts uses: actions/download-artifact@v3 @@ -344,36 +293,30 @@ EOF echo "## ๐Ÿ“Š Results" >> security-summary.md echo "" >> security-summary.md - # Dependency scan results (support array/object formats) - if ls security-reports-*/safety-report.json >/dev/null 2>&1; then - vuln_count=$(jq -s ' - def countfile: - if type=="array" then length - else ((.vulnerabilities // []) | length) - end; - add | (if type=="number" then . else 0 end) - ' security-reports-*/safety-report.json 2>/dev/null || echo "0") - if [ "${vuln_count:-0}" -eq 0 ]; then + # Dependency scan results + if [ -f security-reports-*/safety-report.json ]; then + vuln_count=$(jq '.vulnerabilities | length' security-reports-*/safety-report.json 2>/dev/null || echo "0") + if [ "$vuln_count" -eq 0 ]; then echo "- โœ… **Dependency Scan**: No vulnerabilities found" >> security-summary.md else - echo "- โš ๏ธ **Dependency Scan**: ${vuln_count} vulnerabilities found" >> security-summary.md + echo "- โš ๏ธ **Dependency Scan**: $vuln_count vulnerabilities found" >> security-summary.md fi else echo "- โ“ **Dependency Scan**: Results not available" >> security-summary.md fi - # Docker scan results (Trivy removed) + # Docker scan results (removed Trivy) echo "- โญ๏ธ **Docker Scan**: Skipped (Trivy removed)" >> security-summary.md # License check results - if ls license-report-*/licenses.json >/dev/null 2>&1; then + if [ -f license-report-*/licenses.json ]; then echo "- โœ… **License Check**: Completed" >> security-summary.md else echo "- โ“ **License Check**: Results not available" >> security-summary.md fi # Code quality results - if ls code-quality-reports-*/complexity-report.json >/dev/null 2>&1; then + if [ -f code-quality-reports-*/complexity-report.json ]; then echo "- โœ… **Code Quality**: Analyzed" >> security-summary.md else echo "- โ“ **Code Quality**: Results not available" >> security-summary.md @@ -388,6 +331,6 @@ EOF - name: Upload security summary uses: actions/upload-artifact@v3 - with: + with: name: security-summary-${{ github.run_number }} path: security-summary.md