Files
git.stella-ops.org/.gitea/workflows/sast-scan.yml

387 lines
14 KiB
YAML

# .gitea/workflows/sast-scan.yml
# Static Application Security Testing (SAST) Workflow
# Sprint: CI/CD Enhancement - Security Scanning (Tier 2)
#
# Purpose: Detect security vulnerabilities in source code through static analysis
# - Code injection vulnerabilities
# - Authentication/authorization issues
# - Cryptographic weaknesses
# - Data exposure risks
# - OWASP Top 10 detection
#
# Supported Languages: C#/.NET, JavaScript/TypeScript, Python, YAML, Dockerfile
#
# PLACEHOLDER: Choose your SAST scanner implementation below
# Options:
# 1. Semgrep - Fast, open-source, good .NET support
# 2. CodeQL - GitHub's analysis engine
# 3. SonarQube - Enterprise-grade with dashboards
# 4. Snyk Code - Commercial with good accuracy
name: SAST Scanning
on:
push:
branches: [main, develop]
paths:
- 'src/**'
- '*.csproj'
- '*.cs'
- '*.ts'
- '*.js'
- '*.py'
- 'Dockerfile*'
pull_request:
paths:
- 'src/**'
- '*.csproj'
- '*.cs'
- '*.ts'
- '*.js'
- '*.py'
- 'Dockerfile*'
schedule:
- cron: '30 3 * * 1' # Weekly on Monday at 3:30 AM UTC
workflow_dispatch:
inputs:
scan_level:
description: 'Scan thoroughness level'
type: choice
options:
- quick
- standard
- comprehensive
default: standard
fail_on_findings:
description: 'Fail workflow on findings'
type: boolean
default: true
env:
DOTNET_VERSION: '10.0.100'
TZ: UTC
jobs:
# ===========================================================================
# PLACEHOLDER SAST IMPLEMENTATION
# ===========================================================================
#
# IMPORTANT: Configure your preferred SAST tool by uncommenting ONE of the
# implementation options below. Each option includes the necessary steps
# and configuration for that specific tool.
#
# ===========================================================================
sast-scan:
name: SAST Analysis
runs-on: ubuntu-22.04
timeout-minutes: 30
permissions:
security-events: write
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
# =========================================================================
# PLACEHOLDER: Uncomment your preferred SAST tool configuration
# =========================================================================
- name: SAST Scan Placeholder
run: |
echo "::notice::SAST scanning placeholder - configure your scanner below"
echo ""
echo "Available SAST options:"
echo ""
echo "1. SEMGREP (Recommended for open-source)"
echo " Uncomment the Semgrep section below"
echo " - Fast, accurate, good .NET support"
echo " - Free for open-source projects"
echo ""
echo "2. CODEQL (GitHub native)"
echo " Uncomment the CodeQL section below"
echo " - Deep analysis capabilities"
echo " - Native GitHub integration"
echo ""
echo "3. SONARQUBE (Enterprise)"
echo " Uncomment the SonarQube section below"
echo " - Comprehensive dashboards"
echo " - Technical debt tracking"
echo ""
echo "4. SNYK CODE (Commercial)"
echo " Uncomment the Snyk section below"
echo " - High accuracy"
echo " - Good IDE integration"
# =========================================================================
# OPTION 1: SEMGREP
# =========================================================================
# Uncomment the following section to use Semgrep:
#
# - name: Run Semgrep
# uses: returntocorp/semgrep-action@v1
# with:
# config: >-
# p/default
# p/security-audit
# p/owasp-top-ten
# p/csharp
# p/javascript
# p/typescript
# p/python
# p/docker
# env:
# SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
# =========================================================================
# OPTION 2: CODEQL
# =========================================================================
# Uncomment the following section to use CodeQL:
#
# - name: Initialize CodeQL
# uses: github/codeql-action/init@v3
# with:
# languages: csharp, javascript
# queries: security-and-quality
#
# - name: Build for CodeQL
# run: |
# dotnet build src/StellaOps.sln --configuration Release
#
# - name: Perform CodeQL Analysis
# uses: github/codeql-action/analyze@v3
# with:
# category: "/language:csharp"
# =========================================================================
# OPTION 3: SONARQUBE
# =========================================================================
# Uncomment the following section to use SonarQube:
#
# - name: SonarQube Scan
# uses: SonarSource/sonarqube-scan-action@master
# env:
# SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
# SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
# with:
# args: >
# -Dsonar.projectKey=stellaops
# -Dsonar.sources=src/
# -Dsonar.exclusions=**/bin/**,**/obj/**,**/node_modules/**
# =========================================================================
# OPTION 4: SNYK CODE
# =========================================================================
# Uncomment the following section to use Snyk Code:
#
# - name: Setup Snyk
# uses: snyk/actions/setup@master
#
# - name: Snyk Code Test
# run: snyk code test --sarif-file-output=snyk-code.sarif
# env:
# SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
# continue-on-error: true
#
# - name: Upload Snyk results
# uses: github/codeql-action/upload-sarif@v3
# with:
# sarif_file: snyk-code.sarif
# ===========================================================================
# .NET SECURITY ANALYSIS (built-in)
# ===========================================================================
dotnet-security:
name: .NET Security Analysis
runs-on: ubuntu-22.04
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
include-prerelease: true
- name: Restore packages
run: dotnet restore src/StellaOps.sln
- name: Run Security Code Analysis
run: |
# Enable nullable reference types warnings as errors for security
dotnet build src/StellaOps.sln \
--configuration Release \
--no-restore \
/p:TreatWarningsAsErrors=false \
/p:EnableNETAnalyzers=true \
/p:AnalysisLevel=latest \
/warnaserror:CA2100,CA2109,CA2119,CA2153,CA2300,CA2301,CA2302,CA2305,CA2310,CA2311,CA2312,CA2315,CA2321,CA2322,CA2326,CA2327,CA2328,CA2329,CA2330,CA2350,CA2351,CA2352,CA2353,CA2354,CA2355,CA2356,CA2361,CA2362,CA3001,CA3002,CA3003,CA3004,CA3005,CA3006,CA3007,CA3008,CA3009,CA3010,CA3011,CA3012,CA3061,CA3075,CA3076,CA3077,CA3147,CA5350,CA5351,CA5358,CA5359,CA5360,CA5361,CA5362,CA5363,CA5364,CA5365,CA5366,CA5367,CA5368,CA5369,CA5370,CA5371,CA5372,CA5373,CA5374,CA5375,CA5376,CA5377,CA5378,CA5379,CA5380,CA5381,CA5382,CA5383,CA5384,CA5385,CA5386,CA5387,CA5388,CA5389,CA5390,CA5391,CA5392,CA5393,CA5394,CA5395,CA5396,CA5397,CA5398,CA5399,CA5400,CA5401,CA5402,CA5403 \
2>&1 | tee build-security.log || true
- name: Parse security warnings
run: |
echo "### .NET Security Analysis" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
# Count security warnings
SECURITY_WARNINGS=$(grep -E "warning CA[235][0-9]{3}" build-security.log | wc -l || echo "0")
echo "- Security warnings found: $SECURITY_WARNINGS" >> $GITHUB_STEP_SUMMARY
if [[ $SECURITY_WARNINGS -gt 0 ]]; then
echo "" >> $GITHUB_STEP_SUMMARY
echo "<details><summary>Security Warnings</summary>" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
grep -E "warning CA[235][0-9]{3}" build-security.log | head -50 >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "</details>" >> $GITHUB_STEP_SUMMARY
fi
- name: Upload security log
uses: actions/upload-artifact@v4
if: always()
with:
name: sast-dotnet-security-log
path: build-security.log
retention-days: 14
# ===========================================================================
# DEPENDENCY VULNERABILITY CHECK
# ===========================================================================
dependency-check:
name: Dependency Vulnerabilities
runs-on: ubuntu-22.04
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
include-prerelease: true
- name: Run vulnerability audit
run: |
echo "### Dependency Vulnerability Audit" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
# Check for known vulnerabilities in NuGet packages
dotnet list src/StellaOps.sln package --vulnerable --include-transitive 2>&1 | tee vuln-report.txt || true
# Parse results
VULN_COUNT=$(grep -c "has the following vulnerable packages" vuln-report.txt || echo "0")
if [[ $VULN_COUNT -gt 0 ]]; then
echo "::warning::Found $VULN_COUNT projects with vulnerable dependencies"
echo "- Projects with vulnerabilities: $VULN_COUNT" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "<details><summary>Vulnerability Report</summary>" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
cat vuln-report.txt >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "</details>" >> $GITHUB_STEP_SUMMARY
else
echo "No known vulnerabilities found in dependencies." >> $GITHUB_STEP_SUMMARY
fi
- name: Upload vulnerability report
uses: actions/upload-artifact@v4
if: always()
with:
name: sast-vulnerability-report
path: vuln-report.txt
retention-days: 14
# ===========================================================================
# DOCKERFILE SECURITY LINTING
# ===========================================================================
dockerfile-lint:
name: Dockerfile Security
runs-on: ubuntu-22.04
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Find Dockerfiles
id: find
run: |
DOCKERFILES=$(find . -name "Dockerfile*" -type f ! -path "./node_modules/*" | jq -R -s -c 'split("\n") | map(select(length > 0))')
COUNT=$(echo "$DOCKERFILES" | jq 'length')
echo "files=$DOCKERFILES" >> $GITHUB_OUTPUT
echo "count=$COUNT" >> $GITHUB_OUTPUT
echo "Found $COUNT Dockerfiles"
- name: Install Hadolint
if: steps.find.outputs.count != '0'
run: |
wget -qO hadolint https://github.com/hadolint/hadolint/releases/download/v2.12.0/hadolint-Linux-x86_64
chmod +x hadolint
sudo mv hadolint /usr/local/bin/
- name: Lint Dockerfiles
if: steps.find.outputs.count != '0'
run: |
echo "### Dockerfile Security Lint" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
TOTAL_ISSUES=0
for dockerfile in $(echo '${{ steps.find.outputs.files }}' | jq -r '.[]'); do
echo "Linting: $dockerfile"
ISSUES=$(hadolint --format json "$dockerfile" 2>/dev/null || echo "[]")
ISSUE_COUNT=$(echo "$ISSUES" | jq 'length')
TOTAL_ISSUES=$((TOTAL_ISSUES + ISSUE_COUNT))
if [[ $ISSUE_COUNT -gt 0 ]]; then
echo "- **$dockerfile**: $ISSUE_COUNT issues" >> $GITHUB_STEP_SUMMARY
fi
done
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Total issues found: $TOTAL_ISSUES**" >> $GITHUB_STEP_SUMMARY
if [[ $TOTAL_ISSUES -gt 0 ]] && [[ "${{ github.event.inputs.fail_on_findings }}" == "true" ]]; then
echo "::warning::Found $TOTAL_ISSUES Dockerfile security issues"
fi
# ===========================================================================
# SUMMARY
# ===========================================================================
summary:
name: SAST Summary
runs-on: ubuntu-22.04
needs: [sast-scan, dotnet-security, dependency-check, dockerfile-lint]
if: always()
steps:
- name: Generate summary
run: |
echo "## SAST Scan Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Check | Status |" >> $GITHUB_STEP_SUMMARY
echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY
echo "| SAST Analysis | ${{ needs.sast-scan.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| .NET Security | ${{ needs.dotnet-security.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Dependency Check | ${{ needs.dependency-check.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Dockerfile Lint | ${{ needs.dockerfile-lint.result }} |" >> $GITHUB_STEP_SUMMARY
- name: Check for failures
if: |
github.event.inputs.fail_on_findings == 'true' &&
(needs.sast-scan.result == 'failure' ||
needs.dotnet-security.result == 'failure' ||
needs.dependency-check.result == 'failure')
run: exit 1