Dependency Scan #13
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Dependency Scan | |
on: | |
schedule: | |
- cron: '0 0 * * 1' # Run weekly on Monday at midnight UTC | |
workflow_dispatch: | |
inputs: | |
org_name: | |
description: 'GitHub Organization Name (optional)' | |
required: false | |
repo_list: | |
description: 'Comma-separated list of repositories (optional)' | |
required: false | |
log_level: | |
description: 'Logging level' | |
required: false | |
type: choice | |
options: | |
- INFO | |
- DEBUG | |
- WARNING | |
- ERROR | |
default: 'INFO' | |
vulnerability_threshold: | |
description: 'Number of vulnerabilities to trigger issue creation' | |
required: false | |
type: number | |
default: 10 | |
permissions: | |
security-events: read | |
contents: write # Needed for committing the report | |
issues: write # Needed for creating issues | |
jobs: | |
scan: | |
runs-on: ubuntu-latest | |
steps: | |
- name: Checkout repository | |
uses: actions/checkout@v4 | |
with: | |
repository: ${{ github.repository_owner }}/.github # Checkout .github repo | |
ref: main # Or your default branch | |
token: ${{ secrets.DEPENDENCY_SCAN_TOKEN }} # Use a PAT or GitHub App token with appropriate permissions! | |
- name: Set up Python | |
uses: actions/setup-python@v5 | |
with: | |
python-version: '3.10' | |
cache: 'pip' | |
cache-dependency-path: scripts/requirements.txt | |
- name: Install dependencies | |
run: | | |
python -m pip install --upgrade pip | |
pip install requests | |
- name: Generate timestamp | |
id: timestamp | |
run: echo "timestamp=$(date +%Y%m%d_%H%M%S)" >> $GITHUB_OUTPUT | |
- name: Run dependency scan | |
id: run-scan | |
env: | |
GITHUB_TOKEN: ${{ secrets.DEPENDENCY_SCAN_TOKEN }} | |
ORG_NAME: ${{ github.event.inputs.org_name || github.repository_owner }} | |
REPO_LIST: ${{ github.event.inputs.repo_list }} | |
REPORT_FILE: "vulnerability_report_${{ steps.timestamp.outputs.timestamp }}.csv" | |
run: | | |
COMMAND="python scripts/dependency_scanner.py \ | |
--token $GITHUB_TOKEN \ | |
--output $REPORT_FILE \ | |
--log-level ${{ github.event.inputs.log_level || 'INFO' }} \ | |
--max-workers ${{ github.event.inputs.max_workers || 10 }} \ | |
--max-retries 3" | |
if [[ -n "$ORG_NAME" ]]; then | |
COMMAND="$COMMAND --org $ORG_NAME" | |
fi | |
if [[ -n "$REPO_LIST" ]]; then | |
COMMAND="$COMMAND --repo-list $REPO_LIST" | |
fi | |
$COMMAND | |
echo "report_path=reports/$REPORT_FILE" >> $GITHUB_OUTPUT | |
- name: Check for No Repositories | |
id: check-repos | |
if: success() | |
run: | | |
if grep -q "__NO_REPOS__" ${{ steps.run-scan.outputs.report_path }}/../output.txt; then | |
echo "No repositories found in the organization. Exiting." | |
exit 1 | |
fi | |
- name: Process report statistics (inline) | |
id: stats | |
if: success() && steps.check-repos.outcome == 'success' | |
run: | | |
STATS=$(grep "__STATS_START__" ${{ steps.run-scan.outputs.report_path }}/../output.txt | sed 's/__STATS_START__//' | sed 's/__STATS_END__//') | |
echo "total_vulnerabilities=$(echo $STATS | cut -d',' -f1 | cut -d'=' -f2)" >> $GITHUB_OUTPUT | |
echo "processed_repos=$(echo $STATS | cut -d',' -f2 | cut -d'=' -f2)" >> $GITHUB_OUTPUT | |
echo "Total vulnerabilities found: $(echo $STATS | cut -d',' -f1 | cut -d'=' -f2)" | |
echo "Processed repos: $(echo $STATS | cut -d',' -f2 | cut -d'=' -f2)" | |
- name: Create summary issue (using github-script) | |
if: success() && steps.check-repos.outcome == 'success' && steps.stats.outputs.total_vulnerabilities > inputs.vulnerability_threshold | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
const stats = { | |
total: '${{ steps.stats.outputs.total_vulnerabilities }}', | |
processedRepos: '${{ steps.stats.outputs.processed_repos }}', | |
}; | |
const now = new Date(); | |
const formattedDate = now.toLocaleDateString('en-US', { | |
year: 'numeric', | |
month: 'long', | |
day: 'numeric' | |
}); | |
const body = ` | |
# Dependency Vulnerability Report Summary | |
Report generated on: ${now.toISOString()} | |
## Statistics | |
- Total vulnerabilities found: ${stats.total} | |
- Repositories processed: ${stats.processedRepos} | |
## Details | |
- Report artifact: [Download report](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}) | |
- Workflow run: [View details](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}) | |
## Configuration | |
- Log level: ${{ github.event.inputs.log_level || 'INFO' }} | |
- Vulnerability threshold: ${{ github.event.inputs.vulnerability_threshold || '10'}} | |
`; | |
await github.rest.issues.create({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
title: \`⚠️ Dependency Vulnerability Report - \${formattedDate}\`, | |
body: body, | |
labels: ['dependency-vulnerability', 'report'] | |
}); | |
- name: Commit and Push Report | |
if: success() && steps.check-repos.outcome == 'success' | |
uses: stefanzweifel/git-auto-commit-action@v5 | |
with: | |
commit_message: "Add dependency vulnerability report: ${{ steps.timestamp.outputs.timestamp }}" | |
repository: ./ # Commit to the root of the checked-out repo | |
file_pattern: reports/*.csv | |
commit_user_name: GitHub Actions | |
commit_user_email: [email protected] | |
commit_author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com> | |
push_options: '--force' | |
token: ${{ secrets.DEPENDENCY_SCAN_TOKEN }} | |
- name: Notify on failure | |
if: failure() | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
const body = ` | |
# 🚨 Dependency Vulnerability Report Generation Failed | |
Workflow run failed at ${new Date().toISOString()} | |
## Details | |
- Run ID: \`${context.runId}\` | |
- Trigger: ${context.eventName} | |
- Actor: @${context.actor} | |
## Links | |
- [View run details](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}) | |
- [View workflow file](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/blob/main/.github/workflows/dependency-scan.yml) | |
Please check the workflow logs for detailed error information. | |
`; | |
await github.rest.issues.create({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
title: '🚨 Dependency Vulnerability Report Generation Failed', | |
body: body, | |
labels: ['dependency-vulnerability', 'failed'] | |
}); | |
- name: Clean up | |
if: always() | |
run: | | |
echo "No clean up required." | |
concurrency: | |
group: ${{ github.workflow }}-${{ github.ref }} | |
cancel-in-progress: true | |