387 lines
14 KiB
YAML
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
|