Skip to content

Security Scanning

Security Scanning #15

Workflow file for this run

name: Security Scanning
on:
push:
branches: [ main ]
paths:
- 'src/**'
- 'package.json'
- 'bun.lock'
- '.github/workflows/security.yml'
schedule:
# Run security scan weekly on Monday at 2 AM UTC
- cron: '0 2 * * 1'
workflow_dispatch:
permissions:
contents: read
security-events: write
jobs:
dependency-audit:
name: Dependency Audit
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: '1.2.14'
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Run Bun audit
run: |
echo "## 🔒 Security Audit Report" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
# Note: Bun doesn't have a built-in audit command yet, so we use alternative tools
# Check with npm audit for known vulnerabilities (works with package.json)
if bunx better-npm-audit audit --level moderate > audit-results.txt 2>&1; then
echo "✅ No security vulnerabilities found!" >> $GITHUB_STEP_SUMMARY
else
echo "⚠️ Security vulnerabilities detected:" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
cat audit-results.txt >> $GITHUB_STEP_SUMMARY || true
echo '```' >> $GITHUB_STEP_SUMMARY
fi
continue-on-error: true
- name: Check for known vulnerabilities
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
scan-ref: '.'
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH,MEDIUM'
ignore-unfixed: true
- name: Upload Trivy results to GitHub Security
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: 'trivy-results.sarif'
license-check:
name: License Compliance
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: '1.2.14'
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Check licenses
run: |
echo "## 📋 License Compliance Report" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
# Install license checker
bunx license-checker --summary --excludePrivatePackages > license-summary.txt
# Display summary
echo "### License Summary" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
cat license-summary.txt >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
# Check for problematic licenses
PROBLEMATIC_LICENSES="GPL|AGPL|LGPL|SSPL|EUPL"
if bunx license-checker --excludePrivatePackages --onlyAllow "MIT;Apache-2.0;BSD-2-Clause;BSD-3-Clause;ISC;CC0-1.0;CC-BY-3.0;CC-BY-4.0;Unlicense;WTFPL" > /dev/null 2>&1; then
echo "" >> $GITHUB_STEP_SUMMARY
echo "✅ All licenses are compatible!" >> $GITHUB_STEP_SUMMARY
else
echo "" >> $GITHUB_STEP_SUMMARY
echo "⚠️ Some licenses may need review" >> $GITHUB_STEP_SUMMARY
fi
continue-on-error: true
semgrep:
name: Semgrep SAST
runs-on: ubuntu-latest
container:
image: semgrep/semgrep
if: (github.actor != 'dependabot[bot]')
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run Semgrep CI
run: |
# Run Semgrep with local rules (no app token required)
semgrep ci \
--config=auto \
--config=p/security-audit \
--config=p/typescript \
--config=p/javascript \
--config=p/nodejs \
--config=p/owasp-top-ten \
--json \
--output=semgrep-results.json || true
# Generate summary
echo "## 🔍 SAST Scan Results" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ -f semgrep-results.json ]; then
FINDINGS=$(jq '.results | length' semgrep-results.json)
if [ "$FINDINGS" -eq 0 ]; then
echo "✅ No security issues found by Semgrep!" >> $GITHUB_STEP_SUMMARY
else
echo "⚠️ Found $FINDINGS potential security issues" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
# Show top 5 findings
echo "### Top Findings:" >> $GITHUB_STEP_SUMMARY
jq -r '.results[:5] | .[] | "- **\(.check_id)**: \(.path) (line \(.start.line))"' semgrep-results.json >> $GITHUB_STEP_SUMMARY 2>/dev/null || true
fi
fi
- name: Generate SARIF
if: always()
run: |
# Convert to SARIF for GitHub Security tab
semgrep ci \
--config=auto \
--config=p/security-audit \
--config=p/typescript \
--config=p/javascript \
--config=p/nodejs \
--config=p/owasp-top-ten \
--sarif \
--output=semgrep.sarif || true
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
if: always() && hashFiles('semgrep.sarif') != ''
with:
sarif_file: semgrep.sarif
category: semgrep
typescript-strict-checks:
name: TypeScript Security Checks
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: '1.2.14'
- name: Install dependencies
run: bun install --frozen-lockfile
- name: TypeScript strict null checks
run: |
# Create a strict tsconfig for security checks
cat > tsconfig.strict.json << 'EOF'
{
"extends": "./tsconfig.json",
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"alwaysStrict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true
}
}
EOF
# Run strict type checking
echo "## 🔒 TypeScript Strict Security Checks" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if bunx tsc --project tsconfig.strict.json --noEmit; then
echo "✅ All TypeScript strict checks passed!" >> $GITHUB_STEP_SUMMARY
else
echo "⚠️ Some TypeScript strict checks failed. Review the output above." >> $GITHUB_STEP_SUMMARY
fi
secrets-scan:
name: Secret Scanning
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Run Gitleaks
uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Check for hardcoded secrets
run: |
echo "## 🔐 Secret Scanning Report" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
# Pattern checks for common secret patterns
PATTERNS=(
"password\s*=\s*[\"'][^\"']+[\"']"
"api[_-]?key\s*=\s*[\"'][^\"']+[\"']"
"token\s*=\s*[\"'][^\"']+[\"']"
"secret\s*=\s*[\"'][^\"']+[\"']"
"private[_-]?key"
)
FOUND_ISSUES=false
for pattern in "${PATTERNS[@]}"; do
if grep -r -i -E "$pattern" --include="*.ts" --include="*.js" --include="*.json" --exclude-dir=node_modules --exclude-dir=dist . > /dev/null 2>&1; then
echo "⚠️ Potential secrets found matching pattern: $pattern" >> $GITHUB_STEP_SUMMARY
FOUND_ISSUES=true
fi
done
if [ "$FOUND_ISSUES" = false ]; then
echo "✅ No hardcoded secrets detected!" >> $GITHUB_STEP_SUMMARY
fi
dependency-review:
name: Dependency Review
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Dependency Review
uses: actions/dependency-review-action@v4
with:
fail-on-severity: moderate
deny-licenses: GPL-3.0, AGPL-3.0, LGPL-3.0
comment-summary-in-pr: always
security-report:
name: Security Report Summary
runs-on: ubuntu-latest
needs: [dependency-audit, license-check, semgrep, typescript-strict-checks, secrets-scan]
if: always()
steps:
- name: Generate Security Summary
run: |
echo "# 🛡️ Security Scan Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Check | Status |" >> $GITHUB_STEP_SUMMARY
echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY
echo "| Dependency Audit | ${{ needs.dependency-audit.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| License Compliance | ${{ needs.license-check.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Semgrep SAST | ${{ needs.semgrep.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| TypeScript Checks | ${{ needs.typescript-strict-checks.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Secrets Scan | ${{ needs.secrets-scan.result }} |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ needs.dependency-audit.result }}" != "success" ] || \
[ "${{ needs.license-check.result }}" != "success" ] || \
[ "${{ needs.semgrep.result }}" != "success" ] || \
[ "${{ needs.typescript-strict-checks.result }}" != "success" ] || \
[ "${{ needs.secrets-scan.result }}" != "success" ]; then
echo "⚠️ **Some security checks require attention**" >> $GITHUB_STEP_SUMMARY
else
echo "✅ **All security checks passed!**" >> $GITHUB_STEP_SUMMARY
fi